import 'mocha' ; import { expect } from 'chai' ; import { FunctionData } from 'js-yaml-loader!@/*' ; import { ILanguageSyntax } from '@/domain/ScriptCode' ; import { ScriptCompiler } from '@/application/Parser/Script/Compiler/ScriptCompiler' ; import { ISharedFunctionsParser } from '@/application/Parser/Script/Compiler/Function/ISharedFunctionsParser' ; import { ICompiledCode } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/ICompiledCode' ; import { IFunctionCallCompiler } from '@/application/Parser/Script/Compiler/Function/Call/Compiler/IFunctionCallCompiler' ; import { LanguageSyntaxStub } from '@tests/unit/stubs/LanguageSyntaxStub' ; import { ScriptDataStub } from '@tests/unit/stubs/ScriptDataStub' ; import { FunctionDataStub } from '@tests/unit/stubs/FunctionDataStub' ; import { FunctionCallCompilerStub } from '@tests/unit/stubs/FunctionCallCompilerStub' ; import { SharedFunctionsParserStub } from '@tests/unit/stubs/SharedFunctionsParserStub' ; import { SharedFunctionCollectionStub } from '@tests/unit/stubs/SharedFunctionCollectionStub' ; import { parseFunctionCalls } from '@/application/Parser/Script/Compiler/Function/Call/FunctionCallParser' ; import { FunctionCallDataStub } from '@tests/unit/stubs/FunctionCallDataStub' ; describe ( 'ScriptCompiler' , ( ) => { describe ( 'ctor' , ( ) => { it ( 'throws if syntax is undefined' , ( ) => { const expectedError = 'undefined syntax' ; const act = ( ) => new ScriptCompilerBuilder ( ) . withSomeFunctions ( ) . withSyntax ( undefined ) . build ( ) ; expect ( act ) . to . throw ( expectedError ) ; } ) ; } ) ; describe ( 'canCompile' , ( ) => { it ( 'throws if script is undefined' , ( ) => { const expectedError = 'undefined script' ; const argument = undefined ; const builder = new ScriptCompilerBuilder ( ) . withEmptyFunctions ( ) . build ( ) ; const act = ( ) => builder . canCompile ( argument ) ; expect ( act ) . to . throw ( expectedError ) ; } ) ; it ( 'returns true if "call" is defined' , ( ) => { const sut = new ScriptCompilerBuilder ( ) . withEmptyFunctions ( ) . build ( ) ; const script = ScriptDataStub . createWithCall ( ) ; const actual = sut . canCompile ( script ) ; expect ( actual ) . to . equal ( true ) ; } ) ; it ( 'returns false if "call" is undefined' , ( ) => { const sut = new ScriptCompilerBuilder ( ) . withEmptyFunctions ( ) . build ( ) ; const script = ScriptDataStub . createWithCode ( ) ; const actual = sut . canCompile ( script ) ; expect ( actual ) . to . equal ( false ) ; } ) ; } ) ; describe ( 'compile' , ( ) => { it ( 'throws if script is undefined' , ( ) => { const expectedError = 'undefined script' ; const argument = undefined ; const builder = new ScriptCompilerBuilder ( ) . withEmptyFunctions ( ) . build ( ) ; const act = ( ) => builder . compile ( argument ) ; expect ( act ) . to . throw ( expectedError ) ; } ) ; it ( 'returns code as expected' , ( ) => { const expected = { code : 'expected-code' , revertCode : 'expected-revert-code' , } ; const call = new FunctionCallDataStub ( ) ; const script = ScriptDataStub . createWithCall ( call ) ; const functions = [ FunctionDataStub . createWithCode ( ) . withName ( 'existing-func' ) ] ; const compiledFunctions = new SharedFunctionCollectionStub ( ) ; const functionParserMock = new SharedFunctionsParserStub ( ) ; functionParserMock . setup ( functions , compiledFunctions ) ; const callCompilerMock = new FunctionCallCompilerStub ( ) ; callCompilerMock . setup ( parseFunctionCalls ( call ) , compiledFunctions , expected ) ; const sut = new ScriptCompilerBuilder ( ) . withFunctions ( ... functions ) . withSharedFunctionsParser ( functionParserMock ) . withFunctionCallCompiler ( callCompilerMock ) . build ( ) ; const code = sut . compile ( script ) ; expect ( code . execute ) . to . equal ( expected . code ) ; expect ( code . revert ) . to . equal ( expected . revertCode ) ; } ) ; it ( 'creates with expected syntax' , ( ) => { let isUsed = false ; const syntax = { get commentDelimiters ( ) { isUsed = true ; return [ ] ; } , get commonCodeParts ( ) { isUsed = true ; return [ ] ; } , } ; const sut = new ScriptCompilerBuilder ( ) . withSomeFunctions ( ) . withSyntax ( syntax ) . build ( ) ; const scriptData = ScriptDataStub . createWithCall ( ) ; sut . compile ( scriptData ) ; expect ( isUsed ) . to . equal ( true ) ; } ) ; it ( 'rethrows error with script name' , ( ) => { const scriptName = 'scriptName' ; const innerError = 'innerError' ; const expectedError = `Script "${ scriptName }" ${ innerError }` ; const callCompiler = { compileCall : ( ) => { throw new Error ( innerError ) ; } , } ; const scriptData = ScriptDataStub . createWithCall ( ) . withName ( scriptName ) ; const sut = new ScriptCompilerBuilder ( ) . withSomeFunctions ( ) . withFunctionCallCompiler ( callCompiler ) . build ( ) ; const act = ( ) => sut . compile ( scriptData ) ; expect ( act ) . to . throw ( expectedError ) ; } ) ; it ( 'rethrows error from ScriptCode with script name' , ( ) => { const scriptName = 'scriptName' ; const expectedError = `Script "${ scriptName }" code is empty or undefined` ; const callCompiler = { compileCall : ( ) => ( { code : undefined , revertCode : undefined } ) , } ; const scriptData = ScriptDataStub . createWithCall ( ) . withName ( scriptName ) ; const sut = new ScriptCompilerBuilder ( ) . withSomeFunctions ( ) . withFunctionCallCompiler ( callCompiler ) . build ( ) ; const act = ( ) => sut . compile ( scriptData ) ; expect ( act ) . to . throw ( expectedError ) ; } ) ; } ) ; } ) ; class ScriptCompilerBuilder { private static createFunctions ( ... names ) { return names . map ( ( functionName ) => { return FunctionDataStub . createWithCode ( ) . withName ( functionName ) ; } ) ; } private functions : FunctionData [ ] ; private syntax : ILanguageSyntax = new LanguageSyntaxStub ( ) ; private sharedFunctionsParser : ISharedFunctionsParser = new SharedFunctionsParserStub ( ) ; private callCompiler : IFunctionCallCompiler = new FunctionCallCompilerStub ( ) ; public withFunctions ( ... functions ) { this . functions = functions ; return this ; } public withSomeFunctions ( ) { this . functions = ScriptCompilerBuilder . createFunctions ( 'test-function' ) ; return this ; } public withFunctionNames ( ... functionNames ) { this . functions = ScriptCompilerBuilder . createFunctions ( ... functionNames ) ; return this ; } public withEmptyFunctions ( ) { this . functions = [ ] ; return this ; } public withSyntax ( syntax ) { this . syntax = syntax ; return this ; } public withSharedFunctionsParser ( sharedFunctionsParser ) { this . sharedFunctionsParser = sharedFunctionsParser ; return this ; } public withFunctionCallCompiler ( callCompiler ) { this . callCompiler = callCompiler ; return this ; } public build ( ) { if ( ! this . functions ) { throw new Error ( 'Function behavior not defined' ) ; } return new ScriptCompiler ( this . functions , this . syntax , this . callCompiler , this . sharedFunctionsParser ) ; } }