Relax and improve code validation
Rework code validation to be bound to a context and not context-independent. It means that the generated code is validated based on different phases during the compilation. This is done by moving validation from `ScriptCode` constructor to a different callable function. It removes duplicate detection for function calls once a call is fully compiled, but still checks for duplicates inside each function body that has inline code. This allows for having duplicates in final scripts (thus relaxing the duplicate detection), e.g., when multiple calls to the same function is made. It fixes non-duplicates (when using common syntax) being misrepresented as duplicate lines. It improves the output of errors, such as printing valid lines, to give more context. This improvement also fixes empty line validation not showing the right empty lines in the error output. Empty line validation shows tabs and whitespaces more clearly. Finally, it adds more tests including tests for existing logic, such as singleton factories.
This commit is contained in:
@@ -11,8 +11,15 @@ import { FunctionCallArgumentCollectionStub } from '@tests/unit/shared/Stubs/Fun
|
||||
import { FunctionCallStub } from '@tests/unit/shared/Stubs/FunctionCallStub';
|
||||
import { ExpressionsCompilerStub } from '@tests/unit/shared/Stubs/ExpressionsCompilerStub';
|
||||
import { itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests';
|
||||
import { itIsSingleton } from '@tests/unit/shared/TestCases/SingletonTests';
|
||||
|
||||
describe('FunctionCallCompiler', () => {
|
||||
describe('instance', () => {
|
||||
itIsSingleton({
|
||||
getter: () => FunctionCallCompiler.instance,
|
||||
expectedType: FunctionCallCompiler,
|
||||
});
|
||||
});
|
||||
describe('compileCall', () => {
|
||||
describe('parameter validation', () => {
|
||||
describe('call', () => {
|
||||
@@ -172,7 +179,7 @@ describe('FunctionCallCompiler', () => {
|
||||
const args = new FunctionCallArgumentCollectionStub().withArguments(testCase.callArgs);
|
||||
const { code } = func.body;
|
||||
const expressionsCompilerMock = new ExpressionsCompilerStub()
|
||||
.setup({ givenCode: code.do, givenArgs: args, result: expected.execute })
|
||||
.setup({ givenCode: code.execute, givenArgs: args, result: expected.execute })
|
||||
.setup({ givenCode: code.revert, givenArgs: args, result: expected.revert });
|
||||
const sut = new MockableFunctionCallCompiler(expressionsCompilerMock);
|
||||
// act
|
||||
@@ -209,7 +216,7 @@ describe('FunctionCallCompiler', () => {
|
||||
const expressionsCompilerMock = new ExpressionsCompilerStub()
|
||||
.setupToReturnFunctionCode(firstFunction, firstFunctionCallArgs)
|
||||
.setupToReturnFunctionCode(secondFunction, secondFunctionCallArgs);
|
||||
const expectedExecute = `${firstFunction.body.code.do}\n${secondFunction.body.code.do}`;
|
||||
const expectedExecute = `${firstFunction.body.code.execute}\n${secondFunction.body.code.execute}`;
|
||||
const expectedRevert = `${firstFunction.body.code.revert}\n${secondFunction.body.code.revert}`;
|
||||
const functions = new SharedFunctionCollectionStub()
|
||||
.withFunction(firstFunction)
|
||||
@@ -244,7 +251,7 @@ describe('FunctionCallCompiler', () => {
|
||||
};
|
||||
const expressionsCompilerMock = new ExpressionsCompilerStub()
|
||||
.setup({
|
||||
givenCode: functions.deep.body.code.do,
|
||||
givenCode: functions.deep.body.code.execute,
|
||||
givenArgs: emptyArgs,
|
||||
result: expected.code,
|
||||
})
|
||||
@@ -312,7 +319,7 @@ describe('FunctionCallCompiler', () => {
|
||||
})
|
||||
// set-up compiling of deep, compiled argument should be sent
|
||||
.setup({
|
||||
givenCode: scenario.deep.getFunction().body.code.do,
|
||||
givenCode: scenario.deep.getFunction().body.code.execute,
|
||||
givenArgs: scenario.front.callArgs.expectedCallDeep(),
|
||||
result: expected.code,
|
||||
})
|
||||
@@ -407,7 +414,7 @@ describe('FunctionCallCompiler', () => {
|
||||
})
|
||||
// Compiling of third functions code with expected arguments
|
||||
.setup({
|
||||
givenCode: scenario.third.getFunction().body.code.do,
|
||||
givenCode: scenario.third.getFunction().body.code.execute,
|
||||
givenArgs: scenario.second.callArgs.expectedToThird(),
|
||||
result: expected.code,
|
||||
})
|
||||
@@ -491,7 +498,7 @@ describe('FunctionCallCompiler', () => {
|
||||
.setupToReturnFunctionCode(functions.call2.deep.getFunction(), emptyArgs);
|
||||
const sut = new MockableFunctionCallCompiler(expressionsCompilerMock);
|
||||
const expected = {
|
||||
code: `${functions.call1.deep.getFunction().body.code.do}\n${functions.call2.deep.getFunction().body.code.do}`,
|
||||
code: `${functions.call1.deep.getFunction().body.code.execute}\n${functions.call2.deep.getFunction().body.code.execute}`,
|
||||
revert: `${functions.call1.deep.getFunction().body.code.revert}\n${functions.call2.deep.getFunction().body.code.revert}`,
|
||||
};
|
||||
// act
|
||||
|
||||
@@ -12,174 +12,174 @@ import {
|
||||
} from '@tests/unit/shared/TestCases/AbsentTests';
|
||||
|
||||
describe('SharedFunction', () => {
|
||||
describe('name', () => {
|
||||
runForEachFactoryMethod((build) => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expected = 'expected-function-name';
|
||||
const builder = new SharedFunctionBuilder()
|
||||
.withName(expected);
|
||||
// act
|
||||
const sut = build(builder);
|
||||
// assert
|
||||
expect(sut.name).equal(expected);
|
||||
});
|
||||
it('throws when absent', () => {
|
||||
itEachAbsentStringValue((absentValue) => {
|
||||
// arrange
|
||||
const expectedError = 'missing function name';
|
||||
const builder = new SharedFunctionBuilder()
|
||||
.withName(absentValue);
|
||||
// act
|
||||
const act = () => build(builder);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('parameters', () => {
|
||||
runForEachFactoryMethod((build) => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expected = new FunctionParameterCollectionStub()
|
||||
.withParameterName('test-parameter');
|
||||
const builder = new SharedFunctionBuilder()
|
||||
.withParameters(expected);
|
||||
// act
|
||||
const sut = build(builder);
|
||||
// assert
|
||||
expect(sut.parameters).equal(expected);
|
||||
});
|
||||
describe('throws if missing', () => {
|
||||
itEachAbsentObjectValue((absentValue) => {
|
||||
// arrange
|
||||
const expectedError = 'missing parameters';
|
||||
const parameters = absentValue;
|
||||
const builder = new SharedFunctionBuilder()
|
||||
.withParameters(parameters);
|
||||
// act
|
||||
const act = () => build(builder);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('body', () => {
|
||||
describe('createFunctionWithInlineCode', () => {
|
||||
describe('code', () => {
|
||||
describe('SharedFunction', () => {
|
||||
describe('name', () => {
|
||||
runForEachFactoryMethod((build) => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expected = 'expected-code';
|
||||
const expected = 'expected-function-name';
|
||||
const builder = new SharedFunctionBuilder()
|
||||
.withName(expected);
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withCode(expected)
|
||||
.createFunctionWithInlineCode();
|
||||
const sut = build(builder);
|
||||
// assert
|
||||
expect(sut.body.code.do).equal(expected);
|
||||
expect(sut.name).equal(expected);
|
||||
});
|
||||
describe('throws if absent', () => {
|
||||
it('throws when absent', () => {
|
||||
itEachAbsentStringValue((absentValue) => {
|
||||
// arrange
|
||||
const functionName = 'expected-function-name';
|
||||
const expectedError = `undefined code in function "${functionName}"`;
|
||||
const invalidValue = absentValue;
|
||||
const expectedError = 'missing function name';
|
||||
const builder = new SharedFunctionBuilder()
|
||||
.withName(absentValue);
|
||||
// act
|
||||
const act = () => new SharedFunctionBuilder()
|
||||
.withName(functionName)
|
||||
.withCode(invalidValue)
|
||||
.createFunctionWithInlineCode();
|
||||
const act = () => build(builder);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('revertCode', () => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const testData = [
|
||||
'expected-revert-code',
|
||||
...AbsentStringTestCases.map((testCase) => testCase.absentValue),
|
||||
];
|
||||
for (const data of testData) {
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withRevertCode(data)
|
||||
.createFunctionWithInlineCode();
|
||||
// assert
|
||||
expect(sut.body.code.revert).equal(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('sets type as expected', () => {
|
||||
// arrange
|
||||
const expectedType = FunctionBodyType.Code;
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.createFunctionWithInlineCode();
|
||||
// assert
|
||||
expect(sut.body.type).equal(expectedType);
|
||||
});
|
||||
it('calls are undefined', () => {
|
||||
// arrange
|
||||
const expectedCalls = undefined;
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.createFunctionWithInlineCode();
|
||||
// assert
|
||||
expect(sut.body.calls).equal(expectedCalls);
|
||||
});
|
||||
});
|
||||
describe('createCallerFunction', () => {
|
||||
describe('callSequence', () => {
|
||||
describe('parameters', () => {
|
||||
runForEachFactoryMethod((build) => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expected = [
|
||||
new FunctionCallStub().withFunctionName('firstFunction'),
|
||||
new FunctionCallStub().withFunctionName('secondFunction'),
|
||||
];
|
||||
const expected = new FunctionParameterCollectionStub()
|
||||
.withParameterName('test-parameter');
|
||||
const builder = new SharedFunctionBuilder()
|
||||
.withParameters(expected);
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withCallSequence(expected)
|
||||
.createCallerFunction();
|
||||
const sut = build(builder);
|
||||
// assert
|
||||
expect(sut.body.calls).equal(expected);
|
||||
expect(sut.parameters).equal(expected);
|
||||
});
|
||||
describe('throws if missing', () => {
|
||||
itEachAbsentCollectionValue((absentValue) => {
|
||||
itEachAbsentObjectValue((absentValue) => {
|
||||
// arrange
|
||||
const functionName = 'invalidFunction';
|
||||
const callSequence = absentValue;
|
||||
const expectedError = `missing call sequence in function "${functionName}"`;
|
||||
const expectedError = 'missing parameters';
|
||||
const parameters = absentValue;
|
||||
const builder = new SharedFunctionBuilder()
|
||||
.withParameters(parameters);
|
||||
// act
|
||||
const act = () => new SharedFunctionBuilder()
|
||||
.withName(functionName)
|
||||
.withCallSequence(callSequence)
|
||||
.createCallerFunction();
|
||||
const act = () => build(builder);
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('sets type as expected', () => {
|
||||
});
|
||||
});
|
||||
describe('createFunctionWithInlineCode', () => {
|
||||
describe('code', () => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expectedType = FunctionBodyType.Calls;
|
||||
const expected = 'expected-code';
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.createCallerFunction();
|
||||
.withCode(expected)
|
||||
.createFunctionWithInlineCode();
|
||||
// assert
|
||||
expect(sut.body.type).equal(expectedType);
|
||||
expect(sut.body.code.execute).equal(expected);
|
||||
});
|
||||
it('code is undefined', () => {
|
||||
describe('throws if absent', () => {
|
||||
itEachAbsentStringValue((absentValue) => {
|
||||
// arrange
|
||||
const functionName = 'expected-function-name';
|
||||
const expectedError = `undefined code in function "${functionName}"`;
|
||||
const invalidValue = absentValue;
|
||||
// act
|
||||
const act = () => new SharedFunctionBuilder()
|
||||
.withName(functionName)
|
||||
.withCode(invalidValue)
|
||||
.createFunctionWithInlineCode();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
describe('revertCode', () => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expectedCode = undefined;
|
||||
const testData = [
|
||||
'expected-revert-code',
|
||||
...AbsentStringTestCases.map((testCase) => testCase.absentValue),
|
||||
];
|
||||
for (const data of testData) {
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withRevertCode(data)
|
||||
.createFunctionWithInlineCode();
|
||||
// assert
|
||||
expect(sut.body.code.revert).equal(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
it('sets type as expected', () => {
|
||||
// arrange
|
||||
const expectedType = FunctionBodyType.Code;
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.createFunctionWithInlineCode();
|
||||
// assert
|
||||
expect(sut.body.type).equal(expectedType);
|
||||
});
|
||||
it('calls are undefined', () => {
|
||||
// arrange
|
||||
const expectedCalls = undefined;
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.createFunctionWithInlineCode();
|
||||
// assert
|
||||
expect(sut.body.calls).equal(expectedCalls);
|
||||
});
|
||||
});
|
||||
describe('createCallerFunction', () => {
|
||||
describe('callSequence', () => {
|
||||
it('sets as expected', () => {
|
||||
// arrange
|
||||
const expected = [
|
||||
new FunctionCallStub().withFunctionName('firstFunction'),
|
||||
new FunctionCallStub().withFunctionName('secondFunction'),
|
||||
];
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.withCallSequence(expected)
|
||||
.createCallerFunction();
|
||||
// assert
|
||||
expect(sut.body.code).equal(expectedCode);
|
||||
expect(sut.body.calls).equal(expected);
|
||||
});
|
||||
describe('throws if missing', () => {
|
||||
itEachAbsentCollectionValue((absentValue) => {
|
||||
// arrange
|
||||
const functionName = 'invalidFunction';
|
||||
const callSequence = absentValue;
|
||||
const expectedError = `missing call sequence in function "${functionName}"`;
|
||||
// act
|
||||
const act = () => new SharedFunctionBuilder()
|
||||
.withName(functionName)
|
||||
.withCallSequence(callSequence)
|
||||
.createCallerFunction();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
});
|
||||
it('sets type as expected', () => {
|
||||
// arrange
|
||||
const expectedType = FunctionBodyType.Calls;
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.createCallerFunction();
|
||||
// assert
|
||||
expect(sut.body.type).equal(expectedType);
|
||||
});
|
||||
it('code is undefined', () => {
|
||||
// arrange
|
||||
const expectedCode = undefined;
|
||||
// act
|
||||
const sut = new SharedFunctionBuilder()
|
||||
.createCallerFunction();
|
||||
// assert
|
||||
expect(sut.body.code).equal(expectedCode);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,18 +8,46 @@ import { ParameterDefinitionDataStub } from '@tests/unit/shared/Stubs/ParameterD
|
||||
import { FunctionParameter } from '@/application/Parser/Script/Compiler/Function/Parameter/FunctionParameter';
|
||||
import { FunctionCallDataStub } from '@tests/unit/shared/Stubs/FunctionCallDataStub';
|
||||
import { itEachAbsentCollectionValue, itEachAbsentObjectValue } from '@tests/unit/shared/TestCases/AbsentTests';
|
||||
import { ILanguageSyntax } from '@/application/Parser/Script/Validation/Syntax/ILanguageSyntax';
|
||||
import { LanguageSyntaxStub } from '@tests/unit/shared/Stubs/LanguageSyntaxStub';
|
||||
import { itIsSingleton } from '@tests/unit/shared/TestCases/SingletonTests';
|
||||
import { CodeValidatorStub } from '@tests/unit/shared/Stubs/CodeValidatorStub';
|
||||
import { ICodeValidator } from '@/application/Parser/Script/Validation/ICodeValidator';
|
||||
import { NoEmptyLines } from '@/application/Parser/Script/Validation/Rules/NoEmptyLines';
|
||||
import { NoDuplicatedLines } from '@/application/Parser/Script/Validation/Rules/NoDuplicatedLines';
|
||||
|
||||
describe('SharedFunctionsParser', () => {
|
||||
describe('instance', () => {
|
||||
itIsSingleton({
|
||||
getter: () => SharedFunctionsParser.instance,
|
||||
expectedType: SharedFunctionsParser,
|
||||
});
|
||||
});
|
||||
describe('parseFunctions', () => {
|
||||
describe('throws if syntax is missing', () => {
|
||||
itEachAbsentObjectValue((absentValue) => {
|
||||
// arrange
|
||||
const expectedError = 'missing syntax';
|
||||
const syntax = absentValue;
|
||||
// act
|
||||
const act = () => new ParseFunctionsCallerWithDefaults()
|
||||
.withSyntax(syntax)
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
});
|
||||
describe('validates functions', () => {
|
||||
describe('throws if one of the functions is undefined', () => {
|
||||
itEachAbsentObjectValue((absentValue) => {
|
||||
// arrange
|
||||
const expectedError = 'some functions are undefined';
|
||||
const functions = [FunctionDataStub.createWithCode(), absentValue];
|
||||
const sut = new SharedFunctionsParser();
|
||||
const sut = new ParseFunctionsCallerWithDefaults();
|
||||
// act
|
||||
const act = () => sut.parseFunctions(functions);
|
||||
const act = () => sut
|
||||
.withFunctions(functions)
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
@@ -32,9 +60,10 @@ describe('SharedFunctionsParser', () => {
|
||||
FunctionDataStub.createWithCode().withName(name),
|
||||
FunctionDataStub.createWithCode().withName(name),
|
||||
];
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const act = () => sut.parseFunctions(functions);
|
||||
const act = () => new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions(functions)
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
@@ -47,9 +76,10 @@ describe('SharedFunctionsParser', () => {
|
||||
FunctionDataStub.createWithoutCallOrCodes().withName('func-1').withCode(code),
|
||||
FunctionDataStub.createWithoutCallOrCodes().withName('func-2').withCode(code),
|
||||
];
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const act = () => sut.parseFunctions(functions);
|
||||
const act = () => new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions(functions)
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
@@ -63,9 +93,10 @@ describe('SharedFunctionsParser', () => {
|
||||
FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName('func-2').withCode('code-2').withRevertCode(revertCode),
|
||||
];
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const act = () => sut.parseFunctions(functions);
|
||||
const act = () => new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions(functions)
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
@@ -79,9 +110,10 @@ describe('SharedFunctionsParser', () => {
|
||||
.withName(functionName)
|
||||
.withCode('code')
|
||||
.withMockCall();
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const act = () => sut.parseFunctions([invalidFunction]);
|
||||
const act = () => new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions([invalidFunction])
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
@@ -91,9 +123,10 @@ describe('SharedFunctionsParser', () => {
|
||||
const expectedError = `neither "code" or "call" is defined in "${functionName}"`;
|
||||
const invalidFunction = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName(functionName);
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const act = () => sut.parseFunctions([invalidFunction]);
|
||||
const act = () => new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions([invalidFunction])
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
@@ -116,14 +149,34 @@ describe('SharedFunctionsParser', () => {
|
||||
.createWithCall()
|
||||
.withParametersObject(testCase.invalidType as never);
|
||||
const expectedError = `parameters must be an array of objects in function(s) "${func.name}"`;
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const act = () => sut.parseFunctions([func]);
|
||||
const act = () => new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions([func])
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
});
|
||||
}
|
||||
});
|
||||
it('validates function code as expected when code is defined', () => {
|
||||
// arrange
|
||||
const expectedRules = [NoEmptyLines, NoDuplicatedLines];
|
||||
const functionData = FunctionDataStub
|
||||
.createWithCode()
|
||||
.withCode('expected code to be validated')
|
||||
.withRevertCode('expected revert code to be validated');
|
||||
const validator = new CodeValidatorStub();
|
||||
// act
|
||||
new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions([functionData])
|
||||
.withValidator(validator)
|
||||
.parseFunctions();
|
||||
// assert
|
||||
validator.assertHistory({
|
||||
validatedCodes: [functionData.code, functionData.revertCode],
|
||||
rules: expectedRules,
|
||||
});
|
||||
});
|
||||
it('rethrows including function name when FunctionParameter throws', () => {
|
||||
// arrange
|
||||
const invalidParameterName = 'invalid function p@r4meter name';
|
||||
@@ -139,8 +192,9 @@ describe('SharedFunctionsParser', () => {
|
||||
.withParameters(new ParameterDefinitionDataStub().withName(invalidParameterName));
|
||||
|
||||
// act
|
||||
const sut = new SharedFunctionsParser();
|
||||
const act = () => sut.parseFunctions([functionData]);
|
||||
const act = () => new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions([functionData])
|
||||
.parseFunctions();
|
||||
|
||||
// assert
|
||||
expect(act).to.throw(expectedError);
|
||||
@@ -148,10 +202,10 @@ describe('SharedFunctionsParser', () => {
|
||||
});
|
||||
describe('given empty functions, returns empty collection', () => {
|
||||
itEachAbsentCollectionValue((absentValue) => {
|
||||
// arrange
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const actual = sut.parseFunctions(absentValue);
|
||||
const actual = new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions(absentValue)
|
||||
.parseFunctions();
|
||||
// assert
|
||||
expect(actual).to.not.equal(undefined);
|
||||
});
|
||||
@@ -169,9 +223,10 @@ describe('SharedFunctionsParser', () => {
|
||||
new ParameterDefinitionDataStub().withName('expectedParameter').withOptionality(true),
|
||||
new ParameterDefinitionDataStub().withName('expectedParameter2').withOptionality(false),
|
||||
);
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const collection = sut.parseFunctions([expected]);
|
||||
const collection = new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions([expected])
|
||||
.parseFunctions();
|
||||
// expect
|
||||
const actual = collection.getFunctionByName(name);
|
||||
expectEqualName(expected, actual);
|
||||
@@ -188,9 +243,10 @@ describe('SharedFunctionsParser', () => {
|
||||
const data = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName('caller-function')
|
||||
.withCall(call);
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const collection = sut.parseFunctions([data]);
|
||||
const collection = new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions([data])
|
||||
.parseFunctions();
|
||||
// expect
|
||||
const actual = collection.getFunctionByName(data.name);
|
||||
expectEqualName(data, actual);
|
||||
@@ -211,9 +267,10 @@ describe('SharedFunctionsParser', () => {
|
||||
const caller2 = FunctionDataStub.createWithoutCallOrCodes()
|
||||
.withName('caller-function-2')
|
||||
.withCall([call1, call2]);
|
||||
const sut = new SharedFunctionsParser();
|
||||
// act
|
||||
const collection = sut.parseFunctions([caller1, caller2]);
|
||||
const collection = new ParseFunctionsCallerWithDefaults()
|
||||
.withFunctions([caller1, caller2])
|
||||
.parseFunctions();
|
||||
// expect
|
||||
const compiledCaller1 = collection.getFunctionByName(caller1.name);
|
||||
expectEqualName(caller1, compiledCaller1);
|
||||
@@ -228,6 +285,34 @@ describe('SharedFunctionsParser', () => {
|
||||
});
|
||||
});
|
||||
|
||||
class ParseFunctionsCallerWithDefaults {
|
||||
private syntax: ILanguageSyntax = new LanguageSyntaxStub();
|
||||
|
||||
private codeValidator: ICodeValidator = new CodeValidatorStub();
|
||||
|
||||
private functions: readonly FunctionData[] = [FunctionDataStub.createWithCode()];
|
||||
|
||||
public withSyntax(syntax: ILanguageSyntax) {
|
||||
this.syntax = syntax;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withValidator(codeValidator: ICodeValidator) {
|
||||
this.codeValidator = codeValidator;
|
||||
return this;
|
||||
}
|
||||
|
||||
public withFunctions(functions: readonly FunctionData[]) {
|
||||
this.functions = functions;
|
||||
return this;
|
||||
}
|
||||
|
||||
public parseFunctions() {
|
||||
const sut = new SharedFunctionsParser(this.codeValidator);
|
||||
return sut.parseFunctions(this.functions, this.syntax);
|
||||
}
|
||||
}
|
||||
|
||||
function expectEqualName(expected: FunctionDataStub, actual: ISharedFunction): void {
|
||||
expect(actual.name).to.equal(expected.name);
|
||||
}
|
||||
@@ -250,7 +335,7 @@ function expectEqualFunctionWithInlineCode(
|
||||
): void {
|
||||
expect(actual.body, `function "${actual.name}" has no body`);
|
||||
expect(actual.body.code, `function "${actual.name}" has no code`);
|
||||
expect(actual.body.code.do).to.equal(expected.code);
|
||||
expect(actual.body.code.execute).to.equal(expected.code);
|
||||
expect(actual.body.code.revert).to.equal(expected.revertCode);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user