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

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