Skip to content

Commit f067ad3

Browse files
feat: add new logic get test command
1 parent e11cce0 commit f067ad3

File tree

6 files changed

+207
-27
lines changed

6 files changed

+207
-27
lines changed

messages/logicgettest.md

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# summary
2+
3+
Get the results of a test run.
4+
5+
# description
6+
7+
When you run 'sf logic run test' asynchronously, it returns a test run ID. Use that ID with this command to see the results.
8+
9+
To see code coverage results, use the --code-coverage flag with --result-format. The output displays a high-level summary of the test run and the code coverage values for classes in your org. If you specify human-readable result format, use the --detailed-coverage flag to see detailed coverage results for each test method run.
10+
11+
# examples
12+
13+
- Get the results for a specific test run ID in the default human-readable format:
14+
15+
<%= config.bin %> <%= command.id %> --test-run-id <test run id>
16+
17+
- Get the results for a specific test run ID, format them as JUnit, and save them to the "test-results/junit" directory:
18+
19+
<%= config.bin %> <%= command.id %> --test-run-id <test run id> --result-format junit
20+
21+
# flags.test-run-id.summary
22+
23+
ID of the test run.
24+
25+
# flags.output-dir.summary
26+
27+
Directory in which to store test result files.
28+
29+
# flags.concise.summary
30+
31+
Display only failed test results; works with human-readable output only.
32+
33+
# apexLibErr
34+
35+
Unknown error in Apex Library: %s
36+
37+
# flags.detailed-coverage.summary
38+
39+
Display detailed code coverage per test.

src/commands/apex/get/test.ts

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,17 @@
55
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
66
*/
77

8-
import { TestService } from '@salesforce/apex-node';
98
import {
109
Flags,
1110
loglevel,
1211
orgApiVersionFlagWithDeprecations,
1312
requiredOrgFlagWithDeprecations,
1413
SfCommand,
15-
Ux,
1614
} from '@salesforce/sf-plugins-core';
1715
import { Messages } from '@salesforce/core';
18-
import { RunResult, TestReporter } from '../../../reporters/index.js';
16+
import { RunResult } from '../../../reporters/index.js';
1917
import { codeCoverageFlag, resultFormatFlag } from '../../../flags.js';
18+
import { TestGetBase } from '../../../shared/TestGetBase.js';
2019

2120
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
2221
const messages = Messages.loadMessages('@salesforce/plugin-apex', 'gettest');
@@ -60,20 +59,18 @@ export default class Test extends SfCommand<RunResult> {
6059
public async run(): Promise<RunResult> {
6160
const { flags } = await this.parse(Test);
6261

63-
const conn = flags['target-org'].getConnection(flags['api-version']);
64-
65-
const testService = new TestService(conn);
66-
const result = await testService.reportAsyncResults(flags['test-run-id'], flags['code-coverage']);
67-
68-
const testReporter = new TestReporter(new Ux({ jsonEnabled: this.jsonEnabled() }), conn);
69-
70-
return testReporter.report(result, {
71-
'output-dir': flags['output-dir'],
72-
'result-format': flags['result-format'],
62+
// Use shared business logic
63+
return TestGetBase.execute({
64+
connection: flags['target-org'].getConnection(flags['api-version']),
65+
testRunId: flags['test-run-id'],
66+
codeCoverage: flags['code-coverage'],
67+
outputDir: flags['output-dir'],
68+
resultFormat: flags['result-format'],
7369
json: flags.json,
74-
'code-coverage': flags['code-coverage'],
75-
'detailed-coverage': flags['detailed-coverage'],
70+
detailedCoverage: flags['detailed-coverage'],
7671
concise: flags.concise,
72+
jsonEnabled: this.jsonEnabled(),
73+
isUnifiedLogic: false
7774
});
7875
}
7976
}

src/commands/logic/get/test.ts

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
/*
2+
* Copyright (c) 2020, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import {
9+
Flags,
10+
loglevel,
11+
orgApiVersionFlagWithDeprecations,
12+
requiredOrgFlagWithDeprecations,
13+
SfCommand,
14+
} from '@salesforce/sf-plugins-core';
15+
import { Messages } from '@salesforce/core';
16+
import { RunResult } from '../../../reporters/index.js';
17+
import { codeCoverageFlag, resultFormatFlag } from '../../../flags.js';
18+
import { TestGetBase } from '../../../shared/TestGetBase.js';
19+
20+
Messages.importMessagesDirectoryFromMetaUrl(import.meta.url);
21+
const messages = Messages.loadMessages('@salesforce/plugin-apex', 'logicgettest');
22+
23+
export default class Test extends SfCommand<RunResult> {
24+
public static readonly summary = messages.getMessage('summary');
25+
public static readonly description = messages.getMessage('description');
26+
public static readonly examples = messages.getMessages('examples');
27+
public static readonly deprecateAliases = true;
28+
public static readonly aliases = ['force:logic:test:report'];
29+
30+
public static readonly flags = {
31+
'target-org': requiredOrgFlagWithDeprecations,
32+
'api-version': orgApiVersionFlagWithDeprecations,
33+
loglevel,
34+
'test-run-id': Flags.salesforceId({
35+
deprecateAliases: true,
36+
aliases: ['testrunid'],
37+
char: 'i',
38+
summary: messages.getMessage('flags.test-run-id.summary'),
39+
required: true,
40+
startsWith: '707',
41+
length: 'both',
42+
}),
43+
'code-coverage': codeCoverageFlag,
44+
'detailed-coverage': Flags.boolean({
45+
summary: messages.getMessage('flags.detailed-coverage.summary'),
46+
dependsOn: ['code-coverage'],
47+
}),
48+
'output-dir': Flags.directory({
49+
aliases: ['outputdir', 'output-directory'],
50+
deprecateAliases: true,
51+
char: 'd',
52+
summary: messages.getMessage('flags.output-dir.summary'),
53+
}),
54+
'result-format': resultFormatFlag,
55+
concise: Flags.boolean({
56+
summary: messages.getMessage('flags.concise.summary'),
57+
})
58+
};
59+
60+
public async run(): Promise<RunResult> {
61+
const { flags } = await this.parse(Test);
62+
63+
// Use shared business logic
64+
return TestGetBase.execute({
65+
connection: flags['target-org'].getConnection(),
66+
testRunId: flags['test-run-id'],
67+
codeCoverage: flags['code-coverage'],
68+
outputDir: flags['output-dir'],
69+
resultFormat: flags['result-format'],
70+
json: flags.json,
71+
detailedCoverage: false,
72+
concise: flags.concise,
73+
jsonEnabled: this.jsonEnabled(),
74+
isUnifiedLogic: true
75+
});
76+
}
77+
}

src/reporters/jsonReporter.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ type CliCoverageResult = {
8585
};
8686

8787
export class JsonReporter {
88-
public format(result: TestResult): RunResult {
88+
public format(result: TestResult, includeCategory: boolean = false): RunResult {
8989
const returnObject: RunResult = {
9090
summary: {
9191
// result.summary contains more information than we want to return, so we'll specify each key we want to return instead of using ...
@@ -121,6 +121,7 @@ export class JsonReporter {
121121
},
122122
RunTime: test.runTime,
123123
FullName: test.fullName,
124+
...(includeCategory ? { Category: test.category } : {}),
124125
})),
125126
...(result.codecoverage
126127
? {

src/reporters/testReporter.ts

Lines changed: 14 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -45,18 +45,20 @@ export class TestReporter {
4545
json?: boolean;
4646
'code-coverage'?: boolean;
4747
concise: boolean;
48+
isUnifiedLogic?: boolean;
4849
}
4950
): Promise<RunResult> {
5051
if (options['output-dir']) {
51-
const jsonOutput = this.formatResultInJson(result);
52+
const jsonOutput = this.formatResultInJson(result, options.isUnifiedLogic);
5253
const outputDirConfig = this.buildOutputDirConfig(
5354
result,
5455
jsonOutput,
5556
options['output-dir'],
5657
options['result-format'] as ResultFormat | undefined,
5758
Boolean(options['detailed-coverage']),
5859
options.concise,
59-
options.synchronous
60+
options.synchronous,
61+
options.isUnifiedLogic
6062
);
6163

6264
const testService = new TestService(this.connection);
@@ -70,7 +72,7 @@ export class TestReporter {
7072
}
7173
switch (options['result-format']) {
7274
case 'human':
73-
this.logHuman(result, options['detailed-coverage'] as boolean, options['concise'], options['output-dir']);
75+
this.logHuman(result, options['detailed-coverage'] as boolean, options['concise'], options['output-dir'], options.isUnifiedLogic);
7476
break;
7577
case 'tap':
7678
this.logTap(result);
@@ -83,7 +85,7 @@ export class TestReporter {
8385
if (!options.json) {
8486
this.ux.styledJSON({
8587
status: process.exitCode,
86-
result: this.formatResultInJson(result),
88+
result: this.formatResultInJson(result, options.isUnifiedLogic),
8789
});
8890
}
8991
break;
@@ -95,7 +97,7 @@ export class TestReporter {
9597
throw messages.createError('testResultProcessErr', [(e as Error).message]);
9698
}
9799

98-
return this.formatResultInJson(result);
100+
return this.formatResultInJson(result, options.isUnifiedLogic);
99101
}
100102
/**
101103
* Builds output directory configuration with CLI format result files
@@ -116,7 +118,8 @@ export class TestReporter {
116118
resultFormat: ResultFormat | undefined,
117119
detailedCoverage: boolean,
118120
concise: boolean,
119-
synchronous = false
121+
synchronous = false,
122+
isUnifiedLogic?: boolean
120123
): OutputDirConfig {
121124
const outputDirConfig: OutputDirConfig = {
122125
dirPath: outputDir,
@@ -164,7 +167,7 @@ export class TestReporter {
164167
case ResultFormat.human:
165168
outputDirConfig.fileInfos?.push({
166169
filename: 'test-result.txt',
167-
content: new HumanReporter().format(result, detailedCoverage, concise),
170+
content: new HumanReporter().format(result, detailedCoverage, concise, isUnifiedLogic),
168171
});
169172
break;
170173
default:
@@ -174,22 +177,22 @@ export class TestReporter {
174177

175178
return outputDirConfig;
176179
}
177-
private formatResultInJson(result: TestResult): RunResult {
180+
private formatResultInJson(result: TestResult, isUnifiedLogic?: boolean): RunResult {
178181
try {
179182
const reporter = new JsonReporter();
180-
return reporter.format(result);
183+
return reporter.format(result, isUnifiedLogic);
181184
} catch (e) {
182185
this.ux.styledJSON(result);
183186
throw messages.createError('testResultProcessErr', [(e as Error).message]);
184187
}
185188
}
186189

187-
private logHuman(result: TestResult, detailedCoverage: boolean, concise: boolean, outputDir?: string): void {
190+
private logHuman(result: TestResult, detailedCoverage: boolean, concise: boolean, outputDir?: string, isUnifiedLogic?: boolean): void {
188191
if (outputDir) {
189192
this.ux.log(messages.getMessage('outputDirHint', [outputDir]));
190193
}
191194
const humanReporter = new HumanReporter();
192-
const output = humanReporter.format(result, detailedCoverage, concise);
195+
const output = humanReporter.format(result, detailedCoverage, concise, isUnifiedLogic);
193196
this.ux.log(output);
194197
}
195198

src/shared/TestGetBase.ts

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
/*
2+
* Copyright (c) 2020, salesforce.com, inc.
3+
* All rights reserved.
4+
* Licensed under the BSD 3-Clause license.
5+
* For full license text, see LICENSE.txt file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
8+
import { TestService } from '@salesforce/apex-node';
9+
import { Connection } from '@salesforce/core';
10+
import { Ux } from '@salesforce/sf-plugins-core';
11+
import { RunResult, TestReporter } from '../reporters/index.js';
12+
13+
export type TestGetBaseOptions = {
14+
connection: Connection;
15+
testRunId: string;
16+
codeCoverage?: boolean;
17+
outputDir?: string;
18+
resultFormat?: string;
19+
json?: boolean;
20+
detailedCoverage?: boolean;
21+
concise?: boolean;
22+
isUnifiedLogic?: boolean;
23+
testCategory?: string;
24+
jsonEnabled: boolean;
25+
additionalReportOptions?: Record<string, unknown>;
26+
};
27+
28+
/**
29+
* Common logic for getting test results.
30+
*/
31+
export class TestGetBase {
32+
public static async execute(options: TestGetBaseOptions): Promise<RunResult> {
33+
const {
34+
connection,
35+
testRunId,
36+
codeCoverage,
37+
outputDir,
38+
resultFormat,
39+
json,
40+
detailedCoverage,
41+
concise,
42+
jsonEnabled,
43+
isUnifiedLogic
44+
} = options;
45+
46+
const testService = new TestService(connection);
47+
const result = await testService.reportAsyncResults(testRunId, codeCoverage);
48+
49+
const testReporter = new TestReporter(new Ux({ jsonEnabled }), connection);
50+
51+
const reportOptions = {
52+
'output-dir': outputDir,
53+
'result-format': resultFormat,
54+
json,
55+
'code-coverage': codeCoverage,
56+
'detailed-coverage': detailedCoverage,
57+
concise: concise ?? false,
58+
isUnifiedLogic
59+
};
60+
61+
return testReporter.report(result, reportOptions);
62+
}
63+
}

0 commit comments

Comments
 (0)