Refactor to improve iterations

- Use function abstractions (such as map, reduce, filter etc.) over
  for-of loops to gain benefits of having less side effects and easier
  readability.
- Enable `downLevelIterations` for writing modern code with lazy evaluation.
- Refactor for of loops to named abstractions to clearly express their
  intentions without needing to analyse the loop itself.
- Add missing cases for changes that had no tests.
This commit is contained in:
undergroundwires
2022-01-04 21:45:22 +01:00
parent bd23faa28f
commit 31f70913a2
35 changed files with 342 additions and 343 deletions

View File

@@ -25,7 +25,7 @@ function testExpectedInstanceTypes<T>(
) {
describe('create returns expected instances', () => {
// arrange
for (const language of Array.from(expectedTypes.keys())) {
for (const language of expectedTypes.keys()) {
it(ScriptingLanguage[language], () => {
// act
const expected = expectedTypes.get(language);

View File

@@ -197,7 +197,7 @@ class UserScriptGeneratorMock implements IUserScriptGenerator {
selectedScripts: readonly SelectedScript[],
scriptingDefinition: IScriptingDefinition,
): IUserScript {
for (const [parameters, result] of Array.from(this.prePlanned)) {
for (const [parameters, result] of this.prePlanned) {
if (selectedScripts === parameters.scripts
&& scriptingDefinition === parameters.definition) {
return result;

View File

@@ -13,16 +13,23 @@ describe('CodeChangedEvent', () => {
it('throws when code position is out of range', () => {
// arrange
const code = 'singleline code';
const nonExistingLine1 = 2;
const nonExistingLine2 = 31;
const newScripts = new Map<SelectedScript, ICodePosition>([
[new SelectedScriptStub('1'), new CodePosition(0, 2 /* nonexisting line */)],
[new SelectedScriptStub('1'), new CodePosition(0, nonExistingLine1)],
[new SelectedScriptStub('2'), new CodePosition(0, nonExistingLine2)],
]);
// act
const act = () => new CodeChangedEventBuilder()
.withCode(code)
.withNewScripts(newScripts)
.build();
let errorText = '';
try {
new CodeChangedEventBuilder()
.withCode(code)
.withNewScripts(newScripts)
.build();
} catch (error) { errorText = error.message; }
// assert
expect(act).to.throw();
expect(errorText).to.include(nonExistingLine1);
expect(errorText).to.include(nonExistingLine2);
});
describe('does not throw with valid code position', () => {
// arrange

View File

@@ -151,6 +151,14 @@ describe('ExpressionsCompiler', () => {
.withArgument('parameter2', 'value'),
expectedError: 'parameter value(s) not provided for: "parameter1", "parameter3" but used in code',
},
{
name: 'parameter names are not repeated in error message',
expressions: [
new ExpressionStub().withParameterNames(['parameter1', 'parameter1', 'parameter2', 'parameter2'], false),
],
args: new FunctionCallArgumentCollectionStub(),
expectedError: 'parameter value(s) not provided for: "parameter1", "parameter2" but used in code',
},
];
for (const testCase of testCases) {
it(testCase.name, () => {

View File

@@ -31,6 +31,31 @@ describe('RegexParser', () => {
});
}
});
it('throws when position is invalid', () => {
// arrange
const regexMatchingEmpty = /^/gm; /* expressions cannot be empty */
const code = 'unimportant';
const expectedErrorParts = [
`[${RegexParserConcrete.constructor.name}]`,
'invalid script position',
`Regex: ${regexMatchingEmpty}`,
`Code: ${code}`,
];
const sut = new RegexParserConcrete(regexMatchingEmpty);
// act
let error: string;
try {
sut.findExpressions(code);
} catch (err) {
error = err.message;
}
// assert
expect(
expectedErrorParts.every((part) => error.includes(part)),
`Expected parts: ${expectedErrorParts.join(', ')}`
+ `Actual error: ${error}`,
);
});
describe('matches regex as expected', () => {
// arrange
const testCases = [

View File

@@ -70,7 +70,7 @@ describe('FunctionCallCompiler', () => {
},
{
name: 'provided: an unexpected parameter, when: none required',
functionParameters: undefined,
functionParameters: [],
callParameters: ['unexpected-call-parameter'],
expectedError:
`Function "${functionName}" has unexpected parameter(s) provided: "unexpected-call-parameter"`
@@ -90,10 +90,10 @@ describe('FunctionCallCompiler', () => {
const func = new SharedFunctionStub(FunctionBodyType.Code)
.withName('test-function-name')
.withParameterNames(...testCase.functionParameters);
let params: FunctionCallParametersData = {};
for (const parameter of testCase.callParameters) {
params = { ...params, [parameter]: 'defined-parameter-value ' };
}
const params = testCase.callParameters
.reduce((result, parameter) => {
return { ...result, [parameter]: 'defined-parameter-value ' };
}, {} as FunctionCallParametersData);
const call = new FunctionCallStub()
.withFunctionName(func.name)
.withArguments(params);

View File

@@ -49,15 +49,11 @@ async function findBadLineNumbers(fileContent: string): Promise<number[]> {
function findLineNumbersEndingWith(content: string, ending: string): number[] {
sanityCheck(content, ending);
const lines = content.split(/\r\n|\r|\n/);
const results = new Array<number>();
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
if (line.trim().endsWith(ending)) {
results.push((i + 1 /* first line is 1 not 0 */));
}
}
return results;
return content
.split(/\r\n|\r|\n/)
.map((line, index) => ({ text: line, index }))
.filter((line) => line.text.trim().endsWith(ending))
.map((line) => line.index + 1 /* first line is 1, not 0 */);
}
function sanityCheck(content: string, ending: string): void {