@wp-tester/results 0.1.2 → 0.1.3
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/dist/phpunit-streaming-reporter.d.ts +8 -1
- package/dist/phpunit-streaming-reporter.d.ts.map +1 -1
- package/dist/phpunit-streaming-reporter.js +32 -2
- package/dist/phpunit-streaming-reporter.js.map +1 -1
- package/dist/streaming.d.ts +42 -17
- package/dist/streaming.d.ts.map +1 -1
- package/dist/streaming.js +156 -44
- package/dist/streaming.js.map +1 -1
- package/dist/streaming.spec.js +82 -2
- package/dist/streaming.spec.js.map +1 -1
- package/dist/summary.d.ts +1 -2
- package/dist/summary.d.ts.map +1 -1
- package/dist/summary.js +11 -14
- package/dist/summary.js.map +1 -1
- package/dist/teamcity-parser.d.ts +28 -1
- package/dist/teamcity-parser.d.ts.map +1 -1
- package/dist/teamcity-parser.js +77 -20
- package/dist/teamcity-parser.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -1
|
@@ -13,7 +13,14 @@ export declare class PHPUnitStreamingReporter extends StreamingReporter {
|
|
|
13
13
|
/**
|
|
14
14
|
* Generate PHPUnit filter command for re-running a specific test
|
|
15
15
|
* PHPUnit uses --filter with ClassName::testName format
|
|
16
|
+
*
|
|
17
|
+
* The suite stack typically contains:
|
|
18
|
+
* - ['ActivityPub', 'Activitypub\\Tests\\Test_Class'] for regular tests
|
|
19
|
+
* - ['ActivityPub', 'Activitypub\\Tests\\Test_Class', 'test_method'] for data providers
|
|
20
|
+
*
|
|
21
|
+
* We need to find the class name (the suite with namespace separators '\\')
|
|
22
|
+
* and use it for the filter: 'Activitypub\\Tests\\Test_Class::testName'
|
|
16
23
|
*/
|
|
17
|
-
protected getFilterCommand(testName: string, suiteName?: string): string | null;
|
|
24
|
+
protected getFilterCommand(testName: string, suiteName?: string, suiteStack?: string[]): string | null;
|
|
18
25
|
}
|
|
19
26
|
//# sourceMappingURL=phpunit-streaming-reporter.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phpunit-streaming-reporter.d.ts","sourceRoot":"","sources":["../src/phpunit-streaming-reporter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAElF;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,iBAAiB;gBACjD,OAAO,GAAE,wBAA6B;IAIlD
|
|
1
|
+
{"version":3,"file":"phpunit-streaming-reporter.d.ts","sourceRoot":"","sources":["../src/phpunit-streaming-reporter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAE,KAAK,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAElF;;GAEG;AACH,qBAAa,wBAAyB,SAAQ,iBAAiB;gBACjD,OAAO,GAAE,wBAA6B;IAIlD;;;;;;;;;;OAUG;IACH,SAAS,CAAC,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,GAAG,IAAI;CAgCvG"}
|
|
@@ -15,12 +15,42 @@ export class PHPUnitStreamingReporter extends StreamingReporter {
|
|
|
15
15
|
/**
|
|
16
16
|
* Generate PHPUnit filter command for re-running a specific test
|
|
17
17
|
* PHPUnit uses --filter with ClassName::testName format
|
|
18
|
+
*
|
|
19
|
+
* The suite stack typically contains:
|
|
20
|
+
* - ['ActivityPub', 'Activitypub\\Tests\\Test_Class'] for regular tests
|
|
21
|
+
* - ['ActivityPub', 'Activitypub\\Tests\\Test_Class', 'test_method'] for data providers
|
|
22
|
+
*
|
|
23
|
+
* We need to find the class name (the suite with namespace separators '\\')
|
|
24
|
+
* and use it for the filter: 'Activitypub\\Tests\\Test_Class::testName'
|
|
18
25
|
*/
|
|
19
|
-
getFilterCommand(testName, suiteName) {
|
|
26
|
+
getFilterCommand(testName, suiteName, suiteStack) {
|
|
27
|
+
// If we have a suite stack, find the class name (contains namespace separator)
|
|
28
|
+
if (suiteStack && suiteStack.length > 0) {
|
|
29
|
+
// Find the last suite that contains a namespace separator (backslash)
|
|
30
|
+
// This is typically the class name
|
|
31
|
+
let className = null;
|
|
32
|
+
for (let i = suiteStack.length - 1; i >= 0; i--) {
|
|
33
|
+
if (suiteStack[i].includes('\\')) {
|
|
34
|
+
className = suiteStack[i];
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// If we found a class name, use it
|
|
39
|
+
if (className) {
|
|
40
|
+
// Escape backslashes for shell (single \ becomes \\)
|
|
41
|
+
// This allows users to copy-paste the filter directly
|
|
42
|
+
const filterArg = `${className}::${testName}`.replace(/\\/g, '\\\\');
|
|
43
|
+
return `-- --filter '${filterArg}'`;
|
|
44
|
+
}
|
|
45
|
+
// Otherwise, fall back to using the first suite
|
|
46
|
+
const filterArg = `${suiteStack[0]}::${testName}`.replace(/\\/g, '\\\\');
|
|
47
|
+
return `-- --filter '${filterArg}'`;
|
|
48
|
+
}
|
|
49
|
+
// Fallback to old behavior if no suite stack is available
|
|
20
50
|
const filterArg = suiteName
|
|
21
51
|
? `${suiteName}::${testName}`
|
|
22
52
|
: testName;
|
|
23
|
-
return `-- --filter '${filterArg}'`;
|
|
53
|
+
return `-- --filter '${filterArg.replace(/\\/g, '\\\\')}'`;
|
|
24
54
|
}
|
|
25
55
|
}
|
|
26
56
|
//# sourceMappingURL=phpunit-streaming-reporter.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"phpunit-streaming-reporter.js","sourceRoot":"","sources":["../src/phpunit-streaming-reporter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAiC,MAAM,gBAAgB,CAAC;AAElF;;GAEG;AACH,MAAM,OAAO,wBAAyB,SAAQ,iBAAiB;IAC7D,YAAY,UAAoC,EAAE;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;IAED
|
|
1
|
+
{"version":3,"file":"phpunit-streaming-reporter.js","sourceRoot":"","sources":["../src/phpunit-streaming-reporter.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,iBAAiB,EAAiC,MAAM,gBAAgB,CAAC;AAElF;;GAEG;AACH,MAAM,OAAO,wBAAyB,SAAQ,iBAAiB;IAC7D,YAAY,UAAoC,EAAE;QAChD,KAAK,CAAC,OAAO,CAAC,CAAC;IACjB,CAAC;IAED;;;;;;;;;;OAUG;IACO,gBAAgB,CAAC,QAAgB,EAAE,SAAkB,EAAE,UAAqB;QACpF,+EAA+E;QAC/E,IAAI,UAAU,IAAI,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACxC,sEAAsE;YACtE,mCAAmC;YACnC,IAAI,SAAS,GAAkB,IAAI,CAAC;YACpC,KAAK,IAAI,CAAC,GAAG,UAAU,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC;gBAChD,IAAI,UAAU,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC;oBACjC,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;oBAC1B,MAAM;gBACR,CAAC;YACH,CAAC;YAED,mCAAmC;YACnC,IAAI,SAAS,EAAE,CAAC;gBACd,qDAAqD;gBACrD,sDAAsD;gBACtD,MAAM,SAAS,GAAG,GAAG,SAAS,KAAK,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBACrE,OAAO,gBAAgB,SAAS,GAAG,CAAC;YACtC,CAAC;YAED,gDAAgD;YAChD,MAAM,SAAS,GAAG,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,QAAQ,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YACzE,OAAO,gBAAgB,SAAS,GAAG,CAAC;QACtC,CAAC;QAED,0DAA0D;QAC1D,MAAM,SAAS,GAAG,SAAS;YACzB,CAAC,CAAC,GAAG,SAAS,KAAK,QAAQ,EAAE;YAC7B,CAAC,CAAC,QAAQ,CAAC;QACb,OAAO,gBAAgB,SAAS,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC;IAC7D,CAAC;CACF"}
|
package/dist/streaming.d.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface TestEvent {
|
|
|
15
15
|
type: "test:start" | "test:pass" | "test:fail" | "test:skip" | "test:pending";
|
|
16
16
|
name: string;
|
|
17
17
|
suiteName?: string;
|
|
18
|
+
suiteStack?: string[];
|
|
18
19
|
duration?: number;
|
|
19
20
|
message?: string;
|
|
20
21
|
trace?: string;
|
|
@@ -57,18 +58,26 @@ export interface StreamWriter {
|
|
|
57
58
|
*/
|
|
58
59
|
export declare const stdoutWriter: StreamWriter;
|
|
59
60
|
/**
|
|
60
|
-
* Filter options for controlling which test statuses are
|
|
61
|
+
* Filter options for controlling which test statuses are shown.
|
|
62
|
+
*
|
|
63
|
+
* Filters affect:
|
|
64
|
+
* - Console display: Only matching tests are shown
|
|
65
|
+
* - Report tests array: Only matching tests are included in getReport().results.tests
|
|
66
|
+
* - Summary counts: Always show accurate totals for ALL tests (not filtered)
|
|
67
|
+
*
|
|
68
|
+
* This allows --failed-only to produce a JSON report with only failed tests in the
|
|
69
|
+
* array while still showing accurate total counts in the summary.
|
|
61
70
|
*/
|
|
62
71
|
export interface ReporterFilterOptions {
|
|
63
|
-
/** Show passed tests (default: false) */
|
|
72
|
+
/** Show/include passed tests (default: false) */
|
|
64
73
|
passed?: boolean;
|
|
65
|
-
/** Show failed tests (default: false) */
|
|
74
|
+
/** Show/include failed tests (default: false) */
|
|
66
75
|
failed?: boolean;
|
|
67
|
-
/** Show skipped tests (default: false) */
|
|
76
|
+
/** Show/include skipped tests (default: false) */
|
|
68
77
|
skipped?: boolean;
|
|
69
|
-
/** Show pending tests (default: false) */
|
|
78
|
+
/** Show/include pending tests (default: false) */
|
|
70
79
|
pending?: boolean;
|
|
71
|
-
/** Show other test statuses (default: false) */
|
|
80
|
+
/** Show/include other test statuses (default: false) */
|
|
72
81
|
other?: boolean;
|
|
73
82
|
}
|
|
74
83
|
/**
|
|
@@ -79,7 +88,7 @@ export interface StreamingReporterOptions {
|
|
|
79
88
|
showRunBoundaries?: boolean;
|
|
80
89
|
showSummary?: boolean;
|
|
81
90
|
enabled?: boolean;
|
|
82
|
-
/** Filter options for which test statuses to display */
|
|
91
|
+
/** Filter options for which test statuses to display and include in reports */
|
|
83
92
|
filter?: ReporterFilterOptions;
|
|
84
93
|
}
|
|
85
94
|
/**
|
|
@@ -98,16 +107,19 @@ export declare class StreamingReporter {
|
|
|
98
107
|
private showRunBoundaries;
|
|
99
108
|
private showSummary;
|
|
100
109
|
private filter;
|
|
110
|
+
private useLogUpdate;
|
|
101
111
|
private state;
|
|
102
|
-
private lastOutputLineCount;
|
|
103
112
|
private spinnerFrame;
|
|
104
113
|
private spinnerInterval;
|
|
114
|
+
private renderThrottleTimeout;
|
|
115
|
+
private pendingRender;
|
|
116
|
+
private readonly renderInterval;
|
|
105
117
|
constructor(options?: StreamingReporterOptions);
|
|
106
118
|
/**
|
|
107
119
|
* Generate filter command for re-running a specific test
|
|
108
120
|
* Override in subclasses for framework-specific behavior
|
|
109
121
|
*/
|
|
110
|
-
protected getFilterCommand(_testName: string, _suiteName?: string): string | null;
|
|
122
|
+
protected getFilterCommand(_testName: string, _suiteName?: string, _suiteStack?: string[]): string | null;
|
|
111
123
|
/**
|
|
112
124
|
* Enable or disable run boundaries (header and summary)
|
|
113
125
|
*/
|
|
@@ -125,13 +137,21 @@ export declare class StreamingReporter {
|
|
|
125
137
|
*/
|
|
126
138
|
private stopSpinner;
|
|
127
139
|
/**
|
|
128
|
-
* Clear
|
|
140
|
+
* Clear throttle timeout and execute any pending render
|
|
129
141
|
*/
|
|
130
|
-
private
|
|
142
|
+
private clearThrottle;
|
|
131
143
|
/**
|
|
132
|
-
* Render the current state to output
|
|
144
|
+
* Render the current state to output (throttled)
|
|
133
145
|
*/
|
|
134
146
|
private render;
|
|
147
|
+
/**
|
|
148
|
+
* Execute render immediately without throttling
|
|
149
|
+
*/
|
|
150
|
+
private renderImmediate;
|
|
151
|
+
/**
|
|
152
|
+
* Render final output (used in non-interactive mode)
|
|
153
|
+
*/
|
|
154
|
+
private renderFinal;
|
|
135
155
|
/**
|
|
136
156
|
* Check if a suite or any of its descendants has visible content
|
|
137
157
|
*/
|
|
@@ -191,29 +211,34 @@ export declare class StreamingReporter {
|
|
|
191
211
|
/**
|
|
192
212
|
* Called when a test starts
|
|
193
213
|
*/
|
|
194
|
-
onTestStart(name: string, suiteName?: string, fileId?: string): void;
|
|
214
|
+
onTestStart(name: string, suiteName?: string, suiteStack?: string[], fileId?: string): void;
|
|
195
215
|
/**
|
|
196
216
|
* Called when a test passes
|
|
197
217
|
*/
|
|
198
|
-
onTestPass(name: string, duration: number, suiteName?: string, fileId?: string): void;
|
|
218
|
+
onTestPass(name: string, duration: number, suiteName?: string, suiteStack?: string[], fileId?: string): void;
|
|
199
219
|
/**
|
|
200
220
|
* Called when a test fails
|
|
201
221
|
*/
|
|
202
|
-
onTestFail(name: string, duration: number, message?: string, trace?: string, suiteName?: string, fileId?: string): void;
|
|
222
|
+
onTestFail(name: string, duration: number, message?: string, trace?: string, suiteName?: string, suiteStack?: string[], fileId?: string): void;
|
|
203
223
|
/**
|
|
204
224
|
* Called when a test is skipped
|
|
205
225
|
*/
|
|
206
|
-
onTestSkip(name: string, reason?: string, suiteName?: string, fileId?: string): void;
|
|
226
|
+
onTestSkip(name: string, reason?: string, suiteName?: string, suiteStack?: string[], fileId?: string): void;
|
|
207
227
|
/**
|
|
208
228
|
* Called when a test is pending
|
|
209
229
|
*/
|
|
210
|
-
onTestPending(name: string, suiteName?: string, fileId?: string): void;
|
|
230
|
+
onTestPending(name: string, suiteName?: string, suiteStack?: string[], fileId?: string): void;
|
|
211
231
|
/**
|
|
212
232
|
* Print final summary
|
|
213
233
|
*/
|
|
214
234
|
private printSummary;
|
|
215
235
|
/**
|
|
216
236
|
* Get the current report in CTRF format
|
|
237
|
+
*
|
|
238
|
+
* The tests array is filtered based on the reporter's filter options,
|
|
239
|
+
* but the summary counts reflect ALL tests for accurate totals.
|
|
240
|
+
* This allows the JSON report to contain only relevant tests while
|
|
241
|
+
* showing complete statistics.
|
|
217
242
|
*/
|
|
218
243
|
getReport(): Report;
|
|
219
244
|
/**
|
package/dist/streaming.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../src/streaming.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAc,MAAM,EAAE,MAAM,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"streaming.d.ts","sourceRoot":"","sources":["../src/streaming.ts"],"names":[],"mappings":"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"}
|
package/dist/streaming.js
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import pc from "picocolors";
|
|
11
11
|
import { applyDiffHighlighting } from "./diff-utils.js";
|
|
12
12
|
import { SPINNER_FRAMES } from "./spinner.js";
|
|
13
|
+
import logUpdate from "log-update";
|
|
13
14
|
/**
|
|
14
15
|
* Default stdout writer
|
|
15
16
|
*/
|
|
@@ -46,13 +47,21 @@ export class StreamingReporter {
|
|
|
46
47
|
this.showRunBoundaries = true;
|
|
47
48
|
this.showSummary = true;
|
|
48
49
|
// Rendering state
|
|
49
|
-
this.lastOutputLineCount = 0;
|
|
50
50
|
this.spinnerFrame = 0;
|
|
51
51
|
this.spinnerInterval = null;
|
|
52
|
+
// Throttling state
|
|
53
|
+
this.renderThrottleTimeout = null;
|
|
54
|
+
this.pendingRender = false;
|
|
55
|
+
this.renderInterval = 50; // Render at most every 50ms (~20fps)
|
|
52
56
|
this.writer = options.writer ?? stdoutWriter;
|
|
53
57
|
this.showRunBoundaries = options.showRunBoundaries ?? true;
|
|
54
58
|
this.showSummary = options.showSummary ?? true;
|
|
55
59
|
this.enabled = options.enabled ?? true;
|
|
60
|
+
// Use log-update only if:
|
|
61
|
+
// - Reporter is enabled
|
|
62
|
+
// - We're using the default stdout writer (not a custom writer)
|
|
63
|
+
// - stdout is a TTY (log-update handles this check internally)
|
|
64
|
+
this.useLogUpdate = this.enabled && this.writer === stdoutWriter && (process.stdout.isTTY ?? false);
|
|
56
65
|
// Default filter: show all statuses (for backwards compatibility)
|
|
57
66
|
this.filter = options.filter ?? {
|
|
58
67
|
passed: true,
|
|
@@ -77,7 +86,7 @@ export class StreamingReporter {
|
|
|
77
86
|
* Generate filter command for re-running a specific test
|
|
78
87
|
* Override in subclasses for framework-specific behavior
|
|
79
88
|
*/
|
|
80
|
-
getFilterCommand(_testName, _suiteName) {
|
|
89
|
+
getFilterCommand(_testName, _suiteName, _suiteStack) {
|
|
81
90
|
return null;
|
|
82
91
|
}
|
|
83
92
|
/**
|
|
@@ -110,19 +119,19 @@ export class StreamingReporter {
|
|
|
110
119
|
this.onSuiteEnd(event.name, event.fileId);
|
|
111
120
|
break;
|
|
112
121
|
case "test:start":
|
|
113
|
-
this.onTestStart(event.name, event.suiteName, event.fileId);
|
|
122
|
+
this.onTestStart(event.name, event.suiteName, event.suiteStack, event.fileId);
|
|
114
123
|
break;
|
|
115
124
|
case "test:pass":
|
|
116
|
-
this.onTestPass(event.name, event.duration || 0, event.suiteName, event.fileId);
|
|
125
|
+
this.onTestPass(event.name, event.duration || 0, event.suiteName, event.suiteStack, event.fileId);
|
|
117
126
|
break;
|
|
118
127
|
case "test:fail":
|
|
119
|
-
this.onTestFail(event.name, event.duration || 0, event.message, event.trace, event.suiteName, event.fileId);
|
|
128
|
+
this.onTestFail(event.name, event.duration || 0, event.message, event.trace, event.suiteName, event.suiteStack, event.fileId);
|
|
120
129
|
break;
|
|
121
130
|
case "test:skip":
|
|
122
|
-
this.onTestSkip(event.name, event.message, event.suiteName, event.fileId);
|
|
131
|
+
this.onTestSkip(event.name, event.message, event.suiteName, event.suiteStack, event.fileId);
|
|
123
132
|
break;
|
|
124
133
|
case "test:pending":
|
|
125
|
-
this.onTestPending(event.name, event.suiteName, event.fileId);
|
|
134
|
+
this.onTestPending(event.name, event.suiteName, event.suiteStack, event.fileId);
|
|
126
135
|
break;
|
|
127
136
|
}
|
|
128
137
|
}
|
|
@@ -147,22 +156,46 @@ export class StreamingReporter {
|
|
|
147
156
|
}
|
|
148
157
|
}
|
|
149
158
|
/**
|
|
150
|
-
* Clear
|
|
159
|
+
* Clear throttle timeout and execute any pending render
|
|
151
160
|
*/
|
|
152
|
-
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
161
|
+
clearThrottle() {
|
|
162
|
+
if (this.renderThrottleTimeout) {
|
|
163
|
+
clearTimeout(this.renderThrottleTimeout);
|
|
164
|
+
this.renderThrottleTimeout = null;
|
|
165
|
+
}
|
|
166
|
+
// Execute any pending render
|
|
167
|
+
if (this.pendingRender) {
|
|
168
|
+
this.renderImmediate();
|
|
158
169
|
}
|
|
159
170
|
}
|
|
160
171
|
/**
|
|
161
|
-
* Render the current state to output
|
|
172
|
+
* Render the current state to output (throttled)
|
|
162
173
|
*/
|
|
163
174
|
render() {
|
|
164
175
|
if (!this.enabled)
|
|
165
176
|
return;
|
|
177
|
+
// Mark that a render is pending
|
|
178
|
+
this.pendingRender = true;
|
|
179
|
+
// If we're already waiting for a throttled render, just update the flag
|
|
180
|
+
if (this.renderThrottleTimeout) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
// Execute render immediately and schedule the next allowed render time
|
|
184
|
+
this.renderThrottleTimeout = setTimeout(() => {
|
|
185
|
+
this.renderThrottleTimeout = null;
|
|
186
|
+
// If another render was requested while throttled, execute it now
|
|
187
|
+
if (this.pendingRender) {
|
|
188
|
+
this.renderImmediate();
|
|
189
|
+
}
|
|
190
|
+
}, this.renderInterval);
|
|
191
|
+
// Execute the first render immediately
|
|
192
|
+
this.renderImmediate();
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* Execute render immediately without throttling
|
|
196
|
+
*/
|
|
197
|
+
renderImmediate() {
|
|
198
|
+
this.pendingRender = false;
|
|
166
199
|
// Build output lines
|
|
167
200
|
const lines = [];
|
|
168
201
|
// Check if there are any running tests or loading suites
|
|
@@ -182,12 +215,16 @@ export class StreamingReporter {
|
|
|
182
215
|
for (const file of this.state.files.values()) {
|
|
183
216
|
this.renderFile(file, lines);
|
|
184
217
|
}
|
|
185
|
-
//
|
|
186
|
-
this.
|
|
187
|
-
|
|
188
|
-
|
|
218
|
+
// Render output based on environment
|
|
219
|
+
if (this.useLogUpdate) {
|
|
220
|
+
// TTY mode: use log-update for live updates with clearing
|
|
221
|
+
logUpdate(lines.join('\n'));
|
|
222
|
+
}
|
|
223
|
+
else {
|
|
224
|
+
// Non-TTY mode or custom writer: render once only when tests complete
|
|
225
|
+
// Skip intermediate renders to avoid duplicate output
|
|
226
|
+
// Tests and non-interactive environments see final state only
|
|
189
227
|
}
|
|
190
|
-
this.lastOutputLineCount = lines.length;
|
|
191
228
|
// Start/stop spinner based on running tests
|
|
192
229
|
if (hasRunningTests && !this.spinnerInterval) {
|
|
193
230
|
this.startSpinner();
|
|
@@ -196,6 +233,20 @@ export class StreamingReporter {
|
|
|
196
233
|
this.stopSpinner();
|
|
197
234
|
}
|
|
198
235
|
}
|
|
236
|
+
/**
|
|
237
|
+
* Render final output (used in non-interactive mode)
|
|
238
|
+
*/
|
|
239
|
+
renderFinal() {
|
|
240
|
+
const lines = [];
|
|
241
|
+
// Render each file
|
|
242
|
+
for (const file of this.state.files.values()) {
|
|
243
|
+
this.renderFile(file, lines);
|
|
244
|
+
}
|
|
245
|
+
// Write final output
|
|
246
|
+
for (const line of lines) {
|
|
247
|
+
this.writer.writeLine(line);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
199
250
|
/**
|
|
200
251
|
* Check if a suite or any of its descendants has visible content
|
|
201
252
|
*/
|
|
@@ -206,7 +257,7 @@ export class StreamingReporter {
|
|
|
206
257
|
return true;
|
|
207
258
|
}
|
|
208
259
|
// Check if suite has any visible tests
|
|
209
|
-
const hasVisibleTests = suite.tests.some(test => this.shouldShowStatus(test.status));
|
|
260
|
+
const hasVisibleTests = suite.tests.some((test) => this.shouldShowStatus(test.status));
|
|
210
261
|
if (hasVisibleTests) {
|
|
211
262
|
return true;
|
|
212
263
|
}
|
|
@@ -368,7 +419,7 @@ export class StreamingReporter {
|
|
|
368
419
|
}
|
|
369
420
|
// Add filter to re-run this specific test
|
|
370
421
|
if (test.name) {
|
|
371
|
-
const filterCmd = this.getFilterCommand(test.name, test.suiteName);
|
|
422
|
+
const filterCmd = this.getFilterCommand(test.name, test.suiteName, test.suiteStack);
|
|
372
423
|
if (filterCmd) {
|
|
373
424
|
lines.push(`${traceIndent}${pc.dim("Re-run only this test by appending:")}`);
|
|
374
425
|
lines.push(`${traceIndent}${pc.dim(filterCmd)}`);
|
|
@@ -411,14 +462,36 @@ export class StreamingReporter {
|
|
|
411
462
|
* Find or create a suite in a file
|
|
412
463
|
*/
|
|
413
464
|
getOrCreateSuite(file, suiteName) {
|
|
414
|
-
// Find existing suite
|
|
415
|
-
|
|
465
|
+
// Find existing suite with matching name, depth, AND parent context
|
|
466
|
+
// This prevents suites with the same name from being confused when they're
|
|
467
|
+
// in different branches of the suite tree (e.g., Test_Accept::test_method
|
|
468
|
+
// vs Test_Reject::test_method both creating a "test_method" sub-suite)
|
|
469
|
+
const currentDepth = file.currentSuiteStack.length;
|
|
470
|
+
// Create a snapshot of the current parent stack (excluding the current suite name)
|
|
471
|
+
const parentStack = [...file.currentSuiteStack];
|
|
472
|
+
// Find a suite with matching name, depth, and parent stack
|
|
473
|
+
let suite = file.suites.find((s) => {
|
|
474
|
+
if (s.name !== suiteName || s.depth !== currentDepth) {
|
|
475
|
+
return false;
|
|
476
|
+
}
|
|
477
|
+
// Check if parent stacks match
|
|
478
|
+
if (s.parentStack.length !== parentStack.length) {
|
|
479
|
+
return false;
|
|
480
|
+
}
|
|
481
|
+
for (let i = 0; i < parentStack.length; i++) {
|
|
482
|
+
if (s.parentStack[i] !== parentStack[i]) {
|
|
483
|
+
return false;
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return true;
|
|
487
|
+
});
|
|
416
488
|
if (!suite) {
|
|
417
489
|
suite = {
|
|
418
490
|
name: suiteName,
|
|
419
|
-
depth:
|
|
491
|
+
depth: currentDepth,
|
|
420
492
|
tests: [],
|
|
421
493
|
isLoading: true, // Start in loading state
|
|
494
|
+
parentStack: parentStack,
|
|
422
495
|
};
|
|
423
496
|
file.suites.push(suite);
|
|
424
497
|
}
|
|
@@ -452,13 +525,23 @@ export class StreamingReporter {
|
|
|
452
525
|
pendingTests: 0,
|
|
453
526
|
isRunning: true,
|
|
454
527
|
};
|
|
455
|
-
|
|
528
|
+
// Clear any previous log-update state
|
|
529
|
+
if (this.useLogUpdate) {
|
|
530
|
+
logUpdate.clear();
|
|
531
|
+
}
|
|
532
|
+
// Clear any previous throttle state
|
|
533
|
+
if (this.renderThrottleTimeout) {
|
|
534
|
+
clearTimeout(this.renderThrottleTimeout);
|
|
535
|
+
this.renderThrottleTimeout = null;
|
|
536
|
+
}
|
|
537
|
+
this.pendingRender = false;
|
|
456
538
|
}
|
|
457
539
|
/**
|
|
458
540
|
* Called when test run ends
|
|
459
541
|
*/
|
|
460
542
|
onRunEnd() {
|
|
461
543
|
this.stopSpinner();
|
|
544
|
+
this.clearThrottle(); // Clear any pending throttled renders
|
|
462
545
|
this.state.isRunning = false;
|
|
463
546
|
// Clean up any tests still in "running" state across all files
|
|
464
547
|
// This ensures we never show spinners in the final output
|
|
@@ -476,7 +559,14 @@ export class StreamingReporter {
|
|
|
476
559
|
}
|
|
477
560
|
}
|
|
478
561
|
// Final render
|
|
479
|
-
this.
|
|
562
|
+
if (this.useLogUpdate) {
|
|
563
|
+
// Clear the live updating display
|
|
564
|
+
logUpdate.clear();
|
|
565
|
+
logUpdate.done();
|
|
566
|
+
}
|
|
567
|
+
// Always use renderFinal for the final output to avoid truncation
|
|
568
|
+
// log-update is only for live updates, not for final large output
|
|
569
|
+
this.renderFinal();
|
|
480
570
|
if (!this.enabled || !this.showSummary)
|
|
481
571
|
return;
|
|
482
572
|
const duration = Date.now() - this.state.startTime;
|
|
@@ -548,7 +638,7 @@ export class StreamingReporter {
|
|
|
548
638
|
/**
|
|
549
639
|
* Called when a test starts
|
|
550
640
|
*/
|
|
551
|
-
onTestStart(name, suiteName, fileId) {
|
|
641
|
+
onTestStart(name, suiteName, suiteStack, fileId) {
|
|
552
642
|
const file = fileId
|
|
553
643
|
? this.getOrCreateFile(fileId)
|
|
554
644
|
: this.getOrCreateFile("__global__");
|
|
@@ -565,6 +655,7 @@ export class StreamingReporter {
|
|
|
565
655
|
suite.tests.push({
|
|
566
656
|
name,
|
|
567
657
|
suiteName,
|
|
658
|
+
suiteStack,
|
|
568
659
|
status: "running",
|
|
569
660
|
});
|
|
570
661
|
}
|
|
@@ -574,7 +665,7 @@ export class StreamingReporter {
|
|
|
574
665
|
/**
|
|
575
666
|
* Called when a test passes
|
|
576
667
|
*/
|
|
577
|
-
onTestPass(name, duration, suiteName, fileId) {
|
|
668
|
+
onTestPass(name, duration, suiteName, suiteStack, fileId) {
|
|
578
669
|
const file = fileId
|
|
579
670
|
? this.getOrCreateFile(fileId)
|
|
580
671
|
: this.getOrCreateFile("__global__");
|
|
@@ -595,12 +686,14 @@ export class StreamingReporter {
|
|
|
595
686
|
test.status = "passed";
|
|
596
687
|
test.duration = duration;
|
|
597
688
|
test.suiteName = suiteName; // Ensure suiteName is set
|
|
689
|
+
test.suiteStack = suiteStack;
|
|
598
690
|
}
|
|
599
691
|
else {
|
|
600
692
|
// Test start event hasn't arrived yet - create the test with completed state
|
|
601
693
|
suite.tests.push({
|
|
602
694
|
name,
|
|
603
695
|
suiteName,
|
|
696
|
+
suiteStack,
|
|
604
697
|
status: "passed",
|
|
605
698
|
duration,
|
|
606
699
|
});
|
|
@@ -612,7 +705,7 @@ export class StreamingReporter {
|
|
|
612
705
|
/**
|
|
613
706
|
* Called when a test fails
|
|
614
707
|
*/
|
|
615
|
-
onTestFail(name, duration, message, trace, suiteName, fileId) {
|
|
708
|
+
onTestFail(name, duration, message, trace, suiteName, suiteStack, fileId) {
|
|
616
709
|
const file = fileId
|
|
617
710
|
? this.getOrCreateFile(fileId)
|
|
618
711
|
: this.getOrCreateFile("__global__");
|
|
@@ -635,12 +728,14 @@ export class StreamingReporter {
|
|
|
635
728
|
test.message = message;
|
|
636
729
|
test.trace = trace;
|
|
637
730
|
test.suiteName = suiteName; // Ensure suiteName is set
|
|
731
|
+
test.suiteStack = suiteStack;
|
|
638
732
|
}
|
|
639
733
|
else {
|
|
640
734
|
// Test start event hasn't arrived yet - create the test with completed state
|
|
641
735
|
suite.tests.push({
|
|
642
736
|
name,
|
|
643
737
|
suiteName,
|
|
738
|
+
suiteStack,
|
|
644
739
|
status: "failed",
|
|
645
740
|
duration,
|
|
646
741
|
message,
|
|
@@ -654,7 +749,7 @@ export class StreamingReporter {
|
|
|
654
749
|
/**
|
|
655
750
|
* Called when a test is skipped
|
|
656
751
|
*/
|
|
657
|
-
onTestSkip(name, reason, suiteName, fileId) {
|
|
752
|
+
onTestSkip(name, reason, suiteName, suiteStack, fileId) {
|
|
658
753
|
const file = fileId
|
|
659
754
|
? this.getOrCreateFile(fileId)
|
|
660
755
|
: this.getOrCreateFile("__global__");
|
|
@@ -675,12 +770,14 @@ export class StreamingReporter {
|
|
|
675
770
|
test.status = "skipped";
|
|
676
771
|
test.message = reason;
|
|
677
772
|
test.suiteName = suiteName; // Ensure suiteName is set
|
|
773
|
+
test.suiteStack = suiteStack;
|
|
678
774
|
}
|
|
679
775
|
else {
|
|
680
776
|
// Test start event hasn't arrived yet - create the test with completed state
|
|
681
777
|
suite.tests.push({
|
|
682
778
|
name,
|
|
683
779
|
suiteName,
|
|
780
|
+
suiteStack,
|
|
684
781
|
status: "skipped",
|
|
685
782
|
message: reason,
|
|
686
783
|
});
|
|
@@ -692,7 +789,7 @@ export class StreamingReporter {
|
|
|
692
789
|
/**
|
|
693
790
|
* Called when a test is pending
|
|
694
791
|
*/
|
|
695
|
-
onTestPending(name, suiteName, fileId) {
|
|
792
|
+
onTestPending(name, suiteName, suiteStack, fileId) {
|
|
696
793
|
const file = fileId
|
|
697
794
|
? this.getOrCreateFile(fileId)
|
|
698
795
|
: this.getOrCreateFile("__global__");
|
|
@@ -703,11 +800,13 @@ export class StreamingReporter {
|
|
|
703
800
|
const test = suite.tests.find((t) => t.name === name && t.suiteName === suiteName);
|
|
704
801
|
if (test) {
|
|
705
802
|
test.status = "pending";
|
|
803
|
+
test.suiteStack = suiteStack;
|
|
706
804
|
}
|
|
707
805
|
else {
|
|
708
806
|
suite.tests.push({
|
|
709
807
|
name,
|
|
710
808
|
suiteName,
|
|
809
|
+
suiteStack,
|
|
711
810
|
status: "pending",
|
|
712
811
|
});
|
|
713
812
|
}
|
|
@@ -740,18 +839,28 @@ export class StreamingReporter {
|
|
|
740
839
|
}
|
|
741
840
|
/**
|
|
742
841
|
* Get the current report in CTRF format
|
|
842
|
+
*
|
|
843
|
+
* The tests array is filtered based on the reporter's filter options,
|
|
844
|
+
* but the summary counts reflect ALL tests for accurate totals.
|
|
845
|
+
* This allows the JSON report to contain only relevant tests while
|
|
846
|
+
* showing complete statistics.
|
|
743
847
|
*/
|
|
744
848
|
getReport() {
|
|
745
849
|
const tests = [];
|
|
746
|
-
// Collect
|
|
850
|
+
// Collect tests from all files, applying filter to tests array
|
|
747
851
|
for (const file of this.state.files.values()) {
|
|
748
852
|
for (const suite of file.suites) {
|
|
749
853
|
for (const test of suite.tests) {
|
|
854
|
+
const status = test.status === "running" ? "other" : test.status;
|
|
855
|
+
// Apply filter - only include tests that match filter criteria
|
|
856
|
+
if (!this.shouldShowStatus(test.status)) {
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
750
859
|
const ctrf = {
|
|
751
860
|
name: test.suiteName
|
|
752
861
|
? `${test.suiteName}::${test.name}`
|
|
753
862
|
: test.name,
|
|
754
|
-
status
|
|
863
|
+
status,
|
|
755
864
|
duration: test.duration || 0,
|
|
756
865
|
};
|
|
757
866
|
if (test.message) {
|
|
@@ -764,6 +873,18 @@ export class StreamingReporter {
|
|
|
764
873
|
}
|
|
765
874
|
}
|
|
766
875
|
}
|
|
876
|
+
// Use actual state counts for accurate totals (includes all tests, even filtered ones)
|
|
877
|
+
// The tests array is filtered, but the summary reflects the true test counts
|
|
878
|
+
const summary = {
|
|
879
|
+
tests: this.state.totalTests,
|
|
880
|
+
passed: this.state.passedTests,
|
|
881
|
+
failed: this.state.failedTests,
|
|
882
|
+
skipped: this.state.skippedTests,
|
|
883
|
+
pending: this.state.pendingTests,
|
|
884
|
+
other: 0, // We don't track "other" status in our state
|
|
885
|
+
start: this.state.startTime,
|
|
886
|
+
stop: Date.now(),
|
|
887
|
+
};
|
|
767
888
|
return {
|
|
768
889
|
reportFormat: "CTRF",
|
|
769
890
|
specVersion: "1.0.0",
|
|
@@ -771,16 +892,7 @@ export class StreamingReporter {
|
|
|
771
892
|
tool: {
|
|
772
893
|
name: this.state.toolName,
|
|
773
894
|
},
|
|
774
|
-
summary
|
|
775
|
-
tests: this.state.totalTests,
|
|
776
|
-
passed: this.state.passedTests,
|
|
777
|
-
failed: this.state.failedTests,
|
|
778
|
-
skipped: this.state.skippedTests,
|
|
779
|
-
pending: this.state.pendingTests,
|
|
780
|
-
other: 0,
|
|
781
|
-
start: this.state.startTime,
|
|
782
|
-
stop: Date.now(),
|
|
783
|
-
},
|
|
895
|
+
summary,
|
|
784
896
|
tests,
|
|
785
897
|
},
|
|
786
898
|
};
|