apextestlist 1.12.3 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +36 -4
  3. package/lib/commands/apextests/list.d.ts +3 -5
  4. package/lib/commands/apextests/list.js +18 -64
  5. package/lib/commands/apextests/list.js.map +1 -1
  6. package/lib/core/listTests.d.ts +2 -0
  7. package/lib/core/listTests.js +62 -0
  8. package/lib/core/listTests.js.map +1 -0
  9. package/lib/parsers/manifestParser.d.ts +10 -0
  10. package/lib/parsers/manifestParser.js +25 -0
  11. package/lib/parsers/manifestParser.js.map +1 -0
  12. package/lib/parsers/metadataFilterParser.d.ts +9 -0
  13. package/lib/parsers/metadataFilterParser.js +50 -0
  14. package/lib/parsers/metadataFilterParser.js.map +1 -0
  15. package/lib/parsers/testNameParser.d.ts +7 -0
  16. package/lib/parsers/testNameParser.js +19 -0
  17. package/lib/parsers/testNameParser.js.map +1 -0
  18. package/lib/parsers/testSuiteParser.d.ts +15 -0
  19. package/lib/parsers/testSuiteParser.js +40 -0
  20. package/lib/parsers/testSuiteParser.js.map +1 -0
  21. package/lib/readers/directorySearcher.d.ts +10 -0
  22. package/lib/readers/directorySearcher.js +82 -0
  23. package/lib/readers/directorySearcher.js.map +1 -0
  24. package/lib/readers/testSuiteSearcher.d.ts +9 -0
  25. package/lib/readers/testSuiteSearcher.js +74 -0
  26. package/lib/readers/testSuiteSearcher.js.map +1 -0
  27. package/lib/utils/concurrencyThreshold.d.ts +1 -0
  28. package/lib/utils/concurrencyThreshold.js +7 -0
  29. package/lib/utils/concurrencyThreshold.js.map +1 -0
  30. package/lib/utils/constants.d.ts +6 -0
  31. package/lib/utils/constants.js +8 -0
  32. package/lib/utils/constants.js.map +1 -0
  33. package/lib/{helpers → utils}/formatters.d.ts +1 -1
  34. package/lib/utils/formatters.js.map +1 -0
  35. package/lib/utils/getPackageDirectories.d.ts +4 -0
  36. package/lib/{helpers → utils}/getPackageDirectories.js +1 -1
  37. package/lib/utils/getPackageDirectories.js.map +1 -0
  38. package/lib/{helpers → utils}/getRepoRoot.js +1 -1
  39. package/lib/utils/getRepoRoot.js.map +1 -0
  40. package/lib/utils/matchWildcard.d.ts +1 -0
  41. package/lib/utils/matchWildcard.js +5 -0
  42. package/lib/utils/matchWildcard.js.map +1 -0
  43. package/lib/utils/types.d.ts +24 -0
  44. package/lib/{helpers → utils}/types.js.map +1 -1
  45. package/lib/utils/validateTests.js.map +1 -0
  46. package/messages/apextests.list.md +8 -0
  47. package/oclif.lock +59 -65
  48. package/oclif.manifest.json +11 -1
  49. package/package.json +4 -3
  50. package/lib/helpers/constants.d.ts +0 -2
  51. package/lib/helpers/constants.js +0 -4
  52. package/lib/helpers/constants.js.map +0 -1
  53. package/lib/helpers/formatters.js.map +0 -1
  54. package/lib/helpers/getPackageDirectories.d.ts +0 -1
  55. package/lib/helpers/getPackageDirectories.js.map +0 -1
  56. package/lib/helpers/getRepoRoot.js.map +0 -1
  57. package/lib/helpers/parsers.d.ts +0 -32
  58. package/lib/helpers/parsers.js +0 -87
  59. package/lib/helpers/parsers.js.map +0 -1
  60. package/lib/helpers/readers.d.ts +0 -21
  61. package/lib/helpers/readers.js +0 -158
  62. package/lib/helpers/readers.js.map +0 -1
  63. package/lib/helpers/types.d.ts +0 -10
  64. package/lib/helpers/validateTests.js.map +0 -1
  65. /package/lib/{helpers → utils}/formatters.js +0 -0
  66. /package/lib/{helpers → utils}/getRepoRoot.d.ts +0 -0
  67. /package/lib/{helpers → utils}/types.js +0 -0
  68. /package/lib/{helpers → utils}/validateTests.d.ts +0 -0
  69. /package/lib/{helpers → utils}/validateTests.js +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,21 @@
5
5
 
6
6
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
7
7
 
8
+ ## [1.13.0](https://github.com/renatoliveira/apex-test-list/compare/v1.12.4...v1.13.0) (2025-06-19)
9
+
10
+
11
+ ### Features
12
+
13
+ * add metadata filter option via yaml ([#234](https://github.com/renatoliveira/apex-test-list/issues/234)) ([1ccaf7b](https://github.com/renatoliveira/apex-test-list/commit/1ccaf7b445feb75c20770879289d1f3d5e1a2ae6))
14
+
15
+ ## [1.12.4](https://github.com/renatoliveira/apex-test-list/compare/v1.12.3...v1.12.4) (2025-06-14)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **deps:** bump @oclif/core from 4.3.0 to 4.3.3 ([#230](https://github.com/renatoliveira/apex-test-list/issues/230)) ([ec2ed5c](https://github.com/renatoliveira/apex-test-list/commit/ec2ed5c1f25ecc2366ebc219b71edc156440a45c))
21
+ * **deps:** bump @salesforce/source-deploy-retrieve ([#231](https://github.com/renatoliveira/apex-test-list/issues/231)) ([5c5d78c](https://github.com/renatoliveira/apex-test-list/commit/5c5d78c5990c61ce3f82b7cbd4ac02f7cc8f1e52))
22
+
8
23
  ## [1.12.3](https://github.com/renatoliveira/apex-test-list/compare/v1.12.2...v1.12.3) (2025-06-01)
9
24
 
10
25
 
package/README.md CHANGED
@@ -13,6 +13,7 @@ A plugin that generates a list of tests that your automated process should run,
13
13
  - [1. `@Tests:` (Custom Comment Annotation)](#1-tests-custom-comment-annotation)
14
14
  - [2. `@TestSuites:` (Custom Comment Annotation)](#2-testsuites-custom-comment-annotation)
15
15
  - [3. `@isTest` (Apex Annotation)](#3-istest-apex-annotation)
16
+ - [4. `.test-dependencies.yml` (Centralized Metadata Filter)](#4-test-dependenciesyml-centralized-metadata-filter)
16
17
  - [Running the Tool](#running-the-tool)
17
18
  - [Handling Missing Tests](#handling-missing-tests)
18
19
  - [Handling Missing Annotations](#handling-missing-annotations)
@@ -31,7 +32,7 @@ sf plugins install apextestlist
31
32
 
32
33
  ## Usage
33
34
 
34
- This tool identifies Apex tests using three annotations:
35
+ This tool identifies Apex tests using either 3 Apex annotations or a separate metadata filter.
35
36
 
36
37
  ### 1. `@Tests:` (Custom Comment Annotation)
37
38
 
@@ -70,6 +71,35 @@ private class SampleTest {
70
71
  }
71
72
  ```
72
73
 
74
+ ### 4. `.test-dependencies.yml` (Centralized Metadata Filter)
75
+
76
+ Instead of using the Apex annotations described previously, you can use this plugin to determine Apex tests with specific Salesforce metadata.
77
+
78
+ This approach is designed to work seamlessly with the `-x`/`--manifest` flag using a manifest file generated by the [`sfdx-git-delta`](https://github.com/scolladon/sfdx-git-delta) plugin.
79
+
80
+ 1. Create a `.test-dependencies.yml` file in the root of your project.
81
+ 2. Map test classes to metadata entries (in `MetadataType:MetadataMember` format).
82
+ 1. Use wildcards (\*) to match all children under a metadata namespace (e.g., all custom fields in an object).
83
+ 2. The plugin will fail if the entries in the YAML don't match the expected format.
84
+
85
+ ```yml
86
+ MyFooTest:
87
+ - CustomField:Account__c.field__c
88
+ - Flow:OpportunityFlowX
89
+ - ValidationRule:Account.RequiredEmail
90
+ - CustomField:Opportunity__c.*
91
+
92
+ MyBarTest:
93
+ - CustomObject:Lead__c
94
+ - Flow:LeadFlowY
95
+ ```
96
+
97
+ 3. Run this plugin's command with the `filter-by-metadata`/`-m` flag **and** the `-x`/`--manifest` flag.
98
+
99
+ ```sh
100
+ sf apextests list -x package/package.xml -m
101
+ ```
102
+
73
103
  ## Running the Tool
74
104
 
75
105
  To generate a test list, run the command in any Salesforce DX project:
@@ -96,7 +126,7 @@ The final deployment command would look like:
96
126
  sf project deploy start --tests SampleTest SuperSampleTest Sample2Test SuperSample2Test SampleTriggerTest
97
127
  ```
98
128
 
99
- If no annotations are found, the command output will be empty and will present this warning:
129
+ If no test methods are found, the command output will be empty and will present this warning:
100
130
 
101
131
  ```sh
102
132
  No test methods found
@@ -104,7 +134,7 @@ No test methods found
104
134
 
105
135
  ### Handling Missing Tests
106
136
 
107
- By default, this tool does **not** verify if the tests specified in `@Tests:` or `@TestSuites:` exist in the project. To enable warnings for missing tests, use:
137
+ By default, this tool does **not** verify if the test methods found exist in the project. To enable warnings for missing tests, use:
108
138
 
109
139
  ```sh
110
140
  sf apextests list --ignore-missing-tests
@@ -130,7 +160,7 @@ sf apextests list --no-warnings
130
160
 
131
161
  ```
132
162
  USAGE
133
- $ sf apextests list -f <value> -x <value> -s -n -d <value> [--json]
163
+ $ sf apextests list -f <value> -x <value> -s -n -d <value> -m [--json]
134
164
 
135
165
  FLAGS
136
166
  -f, --format=<value> Output format. Available options:
@@ -142,6 +172,8 @@ FLAGS
142
172
  -d, --ignore-package-directory Ignore a package directory when looking for test annotations.
143
173
  Should match how they are declared in "sfdx-project.json".
144
174
  Can be declared multiple times.
175
+ -m, --filter-by-metadata [default: false] When enabled with `manifest`, test selection is based on metadata changes (e.g., Flow, CustomObject) rather than Apex annotations.
176
+
145
177
 
146
178
  GLOBAL FLAGS
147
179
  --json Format output as JSON.
@@ -1,18 +1,16 @@
1
1
  import { SfCommand } from '@salesforce/sf-plugins-core';
2
- export type ApextestsListResult = {
3
- tests: string[];
4
- command: string;
5
- };
2
+ import { ApextestsListResult } from '../../utils/types.js';
6
3
  export default class ApextestsList extends SfCommand<ApextestsListResult> {
7
4
  static readonly summary: string;
8
5
  static readonly description: string;
9
6
  static readonly examples: string[];
10
7
  static readonly flags: {
11
- format: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ format: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
12
9
  manifest: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
10
  'ignore-missing-tests': import("@oclif/core/interfaces").BooleanFlag<boolean>;
14
11
  'ignore-package-directory': import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
15
12
  'no-warnings': import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
+ 'filter-by-metadata': import("@oclif/core/interfaces").BooleanFlag<boolean>;
16
14
  };
17
15
  run(): Promise<ApextestsListResult>;
18
16
  }
@@ -1,13 +1,7 @@
1
- /* eslint-disable no-console */
2
1
  'use strict';
3
- /* eslint-disable no-await-in-loop */
4
2
  import { SfCommand, Flags } from '@salesforce/sf-plugins-core';
5
3
  import { Messages } from '@salesforce/core';
6
- import { getPackageDirectories } from '../../helpers/getPackageDirectories.js';
7
- import { validateTests } from '../../helpers/validateTests.js';
8
- import { extractTypeNamesFromManifestFile } from '../../helpers/parsers.js';
9
- import { formatList } from '../../helpers/formatters.js';
10
- import { searchDirectoryForTestClasses, searchDirectoryForTestNamesInTestSuites } from '../../helpers/readers.js';
4
+ import { listTests } from '../../core/listTests.js';
11
5
  Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
12
6
  const messages = Messages.loadMessages('apextestlist', 'apextests.list');
13
7
  export default class ApextestsList extends SfCommand {
@@ -20,6 +14,7 @@ export default class ApextestsList extends SfCommand {
20
14
  description: messages.getMessage('flags.format.description'),
21
15
  char: 'f',
22
16
  required: false,
17
+ default: 'sf',
23
18
  }),
24
19
  manifest: Flags.string({
25
20
  summary: messages.getMessage('flags.manifest.summary'),
@@ -48,66 +43,25 @@ export default class ApextestsList extends SfCommand {
48
43
  required: false,
49
44
  default: false,
50
45
  }),
46
+ 'filter-by-metadata': Flags.boolean({
47
+ summary: messages.getMessage('flags.filter-by-metadata.summary'),
48
+ description: messages.getMessage('flags.filter-by-metadata.description'),
49
+ char: 'm',
50
+ required: false,
51
+ default: false,
52
+ }),
51
53
  };
52
54
  async run() {
53
55
  const { flags } = await this.parse(ApextestsList);
54
- const format = flags.format ?? 'sf';
55
- const manifest = flags.manifest ?? undefined;
56
- const ignoreMissingTests = flags['ignore-missing-tests'] ?? false;
57
- const ignoreDirs = flags['ignore-package-directory'] ?? [];
58
- const noWarnings = flags['no-warnings'];
59
- let testClassesNames = null;
60
- const testSuitesNames = [];
61
- const allTestClasses = [];
62
- const warnings = [];
63
- // Get package directories full paths
64
- const packageDirectories = await getPackageDirectories(ignoreDirs);
65
- if (manifest) {
66
- const manifestMetadata = await extractTypeNamesFromManifestFile(manifest);
67
- testClassesNames = manifestMetadata.filter((name) => !name.endsWith('testSuite-meta.xml'));
68
- }
69
- // Loop through each directory and search for test classes
70
- for (const directory of packageDirectories) {
71
- const searchResult = await searchDirectoryForTestClasses(directory, testClassesNames);
72
- allTestClasses.push(...searchResult.classes);
73
- testSuitesNames.push(...searchResult.testSuites);
74
- if (searchResult.warnings?.length) {
75
- warnings.push(...searchResult.warnings);
76
- }
77
- }
78
- if (testSuitesNames.length > 0) {
79
- for (const directory of packageDirectories.filter((dir) => dir.includes('testSuites'))) {
80
- const testNames = await searchDirectoryForTestNamesInTestSuites(directory, packageDirectories);
81
- allTestClasses.push(...testNames);
82
- }
83
- }
84
- let finalTestMethods = Array.from(new Set(allTestClasses.map((test) => test.trim())));
85
- // If ignore-missing-tests is true, validate the test methods
86
- if (ignoreMissingTests) {
87
- const { validatedTests, warnings: validationWarnings } = await validateTests(finalTestMethods, packageDirectories);
88
- finalTestMethods = validatedTests;
89
- if (validationWarnings.length > 0) {
90
- warnings.push(...validationWarnings);
91
- }
92
- if (validatedTests.length === 0) {
93
- warnings.push('No test methods declared in your annotations were found in your package directories');
94
- }
95
- }
96
- // Print all collected warnings
97
- if (!noWarnings && warnings.length > 0) {
98
- warnings.forEach((warning) => {
99
- this.warn(warning);
100
- });
101
- }
102
- finalTestMethods.sort((a, b) => a.localeCompare(b));
103
- if (finalTestMethods.length === 0) {
104
- this.warn('No test methods found');
105
- return {
106
- tests: [],
107
- command: '',
108
- };
109
- }
110
- const result = await formatList(format, finalTestMethods);
56
+ const result = await listTests({
57
+ format: flags.format,
58
+ manifest: flags.manifest,
59
+ ignoreMissingTests: flags['ignore-missing-tests'],
60
+ ignoreDirs: flags['ignore-package-directory'],
61
+ noWarnings: flags['no-warnings'],
62
+ filterByMetadata: flags['filter-by-metadata'],
63
+ warn: this.warn.bind(this),
64
+ });
111
65
  this.log(result.command);
112
66
  return result;
113
67
  }
@@ -1 +1 @@
1
- {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/apextests/list.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"list.js","sourceRoot":"","sources":["../../../src/commands/apextests/list.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGpD,QAAQ,CAAC,kCAAkC,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,QAAQ,CAAC,YAAY,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;AAEzE,MAAM,CAAC,OAAO,OAAO,aAAc,SAAQ,SAA8B;IAChE,MAAM,CAAU,OAAO,GAAG,QAAQ,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;IACzD,MAAM,CAAU,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC;IACjE,MAAM,CAAU,QAAQ,GAAG,QAAQ,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAE5D,MAAM,CAAU,KAAK,GAAG;QAC7B,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC;YACnB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,sBAAsB,CAAC;YACpD,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,0BAA0B,CAAC;YAC5D,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,IAAI;SACd,CAAC;QACF,QAAQ,EAAE,KAAK,CAAC,MAAM,CAAC;YACrB,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,wBAAwB,CAAC;YACtD,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,4BAA4B,CAAC;YAC9D,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;SAChB,CAAC;QACF,sBAAsB,EAAE,KAAK,CAAC,OAAO,CAAC;YACpC,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,oCAAoC,CAAC;YAClE,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,wCAAwC,CAAC;YAC1E,QAAQ,EAAE,KAAK;YACf,IAAI,EAAE,GAAG;YACT,OAAO,EAAE,KAAK;SACf,CAAC;QACF,0BAA0B,EAAE,KAAK,CAAC,SAAS,CAAC;YAC1C,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,wCAAwC,CAAC;YACtE,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,4CAA4C,CAAC;YAC9E,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,QAAQ,EAAE,IAAI;SACf,CAAC;QACF,aAAa,EAAE,KAAK,CAAC,OAAO,CAAC;YAC3B,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,2BAA2B,CAAC;YACzD,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,+BAA+B,CAAC;YACjE,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACf,CAAC;QACF,oBAAoB,EAAE,KAAK,CAAC,OAAO,CAAC;YAClC,OAAO,EAAE,QAAQ,CAAC,UAAU,CAAC,kCAAkC,CAAC;YAChE,WAAW,EAAE,QAAQ,CAAC,UAAU,CAAC,sCAAsC,CAAC;YACxE,IAAI,EAAE,GAAG;YACT,QAAQ,EAAE,KAAK;YACf,OAAO,EAAE,KAAK;SACf,CAAC;KACH,CAAC;IAGK,KAAK,CAAC,GAAG;QACd,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,aAAa,CAAC,CAAC;QAElD,MAAM,MAAM,GAAG,MAAM,SAAS,CAAC;YAC7B,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,kBAAkB,EAAE,KAAK,CAAC,sBAAsB,CAAC;YACjD,UAAU,EAAE,KAAK,CAAC,0BAA0B,CAAC;YAC7C,UAAU,EAAE,KAAK,CAAC,aAAa,CAAC;YAChC,gBAAgB,EAAE,KAAK,CAAC,oBAAoB,CAAC;YAC7C,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC;SAC3B,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;QACzB,OAAO,MAAM,CAAC;IAChB,CAAC"}
@@ -0,0 +1,2 @@
1
+ import { ListTestsOptions, ApextestsListResult } from '../utils/types.js';
2
+ export declare function listTests({ format, manifest, ignoreMissingTests, ignoreDirs, noWarnings, filterByMetadata, warn, }: ListTestsOptions): Promise<ApextestsListResult>;
@@ -0,0 +1,62 @@
1
+ /* eslint-disable no-await-in-loop */
2
+ import { resolve } from 'node:path';
3
+ import { getPackageDirectories } from '../utils/getPackageDirectories.js';
4
+ import { validateTests } from '../utils/validateTests.js';
5
+ import { extractTypeNamesFromManifestFile } from '../parsers/manifestParser.js';
6
+ import { formatList } from '../utils/formatters.js';
7
+ import { searchDirectoryForTestClasses } from '../readers/directorySearcher.js';
8
+ import { searchDirectoryForTestNamesInTestSuites } from '../readers/testSuiteSearcher.js';
9
+ import { METADATA_FILTER_CONFIG } from '../utils/constants.js';
10
+ import { loadTestMetadataDependencies, selectRelevantTests } from '../parsers/metadataFilterParser.js';
11
+ export async function listTests({ format = 'sf', manifest, ignoreMissingTests = false, ignoreDirs = [], noWarnings = false, filterByMetadata = false, warn = () => { }, }) {
12
+ let testClassesNames = null;
13
+ const testSuitesNames = [];
14
+ const allTestClasses = [];
15
+ const warnings = [];
16
+ const { metadataPaths: packageDirectories, repoRoot } = await getPackageDirectories(ignoreDirs);
17
+ if (manifest) {
18
+ const manifestMetadata = await extractTypeNamesFromManifestFile(manifest);
19
+ testClassesNames = manifestMetadata.filter((name) => !name.endsWith('testSuite-meta.xml'));
20
+ if (filterByMetadata) {
21
+ const ymlConfigPath = resolve(repoRoot, METADATA_FILTER_CONFIG);
22
+ const testMap = await loadTestMetadataDependencies(ymlConfigPath);
23
+ const testsToRun = selectRelevantTests(testMap, manifestMetadata);
24
+ allTestClasses.push(...testsToRun);
25
+ }
26
+ }
27
+ if (!filterByMetadata) {
28
+ for (const directory of packageDirectories) {
29
+ const searchResult = await searchDirectoryForTestClasses(directory, testClassesNames);
30
+ allTestClasses.push(...searchResult.classes);
31
+ testSuitesNames.push(...searchResult.testSuites);
32
+ if (searchResult.warnings?.length) {
33
+ warnings.push(...searchResult.warnings);
34
+ }
35
+ }
36
+ if (testSuitesNames.length > 0) {
37
+ for (const directory of packageDirectories.filter((dir) => dir.includes('testSuites'))) {
38
+ const testNames = await searchDirectoryForTestNamesInTestSuites(directory, packageDirectories);
39
+ allTestClasses.push(...testNames);
40
+ }
41
+ }
42
+ }
43
+ let finalTestMethods = Array.from(new Set(allTestClasses.map((test) => test.trim())));
44
+ if (ignoreMissingTests) {
45
+ const { validatedTests, warnings: validationWarnings } = await validateTests(finalTestMethods, packageDirectories);
46
+ finalTestMethods = validatedTests;
47
+ warnings.push(...validationWarnings);
48
+ }
49
+ if (!noWarnings && warnings.length > 0) {
50
+ warnings.forEach(warn);
51
+ }
52
+ finalTestMethods.sort((a, b) => a.localeCompare(b));
53
+ if (finalTestMethods.length === 0) {
54
+ warn('No test methods found');
55
+ return {
56
+ tests: [],
57
+ command: '',
58
+ };
59
+ }
60
+ return formatList(format, finalTestMethods);
61
+ }
62
+ //# sourceMappingURL=listTests.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"listTests.js","sourceRoot":"","sources":["../../src/core/listTests.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Given a certain manifest file, reads that file and returns the classes,
3
+ * triggers and test suites members with their types as prefix.
4
+ *
5
+ * For example, as ApexClass:MyClass.
6
+ *
7
+ * @param manifestFile the path to the manifest file
8
+ * @returns a list of strings with the type and member names
9
+ */
10
+ export declare function extractTypeNamesFromManifestFile(manifestFile: string): Promise<string[]>;
@@ -0,0 +1,25 @@
1
+ 'use strict';
2
+ import { existsSync } from 'node:fs';
3
+ import { ComponentSet } from '@salesforce/source-deploy-retrieve';
4
+ /**
5
+ * Given a certain manifest file, reads that file and returns the classes,
6
+ * triggers and test suites members with their types as prefix.
7
+ *
8
+ * For example, as ApexClass:MyClass.
9
+ *
10
+ * @param manifestFile the path to the manifest file
11
+ * @returns a list of strings with the type and member names
12
+ */
13
+ export async function extractTypeNamesFromManifestFile(manifestFile) {
14
+ const result = [];
15
+ if (!manifestFile || !existsSync(manifestFile)) {
16
+ return result;
17
+ }
18
+ const componentSet = await ComponentSet.fromManifest({ manifestPath: manifestFile });
19
+ for (const component of componentSet) {
20
+ const typeName = component.type.name;
21
+ result.push(`${typeName}:${component.fullName}`);
22
+ }
23
+ return result.sort((a, b) => a.localeCompare(b));
24
+ }
25
+ //# sourceMappingURL=manifestParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"manifestParser.js","sourceRoot":"","sources":["../../src/parsers/manifestParser.ts"],"names":[],"mappings":"AACA,YAAY,CAAC;AAEb,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,YAAY,EAAG,MAAM,oCAAoC,CAAC;AAEnE;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,gCAAgC,CAAC,YAAoB;IACzE,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,CAAC,YAAY,IAAI,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC/C,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,MAAM,YAAY,GAAiB,MAAM,YAAY,CAAC,YAAY,CAAC,EAAC,YAAY,EAAE,YAAY,EAAC,CAAC,CAAC;IACjG,KAAK,MAAM,SAAS,IAAI,YAAY,EAAE,CAAC;QACrC,MAAM,QAAQ,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,GAAG,QAAQ,IAAI,SAAS,CAAC,QAAQ,EAAE,CAAC,CAAC;IACnD,CAAC;IAED,OAAO,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;AACnD,CAAC"}
@@ -0,0 +1,9 @@
1
+ import { TestMetadataMap } from '../utils/types.js';
2
+ /**
3
+ * Loads the test metadata filter YAML file and confirms it matches the expected structure.
4
+ */
5
+ export declare function loadTestMetadataDependencies(configPath: string): Promise<TestMetadataMap>;
6
+ /**
7
+ * Return test classes that depend on any of the changed metadata.
8
+ */
9
+ export declare function selectRelevantTests(testMap: TestMetadataMap, changedMetadata: string[]): string[];
@@ -0,0 +1,50 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { parse } from 'yaml';
3
+ /**
4
+ * Verifies structure matches the test metadata map type.
5
+ */
6
+ function isTestMetadataMap(obj) {
7
+ if (typeof obj !== 'object' || obj === null)
8
+ return false;
9
+ return Object.entries(obj).every(([key, value]) => typeof key === 'string' &&
10
+ Array.isArray(value) &&
11
+ value.every(v => typeof v === 'string'));
12
+ }
13
+ /**
14
+ * Loads the test metadata filter YAML file and confirms it matches the expected structure.
15
+ */
16
+ export async function loadTestMetadataDependencies(configPath) {
17
+ const content = await readFile(configPath, 'utf-8');
18
+ const parsed = parse(content);
19
+ if (isTestMetadataMap(parsed)) {
20
+ return parsed;
21
+ }
22
+ throw new Error('Invalid test metadata dependencies format in YAML');
23
+ }
24
+ /**
25
+ * Compare two metadata strings like "CustomField:Account__c.Name"
26
+ * against patterns with wildcards, like "CustomField:Account__c.*"
27
+ */
28
+ function matchesDependency(dependency, changed) {
29
+ if (dependency.includes('*')) {
30
+ const pattern = dependency
31
+ .replace('.', '\\.')
32
+ .replace('*', '.*');
33
+ return new RegExp(`^${pattern}$`).test(changed);
34
+ }
35
+ return dependency === changed;
36
+ }
37
+ /**
38
+ * Return test classes that depend on any of the changed metadata.
39
+ */
40
+ export function selectRelevantTests(testMap, changedMetadata) {
41
+ const selected = [];
42
+ for (const [testClass, dependencies] of Object.entries(testMap)) {
43
+ const matches = changedMetadata.some(changed => dependencies.some(dep => matchesDependency(dep, changed)));
44
+ if (matches) {
45
+ selected.push(testClass);
46
+ }
47
+ }
48
+ return selected;
49
+ }
50
+ //# sourceMappingURL=metadataFilterParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadataFilterParser.js","sourceRoot":"","sources":["../../src/parsers/metadataFilterParser.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,KAAK,EAAE,MAAM,MAAM,CAAC;AAI7B;;GAEG;AACH,SAAS,iBAAiB,CAAC,GAAY;IACnC,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAE1D,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,EAAE,KAAK,CAAC,EAAE,EAAE,CAC9C,OAAO,GAAG,KAAK,QAAQ;QACvB,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QACpB,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAAC,CAC1C,CAAC;AACN,CAAC;AAED;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,4BAA4B,CAAC,UAAkB;IACjE,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACpD,MAAM,MAAM,GAAY,KAAK,CAAC,OAAO,CAAC,CAAC;IAEvC,IAAI,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAC;QAC5B,OAAO,MAAM,CAAC;IAClB,CAAC;IAED,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;AACzE,CAAC;AAED;;;GAGG;AACH,SAAS,iBAAiB,CAAC,UAAkB,EAAE,OAAe;IAC1D,IAAI,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,OAAO,GAAG,UAAU;aACrB,OAAO,CAAC,GAAG,EAAE,KAAK,CAAC;aACnB,OAAO,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC;QACxB,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,GAAG,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACpD,CAAC;IACD,OAAO,UAAU,KAAK,OAAO,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAC/B,OAAwB,EACxB,eAAyB;IAEzB,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,KAAK,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9D,MAAM,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAC3C,YAAY,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,iBAAiB,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAC5D,CAAC;QACF,IAAI,OAAO,EAAE,CAAC;YACV,QAAQ,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7B,CAAC;IACL,CAAC;IAED,OAAO,QAAQ,CAAC;AACpB,CAAC"}
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Parses the line in an Apex file that contains the test names.
3
+ *
4
+ * @param testNames names of the tests in an Apex file line
5
+ * @returns list of the test classes
6
+ */
7
+ export declare function parseTestsNames(testNames: string[] | null): string[];
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Parses the line in an Apex file that contains the test names.
3
+ *
4
+ * @param testNames names of the tests in an Apex file line
5
+ * @returns list of the test classes
6
+ */
7
+ export function parseTestsNames(testNames) {
8
+ if (!testNames || testNames.length === 0) {
9
+ return [];
10
+ }
11
+ // remove the prefix @Tests
12
+ return testNames
13
+ .join(' ')
14
+ .replace(/(@tests\s*:\s*)/gi, '')
15
+ .replace(/[,\s]+/g, ' ')
16
+ .trim()
17
+ .split(' ');
18
+ }
19
+ //# sourceMappingURL=testNameParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testNameParser.js","sourceRoot":"","sources":["../../src/parsers/testNameParser.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAAC,SAA0B;IACxD,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACzC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,2BAA2B;IAC3B,OAAO,SAAS;SACb,IAAI,CAAC,GAAG,CAAC;SACT,OAAO,CAAC,mBAAmB,EAAE,EAAE,CAAC;SAChC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,IAAI,EAAE;SACN,KAAK,CAAC,GAAG,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Analyzes the content of a test suite xml file and returns the Apex classes
3
+ * included in it.
4
+ *
5
+ * @param data string with the content of the test suite xml file
6
+ * @returns a list of test classes names in the test suite
7
+ */
8
+ export declare function parseTestSuiteFile(data: string): string[];
9
+ /**
10
+ * Parses the line in an Apex file that contains the test suites names.
11
+ *
12
+ * @param testNames names of the test suites in an Apex file line
13
+ * @returns list of the test suites
14
+ */
15
+ export declare function parseTestSuitesNames(testSuitesNames: string[] | null): string[];
@@ -0,0 +1,40 @@
1
+ 'use strict';
2
+ import { Parser } from 'xml2js';
3
+ /**
4
+ * Analyzes the content of a test suite xml file and returns the Apex classes
5
+ * included in it.
6
+ *
7
+ * @param data string with the content of the test suite xml file
8
+ * @returns a list of test classes names in the test suite
9
+ */
10
+ export function parseTestSuiteFile(data) {
11
+ const result = [];
12
+ new Parser().parseString(data, (error, parsed) => {
13
+ if (error) {
14
+ throw error;
15
+ }
16
+ parsed.ApexTestSuite.testClassName.forEach((testSuite) => {
17
+ result.push(testSuite);
18
+ });
19
+ });
20
+ return result.sort();
21
+ }
22
+ /**
23
+ * Parses the line in an Apex file that contains the test suites names.
24
+ *
25
+ * @param testNames names of the test suites in an Apex file line
26
+ * @returns list of the test suites
27
+ */
28
+ export function parseTestSuitesNames(testSuitesNames) {
29
+ if (!testSuitesNames || testSuitesNames.length === 0) {
30
+ return [];
31
+ }
32
+ // remove the prefix @testsuites
33
+ return testSuitesNames
34
+ .join(' ')
35
+ .replace(/(@testsuites\s*:\s*)/gi, '')
36
+ .replace(/[,\s]+/g, ' ')
37
+ .trim()
38
+ .split(' ');
39
+ }
40
+ //# sourceMappingURL=testSuiteParser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"testSuiteParser.js","sourceRoot":"","sources":["../../src/parsers/testSuiteParser.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAEhC;;;;;;GAMG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC7C,MAAM,MAAM,GAAa,EAAE,CAAC;IAE5B,IAAI,MAAM,EAAE,CAAC,WAAW,CAAC,IAAI,EAAE,CAAC,KAAK,EAAE,MAAsD,EAAE,EAAE;QAC/F,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,SAAS,EAAE,EAAE;YACvD,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,OAAO,MAAM,CAAC,IAAI,EAAE,CAAC;AACvB,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,oBAAoB,CAAC,eAAgC;IACnE,IAAI,CAAC,eAAe,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACrD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,gCAAgC;IAChC,OAAO,eAAe;SACnB,IAAI,CAAC,GAAG,CAAC;SACT,OAAO,CAAC,wBAAwB,EAAE,EAAE,CAAC;SACrC,OAAO,CAAC,SAAS,EAAE,GAAG,CAAC;SACvB,IAAI,EAAE;SACN,KAAK,CAAC,GAAG,CAAC,CAAC;AAChB,CAAC"}
@@ -0,0 +1,10 @@
1
+ import { SearchResult } from '../utils/types.js';
2
+ /**
3
+ * Given a certain directory, search its contents for files that end with '.cls' or '.trigger'.
4
+ * Extracts the test names from the annotations on those files, and returns a list with those.
5
+ *
6
+ * @param directory directory within the project to search for files
7
+ * @param names names of test files
8
+ * @returns list of test classes
9
+ */
10
+ export declare function searchDirectoryForTestClasses(directory: string, names: string[] | null): Promise<SearchResult>;
@@ -0,0 +1,82 @@
1
+ 'use strict';
2
+ import { readdirSync, readFileSync } from 'node:fs';
3
+ import { queue } from 'async';
4
+ import { parseTestSuitesNames } from '../parsers/testSuiteParser.js';
5
+ import { parseTestsNames } from '../parsers/testNameParser.js';
6
+ import { TEST_CLASS_ANNOTATION_REGEX, TEST_NAME_REGEX, TEST_SUITE_NAME_REGEX } from '../utils/constants.js';
7
+ import { getConcurrencyThreshold } from '../utils/concurrencyThreshold.js';
8
+ /**
9
+ * Given a certain directory, search its contents for files that end with '.cls' or '.trigger'.
10
+ * Extracts the test names from the annotations on those files, and returns a list with those.
11
+ *
12
+ * @param directory directory within the project to search for files
13
+ * @param names names of test files
14
+ * @returns list of test classes
15
+ */
16
+ export async function searchDirectoryForTestClasses(directory, names) {
17
+ const testClassesNames = new Set();
18
+ const testSuitesNames = new Set();
19
+ const warnings = [];
20
+ let readDir;
21
+ try {
22
+ readDir = readdirSync(directory, { recursive: true });
23
+ }
24
+ catch (error) {
25
+ throw new Error(`Invalid or inaccessible directory: ${directory}`);
26
+ }
27
+ // Gets the list of Apex files (classes and triggers)
28
+ const apexFiles = readDir.filter((file) => {
29
+ if (!file.endsWith('.cls') && !file.endsWith('.trigger')) {
30
+ return false;
31
+ }
32
+ const fileFullName = file.split('/').pop()?.split('.');
33
+ if (!names) {
34
+ return true;
35
+ }
36
+ if (fileFullName && fileFullName.length > 0) {
37
+ const formattedName = `${fileFullName[1] === 'cls' ? 'ApexClass' : 'ApexTrigger'}:${fileFullName[0]}`;
38
+ return names.includes(formattedName);
39
+ }
40
+ });
41
+ const testClassNameHandler = (fileName) => {
42
+ const path = `${directory}/${fileName}`;
43
+ const data = readFileSync(path, 'utf-8');
44
+ const testNameMatches = Array.from(data.matchAll(TEST_NAME_REGEX));
45
+ const testSuiteMatches = Array.from(data.matchAll(TEST_SUITE_NAME_REGEX));
46
+ const testClassAnnotationMatches = data.match(TEST_CLASS_ANNOTATION_REGEX);
47
+ let hasAnnotation = false;
48
+ if (testNameMatches.length > 0) {
49
+ hasAnnotation = true;
50
+ for (const match of testNameMatches) {
51
+ parseTestsNames(match).forEach((testMethod) => testClassesNames.add(testMethod));
52
+ }
53
+ }
54
+ if (testSuiteMatches.length > 0) {
55
+ hasAnnotation = true;
56
+ for (const match of testSuiteMatches) {
57
+ parseTestSuitesNames(match).forEach((testMethod) => testSuitesNames.add(testMethod));
58
+ }
59
+ }
60
+ if (testClassAnnotationMatches && testClassAnnotationMatches.length > 0) {
61
+ hasAnnotation = true;
62
+ testClassesNames.add(fileName.split('.').shift());
63
+ }
64
+ if (!hasAnnotation) {
65
+ warnings.push(`File "${fileName}" does not contain @tests, @testsuites, or @istest annotations`);
66
+ }
67
+ };
68
+ const testClassNameProcessor = queue((f, cb) => {
69
+ testClassNameHandler(f);
70
+ cb();
71
+ }, getConcurrencyThreshold());
72
+ await testClassNameProcessor.push(apexFiles);
73
+ if (testClassNameProcessor.length() > 0) {
74
+ await testClassNameProcessor.drain();
75
+ }
76
+ return {
77
+ classes: Array.from(testClassesNames),
78
+ testSuites: Array.from(testSuitesNames),
79
+ warnings,
80
+ };
81
+ }
82
+ //# sourceMappingURL=directorySearcher.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"directorySearcher.js","sourceRoot":"","sources":["../../src/readers/directorySearcher.ts"],"names":[],"mappings":"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"}