﻿$engine JScript
$uname xUnitAddTestsDesc
$dname Добавить описания тестовых случае xUnitFor1C
$addin global
$addin stdlib
$addin stdcommands

stdlib.require('TextWindow.js', SelfScript);
stdlib.require('log4js.js', SelfScript);
global.connectGlobals(SelfScript);

var logger = addLogger(SelfScript.uniqueName, Log4js.Level.ERROR); //Log4js.Level.DEBUG

stdcommands.Frame.FileSave.addHandler(SelfScript.self, "onFileSave_AddTestCaseDescriptionsIntoText")

SelfScript.self['macrosВставить определения тестовых случаев xUnitFor1C'] = function() {
	addTestCaseDescriptionsIntoText()
	return true;
}

function addTestCaseDescriptionsIntoText() {
	var textWindow = GetTextWindow();
	if (!textWindow || textWindow.IsReadOnly()) {
		logger.debug('Не нашли текстовое окно или окно для чтения')
		return false;
	}
	
	var appender = new _AppenderTestCaseDescriptionsIntoText(textWindow)
	return appender.addTestCaseDescriptionsIntoText();
}

function onFileSave_AddTestCaseDescriptionsIntoText(cmd)
{
	if(cmd.isBefore)
	{
		var mdObj, av = windows.getActiveView()
		if(!av || !(mdObj = av.mdObj))
			return
		// Посмотрим, не внешний ли отчет/обработка сохраняется.
		if(mdObj.container != mdObj.container.masterContainer) {
		    try{
				addTestCaseDescriptionsIntoText();
		    }catch(e){}
		}
	}
}

function addLogger(loggerName, logLevel) {
	var logger = Log4js.getLogger(loggerName);
	var appender = new Log4js.BrowserConsoleAppender();
	appender.setLayout(new Log4js.PatternLayout(Log4js.PatternLayout.TTCC_CONVERSION_PATTERN));
	// следующий код нужен, чтобы при перезапуске скрипта без перезапуска Конфигуратора лог не задвоится
	appenders = [];
	appenders.push(appender);
	logger.onlog = new Log4js.CustomEvent();
	logger.onclear = new Log4js.CustomEvent();

	logger.setAppenders(appenders); // конец блока исключения задвоения лога
	//logger.addAppender(new Log4js.FileAppender("f:\\somefile.log"));
	logger.setLevel(logLevel);
	return logger;
}


var	TEST_CASE_DESC_FUNC_NAME = 'ПолучитьСписокТестов'
var TEST_CASE_DESC_FUNC_TEMPLATE = 
"Перем юТест;\n\n\
Функция ПолучитьСписокТестов(ЮнитТестирование) Экспорт\n\
\n\
	юТест = ЮнитТестирование;\n\
\n\
	ВсеТесты = Новый Массив;\n\
\n\
	Возврат ВсеТесты;\n\
\n\
КонецФункции\n"

_AppenderTestCaseDescriptionsIntoText = stdlib.Class.extend({
    construct : function (textWindow) {
        this.textWindow = textWindow;
		this.parseSources();
    },
	
	parseSources : function () {
		this.parser = snegopat.parseSources(this.textWindow.text());
    },

	addTestCaseDescriptionsIntoText : function() {
			
		var parser = this.parser //snegopat.parseSources(this.textWindow.text())
			logger.debug('parser.reStream ' + parser.reStream)
			
		var indexTestCaseDescFunc = parser.idxOfName(TEST_CASE_DESC_FUNC_NAME)
			logger.debug('parser.idxOfName("'+TEST_CASE_DESC_FUNC_NAME+'")=' + indexTestCaseDescFunc)
			
		var procNames = this.getTestCases(parser)
		if(!procNames){
			logger.debug('Не нашли определения тестовых случаев')
			return false
		}
		if (-1 == indexTestCaseDescFunc) {
			logger.debug('Не нашли функцию описания тестов "'+TEST_CASE_DESC_FUNC_NAME+'"')
			logger.debug('Вставляю шаблон функции описания тестов "'+TEST_CASE_DESC_FUNC_NAME+'"')
			
			this.textWindow.InsertLine(1, TEST_CASE_DESC_FUNC_TEMPLATE);
			
			parser = snegopat.parseSources(this.textWindow.text())
				logger.debug('parser.reStream ' + parser.reStream)
			
			indexTestCaseDescFunc = parser.idxOfName(TEST_CASE_DESC_FUNC_NAME)
				logger.debug('parser.idxOfName("'+TEST_CASE_DESC_FUNC_NAME+'")=' + indexTestCaseDescFunc)
		}		
		
		var data = this.getLineForInsertTestCaseDescriptions(parser, indexTestCaseDescFunc);
		
		var line = data.EndLine;
		if(line != -1){
			var arrayName = data.ArrayName
			this.deleteExistTestCaseDesc(this.textWindow, data.BeginLine, line, procNames, arrayName)
			
			parser = snegopat.parseSources(this.textWindow.text()) // т.к. изменили текст, заново парсим
				logger.debug('parser.reStream ' + parser.reStream)
			
			data = this.getLineForInsertTestCaseDescriptions(parser, indexTestCaseDescFunc);
			
			line = data.EndLine;
			if(line != -1){
				this.insertTestCaseDescIntoText(this.textWindow, line, procNames, arrayName);
			}
			
			return true;
		}

		logger.debug('Не удалось получить позицию для вставки описания тестовых случаев')
		return false;
	},

	deleteExistTestCaseDesc : function (textWindow, beginLine, endLine, procNames, arrayName) {
		var range = this.textWindow.Range(beginLine, 1, endLine)
		var text = range.GetText()
			logger.debug('Текст функции '+TEST_CASE_DESC_FUNC_NAME + '\n'+text)
		
		for(i=0; i < procNames.length; i++) {
			var reTestCaseDesc = new RegExp( '^\\s*'+arrayName+'\\.Добавить\\(\\s*"'+procNames[i]+'"\\s*\\)\\s*;\\s*$', "igm");
				logger.debug('Регулярное выражение шаблона замены '+reTestCaseDesc.source)
			text = text.replace(reTestCaseDesc,"");
		}
			logger.debug('Новый текст функции '+TEST_CASE_DESC_FUNC_NAME + '\n'+text)
		range.SetText(text)
	},

	insertTestCaseDescIntoText : function (textWindow, line, procNames, arrayName){
		logger.debug('arrayName <' + arrayName+'>')
		array = new Array(procNames.length);
		for(i=0; i < procNames.length; i++) {
			str = '\t'+arrayName+'.Добавить("'+procNames[i]+'");';
			array[i] = str;
				logger.debug('Вставляю строку - ' + str)
		}
		array[procNames.length] = "";
		this.textWindow.InsertLine(line, StringUtils.fromLines(array));
	},

	getLineForInsertTestCaseDescriptions : function (parser, indexTestCaseDescFunc){
		var resEndLine = -1;
		var resArrayName = ''
		var resBeginLine = -1
		var ret = { BeginLine: -1, EndLine: -1, ArrayName: '' };
		
		strForStream = this.addStringToTheLeft(indexTestCaseDescFunc, "0", 6-(""+indexTestCaseDescFunc).length)

			// ищу Функция ПолучитьСписокТестов(юТестирование) Экспорт ... ВсеТесты = Новый Массив; ... Возврат ВсеТесты; ...
		var reTestCaseDescriptionsFuncBody = new RegExp('FuNm('+strForStream+')LpNm(\\d{6})RpEx.*?(Nm(\\d{6})EqNwNm(\\d{6})).*?ReNm(\\d{6})', "g");
		
		lexemsArray = reTestCaseDescriptionsFuncBody.exec(parser.reStream)
		var findRightBody = lexemsArray && lexemsArray.length >= 6
		if(findRightBody)
	    {
	   			logger.debug(TEST_CASE_DESC_FUNC_NAME + ' (конец) lexemsArray.index ' + lexemsArray.index + ' lexemsArray.lastIndex ' + lexemsArray.lastIndex )
			
			var testCaseArrayName = parser.name(lexemsArray[4]) //ВсеТесты из строки "ВсеТесты = Новый Массив"
			var arrayKeywordName = parser.name(lexemsArray[5]) // Массив из этой же строки
			var returnValueName = parser.name(lexemsArray[6]) // Возврат ВсеТесты
				logger.debug('Наименование массива имен тестовых случаев "' + testCaseArrayName+'"')
				
			findRightBody = /массив/i.test(arrayKeywordName) && testCaseArrayName.toLowerCase() == returnValueName.toLowerCase()
			if (findRightBody) {
				
		        var lex = parser.lexem(parser.posToLexem(lexemsArray.index))
			       	logger.debug('Получили данные по началу функции "'+TEST_CASE_DESC_FUNC_NAME+';" , строка ' + lex.line)
				resBeginLine = lex.line
					
		        var lex = parser.lexem(parser.posToLexem(lexemsArray.lastIndex))
			       	logger.debug('Получили данные по строке "Возврат '+testCaseArrayName+';" , строка ' + lex.line)
					
				ret = { BeginLine: resBeginLine, EndLine: lex.line, ArrayName: testCaseArrayName }
			}
			else {
				logger.error('Должна быть строка "Массив". А получили "' + arrayKeywordName+'"')
				logger.error('Должна быть строка "'+testCaseArrayName+'". А получили "' + returnValueName+'"')
			}
	    }
		else {
			logger.error('Не нашли функцию '+TEST_CASE_DESC_FUNC_NAME+' с необходимой структурой');
			if(lexemsArray)
				logger.error('Количество элементов в массиве разбора лексем (lexemsArray.length) должно быть больше или равно 6, а получили "' + lexemsArray.length+'"')
		}
		return ret;
	},

	getTestCases : function (){
		var parser = this.parser;
		var res = new Array();
	    var reStream = parser.reStream
	    var rePublicProcedureWithoutParams = /(Pc)Nm\d{6}LpRpEx/g
	    var reTestCaseName = /^тест/i

	    while(rePublicProcedureWithoutParams.exec(reStream))
	    {
	        var lex = parser.lexem(parser.posToLexem(RegExp.index + 2))
	       	logger.debug('проверяю процедуру ' + lex.text + ", строка " + lex.line)
	        if (reTestCaseName.exec(lex.text)){
	        	logger.debug('\tнашли тестовый случай ' + lex.text + ", строка " + lex.line)
	        	res.push( lex.text )
	        }
	    }
	 		logger.debug('procNames.length ' + res.length)
	   return res.length ? res : null;
	},

	addStringToTheLeft : function (src, str, count) {
		res = src;
		for(i=0; i < count; i++)
			res = str + res;
		return res;
	}
}); // stdlib.Class.extend