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

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