Skip to content

Commit 41c7c13

Browse files
Merge pull request #72 from apex-dev-tools/feedback-fixes
Update report formatting and fix minor issues
2 parents 88da887 + 7cb0889 commit 41c7c13

12 files changed

Lines changed: 176 additions & 108 deletions

File tree

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# test-runner - Changelog
22

3+
## 3.1.0 - 2023-11-24
4+
5+
* Format millisecond/second time values in reports as numbers.
6+
* For JSON: `"time": "12030 ms"` is now `"time": 12030`.
7+
* For JUnit XML: `value="12.03 s"` is now `value="12.03"`.
8+
* Add elapsed time to test run status logging.
9+
* Add `rerunExecutionTime` value to main test reports.
10+
* This is sum of all the re-run `RunTime`s. Similar to `testExecutionTime`.
11+
* Remove spaces in class time CSV report.
12+
* Add output of class time report to JSON.
13+
* Optimise missing test re-run request payload when requesting all tests in a class.
14+
315
## 3.0.0 - 2023-10-23
416

517
* **BREAKING**: `AsyncTestRunner` now does not re-throw errors. Instead it returns a `TestRunnerResult` type which includes all test results retrieved and optional error.

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@apexdevtools/test-runner",
3-
"version": "3.0.0",
3+
"version": "3.1.0",
44
"description": "Apex parallel test runner with reliability goodness",
55
"author": {
66
"name": "Apex Dev Tools Team",
@@ -37,7 +37,7 @@
3737
"url": "https://github.com/apex-dev-tools/test-runner/issues"
3838
},
3939
"homepage": "https://github.com/apex-dev-tools/test-runner#readme",
40-
"packageManager": "pnpm@8.2.0",
40+
"packageManager": "pnpm@8.9.2",
4141
"dependencies": {
4242
"@apexdevtools/sfdx-auth-helper": "^2.1.0",
4343
"@salesforce/apex-node": "^1.6.2",

src/command/Testall.ts

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -194,20 +194,18 @@ export class Testall {
194194

195195
// Filter expected by actual results to find residual
196196
// Try again if something was missed
197-
const missingTests = await this.resolveMissingTests(
198-
expectedTestsPromise,
199-
store.tests
200-
);
197+
const expectedTests = await expectedTestsPromise;
198+
const missingTests = this.resolveMissingTests(expectedTests, store.tests);
201199

202200
if (missingTests.size > 0) {
203201
this._logger.logTestallRerun(missingTests);
204202

205203
const testItems: TestItem[] = Array.from(
206204
missingTests,
207-
([className, methods]) => ({
208-
className,
209-
testMethods: Array.from(methods),
210-
})
205+
([className, methods]) =>
206+
methods.size === expectedTests.get(className)?.size
207+
? { className } // run all methods
208+
: { className, testMethods: Array.from(methods) }
211209
);
212210

213211
await this.asyncRun(
@@ -219,11 +217,10 @@ export class Testall {
219217
}
220218
}
221219

222-
private async resolveMissingTests(
223-
expectedTestsPromise: Promise<Map<string, Set<string>>>,
220+
private resolveMissingTests(
221+
expectedTests: Map<string, Set<string>>,
224222
results: Map<string, ApexTestResult>
225-
): Promise<Map<string, Set<string>>> {
226-
const expectedTests = await expectedTestsPromise;
223+
): Map<string, Set<string>> {
227224
const missingTests = new Map<string, Set<string>>();
228225

229226
expectedTests.forEach((methods, className) => {
@@ -314,7 +311,7 @@ export class Testall {
314311

315312
return this.convertToSyncResult(result, timestamp);
316313
} catch (err) {
317-
this._logger.logMessage(
314+
this._logger.logErrorMessage(
318315
`${getTestName(currentResult)} re-run failed. ${this.getErrorMsg(err)}`
319316
);
320317
}
@@ -383,7 +380,7 @@ export class Testall {
383380

384381
store.saveCoverage(coverage);
385382
} catch (err) {
386-
this._logger.logMessage(
383+
this._logger.logErrorMessage(
387384
`Failed to get coverage: ${this.getErrorMsg(err)}`
388385
);
389386
}

src/log/BaseLogger.ts

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
*/
44

55
import path from 'path';
6+
import os from 'os';
67
import { TestallOptions, getMaxErrorsForReRun } from '../command/Testall';
78
import { ApexTestResult, BaseTestResult } from '../model/ApexTestResult';
89
import { ApexTestRunResult } from '../model/ApexTestRunResult';
@@ -26,27 +27,31 @@ export abstract class BaseLogger implements Logger {
2627
logError(error: MaybeError): void {
2728
if (error instanceof Error) {
2829
if (error.name == 'ALREADY_IN_PROCESS') {
29-
this.logMessage(
30+
this.logErrorMessage(
3031
"One or more of the tests is already queued to run, they can't be requeued"
3132
);
3233
} else {
33-
this.logMessage(error.message);
34+
this.logErrorMessage(error.message);
3435
if (error.stack !== undefined)
35-
this.logMessage('Error stack: ' + error.stack);
36+
this.logErrorMessage('Error stack: ' + error.stack);
3637
}
3738
} else {
38-
this.logMessage('Error: ' + JSON.stringify(error));
39+
this.logErrorMessage('Error: ' + JSON.stringify(error));
3940
}
4041

4142
if (error.data !== undefined) {
42-
this.logMessage('Additional data: ' + JSON.stringify(error.data));
43+
this.logErrorMessage('Additional data: ' + JSON.stringify(error.data));
4344
}
4445
}
4546

4647
logWarning(message: string): void {
4748
this.logMessage('Warning: ' + message);
4849
}
4950

51+
logErrorMessage(message: string): void {
52+
this.logMessage(message);
53+
}
54+
5055
logOutputFile(filepath: string, contents: string): void {
5156
// if filepath is absolute it will be used instead
5257
// given resolve() right to left logic
@@ -106,10 +111,12 @@ export abstract class BaseLogger implements Logger {
106111
// i.e its failed with a different message, show what happened
107112
if (rerunMsg && firstMsg) {
108113
if (rerunMsg !== firstMsg) {
109-
this.logMessage(` [Before] ${firstMsg}`);
110-
this.logMessage(` [After] ${rerunMsg}`);
114+
this.logErrorMessage(`${os.EOL} [Before] ${firstMsg}`);
115+
this.logErrorMessage(` [After] ${rerunMsg}${os.EOL}`);
111116
} else {
112-
this.logMessage(` [Before and After] ${rerunMsg}`);
117+
this.logErrorMessage(
118+
`${os.EOL} [Before and After] ${rerunMsg}${os.EOL}`
119+
);
113120
}
114121
}
115122
}
@@ -134,7 +141,11 @@ export abstract class BaseLogger implements Logger {
134141
);
135142
}
136143

137-
logStatus(testRunResult: ApexTestRunResult, tests: ApexTestResult[]): void {
144+
logStatus(
145+
testRunResult: ApexTestRunResult,
146+
tests: ApexTestResult[],
147+
elapsedTime: string
148+
): void {
138149
const status = testRunResult.Status;
139150
const outcomes = groupByOutcome(tests);
140151
const completed = tests.length;
@@ -144,7 +155,7 @@ export abstract class BaseLogger implements Logger {
144155
const complete = total > 0 ? Math.floor((completed * 100) / total) : 0;
145156

146157
this.logMessage(
147-
`[${status}] Passed: ${passed} | Failed: ${failed} | ${completed}/${total} Complete (${complete}%)`
158+
`${elapsedTime} [${status}] Passed: ${passed} | Failed: ${failed} | ${completed}/${total} Complete (${complete}%)`
148159
);
149160
}
150161

@@ -159,16 +170,22 @@ export abstract class BaseLogger implements Logger {
159170

160171
Object.entries(failedResultsByClassId).forEach(([, results]) => {
161172
const tests = results.slice(0, 2);
173+
const hasMore = results.length > 2;
162174

163-
this.logMessage(` Failing Tests: ${getClassName(tests[0])}`);
175+
this.logErrorMessage(
176+
`${os.EOL} Failing tests in '${getClassName(tests[0])}':`
177+
);
164178

165-
tests.forEach(t => {
179+
tests.forEach((t, i) => {
166180
const msg = t.Message ? ` - ${t.Message}` : '';
167-
this.logMessage(` * ${t.MethodName}${msg}`);
181+
const suffix = !hasMore && i == tests.length - 1 ? os.EOL : '';
182+
this.logErrorMessage(` * ${t.MethodName}${msg}${suffix}`);
168183
});
169184

170-
results.length > 2 &&
171-
this.logMessage(` (and ${results.length - 2} more...)`);
185+
hasMore &&
186+
this.logErrorMessage(
187+
` (and ${results.length - 2} more...)${os.EOL}`
188+
);
172189
});
173190
}
174191

src/log/Logger.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ export interface Logger {
1313

1414
// For general use
1515
logError(error: any): void;
16+
logErrorMessage(message: any): void;
1617
logWarning(message: any): void;
1718
logMessage(message: any): void;
1819

@@ -34,7 +35,11 @@ export interface Logger {
3435
// Test runner
3536
logRunStarted(testRunId: string): void;
3637
logNoProgress(testRunId: string): void;
37-
logStatus(status: ApexTestRunResult, tests: ApexTestResult[]): void;
38+
logStatus(
39+
status: ApexTestRunResult,
40+
tests: ApexTestResult[],
41+
elapsedTime: string
42+
): void;
3843
logTestFailures(newResults: ApexTestResult[]): void;
3944

4045
// Test job cancelling

src/results/ClassTimeGenerator.ts

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { SfDate } from 'jsforce';
88
import path from 'path';
99

1010
/*
11-
* Create a report (CSV) of summary stats for each test class. The report can be useful in finding long running
11+
* Create a report (CSV/JSON) of summary stats for each test class. The report can be useful in finding long running
1212
* test which are delaying the completion of a test run.
1313
*/
1414
export class ClassTimeGenerator implements OutputGenerator {
@@ -52,13 +52,33 @@ export class ClassTimeGenerator implements OutputGenerator {
5252
// Report results as CSV
5353
const lines: string[] = [];
5454
classRanges.forEach((v, k) => {
55-
lines.push(`${k}, ${v[0]}, ${v[1]}, ${v[2]}`);
55+
lines.push(`${k},${v[0]},${v[1]},${v[2]}`);
5656
});
5757
logger.logOutputFile(
5858
path.join(outputDirBase, fileName + '-time.csv'),
59-
'ClassName, StartTime, EndTime, TotalTime\n' +
59+
'ClassName,StartTime,EndTime,TotalTime\n' +
6060
`# ${this.instanceUrl} ${this.orgId} ${this.username}\n` +
6161
lines.join('\n')
6262
);
63+
64+
// Report results as json
65+
const json: {
66+
className: string;
67+
startTime: number;
68+
endTime: number;
69+
totalTime: number;
70+
}[] = [];
71+
classRanges.forEach((v, k) => {
72+
json.push({
73+
className: k,
74+
startTime: v[0],
75+
endTime: v[1],
76+
totalTime: v[2],
77+
});
78+
});
79+
logger.logOutputFile(
80+
path.join(outputDirBase, fileName + '-time.json'),
81+
JSON.stringify(json, undefined, 2)
82+
);
6383
}
6484
}

0 commit comments

Comments
 (0)