diff --git a/packages/code-analyzer-regex-engine/src/plugin.ts b/packages/code-analyzer-regex-engine/src/plugin.ts index 5c5943cf..e44c3fef 100644 --- a/packages/code-analyzer-regex-engine/src/plugin.ts +++ b/packages/code-analyzer-regex-engine/src/plugin.ts @@ -72,7 +72,7 @@ function validateEngineName(engineName: string) { export function createBaseRegexRules(now: Date): RegexRules { return { NoTrailingWhitespace: { - regex: (/(?[ \t]+)((\r?\n)|$)/g).toString(), + regex: (/(?((?<=\S.*)[ \t]+(?=\r?\n|$))|((?<=([ \t]*\r?\n)([ \t]*\r?\n))[ \t]*\r?\n))/g).toString(), file_extensions: ['.cls', '.trigger'], // Currently restricted to apex files only... but we might want to extend where this rule applies in the future description: getMessage('TrailingWhitespaceRuleDescription'), violation_message: getMessage('TrailingWhitespaceRuleMessage'), diff --git a/packages/code-analyzer-regex-engine/test/engine.test.ts b/packages/code-analyzer-regex-engine/test/engine.test.ts index b72026d4..b852423d 100644 --- a/packages/code-analyzer-regex-engine/test/engine.test.ts +++ b/packages/code-analyzer-regex-engine/test/engine.test.ts @@ -268,7 +268,127 @@ describe('Tests for runRules', () => { endColumn: 21 }] }, + { + ruleName: "NoTrailingWhitespace", + message: getMessage('TrailingWhitespaceRuleMessage'), + primaryLocationIndex: 0, + codeLocations: [{ + file: path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines", "EmptyLinesAtEof.cls"), + startLine: 7, + startColumn: 1, + endLine: 8, + endColumn: 1 + }] + }, + { + ruleName: "NoTrailingWhitespace", + message: getMessage('TrailingWhitespaceRuleMessage'), + primaryLocationIndex: 0, + codeLocations: [{ + file: path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines", "MultipleEmptyLines.cls"), + startLine: 6, + startColumn: 1, + endLine: 7, + endColumn: 1 + }] + }, + { + ruleName: "NoTrailingWhitespace", + message: getMessage('TrailingWhitespaceRuleMessage'), + primaryLocationIndex: 0, + codeLocations: [{ + file: path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines", "EmptyLinesWithWhitespace.cls"), + startLine: 6, + startColumn: 1, + endLine: 7, + endColumn: 1 + }] + }, + + ]; + + expect(runResults.violations).toHaveLength(expectedViolations.length); + for (const expectedViolation of expectedViolations) { + expect(runResults.violations).toContainEqual(expectedViolation); + } + }); + + it("NoTrailingWhitespace rule should flag multiple consecutive empty lines", async () => { + const runOptions: RunOptions = createRunOptions( + new Workspace('id', [path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines")])); + const runResults: EngineRunResults = await engine.runRules(["NoTrailingWhitespace"], runOptions); + + const expectedViolations: Violation[] = [ + { + ruleName: "NoTrailingWhitespace", + message: getMessage('TrailingWhitespaceRuleMessage'), + primaryLocationIndex: 0, + codeLocations: [{ + file: path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines", "MultipleEmptyLines.cls"), + startLine: 6, + startColumn: 1, + endLine: 7, + endColumn: 1 + }] + }, + { + ruleName: "NoTrailingWhitespace", + message: getMessage('TrailingWhitespaceRuleMessage'), + primaryLocationIndex: 0, + codeLocations: [{ + file: path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines", "EmptyLinesAtEof.cls"), + startLine: 7, + startColumn: 1, + endLine: 8, + endColumn: 1 + }] + }, + { + ruleName: "NoTrailingWhitespace", + message: getMessage('TrailingWhitespaceRuleMessage'), + primaryLocationIndex: 0, + codeLocations: [{ + file: path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines", "EmptyLinesWithWhitespace.cls"), + startLine: 6, + startColumn: 1, + endLine: 7, + endColumn: 1 + }] + } + ]; + + expect(runResults.violations).toHaveLength(expectedViolations.length); + for (const expectedViolation of expectedViolations) { + expect(runResults.violations).toContainEqual(expectedViolation); + } + }); + + it("NoTrailingWhitespace rule should NOT flag single empty lines between code", async () => { + const runOptions: RunOptions = createRunOptions( + new Workspace('id', [path.resolve(__dirname, "test-data", "apexClassWhitespace", "5_singleEmptyLinesValid")])); + const runResults: EngineRunResults = await engine.runRules(["NoTrailingWhitespace"], runOptions); + + expect(runResults.violations).toHaveLength(0); + }); + + it("NoTrailingWhitespace rule should flag multiple empty lines that contain only whitespace", async () => { + const runOptions: RunOptions = createRunOptions( + new Workspace('id', [path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines", "EmptyLinesWithWhitespace.cls")])); + const runResults: EngineRunResults = await engine.runRules(["NoTrailingWhitespace"], runOptions); + const expectedViolations: Violation[] = [ + { + ruleName: "NoTrailingWhitespace", + message: getMessage('TrailingWhitespaceRuleMessage'), + primaryLocationIndex: 0, + codeLocations: [{ + file: path.resolve(__dirname, "test-data", "apexClassWhitespace", "4_multipleEmptyLines", "EmptyLinesWithWhitespace.cls"), + startLine: 6, + startColumn: 1, + endLine: 7, + endColumn: 1 + }] + } ]; expect(runResults.violations).toHaveLength(expectedViolations.length); diff --git a/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/EmptyLinesAtEof.cls b/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/EmptyLinesAtEof.cls new file mode 100644 index 00000000..145a08fd --- /dev/null +++ b/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/EmptyLinesAtEof.cls @@ -0,0 +1,7 @@ +public class EmptyLinesAtEof { + public void method1() { + System.debug('test'); + } +} + + diff --git a/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/EmptyLinesWithWhitespace.cls b/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/EmptyLinesWithWhitespace.cls new file mode 100644 index 00000000..5c62bd54 --- /dev/null +++ b/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/EmptyLinesWithWhitespace.cls @@ -0,0 +1,10 @@ +public class EmptyLinesWithWhitespace { + public void method1() { + System.debug('test1'); + } + + + public void method2() { + System.debug('test2'); + } +} diff --git a/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/MultipleEmptyLines.cls b/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/MultipleEmptyLines.cls new file mode 100644 index 00000000..1efa8698 --- /dev/null +++ b/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/4_multipleEmptyLines/MultipleEmptyLines.cls @@ -0,0 +1,10 @@ +public class MultipleEmptyLines { + public void method1() { + System.debug('test1'); + } + + + public void method2() { + System.debug('test2'); + } +} diff --git a/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/5_singleEmptyLinesValid/SingleEmptyLines.cls b/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/5_singleEmptyLinesValid/SingleEmptyLines.cls new file mode 100644 index 00000000..3cdf7ff1 --- /dev/null +++ b/packages/code-analyzer-regex-engine/test/test-data/apexClassWhitespace/5_singleEmptyLinesValid/SingleEmptyLines.cls @@ -0,0 +1,13 @@ +public class SingleEmptyLines { + public void method1() { + System.debug('test1'); + } + + public void method2() { + System.debug('test2'); + } + + public void method3() { + System.debug('test3'); + } +}