Refactor code to comply with ESLint rules

Major refactoring using ESLint with rules from AirBnb and Vue.

Enable most of the ESLint rules and do necessary linting in the code.
Also add more information for rules that are disabled to describe what
they are and why they are disabled.

Allow logging (`console.log`) in test files, and in development mode
(e.g. when working with `npm run serve`), but disable it when
environment is production (as pre-configured by Vue). Also add flag
(`--mode production`) in `lint:eslint` command so production linting is
executed earlier in lifecycle.

Disable rules that requires a separate work. Such as ESLint rules that
are broken in TypeScript: no-useless-constructor (eslint/eslint#14118)
and no-shadow (eslint/eslint#13014).
This commit is contained in:
undergroundwires
2022-01-02 18:20:14 +01:00
parent 96265b75de
commit 5b1fbe1e2f
341 changed files with 16126 additions and 15101 deletions

View File

@@ -10,92 +10,94 @@ import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
describe('CategoryCollectionState', () => {
describe('code', () => {
it('initialized with empty code', () => {
// arrange
const collection = new CategoryCollectionStub();
const sut = new CategoryCollectionState(collection);
// act
const code = sut.code.current;
// assert
expect(!code);
});
it('reacts to selection changes as expected', () => {
// arrange
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScriptIds('scriptId'));
const selectionStub = new UserSelection(collection, []);
const expectedCodeGenerator = new ApplicationCode(selectionStub, collection.scripting);
selectionStub.selectAll();
const expectedCode = expectedCodeGenerator.current;
// act
const sut = new CategoryCollectionState(collection);
sut.selection.selectAll();
const actualCode = sut.code.current;
// assert
expect(actualCode).to.equal(expectedCode);
});
describe('code', () => {
it('initialized with empty code', () => {
// arrange
const collection = new CategoryCollectionStub();
const sut = new CategoryCollectionState(collection);
// act
const code = sut.code.current;
// assert
expect(!code);
});
describe('os', () => {
it('same as its collection', () => {
// arrange
const expected = OperatingSystem.macOS;
const collection = new CategoryCollectionStub()
.withOs(expected);
// act
const sut = new CategoryCollectionState(collection);
// assert
const actual = sut.os;
expect(expected).to.equal(actual);
});
it('reacts to selection changes as expected', () => {
// arrange
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScriptIds('scriptId'));
const selectionStub = new UserSelection(collection, []);
const expectedCodeGenerator = new ApplicationCode(selectionStub, collection.scripting);
selectionStub.selectAll();
const expectedCode = expectedCodeGenerator.current;
// act
const sut = new CategoryCollectionState(collection);
sut.selection.selectAll();
const actualCode = sut.code.current;
// assert
expect(actualCode).to.equal(expectedCode);
});
describe('selection', () => {
it('initialized with no selection', () => {
// arrange
const collection = new CategoryCollectionStub();
const sut = new CategoryCollectionState(collection);
// act
const actual = sut.selection.selectedScripts.length;
// assert
expect(actual).to.equal(0);
});
it('can select a script from current collection', () => {
// arrange
const expectedScript = new ScriptStub('scriptId');
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScript(expectedScript));
const sut = new CategoryCollectionState(collection);
// act
sut.selection.selectAll();
// assert
expect(sut.selection.selectedScripts.length).to.equal(1);
expect(sut.selection.isSelected(expectedScript.id)).to.equal(true);
});
});
describe('os', () => {
it('same as its collection', () => {
// arrange
const expected = OperatingSystem.macOS;
const collection = new CategoryCollectionStub()
.withOs(expected);
// act
const sut = new CategoryCollectionState(collection);
// assert
const actual = sut.os;
expect(expected).to.equal(actual);
});
describe('filter', () => {
it('initialized with an empty filter', () => {
// arrange
const collection = new CategoryCollectionStub();
const sut = new CategoryCollectionState(collection);
// act
const actual = sut.filter.currentFilter;
// assert
expect(actual).to.equal(undefined);
});
it('can match a script from current collection', () => {
// arrange
const scriptNameFilter = 'scriptName';
const expectedScript = new ScriptStub('scriptId')
.withName(scriptNameFilter);
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScript(expectedScript));
const sut = new CategoryCollectionState(collection);
// act
let actualScript: IScript;
sut.filter.filtered.on((result) => actualScript = result.scriptMatches[0]);
sut.filter.setFilter(scriptNameFilter);
// assert
expect(expectedScript).to.equal(actualScript);
});
});
describe('selection', () => {
it('initialized with no selection', () => {
// arrange
const collection = new CategoryCollectionStub();
const sut = new CategoryCollectionState(collection);
// act
const actual = sut.selection.selectedScripts.length;
// assert
expect(actual).to.equal(0);
});
it('can select a script from current collection', () => {
// arrange
const expectedScript = new ScriptStub('scriptId');
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScript(expectedScript));
const sut = new CategoryCollectionState(collection);
// act
sut.selection.selectAll();
// assert
expect(sut.selection.selectedScripts.length).to.equal(1);
expect(sut.selection.isSelected(expectedScript.id)).to.equal(true);
});
});
describe('filter', () => {
it('initialized with an empty filter', () => {
// arrange
const collection = new CategoryCollectionStub();
const sut = new CategoryCollectionState(collection);
// act
const actual = sut.filter.currentFilter;
// assert
expect(actual).to.equal(undefined);
});
it('can match a script from current collection', () => {
// arrange
const scriptNameFilter = 'scriptName';
const expectedScript = new ScriptStub('scriptId')
.withName(scriptNameFilter);
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(0).withScript(expectedScript));
const sut = new CategoryCollectionState(collection);
// act
let actualScript: IScript;
sut.filter.filtered.on((result) => {
[actualScript] = result.scriptMatches;
});
sut.filter.setFilter(scriptNameFilter);
// assert
expect(expectedScript).to.equal(actualScript);
});
});
});

View File

@@ -15,177 +15,194 @@ import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
describe('ApplicationCode', () => {
describe('ctor', () => {
it('empty when selection is empty', () => {
// arrange
const selection = new UserSelection(new CategoryCollectionStub(), []);
const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition);
// act
const actual = sut.current;
// assert
expect(actual).to.have.lengthOf(0);
describe('ctor', () => {
it('empty when selection is empty', () => {
// arrange
const selection = new UserSelection(new CategoryCollectionStub(), []);
const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition);
// act
const actual = sut.current;
// assert
expect(actual).to.have.lengthOf(0);
});
it('generates code from script generator when selection is not empty', () => {
// arrange
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
const selectedScripts = scripts.map((script) => script.toSelectedScript());
const selection = new UserSelection(collection, selectedScripts);
const definition = new ScriptingDefinitionStub();
const expected: IUserScript = {
code: 'expected-code',
scriptPositions: new Map(),
};
const generator = new UserScriptGeneratorMock()
.plan({ scripts: selection.selectedScripts, definition }, expected);
const sut = new ApplicationCode(selection, definition, generator);
// act
const actual = sut.current;
// assert
expect(actual).to.equal(expected.code);
});
});
describe('changed event', () => {
describe('code', () => {
it('empty when nothing is selected', () => {
// arrange
let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition);
sut.changed.on((code) => {
signaled = code;
});
it('generates code from script generator when selection is not empty', () => {
// arrange
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts));
const selection = new UserSelection(collection, scripts.map((script) => script.toSelectedScript()));
const definition = new ScriptingDefinitionStub();
const expected: IUserScript = {
code: 'expected-code',
scriptPositions: new Map(),
// act
selection.changed.notify([]);
// assert
expect(signaled.code).to.have.lengthOf(0);
expect(signaled.code).to.equal(sut.current);
});
it('has code when some are selected', () => {
// arrange
let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition);
sut.changed.on((code) => {
signaled = code;
});
// act
selection.changed.notify(scripts.map((s) => new SelectedScript(s, false)));
// assert
expect(signaled.code).to.have.length.greaterThan(0);
expect(signaled.code).to.equal(sut.current);
});
});
describe('calls UserScriptGenerator', () => {
it('sends scripting definition to generator', () => {
// arrange
const expectedDefinition = new ScriptingDefinitionStub();
const collection = new CategoryCollectionStub();
const selection = new UserSelection(collection, []);
const generatorMock: IUserScriptGenerator = {
buildCode: (selectedScripts, definition) => {
if (definition !== expectedDefinition) {
throw new Error('Unexpected scripting definition');
}
return {
code: '',
scriptPositions: new Map<SelectedScript, ICodePosition>(),
};
const generator = new UserScriptGeneratorMock()
.plan({ scripts: selection.selectedScripts, definition }, expected);
const sut = new ApplicationCode(selection, definition, generator);
// act
const actual = sut.current;
// assert
expect(actual).to.equal(expected.code);
});
});
describe('changed event', () => {
describe('code', () => {
it('empty when nothing is selected', () => {
// arrange
let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition);
sut.changed.on((code) => signaled = code);
// act
selection.changed.notify([]);
// assert
expect(signaled.code).to.have.lengthOf(0);
expect(signaled.code).to.equal(sut.current);
});
it('has code when some are selected', () => {
// arrange
let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const definition = new ScriptingDefinitionStub();
const sut = new ApplicationCode(selection, definition);
sut.changed.on((code) => signaled = code);
// act
selection.changed.notify(scripts.map((s) => new SelectedScript(s, false)));
// assert
expect(signaled.code).to.have.length.greaterThan(0);
expect(signaled.code).to.equal(sut.current);
});
});
describe('calls UserScriptGenerator', () => {
it('sends scripting definition to generator', () => {
// arrange
const expectedDefinition = new ScriptingDefinitionStub();
const collection = new CategoryCollectionStub();
const selection = new UserSelection(collection, []);
const generatorMock: IUserScriptGenerator = {
buildCode: (selectedScripts, definition) => {
if (definition !== expectedDefinition) {
throw new Error('Unexpected scripting definition');
}
return {
code: '',
scriptPositions: new Map<SelectedScript, ICodePosition>(),
};
},
};
new ApplicationCode(selection, expectedDefinition, generatorMock);
// act
const act = () => selection.changed.notify([]);
// assert
expect(act).to.not.throw();
});
it('sends selected scripts to generator', () => {
// arrange
const expectedDefinition = new ScriptingDefinitionStub();
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub().withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const generatorMock: IUserScriptGenerator = {
buildCode: (selectedScripts) => {
if (JSON.stringify(selectedScripts) !== JSON.stringify(scriptsToSelect)) {
throw new Error('Unexpected scripts');
}
return {
code: '',
scriptPositions: new Map<SelectedScript, ICodePosition>(),
};
},
};
new ApplicationCode(selection, expectedDefinition, generatorMock);
// act
const act = () => selection.changed.notify(scriptsToSelect);
// assert
expect(act).to.not.throw();
});
it('sets positions from the generator', () => {
// arrange
let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const scriptingDefinition = new ScriptingDefinitionStub();
const totalLines = 20;
const expected = new Map<SelectedScript, ICodePosition>(
[
[scriptsToSelect[0], new CodePosition(0, totalLines / 2)],
[scriptsToSelect[1], new CodePosition(totalLines / 2, totalLines)],
],
);
const generatorMock: IUserScriptGenerator = {
buildCode: () => {
return {
code: '\nREM LINE'.repeat(totalLines),
scriptPositions: expected,
};
},
};
const sut = new ApplicationCode(selection, scriptingDefinition, generatorMock);
sut.changed.on((code) => signaled = code);
// act
selection.changed.notify(scriptsToSelect);
// assert
expect(signaled.getScriptPositionInCode(scripts[0]))
.to.deep.equal(expected.get(scriptsToSelect[0]));
expect(signaled.getScriptPositionInCode(scripts[1]))
.to.deep.equal(expected.get(scriptsToSelect[1]));
});
},
};
// eslint-disable-next-line no-new
new ApplicationCode(selection, expectedDefinition, generatorMock);
// act
const act = () => selection.changed.notify([]);
// assert
expect(act).to.not.throw();
});
it('sends selected scripts to generator', () => {
// arrange
const expectedDefinition = new ScriptingDefinitionStub();
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const generatorMock: IUserScriptGenerator = {
buildCode: (selectedScripts) => {
if (JSON.stringify(selectedScripts) !== JSON.stringify(scriptsToSelect)) {
throw new Error('Unexpected scripts');
}
return {
code: '',
scriptPositions: new Map<SelectedScript, ICodePosition>(),
};
},
};
// eslint-disable-next-line no-new
new ApplicationCode(selection, expectedDefinition, generatorMock);
// act
const act = () => selection.changed.notify(scriptsToSelect);
// assert
expect(act).to.not.throw();
});
it('sets positions from the generator', () => {
// arrange
let signaled: ICodeChangedEvent;
const scripts = [new ScriptStub('first'), new ScriptStub('second')];
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1).withScripts(...scripts));
const scriptsToSelect = scripts.map((script) => new SelectedScript(script, false));
const selection = new UserSelection(collection, scriptsToSelect);
const scriptingDefinition = new ScriptingDefinitionStub();
const totalLines = 20;
const expected = new Map<SelectedScript, ICodePosition>(
[
[scriptsToSelect[0], new CodePosition(0, totalLines / 2)],
[scriptsToSelect[1], new CodePosition(totalLines / 2, totalLines)],
],
);
const generatorMock: IUserScriptGenerator = {
buildCode: () => {
return {
code: '\nREM LINE'.repeat(totalLines),
scriptPositions: expected,
};
},
};
const sut = new ApplicationCode(selection, scriptingDefinition, generatorMock);
sut.changed.on((code) => {
signaled = code;
});
// act
selection.changed.notify(scriptsToSelect);
// assert
expect(signaled.getScriptPositionInCode(scripts[0]))
.to.deep.equal(expected.get(scriptsToSelect[0]));
expect(signaled.getScriptPositionInCode(scripts[1]))
.to.deep.equal(expected.get(scriptsToSelect[1]));
});
});
});
});
interface IScriptGenerationParameters {
scripts: readonly SelectedScript[];
definition: IScriptingDefinition;
scripts: readonly SelectedScript[];
definition: IScriptingDefinition;
}
class UserScriptGeneratorMock implements IUserScriptGenerator {
private prePlanned = new Map<IScriptGenerationParameters, IUserScript>();
public plan(
parameters: IScriptGenerationParameters,
result: IUserScript): UserScriptGeneratorMock {
this.prePlanned.set(parameters, result);
return this;
}
public buildCode(
selectedScripts: readonly SelectedScript[],
scriptingDefinition: IScriptingDefinition): IUserScript {
for (const [parameters, result] of Array.from(this.prePlanned)) {
if (selectedScripts === parameters.scripts
&& scriptingDefinition === parameters.definition) {
return result;
}
}
throw new Error('Unexpected parameters');
private prePlanned = new Map<IScriptGenerationParameters, IUserScript>();
public plan(
parameters: IScriptGenerationParameters,
result: IUserScript,
): UserScriptGeneratorMock {
this.prePlanned.set(parameters, result);
return this;
}
public buildCode(
selectedScripts: readonly SelectedScript[],
scriptingDefinition: IScriptingDefinition,
): IUserScript {
for (const [parameters, result] of Array.from(this.prePlanned)) {
if (selectedScripts === parameters.scripts
&& scriptingDefinition === parameters.definition) {
return result;
}
}
throw new Error('Unexpected parameters');
}
}

View File

@@ -8,140 +8,204 @@ import { SelectedScriptStub } from '@tests/unit/stubs/SelectedScriptStub';
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
describe('CodeChangedEvent', () => {
describe('ctor', () => {
describe('position validation', () => {
it('throws when code position is out of range', () => {
const act = () => new CodeChangedEvent(
'singleline code', [], new Map<SelectedScript, ICodePosition>([
[ new SelectedScriptStub('1'), new CodePosition(0, 2) ],
]),
);
expect(act).to.throw();
});
it('does not throw with valid code position', () => {
const act = () => new CodeChangedEvent(
'singleline code', [], new Map<SelectedScript, ICodePosition>([
[ new SelectedScriptStub('1'), new CodePosition(0, 1) ],
]),
);
expect(act).to.not.throw();
});
});
});
it('code returns expected', () => {
describe('ctor', () => {
describe('position validation', () => {
it('throws when code position is out of range', () => {
// arrange
const expected = 'code';
const code = 'singleline code';
const newScripts = new Map<SelectedScript, ICodePosition>([
[new SelectedScriptStub('1'), new CodePosition(0, 2 /* nonexisting line */)],
]);
// act
const sut = new CodeChangedEvent(
expected, [], new Map<SelectedScript, ICodePosition>(),
);
const actual = sut.code;
const act = () => new CodeChangedEventBuilder()
.withCode(code)
.withNewScripts(newScripts)
.build();
// assert
expect(actual).to.equal(expected);
});
describe('addedScripts', () => {
it('returns new scripts when scripts are added', () => {
// arrange
const expected = [ new ScriptStub('3'), new ScriptStub('4') ];
const initialScripts = [ new SelectedScriptStub('1'), new SelectedScriptStub('2') ];
expect(act).to.throw();
});
describe('does not throw with valid code position', () => {
// arrange
const testCases = [
{
name: 'singleline',
code: 'singleline code',
position: new CodePosition(0, 1),
},
{
name: 'multiline',
code: 'multiline code\nsecond line',
position: new CodePosition(0, 2),
},
];
for (const testCase of testCases) {
it(testCase.name, () => {
const newScripts = new Map<SelectedScript, ICodePosition>([
[initialScripts[0], new CodePosition(0, 1) ],
[initialScripts[1], new CodePosition(0, 1) ],
[new SelectedScript(expected[0], false), new CodePosition(0, 1) ],
[new SelectedScript(expected[1], false), new CodePosition(0, 1) ],
[new SelectedScriptStub('1'), testCase.position],
]);
// act
const sut = new CodeChangedEvent(
'code', initialScripts, newScripts,
);
const actual = sut.addedScripts;
const act = () => new CodeChangedEventBuilder()
.withCode(testCase.code)
.withNewScripts(newScripts)
.build();
// assert
expect(actual).to.have.lengthOf(2);
expect(actual[0]).to.deep.equal(expected[0]);
expect(actual[1]).to.deep.equal(expected[1]);
});
expect(act).to.not.throw();
});
}
});
});
describe('removedScripts', () => {
it('returns removed scripts when script are removed', () => {
// arrange
const existingScripts = [ new SelectedScriptStub('0'), new SelectedScriptStub('1') ];
const removedScripts = [ new SelectedScriptStub('2') ];
const initialScripts = [ ...existingScripts, ...removedScripts ];
const newScripts = new Map<SelectedScript, ICodePosition>([
[initialScripts[0], new CodePosition(0, 1) ],
[initialScripts[1], new CodePosition(0, 1) ],
]);
// act
const sut = new CodeChangedEvent(
'code', initialScripts, newScripts,
);
const actual = sut.removedScripts;
// assert
expect(actual).to.have.lengthOf(removedScripts.length);
expect(actual[0]).to.deep.equal(removedScripts[0].script);
});
});
it('code returns expected', () => {
// arrange
const expected = 'code';
const sut = new CodeChangedEventBuilder()
.withCode(expected)
.build();
// act
const actual = sut.code;
// assert
expect(actual).to.equal(expected);
});
describe('addedScripts', () => {
it('returns new scripts when scripts are added', () => {
// arrange
const expected = [new ScriptStub('3'), new ScriptStub('4')];
const initialScripts = [new SelectedScriptStub('1'), new SelectedScriptStub('2')];
const newScripts = new Map<SelectedScript, ICodePosition>([
[initialScripts[0], new CodePosition(0, 1)],
[initialScripts[1], new CodePosition(0, 1)],
[new SelectedScript(expected[0], false), new CodePosition(0, 1)],
[new SelectedScript(expected[1], false), new CodePosition(0, 1)],
]);
const sut = new CodeChangedEventBuilder()
.withOldScripts(initialScripts)
.withNewScripts(newScripts)
.build();
// act
const actual = sut.addedScripts;
// assert
expect(actual).to.have.lengthOf(2);
expect(actual[0]).to.deep.equal(expected[0]);
expect(actual[1]).to.deep.equal(expected[1]);
});
describe('changedScripts', () => {
it('returns changed scripts when scripts are changed', () => {
// arrange
const initialScripts = [ new SelectedScriptStub('1', false), new SelectedScriptStub('2', false) ];
const newScripts = new Map<SelectedScript, ICodePosition>([
[new SelectedScriptStub('1', true), new CodePosition(0, 1) ],
[new SelectedScriptStub('2', false), new CodePosition(0, 1) ],
]);
// act
const sut = new CodeChangedEvent(
'code', initialScripts, newScripts,
);
const actual = sut.changedScripts;
// assert
expect(actual).to.have.lengthOf(1);
expect(actual[0]).to.deep.equal(initialScripts[0].script);
});
});
describe('removedScripts', () => {
it('returns removed scripts when script are removed', () => {
// arrange
const existingScripts = [new SelectedScriptStub('0'), new SelectedScriptStub('1')];
const removedScripts = [new SelectedScriptStub('2')];
const initialScripts = [...existingScripts, ...removedScripts];
const newScripts = new Map<SelectedScript, ICodePosition>([
[initialScripts[0], new CodePosition(0, 1)],
[initialScripts[1], new CodePosition(0, 1)],
]);
const sut = new CodeChangedEventBuilder()
.withOldScripts(initialScripts)
.withNewScripts(newScripts)
.build();
// act
const actual = sut.removedScripts;
// assert
expect(actual).to.have.lengthOf(removedScripts.length);
expect(actual[0]).to.deep.equal(removedScripts[0].script);
});
describe('isEmpty', () => {
it('returns true when empty', () => {
// arrange
const newScripts = new Map<SelectedScript, ICodePosition>();
const oldScripts = [ new SelectedScriptStub('1', false) ];
const sut = new CodeChangedEvent(
'code', oldScripts, newScripts,
);
// act
const actual = sut.isEmpty();
// assert
expect(actual).to.equal(true);
});
it('returns false when not empty', () => {
// arrange
const oldScripts = [ new SelectedScriptStub('1') ];
const newScripts = new Map<SelectedScript, ICodePosition>( [
[oldScripts[0], new CodePosition(0, 1) ],
]);
const sut = new CodeChangedEvent(
'code', oldScripts, newScripts,
);
// act
const actual = sut.isEmpty();
// assert
expect(actual).to.equal(false);
});
});
describe('changedScripts', () => {
it('returns changed scripts when scripts are changed', () => {
// arrange
const initialScripts = [new SelectedScriptStub('1', false), new SelectedScriptStub('2', false)];
const newScripts = new Map<SelectedScript, ICodePosition>([
[new SelectedScriptStub('1', true), new CodePosition(0, 1)],
[new SelectedScriptStub('2', false), new CodePosition(0, 1)],
]);
const sut = new CodeChangedEventBuilder()
.withOldScripts(initialScripts)
.withNewScripts(newScripts)
.build();
// act
const actual = sut.changedScripts;
// assert
expect(actual).to.have.lengthOf(1);
expect(actual[0]).to.deep.equal(initialScripts[0].script);
});
describe('getScriptPositionInCode', () => {
it('returns expected position for existing script', () => {
// arrange
const script = new ScriptStub('1');
const expected = new CodePosition(0, 1);
const newScripts = new Map<SelectedScript, ICodePosition>( [
[new SelectedScript(script, false), expected ],
]);
const sut = new CodeChangedEvent(
'code', [], newScripts,
);
// act
const actual = sut.getScriptPositionInCode(script);
// assert
expect(actual).to.deep.equal(expected);
});
});
describe('isEmpty', () => {
it('returns true when empty', () => {
// arrange
const newScripts = new Map<SelectedScript, ICodePosition>();
const oldScripts = [new SelectedScriptStub('1', false)];
const sut = new CodeChangedEventBuilder()
.withOldScripts(oldScripts)
.withNewScripts(newScripts)
.build();
// act
const actual = sut.isEmpty();
// assert
expect(actual).to.equal(true);
});
it('returns false when not empty', () => {
// arrange
const oldScripts = [new SelectedScriptStub('1')];
const newScripts = new Map<SelectedScript, ICodePosition>([
[oldScripts[0], new CodePosition(0, 1)],
]);
const sut = new CodeChangedEventBuilder()
.withOldScripts(oldScripts)
.withNewScripts(newScripts)
.build();
// act
const actual = sut.isEmpty();
// assert
expect(actual).to.equal(false);
});
});
describe('getScriptPositionInCode', () => {
it('returns expected position for existing script', () => {
// arrange
const script = new ScriptStub('1');
const expected = new CodePosition(0, 1);
const newScripts = new Map<SelectedScript, ICodePosition>([
[new SelectedScript(script, false), expected],
]);
const sut = new CodeChangedEventBuilder()
.withNewScripts(newScripts)
.build();
// act
const actual = sut.getScriptPositionInCode(script);
// assert
expect(actual).to.deep.equal(expected);
});
});
});
// Prefer over original ctor in tests for easier future ctor refactorings
class CodeChangedEventBuilder {
private code = '[CodeChangedEventBuilder] default code';
private oldScripts: ReadonlyArray<SelectedScript> = [];
private newScripts = new Map<SelectedScript, ICodePosition>();
public withCode(code: string) {
this.code = code;
return this;
}
public withOldScripts(oldScripts: ReadonlyArray<SelectedScript>) {
this.oldScripts = oldScripts;
return this;
}
public withNewScripts(newScripts: Map<SelectedScript, ICodePosition>) {
this.newScripts = newScripts;
return this;
}
public build(): CodeChangedEvent {
return new CodeChangedEvent(
this.code,
this.oldScripts,
this.newScripts,
);
}
}

View File

@@ -3,134 +3,137 @@ import { expect } from 'chai';
import { CodeBuilder } from '@/application/Context/State/Code/Generation/CodeBuilder';
describe('CodeBuilder', () => {
class CodeBuilderConcrete extends CodeBuilder {
private commentDelimiter = '//';
public withCommentDelimiter(delimiter: string): CodeBuilderConcrete {
this.commentDelimiter = delimiter;
return this;
}
protected getCommentDelimiter(): string {
return this.commentDelimiter;
}
protected writeStandardOut(text: string): string {
return text;
}
class CodeBuilderConcrete extends CodeBuilder {
private commentDelimiter = '//';
public withCommentDelimiter(delimiter: string): CodeBuilderConcrete {
this.commentDelimiter = delimiter;
return this;
}
describe('appendLine', () => {
it('when empty appends empty line', () => {
// arrange
const sut = new CodeBuilderConcrete();
// act
sut.appendLine().appendLine().appendLine();
// assert
expect(sut.toString()).to.equal('\n\n');
});
it('when not empty append string in new line', () => {
// arrange
const sut = new CodeBuilderConcrete();
const expected = 'str';
// act
sut.appendLine()
.appendLine(expected);
// assert
const result = sut.toString();
const lines = getLines(result);
expect(lines[1]).to.equal('str');
});
protected getCommentDelimiter(): string {
return this.commentDelimiter;
}
protected writeStandardOut(text: string): string {
return text;
}
}
describe('appendLine', () => {
it('when empty appends empty line', () => {
// arrange
const sut = new CodeBuilderConcrete();
// act
sut.appendLine().appendLine().appendLine();
// assert
expect(sut.toString()).to.equal('\n\n');
});
it('appendFunction', () => {
// arrange
const sut = new CodeBuilderConcrete();
const functionName = 'function';
const code = 'code';
// act
sut.appendFunction(functionName, code);
// assert
const result = sut.toString();
expect(result).to.include(functionName);
expect(result).to.include(code);
it('when not empty append string in new line', () => {
// arrange
const sut = new CodeBuilderConcrete();
const expected = 'str';
// act
sut.appendLine()
.appendLine(expected);
// assert
const result = sut.toString();
const lines = getLines(result);
expect(lines[1]).to.equal('str');
});
it('appendTrailingHyphensCommentLine', () => {
// arrange
const commentDelimiter = '//';
const sut = new CodeBuilderConcrete()
.withCommentDelimiter(commentDelimiter);
const totalHyphens = 5;
const expected = `${commentDelimiter} ${'-'.repeat(totalHyphens)}`;
// act
sut.appendTrailingHyphensCommentLine(totalHyphens);
// assert
const result = sut.toString();
const lines = getLines(result);
expect(lines[0]).to.equal(expected);
});
it('appendFunction', () => {
// arrange
const sut = new CodeBuilderConcrete();
const functionName = 'function';
const code = 'code';
// act
sut.appendFunction(functionName, code);
// assert
const result = sut.toString();
expect(result).to.include(functionName);
expect(result).to.include(code);
});
it('appendTrailingHyphensCommentLine', () => {
// arrange
const commentDelimiter = '//';
const sut = new CodeBuilderConcrete()
.withCommentDelimiter(commentDelimiter);
const totalHyphens = 5;
const expected = `${commentDelimiter} ${'-'.repeat(totalHyphens)}`;
// act
sut.appendTrailingHyphensCommentLine(totalHyphens);
// assert
const result = sut.toString();
const lines = getLines(result);
expect(lines[0]).to.equal(expected);
});
it('appendCommentLine', () => {
// arrange
const commentDelimiter = '//';
const sut = new CodeBuilderConcrete()
.withCommentDelimiter(commentDelimiter);
const comment = 'comment';
const expected = `${commentDelimiter} comment`;
// act
const result = sut
.appendCommentLine(comment)
.toString();
// assert
const lines = getLines(result);
expect(lines[0]).to.equal(expected);
});
it('appendCommentLineWithHyphensAround', () => {
// arrange
const commentDelimiter = '//';
const sut = new CodeBuilderConcrete()
.withCommentDelimiter(commentDelimiter);
const sectionName = 'section';
const totalHyphens = sectionName.length + 3 * 2;
const expected = `${commentDelimiter} ---section---`;
// act
const result = sut
.appendCommentLineWithHyphensAround(sectionName, totalHyphens)
.toString();
// assert
const lines = getLines(result);
expect(lines[1]).to.equal(expected);
});
describe('currentLine', () => {
it('no lines returns zero', () => {
// arrange & act
const sut = new CodeBuilderConcrete();
// assert
expect(sut.currentLine).to.equal(0);
});
it('appendCommentLine', () => {
// arrange
const commentDelimiter = '//';
const sut = new CodeBuilderConcrete()
.withCommentDelimiter(commentDelimiter);
const comment = 'comment';
const expected = `${commentDelimiter} comment`;
// act
const result = sut
.appendCommentLine(comment)
.toString();
// assert
const lines = getLines(result);
expect(lines[0]).to.equal(expected);
it('single line returns one', () => {
// arrange
const sut = new CodeBuilderConcrete();
// act
sut.appendLine();
// assert
expect(sut.currentLine).to.equal(1);
});
it('appendCommentLineWithHyphensAround', () => {
// arrange
const commentDelimiter = '//';
const sut = new CodeBuilderConcrete()
.withCommentDelimiter(commentDelimiter);
const sectionName = 'section';
const totalHyphens = sectionName.length + 3 * 2;
const expected = `${commentDelimiter} ---section---`;
// act
const result = sut
.appendCommentLineWithHyphensAround(sectionName, totalHyphens)
.toString();
// assert
const lines = getLines(result);
expect(lines[1]).to.equal(expected);
it('multiple lines returns as expected', () => {
// arrange
const sut = new CodeBuilderConcrete();
// act
sut.appendLine('1')
.appendCommentLine('2')
.appendLine();
// assert
expect(sut.currentLine).to.equal(3);
});
describe('currentLine', () => {
it('no lines returns zero', () => {
// arrange & act
const sut = new CodeBuilderConcrete();
// assert
expect(sut.currentLine).to.equal(0);
});
it('single line returns one', () => {
// arrange
const sut = new CodeBuilderConcrete();
// act
sut.appendLine();
// assert
expect(sut.currentLine).to.equal(1);
});
it('multiple lines returns as expected', () => {
// arrange
const sut = new CodeBuilderConcrete();
// act
sut.appendLine('1')
.appendCommentLine('2')
.appendLine();
// assert
expect(sut.currentLine).to.equal(3);
});
it('multiple lines in code', () => {
// arrange
const sut = new CodeBuilderConcrete();
// act
sut.appendLine('hello\ncode-here\nwith-3-lines');
// assert
expect(sut.currentLine).to.equal(3);
});
it('multiple lines in code', () => {
// arrange
const sut = new CodeBuilderConcrete();
// act
sut.appendLine('hello\ncode-here\nwith-3-lines');
// assert
expect(sut.currentLine).to.equal(3);
});
});
});
function getLines(text: string): string[] {
return text.split(/\r\n|\r|\n/);
return text.split(/\r\n|\r|\n/);
}

View File

@@ -5,9 +5,9 @@ import { CodeBuilderFactory } from '@/application/Context/State/Code/Generation/
import { ScriptingLanguageFactoryTestRunner } from '@tests/unit/application/Common/ScriptingLanguage/ScriptingLanguageFactoryTestRunner';
describe('CodeBuilderFactory', () => {
const sut = new CodeBuilderFactory();
const runner = new ScriptingLanguageFactoryTestRunner()
.expect(ScriptingLanguage.shellscript, ShellBuilder)
.expect(ScriptingLanguage.batchfile, BatchBuilder);
runner.testCreateMethod(sut);
const sut = new CodeBuilderFactory();
const runner = new ScriptingLanguageFactoryTestRunner()
.expect(ScriptingLanguage.shellscript, ShellBuilder)
.expect(ScriptingLanguage.batchfile, BatchBuilder);
runner.testCreateMethod(sut);
});

View File

@@ -3,57 +3,58 @@ import { expect } from 'chai';
import { BatchBuilder } from '@/application/Context/State/Code/Generation/Languages/BatchBuilder';
describe('BatchBuilder', () => {
class BatchBuilderRevealer extends BatchBuilder {
public getCommentDelimiter(): string {
return super.getCommentDelimiter();
}
public writeStandardOut(text: string): string {
return super.writeStandardOut(text);
}
class BatchBuilderRevealer extends BatchBuilder {
public getCommentDelimiter(): string {
return super.getCommentDelimiter();
}
describe('getCommentDelimiter', () => {
it('returns expected', () => {
// arrange
const expected = '::';
const sut = new BatchBuilderRevealer();
// act
const actual = sut.getCommentDelimiter();
// assert
expect(expected).to.equal(actual);
});
});
describe('writeStandardOut', () => {
const testData = [
{
name: 'plain text',
text: 'test',
expected: 'echo test',
},
{
name: 'text with ampersand',
text: 'a & b',
expected: 'echo a ^& b',
},
{
name: 'text with percent sign',
text: '90%',
expected: 'echo 90%%',
},
{
name: 'text with multiple ampersands and percent signs',
text: 'Me&you in % ? You & me = 0%',
expected: 'echo Me^&you in %% ? You ^& me = 0%%',
},
];
for (const test of testData) {
it(test.name, () => {
// arrange
const sut = new BatchBuilderRevealer();
// act
const actual = sut.writeStandardOut(test.text);
// assert
expect(test.expected).to.equal(actual);
});
}
public writeStandardOut(text: string): string {
return super.writeStandardOut(text);
}
}
describe('getCommentDelimiter', () => {
it('returns expected', () => {
// arrange
const expected = '::';
const sut = new BatchBuilderRevealer();
// act
const actual = sut.getCommentDelimiter();
// assert
expect(expected).to.equal(actual);
});
});
describe('writeStandardOut', () => {
const testData = [
{
name: 'plain text',
text: 'test',
expected: 'echo test',
},
{
name: 'text with ampersand',
text: 'a & b',
expected: 'echo a ^& b',
},
{
name: 'text with percent sign',
text: '90%',
expected: 'echo 90%%',
},
{
name: 'text with multiple ampersands and percent signs',
text: 'Me&you in % ? You & me = 0%',
expected: 'echo Me^&you in %% ? You ^& me = 0%%',
},
];
for (const test of testData) {
it(test.name, () => {
// arrange
const sut = new BatchBuilderRevealer();
// act
const actual = sut.writeStandardOut(test.text);
// assert
expect(test.expected).to.equal(actual);
});
}
});
});

View File

@@ -3,52 +3,53 @@ import { expect } from 'chai';
import { ShellBuilder } from '@/application/Context/State/Code/Generation/Languages/ShellBuilder';
describe('ShellBuilder', () => {
class ShellBuilderRevealer extends ShellBuilder {
public getCommentDelimiter(): string {
return super.getCommentDelimiter();
}
public writeStandardOut(text: string): string {
return super.writeStandardOut(text);
}
class ShellBuilderRevealer extends ShellBuilder {
public getCommentDelimiter(): string {
return super.getCommentDelimiter();
}
describe('getCommentDelimiter', () => {
it('returns expected', () => {
// arrange
const expected = '#';
const sut = new ShellBuilderRevealer();
// act
const actual = sut.getCommentDelimiter();
// assert
expect(expected).to.equal(actual);
});
});
describe('writeStandardOut', () => {
const testData = [
{
name: 'plain text',
text: 'test',
expected: 'echo \'test\'',
},
{
name: 'text with single quote',
text: 'I\'m not who you think I am',
expected: 'echo \'I\'\\\'\'m not who you think I am\'',
},
{
name: 'text with multiple single quotes',
text: 'I\'m what you\'re',
expected: 'echo \'I\'\\\'\'m what you\'\\\'\'re\'',
},
];
for (const test of testData) {
it(test.name, () => {
// arrange
const sut = new ShellBuilderRevealer();
// act
const actual = sut.writeStandardOut(test.text);
// assert
expect(test.expected).to.equal(actual);
});
}
public writeStandardOut(text: string): string {
return super.writeStandardOut(text);
}
}
describe('getCommentDelimiter', () => {
it('returns expected', () => {
// arrange
const expected = '#';
const sut = new ShellBuilderRevealer();
// act
const actual = sut.getCommentDelimiter();
// assert
expect(expected).to.equal(actual);
});
});
describe('writeStandardOut', () => {
const testData = [
{
name: 'plain text',
text: 'test',
expected: 'echo \'test\'',
},
{
name: 'text with single quote',
text: 'I\'m not who you think I am',
expected: 'echo \'I\'\\\'\'m not who you think I am\'',
},
{
name: 'text with multiple single quotes',
text: 'I\'m what you\'re',
expected: 'echo \'I\'\\\'\'m what you\'\\\'\'re\'',
},
];
for (const test of testData) {
it(test.name, () => {
// arrange
const sut = new ShellBuilderRevealer();
// act
const actual = sut.writeStandardOut(test.text);
// assert
expect(test.expected).to.equal(actual);
});
}
});
});

View File

@@ -8,230 +8,239 @@ import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
import { ScriptingDefinitionStub } from '@tests/unit/stubs/ScriptingDefinitionStub';
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)
.withEndCode(undefined);
const expectedStart = `${startCode}\n`;
// act
const code = sut.buildCode([script], definition);
// assert
const actual = code.code;
expect(actual.startsWith(expectedStart));
});
it('is not prepended if empty', () => {
// 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(undefined)
.withEndCode(undefined);
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));
});
it('is not appended if empty', () => {
// 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(undefined);
// act
const code = sut.buildCode([script], definition);
// assert
const actual = code.code;
expect(actual.endsWith(expectedEnd));
});
});
});
it('appends revert script', () => {
describe('scriptingDefinition', () => {
describe('startCode', () => {
it('is prepended if not empty', () => {
// arrange
const sut = new UserScriptGenerator();
const scriptName = 'test non-revert script';
const scriptCode = 'REM nop';
const startCode = 'Start\nCode';
const script = new ScriptStub('id')
.withName(scriptName)
.withRevertCode(scriptCode)
.toSelectedScript(true);
const definition = new ScriptingDefinitionStub();
.withCode('code\nmulti-lined')
.toSelectedScript();
const definition = new ScriptingDefinitionStub()
.withStartCode(startCode)
.withEndCode(undefined);
const expectedStart = `${startCode}\n`;
// act
const actual = sut.buildCode([ script ], definition);
const code = 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();
const actual = code.code;
expect(actual.startsWith(expectedStart));
});
it('is not prepended if empty', () => {
// 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();
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(undefined)
.withEndCode(undefined);
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));
});
it('is not appended if empty', () => {
// 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(undefined);
// act
const code = sut.buildCode([script], definition);
// assert
const actual = code.code;
expect(actual.endsWith(expectedEnd));
});
});
});
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
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)');
});
});
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)');
});
});
});
});
function mockCodeBuilderFactory(mock: ICodeBuilder): ICodeBuilderFactory {
return {
create: () => mock,
};
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;
}
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;
}
}

View File

@@ -3,52 +3,52 @@ import { expect } from 'chai';
import { CodePosition } from '@/application/Context/State/Code/Position/CodePosition';
describe('CodePosition', () => {
describe('ctor', () => {
it('creates with valid parameters', () => {
// arrange
const startPosition = 0;
const endPosition = 5;
// act
const sut = new CodePosition(startPosition, endPosition);
// assert
expect(sut.startLine).to.equal(startPosition);
expect(sut.endLine).to.equal(endPosition);
});
it('throws with negative start position', () => {
// arrange
const startPosition = -1;
const endPosition = 5;
// act
const getSut = () => new CodePosition(startPosition, endPosition);
// assert
expect(getSut).to.throw('Code cannot start in a negative line');
});
it('throws with negative end position', () => {
// arrange
const startPosition = 1;
const endPosition = -5;
// act
const getSut = () => new CodePosition(startPosition, endPosition);
// assert
expect(getSut).to.throw('Code cannot end in a negative line');
});
it('throws when start and end position is same', () => {
// arrange
const startPosition = 0;
const endPosition = 0;
// act
const getSut = () => new CodePosition(startPosition, endPosition);
// assert
expect(getSut).to.throw('Empty code');
});
it('throws when ends before start', () => {
// arrange
const startPosition = 3;
const endPosition = 2;
// act
const getSut = () => new CodePosition(startPosition, endPosition);
// assert
expect(getSut).to.throw('End line cannot be less than start line');
});
describe('ctor', () => {
it('creates with valid parameters', () => {
// arrange
const startPosition = 0;
const endPosition = 5;
// act
const sut = new CodePosition(startPosition, endPosition);
// assert
expect(sut.startLine).to.equal(startPosition);
expect(sut.endLine).to.equal(endPosition);
});
it('throws with negative start position', () => {
// arrange
const startPosition = -1;
const endPosition = 5;
// act
const getSut = () => new CodePosition(startPosition, endPosition);
// assert
expect(getSut).to.throw('Code cannot start in a negative line');
});
it('throws with negative end position', () => {
// arrange
const startPosition = 1;
const endPosition = -5;
// act
const getSut = () => new CodePosition(startPosition, endPosition);
// assert
expect(getSut).to.throw('Code cannot end in a negative line');
});
it('throws when start and end position is same', () => {
// arrange
const startPosition = 0;
const endPosition = 0;
// act
const getSut = () => new CodePosition(startPosition, endPosition);
// assert
expect(getSut).to.throw('Empty code');
});
it('throws when ends before start', () => {
// arrange
const startPosition = 3;
const endPosition = 2;
// act
const getSut = () => new CodePosition(startPosition, endPosition);
// assert
expect(getSut).to.throw('End line cannot be less than start line');
});
});
});

View File

@@ -5,42 +5,42 @@ import { CategoryStub } from '@tests/unit/stubs/CategoryStub';
import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
describe('FilterResult', () => {
describe('hasAnyMatches', () => {
it('false when no matches', () => {
const sut = new FilterResult(
/* scriptMatches */ [],
/* categoryMatches */ [],
'query',
);
const actual = sut.hasAnyMatches();
expect(actual).to.equal(false);
});
it('true when script matches', () => {
const sut = new FilterResult(
/* scriptMatches */ [ new ScriptStub('id') ],
/* categoryMatches */ [],
'query',
);
const actual = sut.hasAnyMatches();
expect(actual).to.equal(true);
});
it('true when category matches', () => {
const sut = new FilterResult(
/* scriptMatches */ [ ],
/* categoryMatches */ [ new CategoryStub(5) ],
'query',
);
const actual = sut.hasAnyMatches();
expect(actual).to.equal(true);
});
it('true when script + category matches', () => {
const sut = new FilterResult(
/* scriptMatches */ [ new ScriptStub('id') ],
/* categoryMatches */ [ new CategoryStub(5) ],
'query',
);
const actual = sut.hasAnyMatches();
expect(actual).to.equal(true);
});
describe('hasAnyMatches', () => {
it('false when no matches', () => {
const sut = new FilterResult(
/* scriptMatches */ [],
/* categoryMatches */ [],
'query',
);
const actual = sut.hasAnyMatches();
expect(actual).to.equal(false);
});
it('true when script matches', () => {
const sut = new FilterResult(
/* scriptMatches */ [new ScriptStub('id')],
/* categoryMatches */ [],
'query',
);
const actual = sut.hasAnyMatches();
expect(actual).to.equal(true);
});
it('true when category matches', () => {
const sut = new FilterResult(
/* scriptMatches */ [],
/* categoryMatches */ [new CategoryStub(5)],
'query',
);
const actual = sut.hasAnyMatches();
expect(actual).to.equal(true);
});
it('true when script + category matches', () => {
const sut = new FilterResult(
/* scriptMatches */ [new ScriptStub('id')],
/* categoryMatches */ [new CategoryStub(5)],
'query',
);
const actual = sut.hasAnyMatches();
expect(actual).to.equal(true);
});
});
});

View File

@@ -7,158 +7,172 @@ import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
import { CategoryCollectionStub } from '@tests/unit/stubs/CategoryCollectionStub';
describe('UserFilter', () => {
describe('removeFilter', () => {
it('signals when removing filter', () => {
// arrange
let isCalled = false;
const sut = new UserFilter(new CategoryCollectionStub());
sut.filterRemoved.on(() => isCalled = true);
// act
sut.removeFilter();
// assert
expect(isCalled).to.be.equal(true);
});
it('sets currentFilter to undefined', () => {
// arrange
const sut = new UserFilter(new CategoryCollectionStub());
// act
sut.setFilter('non-important');
sut.removeFilter();
// assert
expect(sut.currentFilter).to.be.equal(undefined);
});
describe('removeFilter', () => {
it('signals when removing filter', () => {
// arrange
let isCalled = false;
const sut = new UserFilter(new CategoryCollectionStub());
sut.filterRemoved.on(() => {
isCalled = true;
});
// act
sut.removeFilter();
// assert
expect(isCalled).to.be.equal(true);
});
describe('setFilter', () => {
it('signals when no matches', () => {
// arrange
let actual: IFilterResult;
const nonMatchingFilter = 'non matching filter';
const sut = new UserFilter(new CategoryCollectionStub());
sut.filtered.on((filterResult) => actual = filterResult);
// act
sut.setFilter(nonMatchingFilter);
// assert
expect(actual.hasAnyMatches()).be.equal(false);
expect(actual.query).to.equal(nonMatchingFilter);
});
it('sets currentFilter as expected when no matches', () => {
// arrange
const nonMatchingFilter = 'non matching filter';
const sut = new UserFilter(new CategoryCollectionStub());
// act
sut.setFilter(nonMatchingFilter);
// assert
const actual = sut.currentFilter;
expect(actual.hasAnyMatches()).be.equal(false);
expect(actual.query).to.equal(nonMatchingFilter);
});
describe('signals when matches', () => {
describe('signals when script matches', () => {
it('code matches', () => {
// arrange
const code = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const script = new ScriptStub('id').withCode(code);
const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category));
sut.filtered.on((filterResult) => actual = filterResult);
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(0);
expect(actual.scriptMatches).to.have.lengthOf(1);
expect(actual.scriptMatches[0]).to.deep.equal(script);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
it('revertCode matches', () => {
// arrange
const revertCode = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const script = new ScriptStub('id').withRevertCode(revertCode);
const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category));
sut.filtered.on((filterResult) => actual = filterResult);
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(0);
expect(actual.scriptMatches).to.have.lengthOf(1);
expect(actual.scriptMatches[0]).to.deep.equal(script);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
it('name matches', () => {
// arrange
const name = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const script = new ScriptStub('id').withName(name);
const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category));
sut.filtered.on((filterResult) => actual = filterResult);
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(0);
expect(actual.scriptMatches).to.have.lengthOf(1);
expect(actual.scriptMatches[0]).to.deep.equal(script);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
});
it('signals when category matches', () => {
// arrange
const categoryName = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const category = new CategoryStub(55).withName(categoryName);
const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category));
sut.filtered.on((filterResult) => actual = filterResult);
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(1);
expect(actual.categoryMatches[0]).to.deep.equal(category);
expect(actual.scriptMatches).to.have.lengthOf(0);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
it('signals when category and script matches', () => {
// arrange
const matchingText = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const script = new ScriptStub('script')
.withName(matchingText);
const category = new CategoryStub(55)
.withName(matchingText)
.withScript(script);
const collection = new CategoryCollectionStub()
.withAction(category);
const sut = new UserFilter(collection);
sut.filtered.on((filterResult) => actual = filterResult);
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(1);
expect(actual.categoryMatches[0]).to.deep.equal(category);
expect(actual.scriptMatches).to.have.lengthOf(1);
expect(actual.scriptMatches[0]).to.deep.equal(script);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
});
it('sets currentFilter to undefined', () => {
// arrange
const sut = new UserFilter(new CategoryCollectionStub());
// act
sut.setFilter('non-important');
sut.removeFilter();
// assert
expect(sut.currentFilter).to.be.equal(undefined);
});
});
describe('setFilter', () => {
it('signals when no matches', () => {
// arrange
let actual: IFilterResult;
const nonMatchingFilter = 'non matching filter';
const sut = new UserFilter(new CategoryCollectionStub());
sut.filtered.on((filterResult) => {
actual = filterResult;
});
// act
sut.setFilter(nonMatchingFilter);
// assert
expect(actual.hasAnyMatches()).be.equal(false);
expect(actual.query).to.equal(nonMatchingFilter);
});
it('sets currentFilter as expected when no matches', () => {
// arrange
const nonMatchingFilter = 'non matching filter';
const sut = new UserFilter(new CategoryCollectionStub());
// act
sut.setFilter(nonMatchingFilter);
// assert
const actual = sut.currentFilter;
expect(actual.hasAnyMatches()).be.equal(false);
expect(actual.query).to.equal(nonMatchingFilter);
});
describe('signals when matches', () => {
describe('signals when script matches', () => {
it('code matches', () => {
// arrange
const code = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const script = new ScriptStub('id').withCode(code);
const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category));
sut.filtered.on((filterResult) => {
actual = filterResult;
});
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(0);
expect(actual.scriptMatches).to.have.lengthOf(1);
expect(actual.scriptMatches[0]).to.deep.equal(script);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
it('revertCode matches', () => {
// arrange
const revertCode = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const script = new ScriptStub('id').withRevertCode(revertCode);
const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category));
sut.filtered.on((filterResult) => {
actual = filterResult;
});
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(0);
expect(actual.scriptMatches).to.have.lengthOf(1);
expect(actual.scriptMatches[0]).to.deep.equal(script);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
it('name matches', () => {
// arrange
const name = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const script = new ScriptStub('id').withName(name);
const category = new CategoryStub(33).withScript(script);
const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category));
sut.filtered.on((filterResult) => {
actual = filterResult;
});
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(0);
expect(actual.scriptMatches).to.have.lengthOf(1);
expect(actual.scriptMatches[0]).to.deep.equal(script);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
});
it('signals when category matches', () => {
// arrange
const categoryName = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const category = new CategoryStub(55).withName(categoryName);
const sut = new UserFilter(new CategoryCollectionStub()
.withAction(category));
sut.filtered.on((filterResult) => {
actual = filterResult;
});
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(1);
expect(actual.categoryMatches[0]).to.deep.equal(category);
expect(actual.scriptMatches).to.have.lengthOf(0);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
it('signals when category and script matches', () => {
// arrange
const matchingText = 'HELLO world';
const filter = 'Hello WoRLD';
let actual: IFilterResult;
const script = new ScriptStub('script')
.withName(matchingText);
const category = new CategoryStub(55)
.withName(matchingText)
.withScript(script);
const collection = new CategoryCollectionStub()
.withAction(category);
const sut = new UserFilter(collection);
sut.filtered.on((filterResult) => {
actual = filterResult;
});
// act
sut.setFilter(filter);
// assert
expect(actual.hasAnyMatches()).be.equal(true);
expect(actual.categoryMatches).to.have.lengthOf(1);
expect(actual.categoryMatches[0]).to.deep.equal(category);
expect(actual.scriptMatches).to.have.lengthOf(1);
expect(actual.scriptMatches[0]).to.deep.equal(script);
expect(actual.query).to.equal(filter);
expect(sut.currentFilter).to.deep.equal(actual);
});
});
});
});

View File

@@ -4,25 +4,25 @@ import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
import { SelectedScript } from '@/application/Context/State/Selection/SelectedScript';
describe('SelectedScript', () => {
it('id is same as script id', () => {
// arrange
const expectedId = 'scriptId';
const script = new ScriptStub(expectedId);
const sut = new SelectedScript(script, false);
// act
const actualId = sut.id;
// assert
expect(actualId).to.equal(expectedId);
});
it('throws when revert is true for irreversible script', () => {
// arrange
const expectedId = 'scriptId';
const script = new ScriptStub(expectedId)
.withRevertCode(undefined);
// act
function construct() { new SelectedScript(script, true); }
// assert
expect(construct).to.throw('cannot revert an irreversible script');
});
it('id is same as script id', () => {
// arrange
const expectedId = 'scriptId';
const script = new ScriptStub(expectedId);
const sut = new SelectedScript(script, false);
// act
const actualId = sut.id;
// assert
expect(actualId).to.equal(expectedId);
});
it('throws when revert is true for irreversible script', () => {
// arrange
const expectedId = 'scriptId';
const script = new ScriptStub(expectedId)
.withRevertCode(undefined);
// act
// eslint-disable-next-line no-new
function construct() { new SelectedScript(script, true); }
// assert
expect(construct).to.throw('cannot revert an irreversible script');
});
});

View File

@@ -9,390 +9,394 @@ import { ScriptStub } from '@tests/unit/stubs/ScriptStub';
import { UserSelectionTestRunner } from './UserSelectionTestRunner';
describe('UserSelection', () => {
describe('ctor', () => {
describe('has nothing with no initial selection', () => {
// arrange
const allScripts = [
new SelectedScriptStub('s1', false),
];
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(1, allScripts.map((s) => s.script))
// act
.run()
// assert
.expectFinalScripts([]);
});
describe('has initial selection', () => {
// arrange
const scripts = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
];
new UserSelectionTestRunner()
.withSelectedScripts(scripts)
.withCategory(1, scripts.map((s) => s.script))
// act
.run()
// assert
.expectFinalScripts(scripts);
});
describe('ctor', () => {
describe('has nothing with no initial selection', () => {
// arrange
const allScripts = [
new SelectedScriptStub('s1', false),
];
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(1, allScripts.map((s) => s.script))
// act
.run()
// assert
.expectFinalScripts([]);
});
describe('deselectAll removes all items', () => {
describe('has initial selection', () => {
// arrange
const scripts = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
];
new UserSelectionTestRunner()
.withSelectedScripts(scripts)
.withCategory(1, scripts.map((s) => s.script))
// act
.run()
// assert
.expectFinalScripts(scripts);
});
});
describe('deselectAll removes all items', () => {
// arrange
const allScripts = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
new SelectedScriptStub('s3', false),
new SelectedScriptStub('s4', false),
];
const selectedScripts = allScripts.filter(
(s) => ['s1', 's2', 's3'].includes(s.id),
);
new UserSelectionTestRunner()
.withSelectedScripts(selectedScripts)
.withCategory(1, allScripts.map((s) => s.script))
// act
.run((sut) => {
sut.deselectAll();
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts([])
.expectFinalScriptsInEvent(0, []);
});
describe('selectOnly selects expected', () => {
// arrange
const allScripts = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
new SelectedScriptStub('s3', false),
new SelectedScriptStub('s4', false),
];
const selectedScripts = allScripts.filter(
(s) => ['s1', 's2', 's3'].includes(s.id),
);
const scriptsToSelect = allScripts.filter(
(s) => ['s2', 's3', 's4'].includes(s.id),
);
new UserSelectionTestRunner()
.withSelectedScripts(selectedScripts)
.withCategory(1, allScripts.map((s) => s.script))
// act
.run((sut) => {
sut.selectOnly(scriptsToSelect.map((s) => s.script));
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(scriptsToSelect)
.expectFinalScriptsInEvent(0, scriptsToSelect);
});
describe('selectAll selects as expected', () => {
// arrange
const expected = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
];
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(1, expected.map((s) => s.script))
// act
.run((sut) => {
sut.selectAll();
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(expected)
.expectFinalScriptsInEvent(0, expected);
});
describe('addOrUpdateSelectedScript', () => {
describe('adds when item does not exist', () => {
// arrange
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
const expected = [new SelectedScript(scripts[0], false)];
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(1, scripts)
// act
.run((sut) => {
sut.addOrUpdateSelectedScript(scripts[0].id, false);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(expected)
.expectFinalScriptsInEvent(0, expected);
});
describe('updates when item exists', () => {
// arrange
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
const existing = new SelectedScript(scripts[0], false);
const expected = new SelectedScript(scripts[0], true);
new UserSelectionTestRunner()
.withSelectedScripts([existing])
.withCategory(1, scripts)
// act
.run((sut) => {
sut.addOrUpdateSelectedScript(expected.id, expected.revert);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts([expected])
.expectFinalScriptsInEvent(0, [expected]);
});
});
describe('removeAllInCategory', () => {
describe('does nothing when nothing exists', () => {
// arrange
const categoryId = 99;
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(categoryId, scripts)
// act
.run((sut) => {
sut.removeAllInCategory(categoryId);
})
// assert
.expectTotalFiredEvents(0)
.expectFinalScripts([]);
});
describe('removes all when all exists', () => {
// arrange
const categoryId = 34;
const scripts = [new SelectedScriptStub('s1'), new SelectedScriptStub('s2')];
new UserSelectionTestRunner()
.withSelectedScripts(scripts)
.withCategory(categoryId, scripts.map((s) => s.script))
// act
.run((sut) => {
sut.removeAllInCategory(categoryId);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts([]);
});
describe('removes existing when some exists', () => {
// arrange
const categoryId = 55;
const existing = [new ScriptStub('s1'), new ScriptStub('s2')];
const notExisting = [new ScriptStub('s3'), new ScriptStub('s4')];
new UserSelectionTestRunner()
.withSelectedScripts(existing.map((script) => new SelectedScript(script, false)))
.withCategory(categoryId, [...existing, ...notExisting])
// act
.run((sut) => {
sut.removeAllInCategory(categoryId);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts([]);
});
});
describe('addOrUpdateAllInCategory', () => {
describe('when all already exists', () => {
describe('does nothing if nothing is changed', () => {
// arrange
const allScripts = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
new SelectedScriptStub('s3', false),
new SelectedScriptStub('s4', false),
const categoryId = 55;
const existingScripts = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
];
const selectedScripts = allScripts.filter(
(s) => ['s1', 's2', 's3'].includes(s.id));
new UserSelectionTestRunner()
.withSelectedScripts(selectedScripts)
.withCategory(1, allScripts.map((s) => s.script))
.withSelectedScripts(existingScripts)
.withCategory(categoryId, existingScripts.map((s) => s.script))
// act
.run((sut) => {
sut.deselectAll();
})
.run((sut) => {
sut.addOrUpdateAllInCategory(categoryId);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts([])
.expectFinalScriptsInEvent(0, []);
});
describe('selectOnly selects expected', () => {
.expectTotalFiredEvents(0)
.expectFinalScripts(existingScripts);
});
describe('changes revert status of all', () => {
// arrange
const allScripts = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
new SelectedScriptStub('s3', false),
new SelectedScriptStub('s4', false),
const newStatus = false;
const scripts = [
new SelectedScriptStub('e1', !newStatus),
new SelectedScriptStub('e2', !newStatus),
new SelectedScriptStub('e3', newStatus),
];
const selectedScripts = allScripts.filter(
(s) => ['s1', 's2', 's3'].includes(s.id));
const scriptsToSelect = allScripts.filter(
(s) => ['s2', 's3', 's4'].includes(s.id));
const expectedScripts = scripts.map((s) => new SelectedScript(s.script, newStatus));
const categoryId = 31;
new UserSelectionTestRunner()
.withSelectedScripts(selectedScripts)
.withCategory(1, allScripts.map((s) => s.script))
.withSelectedScripts(scripts)
.withCategory(categoryId, scripts.map((s) => s.script))
// act
.run((sut) => {
sut.selectOnly(scriptsToSelect.map((s) => s.script));
})
.run((sut) => {
sut.addOrUpdateAllInCategory(categoryId, newStatus);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(scriptsToSelect)
.expectFinalScriptsInEvent(0, scriptsToSelect);
.expectTotalFiredEvents(1)
.expectFinalScripts(expectedScripts)
.expectFinalScriptsInEvent(0, expectedScripts);
});
});
describe('selectAll selects as expected', () => {
// arrange
const expected = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
];
new UserSelectionTestRunner()
describe('when nothing exists; adds all with given revert status', () => {
const revertStatuses = [true, false];
for (const revertStatus of revertStatuses) {
describe(`when revert status is ${revertStatus}`, () => {
// arrange
const categoryId = 1;
const scripts = [
new SelectedScriptStub('s1', !revertStatus),
new SelectedScriptStub('s2', !revertStatus),
];
const expected = scripts.map((s) => new SelectedScript(s.script, revertStatus));
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(1, expected.map((s) => s.script))
// act
.withCategory(categoryId, scripts.map((s) => s.script))
// act
.run((sut) => {
sut.selectAll();
sut.addOrUpdateAllInCategory(categoryId, revertStatus);
})
// assert
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(expected)
.expectFinalScriptsInEvent(0, expected);
});
}
});
describe('addOrUpdateSelectedScript', () => {
describe('adds when item does not exist', () => {
// arrange
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
const expected = [ new SelectedScript(scripts[0], false) ];
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(1, scripts)
// act
.run((sut) => {
sut.addOrUpdateSelectedScript(scripts[0].id, false);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(expected)
.expectFinalScriptsInEvent(0, expected);
});
describe('updates when item exists', () => {
// arrange
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
const existing = new SelectedScript(scripts[0], false);
const expected = new SelectedScript(scripts[0], true);
new UserSelectionTestRunner()
.withSelectedScripts([existing])
.withCategory(1, scripts)
// act
.run((sut) => {
sut.addOrUpdateSelectedScript(expected.id, expected.revert);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts([ expected ])
.expectFinalScriptsInEvent(0, [ expected ]);
});
describe('when some exists; changes revert status of all', () => {
// arrange
const newStatus = true;
const existing = [
new SelectedScriptStub('e1', true),
new SelectedScriptStub('e2', false),
];
const notExisting = [
new SelectedScriptStub('n3', true),
new SelectedScriptStub('n4', false),
];
const allScripts = [...existing, ...notExisting];
const expectedScripts = allScripts.map((s) => new SelectedScript(s.script, newStatus));
const categoryId = 77;
new UserSelectionTestRunner()
.withSelectedScripts(existing)
.withCategory(categoryId, allScripts.map((s) => s.script))
// act
.run((sut) => {
sut.addOrUpdateAllInCategory(categoryId, newStatus);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(expectedScripts)
.expectFinalScriptsInEvent(0, expectedScripts);
});
describe('removeAllInCategory', () => {
describe('does nothing when nothing exists', () => {
// arrange
const categoryId = 99;
const scripts = [new ScriptStub('s1'), new ScriptStub('s2')];
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(categoryId, scripts)
// act
.run((sut) => {
sut.removeAllInCategory(categoryId);
})
// assert
.expectTotalFiredEvents(0)
.expectFinalScripts([]);
});
describe('removes all when all exists', () => {
// arrange
const categoryId = 34;
const scripts = [new SelectedScriptStub('s1'), new SelectedScriptStub('s2')];
new UserSelectionTestRunner()
.withSelectedScripts(scripts)
.withCategory(categoryId, scripts.map((s) => s.script))
// act
.run((sut) => {
sut.removeAllInCategory(categoryId);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts([]);
});
describe('removes existing when some exists', () => {
// arrange
const categoryId = 55;
const existing = [new ScriptStub('s1'), new ScriptStub('s2')];
const notExisting = [new ScriptStub('s3'), new ScriptStub('s4')];
new UserSelectionTestRunner()
.withSelectedScripts(existing.map((script) => new SelectedScript(script, false)))
.withCategory(categoryId, [ ...existing, ...notExisting ])
// act
.run((sut) => {
sut.removeAllInCategory(categoryId);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts([]);
});
});
describe('isSelected', () => {
it('returns false when not selected', () => {
// arrange
const selectedScript = new ScriptStub('selected');
const notSelectedScript = new ScriptStub('not selected');
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1)
.withScripts(selectedScript, notSelectedScript));
const sut = new UserSelection(collection, [new SelectedScript(selectedScript, false)]);
// act
const actual = sut.isSelected(notSelectedScript.id);
// assert
expect(actual).to.equal(false);
});
describe('addOrUpdateAllInCategory', () => {
describe('when all already exists', () => {
describe('does nothing if nothing is changed', () => {
// arrange
const categoryId = 55;
const existingScripts = [
new SelectedScriptStub('s1', false),
new SelectedScriptStub('s2', false),
];
new UserSelectionTestRunner()
.withSelectedScripts(existingScripts)
.withCategory(categoryId, existingScripts.map((s) => s.script))
// act
.run((sut) => {
sut.addOrUpdateAllInCategory(categoryId);
})
// assert
.expectTotalFiredEvents(0)
.expectFinalScripts(existingScripts);
});
describe('changes revert status of all', () => {
// arrange
const newStatus = false;
const scripts = [
new SelectedScriptStub('e1', !newStatus),
new SelectedScriptStub('e2', !newStatus),
new SelectedScriptStub('e3', newStatus),
];
const expectedScripts = scripts.map((s) => new SelectedScript(s.script, newStatus));
const categoryId = 31;
new UserSelectionTestRunner()
.withSelectedScripts(scripts)
.withCategory(categoryId, scripts.map((s) => s.script))
// act
.run((sut) => {
sut.addOrUpdateAllInCategory(categoryId, newStatus);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(expectedScripts)
.expectFinalScriptsInEvent(0, expectedScripts);
});
});
describe('when nothing exists; adds all with given revert status', () => {
const revertStatuses = [ true, false ];
for (const revertStatus of revertStatuses) {
describe(`when revert status is ${revertStatus}`, () => {
// arrange
const categoryId = 1;
const scripts = [
new SelectedScriptStub('s1', !revertStatus),
new SelectedScriptStub('s2', !revertStatus),
];
const expected = scripts.map((s) => new SelectedScript(s.script, revertStatus));
new UserSelectionTestRunner()
.withSelectedScripts([])
.withCategory(categoryId, scripts.map((s) => s.script))
// act
.run((sut) => {
sut.addOrUpdateAllInCategory(categoryId, revertStatus);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(expected)
.expectFinalScriptsInEvent(0, expected);
});
}
});
describe('when some exists; changes revert status of all', () => {
// arrange
const newStatus = true;
const existing = [
new SelectedScriptStub('e1', true),
new SelectedScriptStub('e2', false),
];
const notExisting = [
new SelectedScriptStub('n3', true),
new SelectedScriptStub('n4', false),
];
const allScripts = [ ...existing, ...notExisting ];
const expectedScripts = allScripts.map((s) => new SelectedScript(s.script, newStatus));
const categoryId = 77;
new UserSelectionTestRunner()
.withSelectedScripts(existing)
.withCategory(categoryId, allScripts.map((s) => s.script))
// act
.run((sut) => {
sut.addOrUpdateAllInCategory(categoryId, newStatus);
})
// assert
.expectTotalFiredEvents(1)
.expectFinalScripts(expectedScripts)
.expectFinalScriptsInEvent(0, expectedScripts);
});
it('returns true when selected', () => {
// arrange
const selectedScript = new ScriptStub('selected');
const notSelectedScript = new ScriptStub('not selected');
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1)
.withScripts(selectedScript, notSelectedScript));
const sut = new UserSelection(collection, [new SelectedScript(selectedScript, false)]);
// act
const actual = sut.isSelected(selectedScript.id);
// assert
expect(actual).to.equal(true);
});
describe('isSelected', () => {
it('returns false when not selected', () => {
// arrange
const selectedScript = new ScriptStub('selected');
const notSelectedScript = new ScriptStub('not selected');
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1)
.withScripts(selectedScript, notSelectedScript));
const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]);
// act
const actual = sut.isSelected(notSelectedScript.id);
// assert
expect(actual).to.equal(false);
});
it('returns true when selected', () => {
// arrange
const selectedScript = new ScriptStub('selected');
const notSelectedScript = new ScriptStub('not selected');
const collection = new CategoryCollectionStub()
.withAction(new CategoryStub(1)
.withScripts(selectedScript, notSelectedScript));
const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]);
// act
const actual = sut.isSelected(selectedScript.id);
// assert
expect(actual).to.equal(true);
});
});
describe('category state', () => {
describe('when no scripts are selected', () => {
// arrange
const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2');
const collection = new CategoryCollectionStub().withAction(category);
const sut = new UserSelection(collection, []);
it('areAllSelected returns false', () => {
// act
const actual = sut.areAllSelected(category);
// assert
expect(actual).to.equal(false);
});
it('isAnySelected returns false', () => {
// act
const actual = sut.isAnySelected(category);
// assert
expect(actual).to.equal(false);
});
});
describe('category state', () => {
describe('when no scripts are selected', () => {
// arrange
const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2');
const collection = new CategoryCollectionStub().withAction(category);
const sut = new UserSelection(collection, [ ]);
it('areAllSelected returns false', () => {
// act
const actual = sut.areAllSelected(category);
// assert
expect(actual).to.equal(false);
});
it('isAnySelected returns false', () => {
// act
const actual = sut.isAnySelected(category);
// assert
expect(actual).to.equal(false);
});
});
describe('when no subscript exists in selected scripts', () => {
// arrange
const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2');
const selectedScript = new ScriptStub('selected');
const collection = new CategoryCollectionStub()
.withAction(category)
.withAction(new CategoryStub(22).withScript(selectedScript));
const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]);
it('areAllSelected returns false', () => {
// act
const actual = sut.areAllSelected(category);
// assert
expect(actual).to.equal(false);
});
it('isAnySelected returns false', () => {
// act
const actual = sut.isAnySelected(category);
// assert
expect(actual).to.equal(false);
});
});
describe('when one of the scripts are selected', () => {
// arrange
const selectedScript = new ScriptStub('selected');
const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2')
.withCategory(new CategoryStub(12).withScript(selectedScript));
const collection = new CategoryCollectionStub().withAction(category);
const sut = new UserSelection(collection, [ new SelectedScript(selectedScript, false) ]);
it('areAllSelected returns false', () => {
// act
const actual = sut.areAllSelected(category);
// assert
expect(actual).to.equal(false);
});
it('isAnySelected returns true', () => {
// act
const actual = sut.isAnySelected(category);
// assert
expect(actual).to.equal(true);
});
});
describe('when all scripts are selected', () => {
// arrange
const firstSelectedScript = new ScriptStub('selected1');
const secondSelectedScript = new ScriptStub('selected2');
const category = new CategoryStub(1)
.withScript(firstSelectedScript)
.withCategory(new CategoryStub(12).withScript(secondSelectedScript));
const collection = new CategoryCollectionStub().withAction(category);
const sut = new UserSelection(collection,
[ firstSelectedScript, secondSelectedScript ].map((s) => new SelectedScript(s, false)));
it('areAllSelected returns true', () => {
// act
const actual = sut.areAllSelected(category);
// assert
expect(actual).to.equal(true);
});
it('isAnySelected returns true', () => {
// act
const actual = sut.isAnySelected(category);
// assert
expect(actual).to.equal(true);
});
});
describe('when no subscript exists in selected scripts', () => {
// arrange
const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2');
const selectedScript = new ScriptStub('selected');
const collection = new CategoryCollectionStub()
.withAction(category)
.withAction(new CategoryStub(22).withScript(selectedScript));
const sut = new UserSelection(collection, [new SelectedScript(selectedScript, false)]);
it('areAllSelected returns false', () => {
// act
const actual = sut.areAllSelected(category);
// assert
expect(actual).to.equal(false);
});
it('isAnySelected returns false', () => {
// act
const actual = sut.isAnySelected(category);
// assert
expect(actual).to.equal(false);
});
});
describe('when one of the scripts are selected', () => {
// arrange
const selectedScript = new ScriptStub('selected');
const category = new CategoryStub(1)
.withScriptIds('non-selected-script-1', 'non-selected-script-2')
.withCategory(new CategoryStub(12).withScript(selectedScript));
const collection = new CategoryCollectionStub().withAction(category);
const sut = new UserSelection(collection, [new SelectedScript(selectedScript, false)]);
it('areAllSelected returns false', () => {
// act
const actual = sut.areAllSelected(category);
// assert
expect(actual).to.equal(false);
});
it('isAnySelected returns true', () => {
// act
const actual = sut.isAnySelected(category);
// assert
expect(actual).to.equal(true);
});
});
describe('when all scripts are selected', () => {
// arrange
const firstSelectedScript = new ScriptStub('selected1');
const secondSelectedScript = new ScriptStub('selected2');
const category = new CategoryStub(1)
.withScript(firstSelectedScript)
.withCategory(new CategoryStub(12).withScript(secondSelectedScript));
const collection = new CategoryCollectionStub().withAction(category);
const selectedScripts = [firstSelectedScript, secondSelectedScript]
.map((s) => new SelectedScript(s, false));
const sut = new UserSelection(collection, selectedScripts);
it('areAllSelected returns true', () => {
// act
const actual = sut.areAllSelected(category);
// assert
expect(actual).to.equal(true);
});
it('isAnySelected returns true', () => {
// act
const actual = sut.isAnySelected(category);
// assert
expect(actual).to.equal(true);
});
});
});
});

View File

@@ -7,72 +7,83 @@ import { UserSelection } from '@/application/Context/State/Selection/UserSelecti
import { IScript } from '@/domain/IScript';
export class UserSelectionTestRunner {
private readonly collection = new CategoryCollectionStub();
private existingScripts: readonly SelectedScript[] = [];
private events: Array<readonly SelectedScript[]> = [];
private sut: UserSelection;
private readonly collection = new CategoryCollectionStub();
public withCategory(categoryId: number, scripts: readonly IScript[]) {
const category = new CategoryStub(categoryId)
.withScripts(...scripts);
this.collection
.withAction(category);
return this;
}
public withSelectedScripts(existingScripts: readonly SelectedScript[]) {
this.existingScripts = existingScripts;
return this;
}
public run(runner?: (sut: UserSelection) => void) {
this.sut = this.createSut();
if (runner) {
runner(this.sut);
}
return this;
}
public expectTotalFiredEvents(amount: number) {
const testName = amount === 0 ? 'does not fire changed event' : `fires changed event ${amount} times`;
it(testName, () => {
expect(this.events).to.have.lengthOf(amount);
});
return this;
}
public expectFinalScripts(finalScripts: readonly SelectedScript[]) {
expectSameScripts(finalScripts, this.sut.selectedScripts);
return this;
}
public expectFinalScriptsInEvent(eventIndex: number, finalScripts: readonly SelectedScript[]) {
expectSameScripts(this.events[eventIndex], finalScripts);
return this;
}
private createSut(): UserSelection {
const sut = new UserSelection(this.collection, this.existingScripts);
sut.changed.on((s) => this.events.push(s));
return sut;
private existingScripts: readonly SelectedScript[] = [];
private events: Array<readonly SelectedScript[]> = [];
private sut: UserSelection;
public withCategory(categoryId: number, scripts: readonly IScript[]) {
const category = new CategoryStub(categoryId)
.withScripts(...scripts);
this.collection
.withAction(category);
return this;
}
public withSelectedScripts(existingScripts: readonly SelectedScript[]) {
this.existingScripts = existingScripts;
return this;
}
public run(runner?: (sut: UserSelection) => void) {
this.sut = this.createSut();
if (runner) {
runner(this.sut);
}
return this;
}
public expectTotalFiredEvents(amount: number) {
const testName = amount === 0 ? 'does not fire changed event' : `fires changed event ${amount} times`;
it(testName, () => {
expect(this.events).to.have.lengthOf(amount);
});
return this;
}
public expectFinalScripts(finalScripts: readonly SelectedScript[]) {
expectSameScripts(finalScripts, this.sut.selectedScripts);
return this;
}
public expectFinalScriptsInEvent(eventIndex: number, finalScripts: readonly SelectedScript[]) {
expectSameScripts(this.events[eventIndex], finalScripts);
return this;
}
private createSut(): UserSelection {
const sut = new UserSelection(this.collection, this.existingScripts);
sut.changed.on((s) => this.events.push(s));
return sut;
}
}
function expectSameScripts(actual: readonly SelectedScript[], expected: readonly SelectedScript[]) {
it('has same expected scripts', () => {
const existingScriptIds = expected.map((script) => script.id).sort();
const expectedScriptIds = actual.map((script) => script.id).sort();
expect(existingScriptIds).to.deep.equal(expectedScriptIds);
});
it('has expected revert state', () => {
const scriptsWithDifferentStatus = actual
.filter((script) => {
const other = expected.find((existing) => existing.id === script.id);
if (!other) {
throw new Error(`Script "${script.id}" does not exist in expected scripts: ${JSON.stringify(expected, null, '\t')}`);
}
return script.revert !== other.revert;
});
expect(scriptsWithDifferentStatus).to.have
.lengthOf(0, 'Scripts with different statuses:\n' + scriptsWithDifferentStatus
.map((s) =>
`[id: ${s.id}, actual status: ${s.revert}, ` +
`expected status: ${expected.find((existing) => existing.id === s.id).revert}]`)
.join(' , '),
);
});
it('has same expected scripts', () => {
const existingScriptIds = expected.map((script) => script.id).sort();
const expectedScriptIds = actual.map((script) => script.id).sort();
expect(existingScriptIds).to.deep.equal(expectedScriptIds);
});
it('has expected revert state', () => {
const scriptsWithDifferentStatus = actual
.filter((script) => {
const other = expected.find((existing) => existing.id === script.id);
if (!other) {
throw new Error(`Script "${script.id}" does not exist in expected scripts: ${JSON.stringify(expected, null, '\t')}`);
}
return script.revert !== other.revert;
});
expect(scriptsWithDifferentStatus).to.have.lengthOf(
0,
`Scripts with different statuses:\n${
scriptsWithDifferentStatus
.map((s) => `[id: ${s.id}, actual status: ${s.revert}, `
+ `expected status: ${expected.find((existing) => existing.id === s.id).revert}]`)
.join(' , ')
}`,
);
});
}