Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion test/TestUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import LocalCatalog from '../src/lib/services/LocalCatalog';


const CATALOG_FIXTURE_PATH = path.join('test', 'catalog-fixtures', 'DefaultCatalogFixture.json');
export const CATALOG_FIXTURE_RULE_COUNT = 15;
export const CATALOG_FIXTURE_RULE_COUNT = 16;
export const CATALOG_FIXTURE_DEFAULT_ENABLED_RULE_COUNT = 11;

// eslint-disable-next-line @typescript-eslint/no-explicit-any
Expand Down
23 changes: 23 additions & 0 deletions test/catalog-fixtures/DefaultCatalogFixture.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@
"paths": [
"https://eslint.org/docs/rules/no-inner-declarations"
]
},
{
"name": "Copy/Paste Detected",
"engine": "cpd",
"paths": []
}
],
"rulesets": [
Expand Down Expand Up @@ -332,6 +337,24 @@
],
"message": "Avoid using if statements without curly braces",
"engine": "pmd"
},
{
"rulesets": [],
"defaultEnabled": true,
"sourcepackage": "cpd",
"languages": [
"apex",
"java",
"visualforce",
"xml"
],
"name": "copy-paste-detected",
"description": "Identify duplicate code blocks.",
"categories": [
"Copy/Paste Detected"
],
"message": "",
"engine": "cpd"
}
]
}
32 changes: 1 addition & 31 deletions test/commands/scanner/e2e.cpd.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import { expect } from "chai";
// @ts-ignore
import { runCommand } from "../../TestUtils";
import path = require("path");
import { ENGINE } from "../../../src/Constants";
import { RuleResult } from "../../../src/types";
import { CpdLanguagesSupported, CpdRuleCategory, CpdRuleDescription, CpdRuleName, CpdViolationSeverity } from "../../../src/lib/cpd/CpdEngine";
import {CpdRuleCategory, CpdRuleName, CpdViolationSeverity } from "../../../src/lib/cpd/CpdEngine";

const Cpd_Test_Code_Path = path.join("test", "code-fixtures", "cpd");
const Vf_File1 = path.join(Cpd_Test_Code_Path, "myVfPage1.page");
Expand Down Expand Up @@ -112,35 +111,6 @@ describe("End to end tests for CPD engine", () => {
});
});
});

describe("Integration with `scanner rule list` command", () => {
describe("Invoking CPD engine", () => {
it("CPD engine rules should not be displayed by default", () => {
const output = runCommand(`scanner rule list --json`);
const results = output.jsonOutput.result as any[];
expect(results.length).to.be.greaterThan(0);

const cpdCatalogs = results.filter(row => row.engine === ENGINE.CPD);
expect(cpdCatalogs).to.have.lengthOf(0);
});

it("CPD engine rules should be displayed when using `--engine cpd`", () => {
const output = runCommand(`scanner rule list --engine cpd --json`);
const results = output.jsonOutput.result as any[];
expect(results.length).equals(1);

// Verify properties of rule.
const rule = results[0];
expect(rule.engine).equals(ENGINE.CPD);
expect(rule.sourcepackage).equals(ENGINE.CPD);
expect(rule.name).equals(CpdRuleName);
expect(rule.description).equals(CpdRuleDescription);
expect(rule.categories).contains(CpdRuleCategory);
expect(rule.languages).has.same.members(CpdLanguagesSupported);
expect(rule.defaultEnabled).equals(true);
});
});
});
});
function verifyEnvVarIsUsedForMinimumTokens(ctx) {
const Minimum_Tokens_50 = [Apex_File1, Apex_File2, Vf_File1, Vf_File2].sort();
Expand Down
89 changes: 0 additions & 89 deletions test/commands/scanner/run.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ import {ENV_VAR_NAMES} from "../../../src/Constants";
import fs = require('fs');
import path = require('path');
import process = require('process');
import tildify = require('tildify');

const pathToApexFolder = path.join('test', 'code-fixtures', 'apex');
const pathToSomeTestClass = path.join('test', 'code-fixtures', 'apex', 'SomeTestClass.cls');
const pathToSomeOtherTestClass = path.join('test', 'code-fixtures', 'apex', 'SomeOtherTestClass.cls');
const pathToAnotherTestClass = path.join('test', 'code-fixtures', 'apex', 'AnotherTestClass.cls');
const pathToYetAnotherTestClass = path.join('test', 'code-fixtures', 'apex', 'YetAnotherTestClass.cls');

Expand All @@ -30,78 +27,12 @@ describe('scanner run', function () {
validateXmlOutput(output.shellOutput.stdout);
});

it('Target path may be relative or absolute', () => {
const output = runCommand(`scanner run --target ${path.join('.', pathToSomeTestClass)} --ruleset ApexUnit --format xml`);
validateXmlOutput(output.shellOutput.stdout);
});

it('When the file contains no violations, a message is logged to the console', () => {
const output = runCommand(`scanner run --target ${pathToYetAnotherTestClass} --ruleset ApexUnit --format xml`);
expect(output.shellOutput.stdout).to.contain(getMessage(BundleName.RunOutputProcessor, 'output.noViolationsDetected', ['pmd, retire-js']));
});
});

describe('Test Case: Running rules against multiple specified files', () => {
it('Both files are evaluated, and any violations are logged', () => {
const output = runCommand(`scanner run --target "${pathToSomeTestClass},${pathToSomeOtherTestClass}" --ruleset ApexUnit --format xml`);
// We'll split the output by the <file> tag first, so we can get each file that violated rules.
const results = output.shellOutput.stdout.split('<result ');
results.shift();
// Verify that each set of violations corresponds to the expected file.
expect(results.length).to.equal(2, 'Only two files should have violated the rules');
expect(results[0]).to.match(/file="test(\/|\\)code-fixtures(\/|\\)apex(\/|\\)SomeOtherTestClass.cls"/);
expect(results[1]).to.match(/file="test(\/|\\)code-fixtures(\/|\\)apex(\/|\\)SomeTestClass.cls"/);

// Now, split each file's violations by the <violation> tag so we can inspect individual violations.
const firstFileViolations = results[0].split('<violation');
firstFileViolations.shift();
expect(firstFileViolations.length).to.equal(1, 'Should be one violation detected in SomeOtherTestClass.cls');
expect(firstFileViolations[0]).to.match(/line="11".+rule="ApexUnitTestClassShouldHaveAsserts"/);

const secondFileViolations = results[1].split('<violation');
secondFileViolations.shift();
expect(secondFileViolations.length).to.equal(2, 'Should be two violations detected in SomeTestClass.cls');
// We'll check each violation in enough depth to be confident that the expected violations were returned in the
// expected order.
expect(secondFileViolations[0]).to.match(/line="11".+rule="ApexUnitTestClassShouldHaveAsserts"/);
expect(secondFileViolations[1]).to.match(/line="19".+rule="ApexUnitTestClassShouldHaveAsserts"/);
});
});

describe('Test Case: Running rules against a folder', () => {
it('Any violations in the folder are logged as an XML', () => {
const output = runCommand(`scanner run --target ${pathToApexFolder} --ruleset ApexUnit --format xml`);
// We'll split the output by the <file> tag first, so we can get each file that violated rules.
const results = output.shellOutput.stdout.split('<result ');
// The first list item is going to be the header, so we need to pull that off.
results.shift();
// Verify that each set of violations corresponds to the expected file.
expect(results.length).to.equal(3, 'Only three files should have violated the rules');
expect(results[0]).to.match(/file="test(\/|\\)code-fixtures(\/|\\)apex(\/|\\)AnotherTestClass.cls"/);
expect(results[1]).to.match(/file="test(\/|\\)code-fixtures(\/|\\)apex(\/|\\)SomeOtherTestClass.cls"/);
expect(results[2]).to.match(/file="test(\/|\\)code-fixtures(\/|\\)apex(\/|\\)SomeTestClass.cls"/);

// Now, split each file's violations by the <violation> tag so we can inspect individual violations.
const firstFileViolations = results[0].split('<violation');
firstFileViolations.shift();
expect(firstFileViolations.length).to.equal(1, 'Should be one violation detected in AnotherTestClass.cls');
expect(firstFileViolations[0]).to.match(/line="6".+rule="ApexUnitTestClassShouldHaveAsserts"/);

const secondFileViolations = results[1].split('<violation');
secondFileViolations.shift();
expect(secondFileViolations.length).to.equal(1, 'Should be one violation detected in SomeOtherTestClass.cls');
expect(secondFileViolations[0]).to.match(/line="11".+rule="ApexUnitTestClassShouldHaveAsserts"/);

const thirdFileViolations = results[2].split('<violation');
thirdFileViolations.shift();
expect(thirdFileViolations.length).to.equal(2, 'Should be two violations detected in SomeTestClass.cls');
// We'll check each violation in enough depth to be confident that the expected violations were returned in the
// expected order.
expect(thirdFileViolations[0]).to.match(/line="11".+rule="ApexUnitTestClassShouldHaveAsserts"/);
expect(thirdFileViolations[1]).to.match(/line="19".+rule="ApexUnitTestClassShouldHaveAsserts"/);
});
});

describe('Test Case: Running multiple rulesets at once', () => {
it('Violations from each rule are logged as an XML', () => {
const output = runCommand(`scanner run --target ${pathToAnotherTestClass} --ruleset ApexUnit,Style --format xml`);
Expand Down Expand Up @@ -425,26 +356,6 @@ describe('scanner run', function () {
})
});

describe('Dynamic Input', () => {

describe('Test Case: Using ~/ shorthand in target', () => {
const pathWithTilde = tildify(path.join(process.cwd(), 'test', 'code-fixtures', 'apex', 'SomeTestClass.cls'));

it('Tilde is expanded to full directory', () => {
const output = runCommand(`scanner run --target ${pathWithTilde} --ruleset ApexUnit --format xml`);
// We'll split the output by the <violation> tag, so we can get individual violations.
const violations = output.shellOutput.stdout.split('<violation');
// The first list item is going to be the header, so we need to pull that off.
violations.shift();
expect(violations.length).to.equal(2, 'Should be two violations detected in the file');
// We'll check each violation in enough depth to be confident that the expected violations were returned in the
// expected order.
expect(violations[0]).to.match(/line="11".+rule="ApexUnitTestClassShouldHaveAsserts"/);
expect(violations[1]).to.match(/line="19".+rule="ApexUnitTestClassShouldHaveAsserts"/);
})
});
});

describe('Edge Cases', () => {
describe('Test case: No output specified', () => {
it('When no format is specified, we default to a TABLE', () => {
Expand Down
33 changes: 31 additions & 2 deletions test/lib/actions/RuleListAction.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,29 @@ describe("Tests for RuleListAction", () => {
describe('Filtering logic', () => {

beforeEach(() => {
sinon.stub(Config.prototype, 'isEngineEnabled').callThrough().withArgs(ENGINE.ESLINT_LWC).resolves(false);
sinon.stub(Config.prototype, 'isEngineEnabled')
.callThrough()
.withArgs(ENGINE.ESLINT_LWC).resolves(false)
.withArgs(ENGINE.CPD).resolves(true);
});

afterEach(() => {
sinon.restore();
});

it('Test Case: Without filters, all rules for enabled engines are returned', async () => {
it('Test Case: Without filters, all rules for enabled and default-runnable engines are returned', async () => {
await ruleListAction.run([]);

let tableData: Ux.Table.Data[] = display.getLastTableData();

for (const rowData of tableData) {
expect(rowData.engine).to.not.equal('eslint-lwc', 'Should not return rules for disabled engine');
// NOTE: Currently, CPD has the unique behavior of only running/listing rules when it's explicitly
// requested via the --engine parameter, even if it's listed as enabled. So since it wasn't
// explicitly requested, it shouldn't be included.
// This behavior is something of an anomaly, and should not be taken as ironclad. If it becomes
// advantageous or convenient to change it, we should take the opportunity to do so.
expect(rowData.engine).to.not.equal('cpd', 'Should not return rule for unrequested CPD engine');
}
});

Expand All @@ -58,6 +67,26 @@ describe("Tests for RuleListAction", () => {
}
});

// NOTE: Currently, CPD has the unique behavior of only running/listing rules when it's explicitly requested via
// the --engine parameter, even if it's listed as enabled. So since it's explicitly requested here, it should
// be included.
// This behavior is something of an anomaly, and should not be taken as ironclad. If it becomes
// advantageous or convenient to change it, we should take the opportunity to do so.
it('Test Case: Filtering explicitly for a default non-runnable engine will return its rules', async () => {
const inputs: Inputs = {
engine: ['cpd']
};

await ruleListAction.run(inputs);

let tableData: Ux.Table.Data[] = display.getLastTableData();
expect(tableData).to.have.length(1);

for (const rowData of tableData) {
expect(rowData.engine).to.equal('cpd');
}
});

it('Edge Case: No matching rules causes empty table', async () => {
const inputs: Inputs = {
category: ['beebleborp']
Expand Down
Loading