add support for shared functions #41

This commit is contained in:
undergroundwires
2020-09-17 21:46:20 +01:00
parent 1a9db31c77
commit 8ce06facbd
27 changed files with 1533 additions and 354 deletions

View File

@@ -5,22 +5,41 @@ import 'mocha';
import { expect } from 'chai';
import { parseCategory } from '@/application/Parser/CategoryParser';
import { RecommendationLevel } from '@/domain/RecommendationLevel';
import { ScriptCompilerStub } from '../../stubs/ScriptCompilerStub';
describe('ApplicationParser', () => {
describe('parseApplication', () => {
it('can parse current application file', () => {
expect(() => parseApplication(applicationFile)).to.not.throw();
// act
const act = () => parseApplication(applicationFile);
// assert
expect(act).to.not.throw();
});
it('throws when undefined', () => {
expect(() => parseApplication(undefined)).to.throw('application is null or undefined');
// arrange
const expectedError = 'application is null or undefined';
// act
const act = () => parseApplication(undefined);
// assert
expect(act).to.throw(expectedError);
});
it('throws when undefined actions', () => {
const sut: ApplicationYaml = { actions: undefined };
expect(() => parseApplication(sut)).to.throw('application does not define any action');
// arrange
const sut: ApplicationYaml = { actions: undefined, functions: undefined };
const expectedError = 'application does not define any action';
// act
const act = () => parseApplication(sut);
// assert
expect(act).to.throw(expectedError);
});
it('throws when has no actions', () => {
const sut: ApplicationYaml = { actions: [] };
expect(() => parseApplication(sut)).to.throw('application does not define any action');
// arrange
const sut: ApplicationYaml = { actions: [], functions: undefined };
const expectedError = 'application does not define any action';
// act
const act = () => parseApplication(sut);
// assert
expect(act).to.throw(expectedError);
});
describe('information', () => {
it('returns expected repository version', () => {
@@ -28,7 +47,7 @@ describe('ApplicationParser', () => {
const expected = 'expected-version';
const env = getProcessEnvironmentStub();
env.VUE_APP_VERSION = expected;
const sut: ApplicationYaml = { actions: [ getTestCategory() ] };
const sut: ApplicationYaml = { actions: [ getTestCategory() ], functions: undefined };
// act
const actual = parseApplication(sut, env).info.version;
// assert
@@ -39,7 +58,7 @@ describe('ApplicationParser', () => {
const expected = 'https://expected-repository.url';
const env = getProcessEnvironmentStub();
env.VUE_APP_REPOSITORY_URL = expected;
const sut: ApplicationYaml = { actions: [ getTestCategory() ] };
const sut: ApplicationYaml = { actions: [ getTestCategory() ], functions: undefined };
// act
const actual = parseApplication(sut, env).info.repositoryUrl;
// assert
@@ -50,7 +69,7 @@ describe('ApplicationParser', () => {
const expected = 'expected-app-name';
const env = getProcessEnvironmentStub();
env.VUE_APP_NAME = expected;
const sut: ApplicationYaml = { actions: [ getTestCategory() ] };
const sut: ApplicationYaml = { actions: [ getTestCategory() ], functions: undefined };
// act
const actual = parseApplication(sut, env).info.name;
// assert
@@ -61,7 +80,7 @@ describe('ApplicationParser', () => {
const expected = 'https://expected.sexy';
const env = getProcessEnvironmentStub();
env.VUE_APP_HOMEPAGE_URL = expected;
const sut: ApplicationYaml = { actions: [ getTestCategory() ] };
const sut: ApplicationYaml = { actions: [ getTestCategory() ], functions: undefined };
// act
const actual = parseApplication(sut, env).info.homepage;
// assert
@@ -71,8 +90,9 @@ describe('ApplicationParser', () => {
it('parses actions', () => {
// arrange
const actions = [ getTestCategory('test1'), getTestCategory('test2') ];
const expected = [ parseCategory(actions[0]), parseCategory(actions[1]) ];
const sut: ApplicationYaml = { actions };
const compiler = new ScriptCompilerStub();
const expected = [ parseCategory(actions[0], compiler), parseCategory(actions[1], compiler) ];
const sut: ApplicationYaml = { actions, functions: undefined };
// act
const actual = parseApplication(sut).actions;
// assert
@@ -103,6 +123,7 @@ function getTestScript(scriptName: string, level: RecommendationLevel = Recommen
code: 'script code',
revertCode: 'revert code',
recommend: RecommendationLevel[level].toLowerCase(),
call: undefined,
};
}

View File

@@ -4,88 +4,150 @@ import { parseCategory } from '@/application/Parser/CategoryParser';
import { YamlCategory, CategoryOrScript, YamlScript } from 'js-yaml-loader!./application.yaml';
import { parseScript } from '@/application/Parser/ScriptParser';
import { parseDocUrls } from '@/application/Parser/DocumentationParser';
import { RecommendationLevel } from '@/domain/RecommendationLevel';
import { ScriptCompilerStub } from '../../stubs/ScriptCompilerStub';
import { YamlScriptStub } from '../../stubs/YamlScriptStub';
describe('CategoryParser', () => {
describe('parseCategory', () => {
it('throws when undefined', () => {
expect(() => parseCategory(undefined)).to.throw('category is null or undefined');
describe('invalid category', () => {
it('throws when undefined', () => {
// arrange
const expectedMessage = 'category is null or undefined';
const category = undefined;
const compiler = new ScriptCompilerStub();
// act
const act = () => parseCategory(category, compiler);
// assert
expect(act).to.throw(expectedMessage);
});
it('throws when children are empty', () => {
// arrange
const expectedMessage = 'category has no children';
const category: YamlCategory = {
category: 'test',
children: [],
};
const compiler = new ScriptCompilerStub();
// act
const act = () => parseCategory(category, compiler);
// assert
expect(act).to.throw(expectedMessage);
});
it('throws when children are undefined', () => {
// arrange
const expectedMessage = 'category has no children';
const category: YamlCategory = {
category: 'test',
children: undefined,
};
const compiler = new ScriptCompilerStub();
// act
const act = () => parseCategory(category, compiler);
// assert
expect(act).to.throw(expectedMessage);
});
it('throws when name is empty or undefined', () => {
// arrange
const expectedMessage = 'category has no name';
const invalidNames = ['', undefined];
invalidNames.forEach((invalidName) => {
const category: YamlCategory = {
category: invalidName,
children: getTestChildren(),
};
const compiler = new ScriptCompilerStub();
// act
const act = () => parseCategory(category, compiler);
// assert
expect(act).to.throw(expectedMessage);
});
});
});
it('throws when children is empty', () => {
const category: YamlCategory = {
category: 'test',
children: [],
};
expect(() => parseCategory(category)).to.throw('category has no children');
it('throws when compiler is undefined', () => {
// arrange
const expectedError = 'undefined compiler';
const compiler = undefined;
const category = getValidCategory();
// act
const act = () => parseCategory(category, compiler);
// assert
expect(act).to.throw(expectedError);
});
it('throws when children is undefined', () => {
const category: YamlCategory = {
category: 'test',
children: undefined,
};
expect(() => parseCategory(category)).to.throw('category has no children');
});
it('throws when name is empty', () => {
const category: YamlCategory = {
category: '',
children: getTestChildren(),
};
expect(() => parseCategory(category)).to.throw('category has no name');
});
it('throws when name is undefined', () => {
const category: YamlCategory = {
category: undefined,
children: getTestChildren(),
};
expect(() => parseCategory(category)).to.throw('category has no name');
});
it('returns expected docs', () => {
// arrange
const url = 'https://privacy.sexy';
const expected = parseDocUrls({ docs: url });
const compiler = new ScriptCompilerStub();
const category: YamlCategory = {
category: 'category name',
children: getTestChildren(),
docs: url,
};
// act
const actual = parseCategory(category).documentationUrls;
const actual = parseCategory(category, compiler).documentationUrls;
// assert
expect(actual).to.deep.equal(expected);
});
it('returns expected scripts', () => {
// arrange
const script = getTestScript();
const expected = [ parseScript(script) ];
const category: YamlCategory = {
category: 'category name',
children: [ script ],
};
// act
const actual = parseCategory(category).scripts;
// assert
expect(actual).to.deep.equal(expected);
describe('parses expected subscript', () => {
it('single script with code', () => {
// arrange
const script = YamlScriptStub.createWithCode();
const compiler = new ScriptCompilerStub();
const expected = [ parseScript(script, compiler) ];
const category: YamlCategory = {
category: 'category name',
children: [ script ],
};
// act
const actual = parseCategory(category, compiler).scripts;
// assert
expect(actual).to.deep.equal(expected);
});
it('single script with function call', () => {
// arrange
const script = YamlScriptStub.createWithCall();
const compiler = new ScriptCompilerStub()
.withCompileAbility(script);
const expected = [ parseScript(script, compiler) ];
const category: YamlCategory = {
category: 'category name',
children: [ script ],
};
// act
const actual = parseCategory(category, compiler).scripts;
// assert
expect(actual).to.deep.equal(expected);
});
it('multiple scripts with function call and code', () => {
// arrange
const callableScript = YamlScriptStub.createWithCall();
const scripts = [ callableScript, YamlScriptStub.createWithCode() ];
const compiler = new ScriptCompilerStub()
.withCompileAbility(callableScript);
const expected = scripts.map((script) => parseScript(script, compiler));
const category: YamlCategory = {
category: 'category name',
children: scripts,
};
// act
const actual = parseCategory(category, compiler).scripts;
// assert
expect(actual).to.deep.equal(expected);
});
});
it('returns expected subcategories', () => {
// arrange
const expected: YamlCategory[] = [ {
category: 'test category',
children: [ getTestScript() ],
children: [ YamlScriptStub.createWithCode() ],
}];
const category: YamlCategory = {
category: 'category name',
children: expected,
};
const compiler = new ScriptCompilerStub();
// act
const actual = parseCategory(category).subCategories;
const actual = parseCategory(category, compiler).subCategories;
// assert
expect(actual).to.have.lengthOf(1);
expect(actual[0].name).to.equal(expected[0].category);
@@ -94,17 +156,16 @@ describe('CategoryParser', () => {
});
});
function getTestChildren(): ReadonlyArray<CategoryOrScript> {
return [
getTestScript(),
];
}
function getTestScript(): YamlScript {
function getValidCategory(): YamlCategory {
return {
name: 'script name',
code: 'script code',
revertCode: 'revert code',
recommend: RecommendationLevel[RecommendationLevel.Standard],
category: 'category name',
children: getTestChildren(),
docs: undefined,
};
}
function getTestChildren(): ReadonlyArray<CategoryOrScript> {
return [
YamlScriptStub.createWithCode(),
];
}

View File

@@ -0,0 +1,363 @@
import 'mocha';
import { expect } from 'chai';
import { ScriptCompiler } from '@/application/Parser/Compiler/ScriptCompiler';
import { YamlScriptStub } from '../../../stubs/YamlScriptStub';
import { YamlFunction, YamlScript, FunctionCall, ScriptFunctionCall, FunctionCallParameters } from 'js-yaml-loader!./application.yaml';
import { IScriptCode } from '@/domain/IScriptCode';
import { IScriptCompiler } from '@/application/Parser/Compiler/IScriptCompiler';
describe('ScriptCompiler', () => {
describe('ctor', () => {
it('throws when functions have same names', () => {
// arrange
const expectedError = `duplicate function name: "same-func-name"`;
const functions: YamlFunction[] = [ {
name: 'same-func-name',
code: 'non-empty-code',
}, {
name: 'same-func-name',
code: 'non-empty-code-2',
}];
// act
const act = () => new ScriptCompiler(functions);
// assert
expect(act).to.throw(expectedError);
});
it('throws when function parameters have same names', () => {
// arrange
const func: YamlFunction = {
name: 'function-name',
code: 'non-empty-code',
parameters: [ 'duplicate', 'duplicate' ],
};
const expectedError = `"${func.name}": duplicate parameter name: "duplicate"`;
// act
const act = () => new ScriptCompiler([func]);
// assert
expect(act).to.throw(expectedError);
});
describe('throws when when function have duplicate code', () => {
it('code', () => {
// arrange
const expectedError = `duplicate "code" in functions: "duplicate-code"`;
const functions: YamlFunction[] = [ {
name: 'func-1',
code: 'duplicate-code',
}, {
name: 'func-2',
code: 'duplicate-code',
}];
// act
const act = () => new ScriptCompiler(functions);
// assert
expect(act).to.throw(expectedError);
});
it('revertCode', () => {
// arrange
const expectedError = `duplicate "revertCode" in functions: "duplicate-revert-code"`;
const functions: YamlFunction[] = [ {
name: 'func-1',
code: 'code-1',
revertCode: 'duplicate-revert-code',
}, {
name: 'func-2',
code: 'code-2',
revertCode: 'duplicate-revert-code',
}];
// act
const act = () => new ScriptCompiler(functions);
// assert
expect(act).to.throw(expectedError);
});
});
});
describe('canCompile', () => {
it('returns true if "call" is defined', () => {
// arrange
const sut = new ScriptCompiler([]);
const script = YamlScriptStub.createWithCall();
// act
const actual = sut.canCompile(script);
// assert
expect(actual).to.equal(true);
});
it('returns false if "call" is undefined', () => {
// arrange
const sut = new ScriptCompiler([]);
const script = YamlScriptStub.createWithCode();
// act
const actual = sut.canCompile(script);
// assert
expect(actual).to.equal(false);
});
});
describe('compile', () => {
describe('invalid state', () => {
it('throws if functions are empty', () => {
// arrange
const expectedError = 'cannot compile without shared functions';
const functions = [];
const sut = new ScriptCompiler(functions);
const script = YamlScriptStub.createWithCall();
// act
const act = () => sut.compile(script);
// assert
expect(act).to.throw(expectedError);
});
it('throws if call is not an object', () => {
// arrange
const expectedError = 'called function(s) must be an object';
const invalidValues = [undefined, 'string', 33];
const sut = new ScriptCompiler(createFunctions());
invalidValues.forEach((invalidValue) => {
const script = YamlScriptStub.createWithoutCallOrCodes() // because call ctor overwrites "undefined"
.withCall(invalidValue as any);
// act
const act = () => sut.compile(script);
// assert
expect(act).to.throw(expectedError);
});
});
describe('invalid function reference', () => {
it('throws if function does not exist', () => {
// arrange
const sut = new ScriptCompiler(createFunctions());
const nonExistingFunctionName = 'non-existing-func';
const expectedError = `called function is not defined "${nonExistingFunctionName}"`;
const call: ScriptFunctionCall = { function: nonExistingFunctionName };
const script = YamlScriptStub.createWithCall(call);
// act
const act = () => sut.compile(script);
// assert
expect(act).to.throw(expectedError);
});
it('throws if function is undefined', () => {
// arrange
const existingFunctionName = 'existing-func';
const sut = new ScriptCompiler(createFunctions(existingFunctionName));
const call: ScriptFunctionCall = [
{ function: existingFunctionName },
undefined,
];
const script = YamlScriptStub.createWithCall(call);
const expectedError = `undefined function call in script "${script.name}"`;
// act
const act = () => sut.compile(script);
// assert
expect(act).to.throw(expectedError);
});
it('throws if function name is not given', () => {
// arrange
const existingFunctionName = 'existing-func';
const sut = new ScriptCompiler(createFunctions(existingFunctionName));
const call: FunctionCall[] = [
{ function: existingFunctionName },
{ function: undefined }];
const script = YamlScriptStub.createWithCall(call);
const expectedError = `empty function name called in script "${script.name}"`;
// act
const act = () => sut.compile(script);
// assert
expect(act).to.throw(expectedError);
});
});
});
describe('builds code as expected', () => {
it('builds single call as expected', () => {
// arrange
const functionName = 'testSharedFunction';
const expected: IScriptCode = {
execute: 'expected-code',
revert: 'expected-revert-code',
};
const func: YamlFunction = {
name: functionName,
parameters: [],
code: expected.execute,
revertCode: expected.revert,
};
const sut = new ScriptCompiler([func]);
const call: FunctionCall = { function: functionName };
const script = YamlScriptStub.createWithCall(call);
// act
const actual = sut.compile(script);
// assert
expect(actual).to.deep.equal(expected);
});
it('builds call sequence as expected', () => {
// arrange
const firstFunction: YamlFunction = {
name: 'first-function-name',
parameters: [],
code: 'first-function-code',
revertCode: 'first-function-revert-code',
};
const secondFunction: YamlFunction = {
name: 'second-function-name',
parameters: [],
code: 'second-function-code',
revertCode: 'second-function-revert-code',
};
const expected: IScriptCode = {
execute: 'first-function-code\nsecond-function-code',
revert: 'first-function-revert-code\nsecond-function-revert-code',
};
const sut = new ScriptCompiler([firstFunction, secondFunction]);
const call: FunctionCall[] = [
{ function: firstFunction.name },
{ function: secondFunction.name },
];
const script = YamlScriptStub.createWithCall(call);
// act
const actual = sut.compile(script);
// assert
expect(actual).to.deep.equal(expected);
});
});
describe('parameter substitution', () => {
describe('substitutes by ignoring whitespaces inside mustaches', () => {
// arrange
const mustacheVariations = [
'Hello {{ $test }}!',
'Hello {{$test }}!',
'Hello {{ $test}}!',
'Hello {{$test}}!'];
mustacheVariations.forEach((variation) => {
it(variation, () => {
// arrange
const env = new TestEnvironment({
code: variation,
parameters: {
test: 'world',
},
});
const expected = env.expect('Hello world!');
// act
const actual = env.sut.compile(env.script);
// assert
expect(actual).to.deep.equal(expected);
});
});
});
describe('substitutes as expected', () => {
it('with different parameters', () => {
// arrange
const env = new TestEnvironment({
code: 'He{{ $firstParameter }} {{ $secondParameter }}!',
parameters: {
firstParameter: 'llo',
secondParameter: 'world',
},
});
const expected = env.expect('Hello world!');
// act
const actual = env.sut.compile(env.script);
// assert
expect(actual).to.deep.equal(expected);
});
it('with same parameter repeated', () => {
// arrange
const env = new TestEnvironment({
code: '{{ $parameter }} {{ $parameter }}!',
parameters: {
parameter: 'Hodor',
},
});
const expected = env.expect('Hodor Hodor!');
// act
const actual = env.sut.compile(env.script);
// assert
expect(actual).to.deep.equal(expected);
});
});
it('throws when parameters is undefined', () => {
// arrange
const env = new TestEnvironment({
code: '{{ $parameter }} {{ $parameter }}!',
});
const expectedError = 'no parameters defined, expected: "parameter"';
// act
const act = () => env.sut.compile(env.script);
// assert
expect(act).to.throw(expectedError);
});
it('throws when parameter value is not provided', () => {
// arrange
const env = new TestEnvironment({
code: '{{ $parameter }} {{ $parameter }}!',
parameters: {
parameter: undefined,
},
});
const expectedError = 'parameter value is not provided for "parameter" in function call';
// act
const act = () => env.sut.compile(env.script);
// assert
expect(act).to.throw(expectedError);
});
it('throws on unknown expressions', () => {
// arrange
const env = new TestEnvironment({
code: '{{ each }}',
parameters: {
parameter: undefined,
},
});
const expectedError = 'unknown expression: "each"';
// act
const act = () => env.sut.compile(env.script);
// assert
expect(act).to.throw(expectedError);
});
});
});
});
interface ITestCase {
code: string;
parameters?: FunctionCallParameters;
}
class TestEnvironment {
public readonly sut: IScriptCompiler;
public readonly script: YamlScript;
constructor(testCase: ITestCase) {
const functionName = 'testFunction';
const func: YamlFunction = {
name: functionName,
parameters: testCase.parameters ? Object.keys(testCase.parameters) : undefined,
code: this.getCode(testCase.code, 'execute'),
revertCode: this.getCode(testCase.code, 'revert'),
};
this.sut = new ScriptCompiler([func]);
const call: FunctionCall = {
function: functionName,
parameters: testCase.parameters,
};
this.script = YamlScriptStub.createWithCall(call);
}
public expect(code: string): IScriptCode {
return {
execute: this.getCode(code, 'execute'),
revert: this.getCode(code, 'revert'),
};
}
private getCode(text: string, type: 'execute' | 'revert'): string {
return `${text} (${type})`;
}
}
function createFunctions(...names: string[]): YamlFunction[] {
if (!names || names.length === 0) {
names = ['test-function'];
}
return names.map((functionName) => {
const func: YamlFunction = {
name: functionName,
parameters: [],
code: `REM test-code (${functionName})`,
revertCode: `REM test-revert-code (${functionName})`,
};
return func;
});
}

View File

@@ -1,58 +1,93 @@
import { YamlScript } from 'js-yaml-loader!./application.yaml';
import 'mocha';
import { expect } from 'chai';
import { parseScript } from '@/application/Parser/ScriptParser';
import { parseDocUrls } from '@/application/Parser/DocumentationParser';
import { RecommendationLevelNames, RecommendationLevel } from '@/domain/RecommendationLevel';
import { ScriptCode } from '@/domain/ScriptCode';
import { ScriptCompilerStub } from '../../stubs/ScriptCompilerStub';
import { YamlScriptStub } from '../../stubs/YamlScriptStub';
describe('ScriptParser', () => {
describe('parseScript', () => {
it('parses name as expected', () => {
// arrange
const script = getValidScript();
script.name = 'expected-name';
const expected = 'test-expected-name';
const script = YamlScriptStub.createWithCode()
.withName(expected);
const compiler = new ScriptCompilerStub();
// act
const actual = parseScript(script);
const actual = parseScript(script, compiler);
// assert
expect(actual.name).to.equal(script.name);
});
it('parses code as expected', () => {
// arrange
const script = getValidScript();
script.code = 'expected-code';
// act
const actual = parseScript(script);
// assert
expect(actual.code).to.equal(script.code);
});
it('parses revertCode as expected', () => {
// arrange
const script = getValidScript();
script.code = 'expected-code';
// act
const actual = parseScript(script);
// assert
expect(actual.revertCode).to.equal(script.revertCode);
expect(actual.name).to.equal(expected);
});
it('parses docs as expected', () => {
// arrange
const script = getValidScript();
script.docs = [ 'https://expected-doc1.com', 'https://expected-doc2.com' ];
const docs = [ 'https://expected-doc1.com', 'https://expected-doc2.com' ];
const script = YamlScriptStub.createWithCode()
.withDocs(docs);
const compiler = new ScriptCompilerStub();
const expected = parseDocUrls(script);
// act
const actual = parseScript(script);
const actual = parseScript(script, compiler);
// assert
expect(actual.documentationUrls).to.deep.equal(expected);
});
describe('invalid script', () => {
it('throws when script is undefined', () => {
// arrange
const expectedError = 'undefined script';
const compiler = new ScriptCompilerStub();
const script = undefined;
// act
const act = () => parseScript(script, compiler);
// assert
expect(act).to.throw(expectedError);
});
it('throws when both function call and code are defined', () => {
// arrange
const expectedError = 'cannot define both "call" and "code"';
const compiler = new ScriptCompilerStub();
const script = YamlScriptStub
.createWithCall()
.withCode('code');
// act
const act = () => parseScript(script, compiler);
// assert
expect(act).to.throw(expectedError);
});
it('throws when both function call and revertCode are defined', () => {
// arrange
const expectedError = 'cannot define "revertCode" if "call" is defined';
const compiler = new ScriptCompilerStub();
const script = YamlScriptStub
.createWithCall()
.withRevertCode('revert-code');
// act
const act = () => parseScript(script, compiler);
// assert
expect(act).to.throw(expectedError);
});
it('throws when neither call or revertCode are defined', () => {
// arrange
const expectedError = 'must define either "call" or "code"';
const compiler = new ScriptCompilerStub();
const script = YamlScriptStub.createWithoutCallOrCodes();
// act
const act = () => parseScript(script, compiler);
// assert
expect(act).to.throw(expectedError);
});
});
describe('level', () => {
it('accepts undefined level', () => {
const undefinedLevels: string[] = [ '', undefined ];
undefinedLevels.forEach((undefinedLevel) => {
// arrange
const script = getValidScript();
const compiler = new ScriptCompilerStub();
const script = YamlScriptStub.createWithCode();
script.recommend = undefinedLevel;
// act
const actual = parseScript(script);
const actual = parseScript(script, compiler);
// assert
expect(actual.level).to.equal(undefined);
});
@@ -60,10 +95,11 @@ describe('ScriptParser', () => {
it('throws on unknown level', () => {
// arrange
const unknownLevel = 'boi';
const script = getValidScript();
const compiler = new ScriptCompilerStub();
const script = YamlScriptStub.createWithCode();
script.recommend = unknownLevel;
// act
const act = () => parseScript(script);
const act = () => parseScript(script, compiler);
// assert
expect(act).to.throw(`unknown level: "${unknownLevel}"`);
});
@@ -71,10 +107,11 @@ describe('ScriptParser', () => {
const nonStringTypes: any[] = [ 5, true ];
nonStringTypes.forEach((nonStringType) => {
// arrange
const script = getValidScript();
const script = YamlScriptStub.createWithCode();
const compiler = new ScriptCompilerStub();
script.recommend = nonStringType;
// act
const act = () => parseScript(script);
const act = () => parseScript(script, compiler);
// assert
expect(act).to.throw(`level must be a string but it was ${typeof nonStringType}`);
});
@@ -84,10 +121,11 @@ describe('ScriptParser', () => {
it(levelText, () => {
// arrange
const expectedLevel = RecommendationLevel[levelText];
const script = getValidScript();
const script = YamlScriptStub.createWithCode();
const compiler = new ScriptCompilerStub();
script.recommend = levelText;
// act
const actual = parseScript(script);
const actual = parseScript(script, compiler);
// assert
expect(actual.level).to.equal(expectedLevel);
});
@@ -95,24 +133,66 @@ describe('ScriptParser', () => {
});
it('parses level case insensitive', () => {
// arrange
const script = getValidScript();
const script = YamlScriptStub.createWithCode();
const compiler = new ScriptCompilerStub();
const expected = RecommendationLevel.Standard;
script.recommend = RecommendationLevel[expected].toUpperCase();
// act
const actual = parseScript(script);
const actual = parseScript(script, compiler);
// assert
expect(actual.level).to.equal(expected);
});
});
describe('code', () => {
it('parses code as expected', () => {
// arrange
const expected = 'expected-code';
const script = YamlScriptStub
.createWithCode()
.withCode(expected);
const compiler = new ScriptCompilerStub();
// act
const parsed = parseScript(script, compiler);
// assert
const actual = parsed.code.execute;
expect(actual).to.equal(expected);
});
it('parses revertCode as expected', () => {
// arrange
const expected = 'expected-revert-code';
const script = YamlScriptStub
.createWithCode()
.withRevertCode(expected);
const compiler = new ScriptCompilerStub();
// act
const parsed = parseScript(script, compiler);
// assert
const actual = parsed.code.revert;
expect(actual).to.equal(expected);
});
describe('compiler', () => {
it('throws when compiler is not defined', () => {
// arrange
const script = YamlScriptStub.createWithCode();
const compiler = undefined;
// act
const act = () => parseScript(script, compiler);
// assert
expect(act).to.throw('undefined compiler');
});
it('gets code from compiler', () => {
// arrange
const expected = new ScriptCode('test-script', 'code', 'revert-code');
const script = YamlScriptStub.createWithCode();
const compiler = new ScriptCompilerStub()
.withCompileAbility(script, expected);
// act
const parsed = parseScript(script, compiler);
// assert
const actual = parsed.code;
expect(actual).to.equal(expected);
});
});
});
});
});
function getValidScript(): YamlScript {
return {
name: 'valid-name',
code: 'valid-code',
revertCode: 'expected revert code',
docs: ['hello.com'],
recommend: RecommendationLevel[RecommendationLevel.Standard].toLowerCase(),
};
}