Files
privacy.sexy/tests/unit/application/Parser/Script/Compiler/Expressions/ExpressionsCompiler.spec.ts
undergroundwires 6a89c6224b Add optionality for parameters
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.
2021-09-02 18:59:25 +01:00

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);
}
}