From 4463655950bb3273f75ec0aabb7e481f172dba6f Mon Sep 17 00:00:00 2001 From: Renato Oliveira Date: Wed, 30 Oct 2024 09:39:58 -0300 Subject: [PATCH 1/2] feat: adds wildcard support for tests in test suites --- src/commands/apextests/list.ts | 2 +- src/helpers/readers.ts | 42 +++++++++++++++++++++++++--- test/commands/apextests/list.test.ts | 8 ++++-- test/units/readers.test.ts | 31 +++++++++++++++++--- 4 files changed, 72 insertions(+), 11 deletions(-) diff --git a/src/commands/apextests/list.ts b/src/commands/apextests/list.ts index 039249d..e1b9fc9 100644 --- a/src/commands/apextests/list.ts +++ b/src/commands/apextests/list.ts @@ -82,7 +82,7 @@ export default class ApextestsList extends SfCommand { 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); } } diff --git a/src/helpers/readers.ts b/src/helpers/readers.ts index a53d96f..8bbbf4d 100644 --- a/src/helpers/readers.ts +++ b/src/helpers/readers.ts @@ -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 { @@ -25,7 +25,10 @@ export function getConcurrencyThreshold(): number { * @param directory directory within the project to search for files * @returns a Promise with a list of test names */ -export async function searchDirectoryForTestNamesInTestSuites(directory: string): Promise { +export async function searchDirectoryForTestNamesInTestSuites( + directory: string, + packageDirectories: string[], +): Promise { const testClassesNames: Set = new Set(); let readDir; @@ -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 = new Set(); + let requiresWildcardSearch = false; // Function that handles the parsing of test suites and helps to build the // list of test classes for the next steps @@ -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); } }; @@ -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. diff --git a/test/commands/apextests/list.test.ts b/test/commands/apextests/list.test.ts index 2c3ea39..2326394 100644 --- a/test/commands/apextests/list.test.ts +++ b/test/commands/apextests/list.test.ts @@ -120,7 +120,9 @@ describe('apextests list', () => { .flatMap((c) => c.args) .join(' '); expect(output).to.equal( - ['UnlistedTest', 'SampleTest', 'SuperSampleTest'].sort((a, b) => a.localeCompare(b)).join(), + ['Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest'] + .sort((a, b) => a.localeCompare(b)) + .join(), ); const warnings = sfCommandStubs.warn .getCalls() @@ -136,7 +138,9 @@ describe('apextests list', () => { .flatMap((c) => c.args) .join(' '); expect(output).to.equal( - ['UnlistedTest', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest'].sort((a, b) => a.localeCompare(b)).join(), + ['Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest'] + .sort((a, b) => a.localeCompare(b)) + .join(), ); const warnings = sfCommandStubs.warn .getCalls() diff --git a/test/units/readers.test.ts b/test/units/readers.test.ts index ab3b78d..ef08a7e 100644 --- a/test/units/readers.test.ts +++ b/test/units/readers.test.ts @@ -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', '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']); @@ -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 = ['UnlistedTest', 'Sample2Test', 'SampleTest'].sort(); + + expect(result).to.deep.equal(tests); + }); +}); From 49ca3d66e1ab38dbf0123b7e3215f1dd4df556ef Mon Sep 17 00:00:00 2001 From: Renato Oliveira Date: Wed, 30 Oct 2024 09:51:43 -0300 Subject: [PATCH 2/2] feat: adds support for tests in namespaces within test suites --- .../testSuites/SampleSuiteWithNSTests.testSuite-meta.xml | 5 +++++ test/commands/apextests/list.nut.ts | 7 +++++-- test/commands/apextests/list.test.ts | 5 +++-- test/units/readers.test.ts | 4 ++-- 4 files changed, 15 insertions(+), 6 deletions(-) create mode 100644 samples/testSuites/SampleSuiteWithNSTests.testSuite-meta.xml diff --git a/samples/testSuites/SampleSuiteWithNSTests.testSuite-meta.xml b/samples/testSuites/SampleSuiteWithNSTests.testSuite-meta.xml new file mode 100644 index 0000000..655571a --- /dev/null +++ b/samples/testSuites/SampleSuiteWithNSTests.testSuite-meta.xml @@ -0,0 +1,5 @@ + + + NS.UnlistedTest + NS.Sample*Test + \ No newline at end of file diff --git a/test/commands/apextests/list.nut.ts b/test/commands/apextests/list.nut.ts index f656bb1..ae99c5e 100644 --- a/test/commands/apextests/list.nut.ts +++ b/test/commands/apextests/list.nut.ts @@ -20,6 +20,7 @@ const VALIDATED_TEST_LIST = [ const TEST_LIST = [ 'FridayTest', 'NotYourLuckyDayTest', + 'NS.UnlistedTest', 'Sample2Test', 'SampleTest', 'SampleTriggerTest', @@ -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(','), ); }); @@ -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(','), ); diff --git a/test/commands/apextests/list.test.ts b/test/commands/apextests/list.test.ts index 2326394..ab656b7 100644 --- a/test/commands/apextests/list.test.ts +++ b/test/commands/apextests/list.test.ts @@ -21,6 +21,7 @@ const VALIDATED_TEST_LIST = [ const TEST_LIST = [ 'FridayTest', 'NotYourLuckyDayTest', + 'NS.UnlistedTest', 'Sample2Test', 'SampleTest', 'SampleTriggerTest', @@ -120,7 +121,7 @@ describe('apextests list', () => { .flatMap((c) => c.args) .join(' '); expect(output).to.equal( - ['Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest'] + ['NS.UnlistedTest', 'Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest'] .sort((a, b) => a.localeCompare(b)) .join(), ); @@ -138,7 +139,7 @@ describe('apextests list', () => { .flatMap((c) => c.args) .join(' '); expect(output).to.equal( - ['Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest'] + ['NS.UnlistedTest', 'Sample2Test', 'SampleTest', 'SampleTriggerTest', 'SuperSampleTest', 'UnlistedTest'] .sort((a, b) => a.localeCompare(b)) .join(), ); diff --git a/test/units/readers.test.ts b/test/units/readers.test.ts index ef08a7e..17d6a63 100644 --- a/test/units/readers.test.ts +++ b/test/units/readers.test.ts @@ -10,7 +10,7 @@ describe('tests of the searchDirectoryForTestNamesInTestSuites fn', () => { const suitePath = './samples/testSuites'; const result = await searchDirectoryForTestNamesInTestSuites(suitePath, ['./samples/classes']); - expect(result).to.deep.equal(['UnlistedTest', 'Sample2Test', 'SampleTest'].sort()); + expect(result).to.deep.equal(['UnlistedTest', 'NS.UnlistedTest', 'Sample2Test', 'SampleTest'].sort()); }); }); @@ -39,7 +39,7 @@ describe('test suite wildcards', () => { 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 = ['UnlistedTest', 'Sample2Test', 'SampleTest'].sort(); + const tests = ['NS.UnlistedTest', 'UnlistedTest', 'Sample2Test', 'SampleTest'].sort(); expect(result).to.deep.equal(tests); });