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

@@ -6,82 +6,82 @@ import { CompositeExpressionParser } from '@/application/Parser/Script/Compiler/
import { ExpressionStub } from '@tests/unit/stubs/ExpressionStub';
describe('CompositeExpressionParser', () => {
describe('ctor', () => {
it('throws if one of the parsers is undefined', () => {
// arrange
const expectedError = 'undefined leaf';
const parsers: readonly IExpressionParser[] = [ undefined, mockParser() ];
// act
const act = () => new CompositeExpressionParser(parsers);
// assert
expect(act).to.throw(expectedError);
});
describe('ctor', () => {
it('throws if one of the parsers is undefined', () => {
// arrange
const expectedError = 'undefined leaf';
const parsers: readonly IExpressionParser[] = [undefined, mockParser()];
// act
const act = () => new CompositeExpressionParser(parsers);
// assert
expect(act).to.throw(expectedError);
});
describe('findExpressions', () => {
describe('returns result from parsers as expected', () => {
// arrange
const pool = [
new ExpressionStub(), new ExpressionStub(), new ExpressionStub(),
new ExpressionStub(), new ExpressionStub(),
];
const testCases = [
{
name: 'from single parsing none',
parsers: [ mockParser() ],
expected: [],
},
{
name: 'from single parsing single',
parsers: [ mockParser(pool[0]) ],
expected: [ pool[0] ],
},
{
name: 'from single parsing multiple',
parsers: [ mockParser(pool[0], pool[1]) ],
expected: [ pool[0], pool[1] ],
},
{
name: 'from multiple parsers with each parsing single',
parsers: [
mockParser(pool[0]),
mockParser(pool[1]),
mockParser(pool[2]),
],
expected: [ pool[0], pool[1], pool[2] ],
},
{
name: 'from multiple parsers with each parsing multiple',
parsers: [
mockParser(pool[0], pool[1]),
mockParser(pool[2], pool[3], pool[4]) ],
expected: [ pool[0], pool[1], pool[2], pool[3], pool[4] ],
},
{
name: 'from multiple parsers with only some parsing',
parsers: [
mockParser(pool[0], pool[1]),
mockParser(),
mockParser(pool[2]),
mockParser(),
],
expected: [ pool[0], pool[1], pool[2] ],
},
];
for (const testCase of testCases) {
it(testCase.name, () => {
const sut = new CompositeExpressionParser(testCase.parsers);
// act
const result = sut.findExpressions('non-important-code');
// expect
expect(result).to.deep.equal(testCase.expected);
});
}
});
describe('findExpressions', () => {
describe('returns result from parsers as expected', () => {
// arrange
const pool = [
new ExpressionStub(), new ExpressionStub(), new ExpressionStub(),
new ExpressionStub(), new ExpressionStub(),
];
const testCases = [
{
name: 'from single parsing none',
parsers: [mockParser()],
expected: [],
},
{
name: 'from single parsing single',
parsers: [mockParser(pool[0])],
expected: [pool[0]],
},
{
name: 'from single parsing multiple',
parsers: [mockParser(pool[0], pool[1])],
expected: [pool[0], pool[1]],
},
{
name: 'from multiple parsers with each parsing single',
parsers: [
mockParser(pool[0]),
mockParser(pool[1]),
mockParser(pool[2]),
],
expected: [pool[0], pool[1], pool[2]],
},
{
name: 'from multiple parsers with each parsing multiple',
parsers: [
mockParser(pool[0], pool[1]),
mockParser(pool[2], pool[3], pool[4])],
expected: [pool[0], pool[1], pool[2], pool[3], pool[4]],
},
{
name: 'from multiple parsers with only some parsing',
parsers: [
mockParser(pool[0], pool[1]),
mockParser(),
mockParser(pool[2]),
mockParser(),
],
expected: [pool[0], pool[1], pool[2]],
},
];
for (const testCase of testCases) {
it(testCase.name, () => {
const sut = new CompositeExpressionParser(testCase.parsers);
// act
const result = sut.findExpressions('non-important-code');
// expect
expect(result).to.deep.equal(testCase.expected);
});
}
});
});
});
function mockParser(...result: IExpression[]): IExpressionParser {
return {
findExpressions: () => result,
};
return {
findExpressions: () => result,
};
}

View File

@@ -3,133 +3,153 @@ import { expect } from 'chai';
import { ExpressionRegexBuilder } from '@/application/Parser/Script/Compiler/Expressions/Parser/Regex/ExpressionRegexBuilder';
describe('ExpressionRegexBuilder', () => {
describe('expectCharacters', () => {
describe('escape single as expected', () => {
const charactersToEscape = [ '.', '$' ];
for (const character of charactersToEscape) {
it(character, () => {
runRegExTest(
// act
(act) => act.expectCharacters(character),
// assert
`\\${character}`);
});
}
});
it('escapes multiple as expected', () => {
runRegExTest(
// act
(act) => act.expectCharacters('.I have no $$.'),
// assert
'\\.I have no \\$\\\$\\.');
});
it('adds as expected', () => {
runRegExTest(
// act
(act) => act.expectCharacters('return as it is'),
// assert
'return as it is');
describe('expectCharacters', () => {
describe('escape single as expected', () => {
const charactersToEscape = ['.', '$'];
for (const character of charactersToEscape) {
it(character, () => {
runRegExTest(
// act
(act) => act.expectCharacters(character),
// assert
`\\${character}`,
);
});
}
});
it('expectOneOrMoreWhitespaces', () => {
it('escapes multiple as expected', () => {
runRegExTest(
// act
(act) => act.expectCharacters('.I have no $$.'),
// assert
'\\.I have no \\$\\$\\.',
);
});
it('adds as expected', () => {
runRegExTest(
// act
(act) => act.expectCharacters('return as it is'),
// assert
'return as it is',
);
});
});
it('expectOneOrMoreWhitespaces', () => {
runRegExTest(
// act
(act) => act.expectOneOrMoreWhitespaces(),
// assert
'\\s+',
);
});
it('matchPipeline', () => {
runRegExTest(
// act
(act) => act.matchPipeline(),
// assert
'\\s*(\\|\\s*.+?)?',
);
});
it('matchUntilFirstWhitespace', () => {
runRegExTest(
// act
(act) => act.matchUntilFirstWhitespace(),
// assert
'([^|\\s]+)',
);
});
it('matchAnythingExceptSurroundingWhitespaces', () => {
runRegExTest(
// act
(act) => act.matchAnythingExceptSurroundingWhitespaces(),
// assert
'\\s*(.+?)\\s*',
);
});
it('expectExpressionStart', () => {
runRegExTest(
// act
(act) => act.expectExpressionStart(),
// assert
'{{\\s*',
);
});
it('expectExpressionEnd', () => {
runRegExTest(
// act
(act) => act.expectExpressionEnd(),
// assert
'\\s*}}',
);
});
describe('buildRegExp', () => {
it('sets global flag', () => {
// arrange
const expected = 'g';
const sut = new ExpressionRegexBuilder()
.expectOneOrMoreWhitespaces();
// act
const actual = sut.buildRegExp().flags;
// assert
expect(actual).to.equal(expected);
});
describe('can combine multiple parts', () => {
it('with', () => {
runRegExTest(
// act
(act) => act.expectOneOrMoreWhitespaces(),
// assert
'\\s+');
});
it('matchPipeline', () => {
(sut) => sut
// act
// {{ $with }}
.expectExpressionStart()
.expectCharacters('with')
.expectOneOrMoreWhitespaces()
.expectCharacters('$')
.matchUntilFirstWhitespace()
.expectExpressionEnd()
// scope
.matchAnythingExceptSurroundingWhitespaces()
// {{ end }}
.expectExpressionStart()
.expectCharacters('end')
.expectExpressionEnd(),
// assert
'{{\\s*with\\s+\\$([^|\\s]+)\\s*}}\\s*(.+?)\\s*{{\\s*end\\s*}}',
);
});
it('scoped substitution', () => {
runRegExTest(
// act
(act) => act.matchPipeline(),
// assert
'\\s*(\\|\\s*.+?)?');
});
it('matchUntilFirstWhitespace', () => {
(sut) => sut
// act
.expectExpressionStart().expectCharacters('.')
.matchPipeline()
.expectExpressionEnd(),
// assert
'{{\\s*\\.\\s*(\\|\\s*.+?)?\\s*}}',
);
});
it('parameter substitution', () => {
runRegExTest(
// act
(act) => act.matchUntilFirstWhitespace(),
// assert
'([^|\\s]+)');
});
it('matchAnythingExceptSurroundingWhitespaces', () => {
runRegExTest(
// act
(act) => act.matchAnythingExceptSurroundingWhitespaces(),
// assert
'\\s*(.+?)\\s*');
});
it('expectExpressionStart', () => {
runRegExTest(
// act
(act) => act.expectExpressionStart(),
// assert
'{{\\s*');
});
it('expectExpressionEnd', () => {
runRegExTest(
// act
(act) => act.expectExpressionEnd(),
// assert
'\\s*}}');
});
describe('buildRegExp', () => {
it('sets global flag', () => {
// arrange
const expected = 'g';
const sut = new ExpressionRegexBuilder()
.expectOneOrMoreWhitespaces();
// act
const actual = sut.buildRegExp().flags;
// assert
expect(actual).to.equal(expected);
});
describe('can combine multiple parts', () => {
it('with', () => {
runRegExTest((sut) => sut
// act
.expectExpressionStart().expectCharacters('with').expectOneOrMoreWhitespaces().expectCharacters('$')
.matchUntilFirstWhitespace()
.expectExpressionEnd()
.matchAnythingExceptSurroundingWhitespaces()
.expectExpressionStart().expectCharacters('end').expectExpressionEnd(),
// assert
'{{\\s*with\\s+\\$([^|\\s]+)\\s*}}\\s*(.+?)\\s*{{\\s*end\\s*}}',
);
});
it('scoped substitution', () => {
runRegExTest((sut) => sut
// act
.expectExpressionStart().expectCharacters('.')
.matchPipeline()
.expectExpressionEnd(),
// assert
'{{\\s*\\.\\s*(\\|\\s*.+?)?\\s*}}',
);
});
it('parameter substitution', () => {
runRegExTest((sut) => sut
// act
.expectExpressionStart().expectCharacters('$')
.matchUntilFirstWhitespace()
.matchPipeline()
.expectExpressionEnd(),
// assert
'{{\\s*\\$([^|\\s]+)\\s*(\\|\\s*.+?)?\\s*}}',
);
});
});
(sut) => sut
// act
.expectExpressionStart().expectCharacters('$')
.matchUntilFirstWhitespace()
.matchPipeline()
.expectExpressionEnd(),
// assert
'{{\\s*\\$([^|\\s]+)\\s*(\\|\\s*.+?)?\\s*}}',
);
});
});
});
});
function runRegExTest(
act: (sut: ExpressionRegexBuilder) => ExpressionRegexBuilder,
expected: string,
) {
// arrange
const sut = new ExpressionRegexBuilder();
// act
const actual = act(sut).buildRegExp().source;
// assert
expect(actual).to.equal(expected);
act: (sut: ExpressionRegexBuilder) => ExpressionRegexBuilder,
expected: string,
) {
// arrange
const sut = new ExpressionRegexBuilder();
// act
const actual = act(sut).buildRegExp().source;
// assert
expect(actual).to.equal(expected);
}

View File

@@ -6,145 +6,148 @@ import { ExpressionPosition } from '@/application/Parser/Script/Compiler/Express
import { FunctionParameterStub } from '@tests/unit/stubs/FunctionParameterStub';
describe('RegexParser', () => {
describe('findExpressions', () => {
describe('throws when code is unexpected', () => {
// arrange
const testCases = [
{
name: 'undefined',
value: undefined,
expectedError: 'undefined code',
},
{
name: 'empty',
value: '',
expectedError: 'undefined code',
},
];
for (const testCase of testCases) {
it(`given ${testCase.name}`, () => {
const sut = new RegexParserConcrete(/unimportant/);
// act
const act = () => sut.findExpressions(testCase.value);
// assert
expect(act).to.throw(testCase.expectedError);
});
}
});
describe('matches regex as expected', () => {
// arrange
const testCases = [
{
name: 'returns no result when regex does not match',
regex: /hello/g,
code: 'world',
},
{
name: 'returns expected when regex matches single',
regex: /hello/g,
code: 'hello world',
},
{
name: 'returns expected when regex matches multiple',
regex: /l/g,
code: 'hello world',
},
];
for (const testCase of testCases) {
it(testCase.name, () => {
const expected = Array.from(testCase.code.matchAll(testCase.regex));
const matches = new Array<RegExpMatchArray>();
const builder = (m: RegExpMatchArray): IPrimitiveExpression => {
matches.push(m);
return mockPrimitiveExpression();
};
const sut = new RegexParserConcrete(testCase.regex, builder);
// act
const expressions = sut.findExpressions(testCase.code);
// assert
expect(expressions).to.have.lengthOf(matches.length);
expect(matches).to.deep.equal(expected);
});
}
});
it('sets evaluator as expected', () => {
// arrange
const expected = getEvaluatorStub();
const regex = /hello/g;
const code = 'hello';
const builder = (): IPrimitiveExpression => ({
evaluator: expected,
});
const sut = new RegexParserConcrete(regex, builder);
// act
const expressions = sut.findExpressions(code);
// assert
expect(expressions).to.have.lengthOf(1);
expect(expressions[0].evaluate === expected);
});
it('sets parameters as expected', () => {
// arrange
const expected = [
new FunctionParameterStub().withName('parameter1').withOptionality(true),
new FunctionParameterStub().withName('parameter2').withOptionality(false),
];
const regex = /hello/g;
const code = 'hello';
const builder = (): IPrimitiveExpression => ({
evaluator: getEvaluatorStub(),
parameters: expected,
});
const sut = new RegexParserConcrete(regex, builder);
// act
const expressions = sut.findExpressions(code);
// assert
expect(expressions).to.have.lengthOf(1);
expect(expressions[0].parameters.all).to.deep.equal(expected);
});
it('sets expected position', () => {
// arrange
const code = 'mate date in state is fate';
const regex = /ate/g;
const expected = [
new ExpressionPosition(1, 4),
new ExpressionPosition(6, 9),
new ExpressionPosition(15, 18),
new ExpressionPosition(23, 26),
];
const sut = new RegexParserConcrete(regex);
// act
const expressions = sut.findExpressions(code);
// assert
const actual = expressions.map((e) => e.position);
expect(actual).to.deep.equal(expected);
describe('findExpressions', () => {
describe('throws when code is unexpected', () => {
// arrange
const testCases = [
{
name: 'undefined',
value: undefined,
expectedError: 'undefined code',
},
{
name: 'empty',
value: '',
expectedError: 'undefined code',
},
];
for (const testCase of testCases) {
it(`given ${testCase.name}`, () => {
const sut = new RegexParserConcrete(/unimportant/);
// act
const act = () => sut.findExpressions(testCase.value);
// assert
expect(act).to.throw(testCase.expectedError);
});
}
});
describe('matches regex as expected', () => {
// arrange
const testCases = [
{
name: 'returns no result when regex does not match',
regex: /hello/g,
code: 'world',
},
{
name: 'returns expected when regex matches single',
regex: /hello/g,
code: 'hello world',
},
{
name: 'returns expected when regex matches multiple',
regex: /l/g,
code: 'hello world',
},
];
for (const testCase of testCases) {
it(testCase.name, () => {
const expected = Array.from(testCase.code.matchAll(testCase.regex));
const matches = new Array<RegExpMatchArray>();
const builder = (m: RegExpMatchArray): IPrimitiveExpression => {
matches.push(m);
return mockPrimitiveExpression();
};
const sut = new RegexParserConcrete(testCase.regex, builder);
// act
const expressions = sut.findExpressions(testCase.code);
// assert
expect(expressions).to.have.lengthOf(matches.length);
expect(matches).to.deep.equal(expected);
});
}
});
it('sets evaluator as expected', () => {
// arrange
const expected = getEvaluatorStub();
const regex = /hello/g;
const code = 'hello';
const builder = (): IPrimitiveExpression => ({
evaluator: expected,
});
const sut = new RegexParserConcrete(regex, builder);
// act
const expressions = sut.findExpressions(code);
// assert
expect(expressions).to.have.lengthOf(1);
expect(expressions[0].evaluate === expected);
});
it('sets parameters as expected', () => {
// arrange
const expected = [
new FunctionParameterStub().withName('parameter1').withOptionality(true),
new FunctionParameterStub().withName('parameter2').withOptionality(false),
];
const regex = /hello/g;
const code = 'hello';
const builder = (): IPrimitiveExpression => ({
evaluator: getEvaluatorStub(),
parameters: expected,
});
const sut = new RegexParserConcrete(regex, builder);
// act
const expressions = sut.findExpressions(code);
// assert
expect(expressions).to.have.lengthOf(1);
expect(expressions[0].parameters.all).to.deep.equal(expected);
});
it('sets expected position', () => {
// arrange
const code = 'mate date in state is fate';
const regex = /ate/g;
const expected = [
new ExpressionPosition(1, 4),
new ExpressionPosition(6, 9),
new ExpressionPosition(15, 18),
new ExpressionPosition(23, 26),
];
const sut = new RegexParserConcrete(regex);
// act
const expressions = sut.findExpressions(code);
// assert
const actual = expressions.map((e) => e.position);
expect(actual).to.deep.equal(expected);
});
});
});
function mockBuilder(): (match: RegExpMatchArray) => IPrimitiveExpression {
return () => ({
evaluator: getEvaluatorStub(),
});
return () => ({
evaluator: getEvaluatorStub(),
});
}
function getEvaluatorStub(): ExpressionEvaluator {
return () => undefined;
return () => undefined;
}
function mockPrimitiveExpression(): IPrimitiveExpression {
return {
evaluator: getEvaluatorStub(),
};
return {
evaluator: getEvaluatorStub(),
};
}
class RegexParserConcrete extends RegexParser {
protected regex: RegExp;
public constructor(
regex: RegExp,
private readonly builder = mockBuilder()) {
super();
this.regex = regex;
}
protected buildExpression(match: RegExpMatchArray): IPrimitiveExpression {
return this.builder(match);
}
protected regex: RegExp;
public constructor(
regex: RegExp,
private readonly builder = mockBuilder(),
) {
super();
this.regex = regex;
}
protected buildExpression(match: RegExpMatchArray): IPrimitiveExpression {
return this.builder(match);
}
}