As part of transition to Vue 3.0 and Vite (#230), this commit facilitates the shift towards building rest of the application using Vite. By doing so, it eliminates reliance on outdated Electron building system that offered limited control, blocking desktop builds (#233). Changes include: - Introduce Vite with Vue 2.0 plugin for test execution. - Remove `mocha`, `chai` and other related dependencies. - Adjust test to Vitest syntax. - Revise and update `tests.md` to document the changes. - Add `@modyfi/vite-plugin-yaml` plugin to be able to use yaml file depended logic on test files, replacing previous webpack behavior. - Fix failing tests that are revealed by Vitest due to unhandled errors and lack of assertments. - Remove the test that depends on Vue CLI populating `process.env`. - Use `jsdom` for unit test environment, adding it to dependency to `package.json` as project now depends on it and it was not specified even though `package-lock.json` included it.
278 lines
11 KiB
TypeScript
278 lines
11 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { UserScriptGenerator } from '@/application/Context/State/Code/Generation/UserScriptGenerator';
|
|
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
|
|
import { ICodeBuilderFactory } from '@/application/Context/State/Code/Generation/ICodeBuilderFactory';
|
|
import { ICodeBuilder } from '@/application/Context/State/Code/Generation/ICodeBuilder';
|
|
import { ScriptStub } from '@tests/unit/shared/Stubs/ScriptStub';
|
|
import { ScriptingDefinitionStub } from '@tests/unit/shared/Stubs/ScriptingDefinitionStub';
|
|
import { itEachAbsentObjectValue, itEachAbsentStringValue } from '@tests/unit/shared/TestCases/AbsentTests';
|
|
import { SelectedScriptStub } from '@tests/unit/shared/Stubs/SelectedScriptStub';
|
|
|
|
describe('UserScriptGenerator', () => {
|
|
describe('scriptingDefinition', () => {
|
|
describe('startCode', () => {
|
|
it('is prepended if not empty', () => {
|
|
// arrange
|
|
const sut = new UserScriptGenerator();
|
|
const startCode = 'Start\nCode';
|
|
const script = new ScriptStub('id')
|
|
.withCode('code\nmulti-lined')
|
|
.toSelectedScript();
|
|
const definition = new ScriptingDefinitionStub()
|
|
.withStartCode(startCode);
|
|
const expectedStart = `${startCode}\n`;
|
|
// act
|
|
const code = sut.buildCode([script], definition);
|
|
// assert
|
|
const actual = code.code;
|
|
expect(actual.startsWith(expectedStart));
|
|
});
|
|
describe('is not prepended if empty', () => {
|
|
itEachAbsentStringValue((absentValue) => {
|
|
// arrange
|
|
const codeBuilderStub = new CodeBuilderStub();
|
|
const sut = new UserScriptGenerator(mockCodeBuilderFactory(codeBuilderStub));
|
|
const script = new ScriptStub('id')
|
|
.withCode('code\nmulti-lined')
|
|
.toSelectedScript();
|
|
const definition = new ScriptingDefinitionStub()
|
|
.withStartCode(absentValue);
|
|
const expectedStart = codeBuilderStub
|
|
.appendFunction(script.script.name, script.script.code.execute)
|
|
.toString();
|
|
// act
|
|
const code = sut.buildCode([script], definition);
|
|
// assert
|
|
const actual = code.code;
|
|
expect(actual.startsWith(expectedStart));
|
|
});
|
|
});
|
|
});
|
|
describe('endCode', () => {
|
|
it('is appended if not empty', () => {
|
|
// arrange
|
|
const sut = new UserScriptGenerator();
|
|
const endCode = 'End\nCode';
|
|
const script = new ScriptStub('id')
|
|
.withCode('code\nmulti-lined')
|
|
.toSelectedScript();
|
|
const definition = new ScriptingDefinitionStub()
|
|
.withEndCode(endCode);
|
|
const expectedEnd = `${endCode}\n`;
|
|
// act
|
|
const code = sut.buildCode([script], definition);
|
|
// assert
|
|
const actual = code.code;
|
|
expect(actual.endsWith(expectedEnd));
|
|
});
|
|
describe('is not appended if empty', () => {
|
|
itEachAbsentStringValue((absentValue) => {
|
|
// arrange
|
|
const codeBuilderStub = new CodeBuilderStub();
|
|
const sut = new UserScriptGenerator(mockCodeBuilderFactory(codeBuilderStub));
|
|
const script = new ScriptStub('id')
|
|
.withCode('code\nmulti-lined')
|
|
.toSelectedScript();
|
|
const expectedEnd = codeBuilderStub
|
|
.appendFunction(script.script.name, script.script.code.execute)
|
|
.toString();
|
|
const definition = new ScriptingDefinitionStub()
|
|
.withEndCode(absentValue);
|
|
// act
|
|
const code = sut.buildCode([script], definition);
|
|
// assert
|
|
const actual = code.code;
|
|
expect(actual.endsWith(expectedEnd));
|
|
});
|
|
});
|
|
});
|
|
describe('throws when absent', () => {
|
|
itEachAbsentObjectValue((absentValue) => {
|
|
// arrange
|
|
const expectedError = 'missing definition';
|
|
const sut = new UserScriptGenerator();
|
|
const scriptingDefinition = absentValue;
|
|
const selectedScripts = [new SelectedScriptStub('a')];
|
|
// act
|
|
const act = () => sut.buildCode(selectedScripts, scriptingDefinition);
|
|
// assert
|
|
expect(act).to.throw(expectedError);
|
|
});
|
|
});
|
|
});
|
|
it('appends revert script', () => {
|
|
// arrange
|
|
const sut = new UserScriptGenerator();
|
|
const scriptName = 'test non-revert script';
|
|
const scriptCode = 'REM nop';
|
|
const script = new ScriptStub('id')
|
|
.withName(scriptName)
|
|
.withRevertCode(scriptCode)
|
|
.toSelectedScript(true);
|
|
const definition = new ScriptingDefinitionStub();
|
|
// act
|
|
const actual = sut.buildCode([script], definition);
|
|
// assert
|
|
expect(actual.code).to.include(`${scriptName} (revert)`);
|
|
expect(actual.code).to.include(scriptCode);
|
|
});
|
|
it('appends non-revert script', () => {
|
|
const sut = new UserScriptGenerator();
|
|
// arrange
|
|
const scriptName = 'test non-revert script';
|
|
const scriptCode = 'REM nop';
|
|
const script = new ScriptStub('id').withName(scriptName).withCode(scriptCode);
|
|
const selectedScripts = [new SelectedScript(script, false)];
|
|
const definition = new ScriptingDefinitionStub();
|
|
// act
|
|
const actual = sut.buildCode(selectedScripts, definition);
|
|
// assert
|
|
expect(actual.code).to.include(scriptName);
|
|
expect(actual.code).to.not.include(`${scriptName} (revert)`);
|
|
expect(actual.code).to.include(scriptCode);
|
|
});
|
|
describe('scriptPositions', () => {
|
|
it('without script; returns empty', () => {
|
|
// arrange
|
|
const sut = new UserScriptGenerator();
|
|
const selectedScripts = [];
|
|
const definition = new ScriptingDefinitionStub();
|
|
// act
|
|
const actual = sut.buildCode(selectedScripts, definition);
|
|
// assert
|
|
expect(actual.scriptPositions.size).to.equal(0);
|
|
});
|
|
describe('with scripts', () => {
|
|
// arrange
|
|
const totalStartCodeLines = 2;
|
|
const totalFunctionNameLines = 4;
|
|
const definition = new ScriptingDefinitionStub()
|
|
.withStartCode('First line\nSecond line');
|
|
describe('single script', () => {
|
|
const testCases = [
|
|
{
|
|
name: 'single-lined',
|
|
scriptCode: 'only line',
|
|
codeLines: 1,
|
|
},
|
|
{
|
|
name: 'multi-lined',
|
|
scriptCode: 'first line\nsecond line',
|
|
codeLines: 2,
|
|
},
|
|
];
|
|
const sut = new UserScriptGenerator();
|
|
for (const testCase of testCases) {
|
|
it(testCase.name, () => {
|
|
const expectedStartLine = totalStartCodeLines
|
|
+ 1 // empty line code begin
|
|
+ 1; // code begin
|
|
const expectedEndLine = expectedStartLine
|
|
+ totalFunctionNameLines
|
|
+ testCase.codeLines;
|
|
const selectedScript = new ScriptStub('script-id')
|
|
.withName('script')
|
|
.withCode(testCase.scriptCode)
|
|
.toSelectedScript(false);
|
|
// act
|
|
const actual = sut.buildCode([selectedScript], definition);
|
|
// expect
|
|
expect(1).to.equal(actual.scriptPositions.size);
|
|
const position = actual.scriptPositions.get(selectedScript);
|
|
expect(expectedStartLine).to.equal(position.startLine, 'Unexpected start line position');
|
|
expect(expectedEndLine).to.equal(position.endLine, 'Unexpected end line position');
|
|
});
|
|
}
|
|
});
|
|
it('multiple scripts', () => {
|
|
const sut = new UserScriptGenerator();
|
|
const selectedScripts = [
|
|
new ScriptStub('1').withCode('only line'),
|
|
new ScriptStub('2').withCode('first line\nsecond line'),
|
|
].map((s) => s.toSelectedScript());
|
|
const expectedFirstScriptStart = totalStartCodeLines
|
|
+ 1 // empty line code begin
|
|
+ 1; // code begin
|
|
const expectedFirstScriptEnd = expectedFirstScriptStart
|
|
+ totalFunctionNameLines
|
|
+ 1; // total code lines
|
|
const expectedSecondScriptStart = expectedFirstScriptEnd
|
|
+ 1 // code end hyphens
|
|
+ 1 // new line
|
|
+ 1; // code begin
|
|
const expectedSecondScriptEnd = expectedSecondScriptStart
|
|
+ totalFunctionNameLines
|
|
+ 2; // total lines of second script
|
|
// act
|
|
const actual = sut.buildCode(selectedScripts, definition);
|
|
// assert
|
|
const firstPosition = actual.scriptPositions.get(selectedScripts[0]);
|
|
const secondPosition = actual.scriptPositions.get(selectedScripts[1]);
|
|
expect(actual.scriptPositions.size).to.equal(2);
|
|
expect(expectedFirstScriptStart).to.equal(firstPosition.startLine, 'Unexpected start line position (first script)');
|
|
expect(expectedFirstScriptEnd).to.equal(firstPosition.endLine, 'Unexpected end line position (first script)');
|
|
expect(expectedSecondScriptStart).to.equal(secondPosition.startLine, 'Unexpected start line position (second script)');
|
|
expect(expectedSecondScriptEnd).to.equal(secondPosition.endLine, 'Unexpected end line position (second script)');
|
|
});
|
|
});
|
|
});
|
|
describe('selectedScripts', () => {
|
|
describe('throws when absent', () => {
|
|
itEachAbsentObjectValue((absentValue) => {
|
|
// arrange
|
|
const expectedError = 'missing scripts';
|
|
const sut = new UserScriptGenerator();
|
|
const scriptingDefinition = new ScriptingDefinitionStub();
|
|
const selectedScripts = absentValue;
|
|
// act
|
|
const act = () => sut.buildCode(selectedScripts, scriptingDefinition);
|
|
// assert
|
|
expect(act).to.throw(expectedError);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
function mockCodeBuilderFactory(mock: ICodeBuilder): ICodeBuilderFactory {
|
|
return {
|
|
create: () => mock,
|
|
};
|
|
}
|
|
|
|
class CodeBuilderStub implements ICodeBuilder {
|
|
public currentLine = 0;
|
|
|
|
private text = '';
|
|
|
|
public appendLine(code?: string): ICodeBuilder {
|
|
this.text += this.text ? `${code}\n` : code;
|
|
this.currentLine++;
|
|
return this;
|
|
}
|
|
|
|
public appendTrailingHyphensCommentLine(totalRepeatHyphens: number): ICodeBuilder {
|
|
return this.appendLine(`trailing-hyphens-${totalRepeatHyphens}`);
|
|
}
|
|
|
|
public appendCommentLine(commentLine?: string): ICodeBuilder {
|
|
return this.appendLine(`Comment | ${commentLine}`);
|
|
}
|
|
|
|
public appendCommentLineWithHyphensAround(
|
|
sectionName: string,
|
|
totalRepeatHyphens: number,
|
|
): ICodeBuilder {
|
|
return this.appendLine(`hyphens-around-${totalRepeatHyphens} | Section name: ${sectionName} | hyphens-around-${totalRepeatHyphens}`);
|
|
}
|
|
|
|
public appendFunction(name: string, code: string): ICodeBuilder {
|
|
return this
|
|
.appendLine(`Function | Name: ${name}`)
|
|
.appendLine(`Function | Code: ${code}`);
|
|
}
|
|
|
|
public toString(): string {
|
|
return this.text;
|
|
}
|
|
}
|