Skip to content

Commit 48b337e

Browse files
authored
fix: handle error for sync test runs & check for valid error first (#710)
* fix: handle error for sync test runs & check for valid error before replacing * style: clarify flag and add line break after new function * fix: repair logic and add tests * fix: improve condition handling and tests, add comments * fix: include test level and fix flagging logic * chore: update assert.fail message
1 parent 0b30e26 commit 48b337e

File tree

2 files changed

+86
-13
lines changed

2 files changed

+86
-13
lines changed

src/commands/apex/run/test.ts

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -170,11 +170,16 @@ export default class Test extends SfCommand<RunCommandResult> {
170170
...(await testService.buildSyncPayload(testLevel, flags.tests?.join(','), flags['class-names']?.join(','))),
171171
skipCodeCoverage: !flags['code-coverage'],
172172
};
173-
return testService.runTestSynchronous(
174-
payload,
175-
flags['code-coverage'],
176-
this.cancellationTokenSource.token
177-
) as Promise<TestResult>;
173+
174+
try {
175+
return (await testService.runTestSynchronous(
176+
payload,
177+
flags['code-coverage'],
178+
this.cancellationTokenSource.token
179+
)) as TestResult;
180+
} catch (e) {
181+
throw handleTestingServerError(SfError.wrap(e), flags, testLevel);
182+
}
178183
}
179184

180185
private async runTestAsynchronous(
@@ -212,17 +217,40 @@ export default class Test extends SfCommand<RunCommandResult> {
212217
flags.wait
213218
)) as TestRunIdResult;
214219
} catch (e) {
215-
const error = SfError.wrap(e);
216-
if (error.message.includes('Always provide a classes, suites, tests, or testLevel property')) {
217-
error.message = 'There are no apex tests to run in the org';
218-
error.actions = ['Ensure Apex Tests exist in the org'];
219-
}
220-
221-
throw error;
220+
throw handleTestingServerError(SfError.wrap(e), flags, testLevel);
222221
}
223222
}
224223
}
225224

225+
function handleTestingServerError(
226+
error: SfError,
227+
flags: {
228+
tests?: string[];
229+
'class-names'?: string[];
230+
'suite-names'?: string[];
231+
},
232+
testLevel: TestLevel
233+
): SfError {
234+
if (!error.message.includes('Always provide a classes, suites, tests, or testLevel property')) {
235+
return error;
236+
}
237+
238+
// If error message condition is valid, return the original error.
239+
const hasSpecifiedTestLevel = testLevel === TestLevel.RunSpecifiedTests;
240+
const hasNoTestNames = !flags.tests?.length;
241+
const hasNoClassNames = !flags['class-names']?.length;
242+
const hasNoSuiteNames = !flags['suite-names']?.length;
243+
if (hasSpecifiedTestLevel && hasNoTestNames && hasNoClassNames && hasNoSuiteNames) {
244+
return error;
245+
}
246+
247+
// Otherwise, assume there are no Apex tests in the org and return clearer message.
248+
return Object.assign(error, {
249+
message: 'There are no Apex tests to run in this org.',
250+
actions: ['Ensure Apex Tests exist in the org, and try again.'],
251+
});
252+
}
253+
226254
const validateFlags = async (
227255
classNames?: string[],
228256
suiteNames?: string[],

test/commands/apex/run/test.test.ts

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { Messages, Org } from '@salesforce/core';
99
import sinon from 'sinon';
1010
import { Ux, stubSfCommandUx } from '@salesforce/sf-plugins-core';
1111
import { assert, expect } from 'chai';
12-
import { TestService } from '@salesforce/apex-node';
12+
import { TestLevel, TestService } from '@salesforce/apex-node';
1313
import Test from '../../../../src/commands/apex/run/test.js';
1414
import {
1515
runWithCoverage,
@@ -544,5 +544,50 @@ describe('apex:test:run', () => {
544544
expect(e.message).to.include('cannot also be provided when using');
545545
}
546546
});
547+
548+
it('rejects no Apex tests in the org', async () => {
549+
const serverError = { message: 'Always provide a classes, suites, tests, or testLevel property' };
550+
const expectedMessage = 'There are no Apex tests to run in this org.';
551+
552+
const runTestAsynchronousSpy = sandbox.stub(TestService.prototype, 'runTestAsynchronous').throws(serverError);
553+
try {
554+
await Test.run(['--test-level', TestLevel.RunSpecifiedTests.toString()]);
555+
assert.fail('Unexpected successful outcome for async specified test run without tests.');
556+
} catch (e) {
557+
assert(e instanceof Error);
558+
expect(e.message).to.include(serverError.message);
559+
}
560+
561+
expect(runTestAsynchronousSpy.calledOnce).to.be.true;
562+
563+
const runTestSynchronousSpy = sandbox.stub(TestService.prototype, 'runTestSynchronous').throws(serverError);
564+
try {
565+
await Test.run([
566+
'--test-level',
567+
TestLevel.RunSpecifiedTests.toString(),
568+
'--class-names',
569+
'myApex',
570+
'--synchronous',
571+
]);
572+
assert.fail('Unexpected successful outcome for sync specified tests run.');
573+
} catch (e) {
574+
assert(e instanceof Error);
575+
expect(e.message).to.include(expectedMessage);
576+
}
577+
578+
expect(runTestSynchronousSpy.calledOnce).to.be.true;
579+
expect(runTestAsynchronousSpy.calledOnce).to.be.true;
580+
581+
try {
582+
await Test.run(['--test-level', TestLevel.RunLocalTests.toString(), '--synchronous']);
583+
assert.fail('Unexpected successful outcome for sync local test run.');
584+
} catch (e) {
585+
assert(e instanceof Error);
586+
expect(e.message).to.include(expectedMessage);
587+
}
588+
589+
expect(runTestSynchronousSpy.calledOnce).to.be.true;
590+
expect(runTestAsynchronousSpy.callCount).to.equal(2);
591+
});
547592
});
548593
});

0 commit comments

Comments
 (0)