This commit allows for parameters that does not require any arguments to be provided in function calls. It changes collection syntax where parameters are list of objects instead of primitive strings. A parameter has now 'name' and 'optional' properties. 'name' is required and used in same way as older strings as parameter definitions. 'Optional' property is optional, 'false' is the default behavior if undefined. It also adds additional validation to restrict parameter names to alphanumeric strings to have a clear syntax in expressions.
166 lines
7.9 KiB
TypeScript
166 lines
7.9 KiB
TypeScript
import 'mocha';
|
|
import { expect } from 'chai';
|
|
import { ExpressionsCompiler } from '@/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler';
|
|
import { IExpressionParser } from '@/application/Parser/Script/Compiler/Expressions/Parser/IExpressionParser';
|
|
import { ExpressionStub } from '@tests/unit/stubs/ExpressionStub';
|
|
import { ExpressionParserStub } from '@tests/unit/stubs/ExpressionParserStub';
|
|
import { FunctionCallArgumentCollectionStub } from '@tests/unit/stubs/FunctionCallArgumentCollectionStub';
|
|
|
|
describe('ExpressionsCompiler', () => {
|
|
describe('compileExpressions', () => {
|
|
describe('combines expressions as expected', () => {
|
|
// arrange
|
|
const code = 'part1 {{ a }} part2 {{ b }} part3';
|
|
const testCases = [
|
|
{
|
|
name: 'with ordered expressions',
|
|
expressions: [
|
|
new ExpressionStub().withPosition(6, 13).withEvaluatedResult('a'),
|
|
new ExpressionStub().withPosition(20, 27).withEvaluatedResult('b'),
|
|
],
|
|
expected: 'part1 a part2 b part3',
|
|
},
|
|
{
|
|
name: 'unordered expressions',
|
|
expressions: [
|
|
new ExpressionStub().withPosition(20, 27).withEvaluatedResult('b'),
|
|
new ExpressionStub().withPosition(6, 13).withEvaluatedResult('a'),
|
|
],
|
|
expected: 'part1 a part2 b part3',
|
|
},
|
|
{
|
|
name: 'with an optional expected argument that is not provided',
|
|
expressions: [
|
|
new ExpressionStub().withPosition(6, 13).withEvaluatedResult('a')
|
|
.withParameterNames(['optionalParameter'], true),
|
|
new ExpressionStub().withPosition(20, 27).withEvaluatedResult('b')
|
|
.withParameterNames(['optionalParameterTwo'], true),
|
|
],
|
|
expected: 'part1 a part2 b part3',
|
|
},
|
|
{
|
|
name: 'with no expressions',
|
|
expressions: [],
|
|
expected: code,
|
|
},
|
|
];
|
|
for (const testCase of testCases) {
|
|
it(testCase.name, () => {
|
|
const expressionParserMock = new ExpressionParserStub()
|
|
.withResult(testCase.expressions);
|
|
const args = new FunctionCallArgumentCollectionStub();
|
|
const sut = new MockableExpressionsCompiler(expressionParserMock);
|
|
// act
|
|
const actual = sut.compileExpressions(code, args);
|
|
// assert
|
|
expect(actual).to.equal(testCase.expected);
|
|
});
|
|
}
|
|
});
|
|
describe('arguments', () => {
|
|
it('passes arguments to expressions as expected', () => {
|
|
// arrange
|
|
const expected = new FunctionCallArgumentCollectionStub()
|
|
.withArgument('test-arg', 'test-value');
|
|
const code = 'non-important';
|
|
const expressions = [
|
|
new ExpressionStub(),
|
|
new ExpressionStub(),
|
|
];
|
|
const expressionParserMock = new ExpressionParserStub()
|
|
.withResult(expressions);
|
|
const sut = new MockableExpressionsCompiler(expressionParserMock);
|
|
// act
|
|
sut.compileExpressions(code, expected);
|
|
// assert
|
|
expect(expressions[0].callHistory).to.have.lengthOf(1);
|
|
expect(expressions[0].callHistory[0]).to.equal(expected);
|
|
expect(expressions[1].callHistory).to.have.lengthOf(1);
|
|
expect(expressions[1].callHistory[0]).to.equal(expected);
|
|
});
|
|
it('throws if arguments is undefined', () => {
|
|
// arrange
|
|
const expectedError = 'undefined args, send empty collection instead';
|
|
const args = undefined;
|
|
const expressionParserMock = new ExpressionParserStub();
|
|
const sut = new MockableExpressionsCompiler(expressionParserMock);
|
|
// act
|
|
const act = () => sut.compileExpressions('code', args);
|
|
// assert
|
|
expect(act).to.throw(expectedError);
|
|
});
|
|
});
|
|
describe('throws when expected argument is not provided but used in code', () => {
|
|
// arrange
|
|
const testCases = [
|
|
{
|
|
name: 'empty parameters',
|
|
expressions: [
|
|
new ExpressionStub().withParameterNames(['parameter'], false),
|
|
],
|
|
args: new FunctionCallArgumentCollectionStub(),
|
|
expectedError: 'parameter value(s) not provided for: "parameter" but used in code',
|
|
},
|
|
{
|
|
name: 'unnecessary parameter is provided',
|
|
expressions: [
|
|
new ExpressionStub().withParameterNames(['parameter'], false),
|
|
],
|
|
args: new FunctionCallArgumentCollectionStub()
|
|
.withArgument('unnecessaryParameter', 'unnecessaryValue'),
|
|
expectedError: 'parameter value(s) not provided for: "parameter" but used in code',
|
|
},
|
|
{
|
|
name: 'multiple values are not provided',
|
|
expressions: [
|
|
new ExpressionStub().withParameterNames(['parameter1'], false),
|
|
new ExpressionStub().withParameterNames(['parameter2', 'parameter3'], false),
|
|
],
|
|
args: new FunctionCallArgumentCollectionStub(),
|
|
expectedError: 'parameter value(s) not provided for: "parameter1", "parameter2", "parameter3" but used in code',
|
|
},
|
|
{
|
|
name: 'some values are provided',
|
|
expressions: [
|
|
new ExpressionStub().withParameterNames(['parameter1'], false),
|
|
new ExpressionStub().withParameterNames(['parameter2', 'parameter3'], false),
|
|
],
|
|
args: new FunctionCallArgumentCollectionStub()
|
|
.withArgument('parameter2', 'value'),
|
|
expectedError: 'parameter value(s) not provided for: "parameter1", "parameter3" but used in code',
|
|
},
|
|
];
|
|
for (const testCase of testCases) {
|
|
it(testCase.name, () => {
|
|
const code = 'non-important-code';
|
|
const expressionParserMock = new ExpressionParserStub()
|
|
.withResult(testCase.expressions);
|
|
const sut = new MockableExpressionsCompiler(expressionParserMock);
|
|
// act
|
|
const act = () => sut.compileExpressions(code, testCase.args);
|
|
// assert
|
|
expect(act).to.throw(testCase.expectedError);
|
|
});
|
|
}
|
|
});
|
|
it('calls parser with expected code', () => {
|
|
// arrange
|
|
const expected = 'expected-code';
|
|
const expressionParserMock = new ExpressionParserStub();
|
|
const sut = new MockableExpressionsCompiler(expressionParserMock);
|
|
const args = new FunctionCallArgumentCollectionStub();
|
|
// act
|
|
sut.compileExpressions(expected, args);
|
|
// assert
|
|
expect(expressionParserMock.callHistory).to.have.lengthOf(1);
|
|
expect(expressionParserMock.callHistory[0]).to.equal(expected);
|
|
});
|
|
});
|
|
});
|
|
|
|
class MockableExpressionsCompiler extends ExpressionsCompiler {
|
|
constructor(extractor: IExpressionParser) {
|
|
super(extractor);
|
|
}
|
|
}
|