From 7297c18b414c7a8ebb3722dc77ce0488ce5d07b8 Mon Sep 17 00:00:00 2001 From: Joshua Feingold Date: Fri, 7 Nov 2025 13:38:47 -0600 Subject: [PATCH 1/3] feat: added RunRelevantTests test-level @W-20152151@ --- messages/sdr.md | 4 ++++ package.json | 2 ++ src/client/metadataApiDeploy.ts | 13 +++++++++++++ src/client/types.ts | 2 +- test/client/metadataApiDeploy.test.ts | 21 +++++++++++++++++++++ yarn.lock | 5 +++++ 6 files changed, 46 insertions(+), 1 deletion(-) diff --git a/messages/sdr.md b/messages/sdr.md index 32d31dec9..5b71679d9 100644 --- a/messages/sdr.md +++ b/messages/sdr.md @@ -22,6 +22,10 @@ Could not find parent type for %s (%s) Component conversion failed: %s +# error_invalid_test_level + +TestLevel cannot be '%s' unless API version is %s or later + # error_merge_metadata_target_unsupported Merge convert for metadata target format currently unsupported diff --git a/package.json b/package.json index 3c8bb2551..6b9e48ea8 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@salesforce/kit": "^3.2.4", "@salesforce/ts-types": "^2.0.12", "@salesforce/types": "^1.5.0", + "@types/semver": "^7.7.1", "fast-levenshtein": "^3.0.0", "fast-xml-parser": "^4.5.3", "got": "^11.8.6", @@ -38,6 +39,7 @@ "mime": "2.6.0", "minimatch": "^9.0.5", "proxy-agent": "^6.4.0", + "semver": "^7.7.3", "yaml": "^2.8.1" }, "devDependencies": { diff --git a/src/client/metadataApiDeploy.ts b/src/client/metadataApiDeploy.ts index 1e2bbb2bf..a7503b987 100644 --- a/src/client/metadataApiDeploy.ts +++ b/src/client/metadataApiDeploy.ts @@ -18,6 +18,7 @@ import { format } from 'node:util'; import { isString } from '@salesforce/ts-types'; import JSZip from 'jszip'; import fs from 'graceful-fs'; +import semver from 'semver'; import { Lifecycle } from '@salesforce/core/lifecycle'; import { Messages } from '@salesforce/core/messages'; import { SfError } from '@salesforce/core/sfError'; @@ -116,6 +117,7 @@ export class MetadataApiDeploy extends MetadataTransfer< public constructor(options: MetadataApiDeployOptions) { super(options); options.apiOptions = { ...MetadataApiDeploy.DEFAULT_OPTIONS.apiOptions, ...options.apiOptions }; + validateOptions(options); this.options = Object.assign({}, options); this.isRestDeploy = !!options.apiOptions?.rest; this.registry = options.registry ?? new RegistryAccess(); @@ -524,6 +526,17 @@ const buildFileResponsesFromComponentSet = } return fileResponses; }; + +const validateOptions = (options: MetadataApiDeployOptions): void => { + const runningRelevantTestsOnly = options.apiOptions?.testLevel === 'RunRelevantTests'; + const beforeApiV66 = options.apiVersion && semver.lt(semver.coerce(options.apiVersion)!, '66.0.0'); + if (runningRelevantTestsOnly && beforeApiV66) { + throw new SfError( + messages.getMessage('error_invalid_test_level', ['RunRelevantTests', '66.0']), + 'InvalidTestLevelSelection' + ); + } +}; /** * register a listener to `scopedPreDeploy` */ diff --git a/src/client/types.ts b/src/client/types.ts index 5da3ece36..dacbfd0c3 100644 --- a/src/client/types.ts +++ b/src/client/types.ts @@ -364,7 +364,7 @@ export type MetadataApiDeployOptions = { runAllTests?: boolean; runTests?: string[]; singlePackage?: boolean; - testLevel?: 'NoTestRun' | 'RunSpecifiedTests' | 'RunLocalTests' | 'RunAllTestsInOrg'; + testLevel?: 'NoTestRun' | 'RunSpecifiedTests' | 'RunLocalTests' | 'RunAllTestsInOrg' | 'RunRelevantTests'; /** * Set to true to use the REST API for deploying. */ diff --git a/test/client/metadataApiDeploy.test.ts b/test/client/metadataApiDeploy.test.ts index c8da492d7..eae00e933 100644 --- a/test/client/metadataApiDeploy.test.ts +++ b/test/client/metadataApiDeploy.test.ts @@ -1256,6 +1256,27 @@ describe('MetadataApiDeploy', () => { expect(mdOpts.zipPath).to.equal('foo/myZip.zip'); }); + it('should disallow "RunRelevantTests" for API versions <66.0', () => { + const constructorError = { + name: 'InvalidTestLevelSelection', + message: messages.getMessage('error_invalid_test_level', ['RunRelevantTests', '66.0']), + }; + try { + new MetadataApiDeploy({ + usernameOrConnection: 'testing', + apiOptions: { + testLevel: 'RunRelevantTests', + }, + apiVersion: '8.0', // Tricksy case here: 8.0 is alphabetically after "66.0" but semantically after it. + }); + assert.fail('Should have thrown an error'); + } catch (e) { + assert(e instanceof Error); + expect(e.name).to.equal(constructorError.name); + expect(e.message).to.equal(constructorError.message); + } + }); + it('should allow mdapi path', () => { const mdApiDeploy = new MetadataApiDeploy({ usernameOrConnection: 'testing', diff --git a/yarn.lock b/yarn.lock index 4ae890bb3..a3340af7b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1039,6 +1039,11 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== +"@types/semver@^7.7.1": + version "7.7.1" + resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" + integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== + "@types/shelljs@^0.8.15": version "0.8.15" resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.15.tgz#22c6ab9dfe05cec57d8e6cb1a95ea173aee9fcac" From adb39cb1c76ef80512f6cf2994b4d8911c21835c Mon Sep 17 00:00:00 2001 From: Joshua Feingold Date: Mon, 10 Nov 2025 15:04:11 -0600 Subject: [PATCH 2/3] feat: feedback on RunRelevantTests implementation @W-20152151@ --- package.json | 2 -- src/client/metadataApiDeploy.ts | 3 +-- yarn.lock | 5 ----- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/package.json b/package.json index 6b9e48ea8..3c8bb2551 100644 --- a/package.json +++ b/package.json @@ -29,7 +29,6 @@ "@salesforce/kit": "^3.2.4", "@salesforce/ts-types": "^2.0.12", "@salesforce/types": "^1.5.0", - "@types/semver": "^7.7.1", "fast-levenshtein": "^3.0.0", "fast-xml-parser": "^4.5.3", "got": "^11.8.6", @@ -39,7 +38,6 @@ "mime": "2.6.0", "minimatch": "^9.0.5", "proxy-agent": "^6.4.0", - "semver": "^7.7.3", "yaml": "^2.8.1" }, "devDependencies": { diff --git a/src/client/metadataApiDeploy.ts b/src/client/metadataApiDeploy.ts index a7503b987..2d3ca0b6f 100644 --- a/src/client/metadataApiDeploy.ts +++ b/src/client/metadataApiDeploy.ts @@ -18,7 +18,6 @@ import { format } from 'node:util'; import { isString } from '@salesforce/ts-types'; import JSZip from 'jszip'; import fs from 'graceful-fs'; -import semver from 'semver'; import { Lifecycle } from '@salesforce/core/lifecycle'; import { Messages } from '@salesforce/core/messages'; import { SfError } from '@salesforce/core/sfError'; @@ -529,7 +528,7 @@ const buildFileResponsesFromComponentSet = const validateOptions = (options: MetadataApiDeployOptions): void => { const runningRelevantTestsOnly = options.apiOptions?.testLevel === 'RunRelevantTests'; - const beforeApiV66 = options.apiVersion && semver.lt(semver.coerce(options.apiVersion)!, '66.0.0'); + const beforeApiV66 = options.apiVersion && Number(options.apiVersion) < 66.0; if (runningRelevantTestsOnly && beforeApiV66) { throw new SfError( messages.getMessage('error_invalid_test_level', ['RunRelevantTests', '66.0']), diff --git a/yarn.lock b/yarn.lock index a3340af7b..4ae890bb3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1039,11 +1039,6 @@ resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.8.tgz#8268a8c57a3e4abd25c165ecd36237db7948a55e" integrity sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ== -"@types/semver@^7.7.1": - version "7.7.1" - resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.7.1.tgz#3ce3af1a5524ef327d2da9e4fd8b6d95c8d70528" - integrity sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA== - "@types/shelljs@^0.8.15": version "0.8.15" resolved "https://registry.yarnpkg.com/@types/shelljs/-/shelljs-0.8.15.tgz#22c6ab9dfe05cec57d8e6cb1a95ea173aee9fcac" From 4c52f3bb431734b93148e6f3aa055bc59d92efbb Mon Sep 17 00:00:00 2001 From: Joshua Feingold Date: Tue, 11 Nov 2025 09:25:20 -0600 Subject: [PATCH 3/3] feat: more feedback on RunRelevantTests implementation @W-20152151@ --- test/client/metadataApiDeploy.test.ts | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/test/client/metadataApiDeploy.test.ts b/test/client/metadataApiDeploy.test.ts index eae00e933..ace098fd6 100644 --- a/test/client/metadataApiDeploy.test.ts +++ b/test/client/metadataApiDeploy.test.ts @@ -1257,19 +1257,22 @@ describe('MetadataApiDeploy', () => { }); it('should disallow "RunRelevantTests" for API versions <66.0', () => { + const testCases = ['8.0', '10.0', '60.0', '65.0']; const constructorError = { name: 'InvalidTestLevelSelection', message: messages.getMessage('error_invalid_test_level', ['RunRelevantTests', '66.0']), }; try { - new MetadataApiDeploy({ - usernameOrConnection: 'testing', - apiOptions: { - testLevel: 'RunRelevantTests', - }, - apiVersion: '8.0', // Tricksy case here: 8.0 is alphabetically after "66.0" but semantically after it. + testCases.forEach((v) => { + new MetadataApiDeploy({ + usernameOrConnection: 'testing', + apiOptions: { + testLevel: 'RunRelevantTests', + }, + apiVersion: v, + }); + assert.fail(`Should have thrown an error for API version ${v}`); }); - assert.fail('Should have thrown an error'); } catch (e) { assert(e instanceof Error); expect(e.name).to.equal(constructorError.name);