Skip to content

Commit

Permalink
Merge pull request #78 from renatoliveira/test-suite-wildcard-support
Browse files Browse the repository at this point in the history
feat: adds wildcard support for tests in test suites
  • Loading branch information
renatoliveira authored Oct 30, 2024
2 parents bdf7a4c + 49ca3d6 commit 8de05f0
Show file tree
Hide file tree
Showing 6 changed files with 83 additions and 13 deletions.
5 changes: 5 additions & 0 deletions samples/testSuites/SampleSuiteWithNSTests.testSuite-meta.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<ApexTestSuite xmlns="http://soap.sforce.com/2006/04/metadata">
<testClassName>NS.UnlistedTest</testClassName>
<testClassName>NS.Sample*Test</testClassName>
</ApexTestSuite>
2 changes: 1 addition & 1 deletion src/commands/apextests/list.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export default class ApextestsList extends SfCommand<ApextestsListResult> {
if (testSuitesNames.length > 0) {
// read the testSuite directory again to get the classes listed on them
for (const directory of packageDirectories.filter((dir) => dir.includes('testSuites'))) {
const testNames: string[] = await searchDirectoryForTestNamesInTestSuites(directory);
const testNames: string[] = await searchDirectoryForTestNamesInTestSuites(directory, packageDirectories);
allTestClasses.push(...testNames);
}
}
Expand Down
42 changes: 38 additions & 4 deletions src/helpers/readers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ import { queue } from 'async';
import { parseTestsNames, parseTestSuiteFile, parseTestSuitesNames } from './parsers.js';
import { SearchResult } from './types.js';

const TEST_NAME_REGEX = /@tests\s*:\s*([^/\n]+)/gi;
const TEST_SUITE_NAME_REGEX = /@testsuites\s*:\s*([^/\n]+)/gi;
const TEST_NAME_REGEX = /@tests\s*:\s*([^/\n]+)/gi;
const TEST_SUITE_NAME_REGEX = /@testsuites\s*:\s*([^/\n]+)/gi;
const TEST_CLASS_ANNOTATION_REGEX = /@istest\n(private|public|global)/gi;

export function getConcurrencyThreshold(): number {
Expand All @@ -25,7 +25,10 @@ export function getConcurrencyThreshold(): number {
* @param directory directory within the project to search for files
* @returns a Promise<string> with a list of test names
*/
export async function searchDirectoryForTestNamesInTestSuites(directory: string): Promise<string[]> {
export async function searchDirectoryForTestNamesInTestSuites(
directory: string,
packageDirectories: string[],
): Promise<string[]> {
const testClassesNames: Set<string> = new Set<string>();
let readDir;

Expand All @@ -37,6 +40,8 @@ export async function searchDirectoryForTestNamesInTestSuites(directory: string)

// Gets the list of test suites
const suiteFiles = readDir.filter((file) => file.endsWith('.testSuite-meta.xml'));
const wildcards: Set<string> = new Set<string>();
let requiresWildcardSearch = false;

// Function that handles the parsing of test suites and helps to build the
// list of test classes for the next steps
Expand All @@ -46,7 +51,12 @@ export async function searchDirectoryForTestNamesInTestSuites(directory: string)
const classes = parseTestSuiteFile(data);

// TODO: Support for wildcards
for (const test of classes.filter((name) => !name.includes('*'))) {
for (const test of classes) {
if (test.includes('*')) {
requiresWildcardSearch = true;
wildcards.add(test);
continue;
}
testClassesNames.add(test);
}
};
Expand All @@ -65,9 +75,33 @@ export async function searchDirectoryForTestNamesInTestSuites(directory: string)
await testSuiteNameProcessor.drain();
}

if (requiresWildcardSearch && packageDirectories) {
for (const pkgDir of packageDirectories) {
// eslint-disable-next-line no-await-in-loop
const searchResult = await searchDirectoryForTestClasses(pkgDir, null);
const testClassesInDir = searchResult.classes;

for (const test of testClassesInDir) {
// if the test class name matches the wildcard, add it to the list
for (const wildcard of wildcards) {
if (!matchWildcard(wildcard, test)) {
continue;
}

testClassesNames.add(test);
}
}
}
}

return Array.from(testClassesNames).sort();
}

export function matchWildcard(wildcard: string, test: string): boolean {
const regex = new RegExp(`^${wildcard.replace('*', '.*')}$`);
return regex.test(test);
}

/**
* Given a certain directory, search its contents for files that end with '.cls' or '.trigger'.
* Extracts the test names from the annotations on those files, and returns a list with those.
Expand Down
7 changes: 5 additions & 2 deletions test/commands/apextests/list.nut.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const VALIDATED_TEST_LIST = [
const TEST_LIST = [
'FridayTest',
'NotYourLuckyDayTest',
'NS.UnlistedTest',
'Sample2Test',
'SampleTest',
'SampleTriggerTest',
Expand Down Expand Up @@ -110,7 +111,9 @@ describe('apextests list NUTs', () => {
const output = execCmd(command, { ensureExitCode: 0 }).shellOutput.stdout;

expect(output.replace('\n', '')).to.equal(
['SampleTest', 'SuperSampleTest', 'UnlistedTest'].sort((a, b) => a.localeCompare(b)).join(','),
['NS.UnlistedTest', 'Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest']
.sort((a, b) => a.localeCompare(b))
.join(','),
);
});

Expand All @@ -119,7 +122,7 @@ describe('apextests list NUTs', () => {
const output = execCmd(command, { ensureExitCode: 0 }).shellOutput.stdout;

expect(output.replace('\n', '')).to.equal(
['SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest']
['NS.UnlistedTest', 'Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest']
.sort((a, b) => a.localeCompare(b))
.join(','),
);
Expand Down
9 changes: 7 additions & 2 deletions test/commands/apextests/list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const VALIDATED_TEST_LIST = [
const TEST_LIST = [
'FridayTest',
'NotYourLuckyDayTest',
'NS.UnlistedTest',
'Sample2Test',
'SampleTest',
'SampleTriggerTest',
Expand Down Expand Up @@ -120,7 +121,9 @@ describe('apextests list', () => {
.flatMap((c) => c.args)
.join(' ');
expect(output).to.equal(
['UnlistedTest', 'SampleTest', 'SuperSampleTest'].sort((a, b) => a.localeCompare(b)).join(),
['NS.UnlistedTest', 'Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest']
.sort((a, b) => a.localeCompare(b))
.join(),
);
const warnings = sfCommandStubs.warn
.getCalls()
Expand All @@ -136,7 +139,9 @@ describe('apextests list', () => {
.flatMap((c) => c.args)
.join(' ');
expect(output).to.equal(
['UnlistedTest', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest'].sort((a, b) => a.localeCompare(b)).join(),
['NS.UnlistedTest', 'Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest']
.sort((a, b) => a.localeCompare(b))
.join(),
);
const warnings = sfCommandStubs.warn
.getCalls()
Expand Down
31 changes: 27 additions & 4 deletions test/units/readers.test.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
import { expect } from 'chai';
import { searchDirectoryForTestClasses, searchDirectoryForTestNamesInTestSuites } from '../../src/helpers/readers.js';
import {
matchWildcard,
searchDirectoryForTestClasses,
searchDirectoryForTestNamesInTestSuites,
} from '../../src/helpers/readers.js';

describe('tests of the searchDirectoryForTestNamesInTestSuites fn', () => {
it('should read the sample suite from the file', async () => {
const suitePath = './samples/testSuites';
const result = await searchDirectoryForTestNamesInTestSuites(suitePath);
const result = await searchDirectoryForTestNamesInTestSuites(suitePath, ['./samples/classes']);

expect(result).to.deep.equal(['UnlistedTest']);
expect(result).to.deep.equal(['UnlistedTest', 'NS.UnlistedTest', 'Sample2Test', 'SampleTest'].sort());
});
});

describe('tests of the searchDirectoryForTestClasses fn', () => {
it('should read the sample suite from the file', async () => {
it('should read the Sample.cls class in the classes directory and return its tests and test suites', async () => {
const classesPath = './samples/classes';
const result = await searchDirectoryForTestClasses(classesPath, ['ApexClass:Sample']);

Expand All @@ -21,3 +25,22 @@ describe('tests of the searchDirectoryForTestClasses fn', () => {
});
});
});

describe('test suite wildcards', () => {
it('should match the wildcard with the test class name', () => {
expect(matchWildcard('*Test', 'SampleTest')).to.be.true;
expect(matchWildcard('Other*Test', 'SampleSomethingTest')).to.be.false;
expect(matchWildcard('Sample*', 'SampleTest')).to.be.true;
expect(matchWildcard('Sample*Test', 'SampleSomethingTest')).to.be.true;
expect(matchWildcard('Sample*Test', 'SampleTest')).to.be.true;
expect(matchWildcard('Sample*Test', 'SuperSampleTest')).to.be.false;
});

it('should read the SampleSuite test suite file and list the test classes with wildcards', async () => {
const suitePath = './samples/testSuites';
const result = await searchDirectoryForTestNamesInTestSuites(suitePath, ['./samples/classes']);
const tests = ['NS.UnlistedTest', 'UnlistedTest', 'Sample2Test', 'SampleTest'].sort();

expect(result).to.deep.equal(tests);
});
});

0 comments on commit 8de05f0

Please sign in to comment.