@quenty/cli-output-helpers 1.17.2 → 1.18.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.
- package/CHANGELOG.md +4 -0
- package/dist/outputHelper.d.ts +11 -0
- package/dist/outputHelper.d.ts.map +1 -1
- package/dist/outputHelper.js +18 -0
- package/dist/outputHelper.js.map +1 -1
- package/dist/reporting/aggregated-batch-reporter.d.ts +31 -0
- package/dist/reporting/aggregated-batch-reporter.d.ts.map +1 -0
- package/dist/reporting/aggregated-batch-reporter.js +40 -0
- package/dist/reporting/aggregated-batch-reporter.js.map +1 -0
- package/dist/reporting/github/formatting.d.ts.map +1 -1
- package/dist/reporting/github/formatting.js +37 -32
- package/dist/reporting/github/formatting.js.map +1 -1
- package/dist/reporting/grouped-reporter.d.ts.map +1 -1
- package/dist/reporting/grouped-reporter.js +7 -28
- package/dist/reporting/grouped-reporter.js.map +1 -1
- package/dist/reporting/index.d.ts +2 -0
- package/dist/reporting/index.d.ts.map +1 -1
- package/dist/reporting/index.js +2 -0
- package/dist/reporting/index.js.map +1 -1
- package/dist/reporting/reporter.d.ts +17 -0
- package/dist/reporting/reporter.d.ts.map +1 -1
- package/dist/reporting/reporter.js.map +1 -1
- package/dist/reporting/result-status.d.ts +101 -0
- package/dist/reporting/result-status.d.ts.map +1 -0
- package/dist/reporting/result-status.js +138 -0
- package/dist/reporting/result-status.js.map +1 -0
- package/dist/reporting/result-status.test.d.ts +2 -0
- package/dist/reporting/result-status.test.d.ts.map +1 -0
- package/dist/reporting/result-status.test.js +108 -0
- package/dist/reporting/result-status.test.js.map +1 -0
- package/dist/reporting/spinner-reporter.d.ts +1 -0
- package/dist/reporting/spinner-reporter.d.ts.map +1 -1
- package/dist/reporting/spinner-reporter.js +18 -33
- package/dist/reporting/spinner-reporter.js.map +1 -1
- package/dist/reporting/summary-table-reporter.d.ts +7 -2
- package/dist/reporting/summary-table-reporter.d.ts.map +1 -1
- package/dist/reporting/summary-table-reporter.js +16 -27
- package/dist/reporting/summary-table-reporter.js.map +1 -1
- package/package.json +2 -2
- package/src/outputHelper.ts +22 -0
- package/src/reporting/aggregated-batch-reporter.ts +45 -0
- package/src/reporting/github/formatting.ts +52 -41
- package/src/reporting/grouped-reporter.ts +20 -42
- package/src/reporting/index.ts +13 -0
- package/src/reporting/reporter.ts +18 -0
- package/src/reporting/result-status.test.ts +149 -0
- package/src/reporting/result-status.ts +227 -0
- package/src/reporting/spinner-reporter.ts +40 -39
- package/src/reporting/summary-table-reporter.ts +29 -36
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One answer to "how did this turn out", for every place that has to say so.
|
|
3
|
+
*
|
|
4
|
+
* A result's state was worked out independently in four places — the summary
|
|
5
|
+
* table, the grouped reporter, the spinner and the PR comment — and each had its
|
|
6
|
+
* own idea of it. "Ran zero tests" was a warning in three of them and invisible
|
|
7
|
+
* in the fourth; the icon was `⚠` in two and `⚠️` in the others; a pass showed
|
|
8
|
+
* its counts in some and not others; and the run-level tallies at the bottom of
|
|
9
|
+
* each were counted separately, so they could disagree with the rows above them.
|
|
10
|
+
*
|
|
11
|
+
* Adding a fifth state made that untenable: a pass whose log output was dropped
|
|
12
|
+
* is still a pass, but not one that should look like every other green row, and
|
|
13
|
+
* teaching five renderers about it one at a time is how the four drifted in the
|
|
14
|
+
* first place.
|
|
15
|
+
*
|
|
16
|
+
* So state is resolved once, here, and the renderers only choose how to draw it.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { OutputHelper } from '../outputHelper.js';
|
|
20
|
+
import {
|
|
21
|
+
type JobPhase,
|
|
22
|
+
type ProgressSummary,
|
|
23
|
+
type ResultCaveat,
|
|
24
|
+
} from './reporter.js';
|
|
25
|
+
import { formatProgressResult, isEmptyTestRun } from './progress-format.js';
|
|
26
|
+
|
|
27
|
+
/** How a result should read at a glance. */
|
|
28
|
+
export type ResultSeverity = 'success' | 'warning' | 'failure';
|
|
29
|
+
|
|
30
|
+
export { type ResultCaveat } from './reporter.js';
|
|
31
|
+
|
|
32
|
+
interface CaveatCopy {
|
|
33
|
+
/** Short enough to sit in a status line next to the verdict. */
|
|
34
|
+
label: string;
|
|
35
|
+
/** The run-level line, when several results share the caveat. */
|
|
36
|
+
tally: (count: number) => string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const CAVEAT_COPY: Record<ResultCaveat, CaveatCopy> = {
|
|
40
|
+
'no-counts': {
|
|
41
|
+
label: 'no test counts reported',
|
|
42
|
+
tally: (count) =>
|
|
43
|
+
`${count} package(s) reported no test counts — a pass here proves nothing about whether tests ran`,
|
|
44
|
+
},
|
|
45
|
+
'logs-lost': {
|
|
46
|
+
label: 'logs lost',
|
|
47
|
+
tally: (count) =>
|
|
48
|
+
`${count} package(s) lost their log output — their counts came back in the run's summary, but nothing could be checked for tracebacks`,
|
|
49
|
+
},
|
|
50
|
+
'empty-run': {
|
|
51
|
+
label: 'ran 0 tests',
|
|
52
|
+
tally: (count) => `${count} package(s) ran 0 tests — check test discovery`,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The parts of a result that decide its state.
|
|
58
|
+
*
|
|
59
|
+
* Structural rather than `PackageResult` so a caller holding some other shape —
|
|
60
|
+
* the batch log parser's per-package result, say — can resolve state without
|
|
61
|
+
* first building a reporting type it has no other use for.
|
|
62
|
+
*/
|
|
63
|
+
export interface ResultStatusInput {
|
|
64
|
+
success: boolean;
|
|
65
|
+
progressSummary?: ProgressSummary;
|
|
66
|
+
failedPhase?: JobPhase;
|
|
67
|
+
/** Per-result override of the reporter's default failure label. */
|
|
68
|
+
failureLabel?: string;
|
|
69
|
+
/**
|
|
70
|
+
* Caveats the producer knows about and the resolver cannot see. `empty-run`
|
|
71
|
+
* and `no-counts` are derived from the counts themselves and need not be
|
|
72
|
+
* passed; `logs-lost` is a fact about the run that only its parser knows.
|
|
73
|
+
*/
|
|
74
|
+
caveats?: ResultCaveat[];
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export interface ResolveResultStatusOptions {
|
|
78
|
+
/** Verdict word for a pass. Default "Passed". */
|
|
79
|
+
successLabel?: string;
|
|
80
|
+
/** Verdict word for a failure. Default "FAILED". */
|
|
81
|
+
failureLabel?: string;
|
|
82
|
+
/**
|
|
83
|
+
* True when this kind of run should have reported test counts, which makes
|
|
84
|
+
* their absence a caveat rather than simply nothing to show.
|
|
85
|
+
*/
|
|
86
|
+
expectsTestCounts?: boolean;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export interface ResultStatus {
|
|
90
|
+
severity: ResultSeverity;
|
|
91
|
+
/** The verdict word alone: "Passed", "FAILED", "Unverified". */
|
|
92
|
+
label: string;
|
|
93
|
+
/** Where it failed, when the failure names a phase. */
|
|
94
|
+
failedPhase?: JobPhase;
|
|
95
|
+
/** Counts or bytes as "(68/68)", or empty when there are none. */
|
|
96
|
+
progress: string;
|
|
97
|
+
/** Everything qualifying this result, in the order they are worth reading. */
|
|
98
|
+
caveats: ResultCaveat[];
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Work out how a result should be presented.
|
|
103
|
+
*
|
|
104
|
+
* A failure stays a failure whatever its caveats; a pass carrying any caveat
|
|
105
|
+
* becomes a warning. Caveats are not exclusive — a run can report no counts and
|
|
106
|
+
* have lost its logs — so they are collected rather than ranked, and every
|
|
107
|
+
* renderer shows all of them.
|
|
108
|
+
*/
|
|
109
|
+
export function resolveResultStatus(
|
|
110
|
+
result: ResultStatusInput,
|
|
111
|
+
options: ResolveResultStatusOptions = {}
|
|
112
|
+
): ResultStatus {
|
|
113
|
+
const caveats: ResultCaveat[] = [];
|
|
114
|
+
|
|
115
|
+
if (
|
|
116
|
+
(options.expectsTestCounts ?? false) &&
|
|
117
|
+
result.progressSummary === undefined
|
|
118
|
+
) {
|
|
119
|
+
caveats.push('no-counts');
|
|
120
|
+
}
|
|
121
|
+
for (const caveat of result.caveats ?? []) {
|
|
122
|
+
if (!caveats.includes(caveat)) {
|
|
123
|
+
caveats.push(caveat);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (isEmptyTestRun(result.progressSummary)) {
|
|
127
|
+
caveats.push('empty-run');
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!result.success) {
|
|
131
|
+
return {
|
|
132
|
+
severity: 'failure',
|
|
133
|
+
label: result.failureLabel ?? options.failureLabel ?? 'FAILED',
|
|
134
|
+
failedPhase: result.failedPhase,
|
|
135
|
+
progress: formatProgressResult(result.progressSummary),
|
|
136
|
+
caveats,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// "Unverified" rather than "Passed": with no counts at all, the run's own
|
|
141
|
+
// account of itself never arrived, and calling that a pass is the claim this
|
|
142
|
+
// is here to avoid making.
|
|
143
|
+
const unverified = caveats.includes('no-counts');
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
severity: caveats.length > 0 ? 'warning' : 'success',
|
|
147
|
+
label: unverified ? 'Unverified' : options.successLabel ?? 'Passed',
|
|
148
|
+
progress: unverified ? '' : formatProgressResult(result.progressSummary),
|
|
149
|
+
caveats,
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* The status as one line of plain text: "Passed (35/35) - logs lost".
|
|
155
|
+
*
|
|
156
|
+
* No icon, no duration and no color — those differ between a markdown table, a
|
|
157
|
+
* terminal line and a collapsible group header, and are the renderer's to add.
|
|
158
|
+
*/
|
|
159
|
+
export function formatStatusText(status: ResultStatus): string {
|
|
160
|
+
const verdict = status.failedPhase
|
|
161
|
+
? `${status.label} at ${status.failedPhase}`
|
|
162
|
+
: status.label;
|
|
163
|
+
const head = [verdict, status.progress].filter(Boolean).join(' ');
|
|
164
|
+
if (status.caveats.length === 0) {
|
|
165
|
+
return head;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return `${head} - ${status.caveats
|
|
169
|
+
.map((caveat) => CAVEAT_COPY[caveat].label)
|
|
170
|
+
.join(', ')}`;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* The icon for a severity.
|
|
175
|
+
*
|
|
176
|
+
* `emoji` for markdown, where they render as intended; `ascii` for a terminal
|
|
177
|
+
* line, where a variation-selector emoji is two columns wide and breaks column
|
|
178
|
+
* alignment.
|
|
179
|
+
*/
|
|
180
|
+
export function statusIcon(
|
|
181
|
+
severity: ResultSeverity,
|
|
182
|
+
style: 'emoji' | 'ascii'
|
|
183
|
+
): string {
|
|
184
|
+
if (style === 'emoji') {
|
|
185
|
+
return severity === 'failure' ? '❌' : severity === 'warning' ? '⚠️' : '✅';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return severity === 'failure' ? '✗' : severity === 'warning' ? '⚠' : '✓';
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Color text by severity, using the shared palette. */
|
|
192
|
+
export function colorStatus(text: string, severity: ResultSeverity): string {
|
|
193
|
+
if (severity === 'failure') {
|
|
194
|
+
return OutputHelper.formatError(text);
|
|
195
|
+
}
|
|
196
|
+
if (severity === 'warning') {
|
|
197
|
+
return OutputHelper.formatWarning(text);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return OutputHelper.formatSuccess(text);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Roll up the caveats across a run, for the lines under a summary.
|
|
205
|
+
*
|
|
206
|
+
* Counted off the same statuses the rows were drawn from, so the tally and the
|
|
207
|
+
* rows cannot disagree — which they could when each reporter counted its own.
|
|
208
|
+
*/
|
|
209
|
+
export function tallyCaveats(
|
|
210
|
+
statuses: ResultStatus[]
|
|
211
|
+
): Array<{ caveat: ResultCaveat; count: number; message: string }> {
|
|
212
|
+
const counts = new Map<ResultCaveat, number>();
|
|
213
|
+
for (const status of statuses) {
|
|
214
|
+
for (const caveat of status.caveats) {
|
|
215
|
+
counts.set(caveat, (counts.get(caveat) ?? 0) + 1);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
const order: ResultCaveat[] = ['no-counts', 'logs-lost', 'empty-run'];
|
|
220
|
+
|
|
221
|
+
return order
|
|
222
|
+
.filter((caveat) => counts.has(caveat))
|
|
223
|
+
.map((caveat) => {
|
|
224
|
+
const count = counts.get(caveat)!;
|
|
225
|
+
return { caveat, count, message: CAVEAT_COPY[caveat].tally(count) };
|
|
226
|
+
});
|
|
227
|
+
}
|
|
@@ -2,11 +2,13 @@ import { OutputHelper } from '../outputHelper.js';
|
|
|
2
2
|
import { formatDurationMs } from '../cli-utils.js';
|
|
3
3
|
import { type JobPhase, type PackageResult, BaseReporter } from './reporter.js';
|
|
4
4
|
import { type IStateTracker } from './state/state-tracker.js';
|
|
5
|
+
import { formatProgressInline } from './progress-format.js';
|
|
5
6
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
colorStatus,
|
|
8
|
+
formatStatusText,
|
|
9
|
+
resolveResultStatus,
|
|
10
|
+
statusIcon,
|
|
11
|
+
} from './result-status.js';
|
|
10
12
|
|
|
11
13
|
export interface SpinnerReporterOptions {
|
|
12
14
|
showLogs: boolean;
|
|
@@ -176,20 +178,19 @@ export class SpinnerReporter extends BaseReporter {
|
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
private _printResultLogs(result: PackageResult): void {
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
: result.failureLabel ?? this._options.failureLabel ?? 'FAILED';
|
|
185
|
-
const formatted = result.success
|
|
186
|
-
? OutputHelper.formatSuccess(status)
|
|
187
|
-
: OutputHelper.formatError(status);
|
|
181
|
+
const status = this._resolve(result);
|
|
182
|
+
const icon = colorStatus(
|
|
183
|
+
statusIcon(status.severity, 'ascii'),
|
|
184
|
+
status.severity
|
|
185
|
+
);
|
|
188
186
|
|
|
189
187
|
console.log(
|
|
190
188
|
`${icon} ${OutputHelper.formatDim('──')} ${
|
|
191
189
|
result.packageName
|
|
192
|
-
} ${OutputHelper.formatDim('──')} ${
|
|
190
|
+
} ${OutputHelper.formatDim('──')} ${colorStatus(
|
|
191
|
+
formatStatusText(status),
|
|
192
|
+
status.severity
|
|
193
|
+
)}`
|
|
193
194
|
);
|
|
194
195
|
|
|
195
196
|
if (result.logs) {
|
|
@@ -203,6 +204,15 @@ export class SpinnerReporter extends BaseReporter {
|
|
|
203
204
|
console.log('');
|
|
204
205
|
}
|
|
205
206
|
|
|
207
|
+
private _resolve(
|
|
208
|
+
result: Parameters<typeof resolveResultStatus>[0]
|
|
209
|
+
): ReturnType<typeof resolveResultStatus> {
|
|
210
|
+
return resolveResultStatus(result, {
|
|
211
|
+
successLabel: this._options.successLabel,
|
|
212
|
+
failureLabel: this._options.failureLabel,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
206
216
|
private _render(): void {
|
|
207
217
|
const lines: string[] = [];
|
|
208
218
|
const spinner = SPINNER_FRAMES[this._spinnerFrame];
|
|
@@ -221,32 +231,23 @@ export class SpinnerReporter extends BaseReporter {
|
|
|
221
231
|
line = ` ${icon} ${OutputHelper.formatDim(
|
|
222
232
|
state.name.padEnd(30)
|
|
223
233
|
)} ${statusText}`;
|
|
224
|
-
} else if (state.status === 'passed') {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
234
|
+
} else if (state.status === 'passed' || state.status === 'failed') {
|
|
235
|
+
// Both terminal states through one resolver: a pass that ran no tests
|
|
236
|
+
// and a pass whose logs went missing are warnings, and deciding that
|
|
237
|
+
// here is how the spinner and the table came to disagree about them.
|
|
238
|
+
const status = this._resolve(
|
|
239
|
+
state.result ?? { success: state.status === 'passed' }
|
|
228
240
|
);
|
|
229
|
-
const
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
)} ${statusText} ${OutputHelper.formatDim(time)}`;
|
|
240
|
-
} else if (state.status === 'failed') {
|
|
241
|
-
const icon = OutputHelper.formatError('✗');
|
|
242
|
-
const failedPhase = state.result?.failedPhase;
|
|
243
|
-
const failureLabel =
|
|
244
|
-
state.result?.failureLabel ?? this._options.failureLabel ?? 'FAILED';
|
|
245
|
-
const plain = failedPhase
|
|
246
|
-
? `${failureLabel} at ${failedPhase}`
|
|
247
|
-
: failureLabel;
|
|
248
|
-
const statusText = OutputHelper.formatError(
|
|
249
|
-
plain.padEnd(STATUS_COLUMN_WIDTH)
|
|
241
|
+
const icon = colorStatus(
|
|
242
|
+
statusIcon(status.severity, 'ascii'),
|
|
243
|
+
status.severity
|
|
244
|
+
);
|
|
245
|
+
const statusText = colorStatus(
|
|
246
|
+
OutputHelper.padVisible(
|
|
247
|
+
formatStatusText(status),
|
|
248
|
+
STATUS_COLUMN_WIDTH
|
|
249
|
+
),
|
|
250
|
+
status.severity
|
|
250
251
|
);
|
|
251
252
|
line = ` ${icon} ${state.name.padEnd(
|
|
252
253
|
30
|
|
@@ -3,7 +3,13 @@ import { formatDurationMs } from '../cli-utils.js';
|
|
|
3
3
|
import { BaseReporter, type PackageResult } from './reporter.js';
|
|
4
4
|
import { formatTable, type TableColumn } from './format-table.js';
|
|
5
5
|
import { type IStateTracker } from './state/state-tracker.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
colorStatus,
|
|
8
|
+
formatStatusText,
|
|
9
|
+
resolveResultStatus,
|
|
10
|
+
tallyCaveats,
|
|
11
|
+
type ResultStatus,
|
|
12
|
+
} from './result-status.js';
|
|
7
13
|
|
|
8
14
|
export interface SummaryTableReporterOptions {
|
|
9
15
|
/** Label for successful results in the table. Default: "Passed" */
|
|
@@ -12,6 +18,11 @@ export interface SummaryTableReporterOptions {
|
|
|
12
18
|
failureLabel?: string;
|
|
13
19
|
/** Verb in the footer, e.g. "tested" in "X tested, Y passed, Z failed". Default: "tested" */
|
|
14
20
|
summaryVerb?: string;
|
|
21
|
+
/**
|
|
22
|
+
* True when this kind of run should have reported test counts, so a result
|
|
23
|
+
* with none is flagged rather than shown as a plain pass.
|
|
24
|
+
*/
|
|
25
|
+
expectsTestCounts?: boolean;
|
|
15
26
|
}
|
|
16
27
|
|
|
17
28
|
/**
|
|
@@ -23,6 +34,7 @@ export class SummaryTableReporter extends BaseReporter {
|
|
|
23
34
|
private _successLabel: string;
|
|
24
35
|
private _failureLabel: string;
|
|
25
36
|
private _summaryVerb: string;
|
|
37
|
+
private _expectsTestCounts: boolean;
|
|
26
38
|
|
|
27
39
|
constructor(state: IStateTracker, options?: SummaryTableReporterOptions) {
|
|
28
40
|
super();
|
|
@@ -30,6 +42,7 @@ export class SummaryTableReporter extends BaseReporter {
|
|
|
30
42
|
this._successLabel = options?.successLabel ?? 'Passed';
|
|
31
43
|
this._failureLabel = options?.failureLabel ?? 'FAILED';
|
|
32
44
|
this._summaryVerb = options?.summaryVerb ?? 'tested';
|
|
45
|
+
this._expectsTestCounts = options?.expectsTestCounts ?? false;
|
|
33
46
|
}
|
|
34
47
|
|
|
35
48
|
override async stopAsync(): Promise<void> {
|
|
@@ -43,7 +56,11 @@ export class SummaryTableReporter extends BaseReporter {
|
|
|
43
56
|
const passed = results.length - failures.length;
|
|
44
57
|
const durationMs = Date.now() - this._state.startTimeMs;
|
|
45
58
|
|
|
46
|
-
|
|
59
|
+
// Resolved once per result and reused for the row and the footer, so the
|
|
60
|
+
// tally under the table cannot disagree with the rows in it.
|
|
61
|
+
const statuses = new Map<PackageResult, ResultStatus>(
|
|
62
|
+
results.map((result) => [result, this._resolve(result)])
|
|
63
|
+
);
|
|
47
64
|
|
|
48
65
|
const columns: TableColumn<PackageResult>[] = [
|
|
49
66
|
{
|
|
@@ -53,9 +70,8 @@ export class SummaryTableReporter extends BaseReporter {
|
|
|
53
70
|
},
|
|
54
71
|
{
|
|
55
72
|
header: 'Status',
|
|
56
|
-
value: (r) =>
|
|
57
|
-
format: (label, r) =>
|
|
58
|
-
this._colorStatus(label, r, () => emptyRunCount++),
|
|
73
|
+
value: (r) => formatStatusText(statuses.get(r)!),
|
|
74
|
+
format: (label, r) => colorStatus(label, statuses.get(r)!.severity),
|
|
59
75
|
minWidth: 26,
|
|
60
76
|
},
|
|
61
77
|
{
|
|
@@ -82,39 +98,16 @@ export class SummaryTableReporter extends BaseReporter {
|
|
|
82
98
|
`${results.length} ${unit} ${this._summaryVerb}, ${passedText}, ${failedText} ${totalTime}`
|
|
83
99
|
);
|
|
84
100
|
|
|
85
|
-
|
|
86
|
-
console.log(
|
|
87
|
-
OutputHelper.formatWarning(
|
|
88
|
-
`⚠ ${emptyRunCount} package(s) ran 0 tests — check test discovery`
|
|
89
|
-
)
|
|
90
|
-
);
|
|
101
|
+
for (const { message } of tallyCaveats([...statuses.values()])) {
|
|
102
|
+
console.log(OutputHelper.formatWarning(`⚠ ${message}`));
|
|
91
103
|
}
|
|
92
104
|
}
|
|
93
105
|
|
|
94
|
-
private
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
}
|
|
101
|
-
const failedPhase = result.failedPhase;
|
|
102
|
-
const failureLabel = result.failureLabel ?? this._failureLabel;
|
|
103
|
-
return failedPhase ? `${failureLabel} at ${failedPhase}` : failureLabel;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
private _colorStatus(
|
|
107
|
-
label: string,
|
|
108
|
-
result: PackageResult,
|
|
109
|
-
countEmpty: () => void
|
|
110
|
-
): string {
|
|
111
|
-
if (result.success) {
|
|
112
|
-
const empty = isEmptyTestRun(result.progressSummary);
|
|
113
|
-
if (empty) countEmpty();
|
|
114
|
-
return empty
|
|
115
|
-
? OutputHelper.formatWarning(label)
|
|
116
|
-
: OutputHelper.formatSuccess(label);
|
|
117
|
-
}
|
|
118
|
-
return OutputHelper.formatError(label);
|
|
106
|
+
private _resolve(result: PackageResult): ResultStatus {
|
|
107
|
+
return resolveResultStatus(result, {
|
|
108
|
+
successLabel: this._successLabel,
|
|
109
|
+
failureLabel: this._failureLabel,
|
|
110
|
+
expectsTestCounts: this._expectsTestCounts,
|
|
111
|
+
});
|
|
119
112
|
}
|
|
120
113
|
}
|