This commit improves feedback when a line is too long to enhance developer/maintainer productivity. - Trim long lines in output to 500 characters - Show character count exceeding max line length - Refactor line formatting for better readability
197 lines
7.1 KiB
TypeScript
197 lines
7.1 KiB
TypeScript
import { describe } from 'vitest';
|
|
import type { CodeLine, InvalidCodeLine } from '@/application/Parser/Executable/Script/Validation/Analyzers/CodeValidationAnalyzer';
|
|
import { ScriptingLanguage } from '@/domain/ScriptingLanguage';
|
|
import { analyzeTooLongLines } from '@/application/Parser/Executable/Script/Validation/Analyzers/AnalyzeTooLongLines';
|
|
import { createCodeLines } from './CreateCodeLines';
|
|
import { expectInvalidCodeLines, expectSameInvalidCodeLines } from './ExpectSameInvalidCodeLines';
|
|
|
|
describe('AnalyzeTooLongLines', () => {
|
|
describe('analyzeTooLongLines', () => {
|
|
describe('returns no results for lines within the maximum length', () => {
|
|
createScriptLanguageScenarios().forEach((scenario) => {
|
|
it(scenario.description, () => {
|
|
// arrange
|
|
const expected: InvalidCodeLine[] = [];
|
|
const context = new TestContext()
|
|
.withLanguage(scenario.language)
|
|
.withLines([
|
|
'A'.repeat(scenario.maxLength),
|
|
'B'.repeat(scenario.maxLength - 1),
|
|
'C'.repeat(100),
|
|
]);
|
|
// act
|
|
const actual = context.analyze();
|
|
// assert
|
|
expectSameInvalidCodeLines(actual, expected);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('identifies a single line exceeding maximum length', () => {
|
|
createScriptLanguageScenarios().forEach((scenario) => {
|
|
it(scenario.description, () => {
|
|
// arrange
|
|
const expectedLength = scenario.maxLength + 1;
|
|
const expectedLineNumber = 2;
|
|
const context = new TestContext()
|
|
.withLanguage(scenario.language)
|
|
.withLines([
|
|
'A'.repeat(scenario.maxLength),
|
|
'B'.repeat(expectedLength),
|
|
'C'.repeat(100),
|
|
]);
|
|
// act
|
|
const actual = context.analyze();
|
|
// assert
|
|
expectInvalidCodeLines(actual, [expectedLineNumber]);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('identifies multiple lines exceeding maximum length', () => {
|
|
createScriptLanguageScenarios().forEach((scenario) => {
|
|
it(scenario.description, () => {
|
|
// arrange
|
|
const expectedInvalidLines: readonly {
|
|
readonly lineNumber: number,
|
|
readonly length: number,
|
|
}[] = [
|
|
{ lineNumber: 1, length: scenario.maxLength + 1 },
|
|
{ lineNumber: 3, length: scenario.maxLength + 2 },
|
|
];
|
|
const context = new TestContext()
|
|
.withLanguage(scenario.language)
|
|
.withLines([
|
|
'A'.repeat(expectedInvalidLines[0].length),
|
|
'B'.repeat(scenario.maxLength),
|
|
'C'.repeat(expectedInvalidLines[1].length),
|
|
]);
|
|
// act
|
|
const actual = context.analyze();
|
|
// assert
|
|
expectInvalidCodeLines(actual, expectedInvalidLines.map((l) => l.lineNumber));
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('error construction', () => {
|
|
describe('outputs actual character length', () => {
|
|
createScriptLanguageScenarios().forEach((scenario) => {
|
|
it(scenario.description, () => {
|
|
// arrange
|
|
const expectedLength = scenario.maxLength + 1;
|
|
const expectedErrorPart = `Line is too long (${expectedLength}).`;
|
|
const context = new TestContext()
|
|
.withLanguage(scenario.language)
|
|
.withLines([
|
|
'B'.repeat(expectedLength),
|
|
]);
|
|
// act
|
|
const actual = context.analyze();
|
|
// assert
|
|
expect(actual).to.have.lengthOf(1);
|
|
const actualError = actual[0].error;
|
|
expect(actualError).to.include(expectedErrorPart);
|
|
});
|
|
});
|
|
});
|
|
describe('outputs maximum allowed character length', () => {
|
|
createScriptLanguageScenarios().forEach((scenario) => {
|
|
it(scenario.description, () => {
|
|
// arrange
|
|
const expectedLength = scenario.maxLength + 1;
|
|
const expectedErrorPart = `It exceed maximum allowed length ${scenario.maxLength}`;
|
|
const context = new TestContext()
|
|
.withLanguage(scenario.language)
|
|
.withLines([
|
|
'B'.repeat(expectedLength),
|
|
]);
|
|
// act
|
|
const actual = context.analyze();
|
|
// assert
|
|
expect(actual).to.have.lengthOf(1);
|
|
const actualError = actual[0].error;
|
|
expect(actualError).to.include(expectedErrorPart);
|
|
});
|
|
});
|
|
});
|
|
describe('outputs total exceeding characters', () => {
|
|
createScriptLanguageScenarios().forEach((scenario) => {
|
|
it(scenario.description, () => {
|
|
// arrange
|
|
const expectedExceedingCharacters = 5;
|
|
const expectedErrorPart = `by ${expectedExceedingCharacters} characters.`;
|
|
const lineLength = scenario.maxLength + expectedExceedingCharacters;
|
|
const context = new TestContext()
|
|
.withLanguage(scenario.language)
|
|
.withLines([
|
|
'B'.repeat(lineLength),
|
|
]);
|
|
// act
|
|
const actual = context.analyze();
|
|
// assert
|
|
expect(actual).to.have.lengthOf(1);
|
|
const actualError = actual[0].error;
|
|
expect(actualError).to.include(expectedErrorPart);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
it('throws an error for unsupported language', () => {
|
|
// arrange
|
|
const unsupportedLanguage = 'unsupported' as unknown as ScriptingLanguage;
|
|
const expectedError = `Unsupported language: ${ScriptingLanguage[unsupportedLanguage]} (${unsupportedLanguage})`;
|
|
const context = new TestContext()
|
|
.withLanguage('unsupported' as unknown as ScriptingLanguage)
|
|
.withLines(['A', 'B', 'C']);
|
|
// act
|
|
const act = () => context.analyze();
|
|
// assert
|
|
expect(act).to.throw(expectedError);
|
|
});
|
|
});
|
|
});
|
|
|
|
interface ScriptLanguageScenario {
|
|
readonly description: string;
|
|
readonly maxLength: number;
|
|
readonly language: ScriptingLanguage;
|
|
}
|
|
|
|
function createScriptLanguageScenarios(): readonly ScriptLanguageScenario[] {
|
|
const maxLengths: Record< // `Record` catches missing entries at compile-time
|
|
ScriptingLanguage, number> = {
|
|
[ScriptingLanguage.batchfile]: 8191,
|
|
[ScriptingLanguage.shellscript]: 1048576,
|
|
};
|
|
return Object.entries(maxLengths).map(([language, length]): ScriptLanguageScenario => ({
|
|
description: `${ScriptingLanguage[language]} (max: ${length})`,
|
|
language: Number.parseInt(language, 10) as ScriptingLanguage,
|
|
maxLength: length,
|
|
}));
|
|
}
|
|
|
|
class TestContext {
|
|
private codeLines: readonly CodeLine[] = createCodeLines(['test-code-line']);
|
|
|
|
private language = ScriptingLanguage.batchfile;
|
|
|
|
public withLines(lines: readonly string[]): this {
|
|
this.codeLines = createCodeLines(lines);
|
|
return this;
|
|
}
|
|
|
|
public withLanguage(language: ScriptingLanguage): this {
|
|
this.language = language;
|
|
return this;
|
|
}
|
|
|
|
public analyze() {
|
|
return analyzeTooLongLines(
|
|
this.codeLines,
|
|
this.language,
|
|
);
|
|
}
|
|
}
|