@epicat/toon-reporter 0.0.9 → 0.0.11
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/README.md +47 -5
- package/dist/playwright.d.mts +3 -0
- package/dist/playwright.mjs +41 -3
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -112,6 +112,27 @@ skipped[2]{at,name}:
|
|
|
112
112
|
"src/utils.test.ts:8:3",handles edge case
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
### With test name filtering
|
|
116
|
+
|
|
117
|
+
When using `--testNamePattern` or `-t`, results show a `(filtered)` indicator:
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
passing: 5 (filtered)
|
|
121
|
+
skipped: 42
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### With timing enabled
|
|
125
|
+
|
|
126
|
+
When `timing: true` is set, shows total duration and per-test timing:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
duration: 52ms
|
|
130
|
+
passing[3]{at,name,ms}:
|
|
131
|
+
"utils.test.ts:4:3",should be fast,1
|
|
132
|
+
"api.test.ts:12:3",should handle requests,50
|
|
133
|
+
"db.test.ts:8:3",should be slow,1
|
|
134
|
+
```
|
|
135
|
+
|
|
115
136
|
### With flaky tests (Playwright)
|
|
116
137
|
|
|
117
138
|
Tests that fail initially but pass on retry are reported as flaky:
|
|
@@ -160,9 +181,10 @@ With `verbose: true`, all files appear with per-file percentages:
|
|
|
160
181
|
|
|
161
182
|
- **Green**: `passing` count
|
|
162
183
|
- **Red**: `failing` header
|
|
163
|
-
- **Yellow**: `flaky` header, file paths
|
|
164
|
-
- **Gray**: `skipped` tests
|
|
184
|
+
- **Yellow**: `flaky` header, `skipped` count, file paths
|
|
185
|
+
- **Gray**: `skipped` tests (detailed list)
|
|
165
186
|
- **Cyan**: `todo` tests
|
|
187
|
+
- **Purple**: `(filtered)` indicator when using `testNamePattern`
|
|
166
188
|
|
|
167
189
|
Colors are enabled when:
|
|
168
190
|
- `COLOR` environment variable is set, OR
|
|
@@ -212,11 +234,29 @@ Include per-file coverage percentages (lines, stmts, branch, funcs) alongside un
|
|
|
212
234
|
reporters: [new ToonReporter({ verbose: true })]
|
|
213
235
|
```
|
|
214
236
|
|
|
215
|
-
|
|
237
|
+
### `timing`
|
|
238
|
+
|
|
239
|
+
Show per-test timing information and total duration. Useful for identifying slow tests.
|
|
240
|
+
|
|
241
|
+
```ts
|
|
242
|
+
reporters: [new ToonReporter({ timing: true })]
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Output:
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
duration: 1m30s52ms
|
|
249
|
+
passing[3]{at,name,ms}:
|
|
250
|
+
"utils.test.ts:4:3",should be fast,1
|
|
251
|
+
"api.test.ts:12:3",should handle requests,50
|
|
252
|
+
"db.test.ts:8:3",should query slowly,90000
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Line Numbers
|
|
216
256
|
|
|
217
257
|
### Vitest
|
|
218
258
|
|
|
219
|
-
To get line:column information for skipped and todo tests, enable `includeTaskLocation` in your vitest config:
|
|
259
|
+
To get line:column information for passing tests (with `timing: true`), skipped, and todo tests, enable `includeTaskLocation` in your vitest config:
|
|
220
260
|
|
|
221
261
|
```ts
|
|
222
262
|
// vitest.config.ts
|
|
@@ -234,7 +274,9 @@ Or via CLI:
|
|
|
234
274
|
npx vitest run --reporter=@epicat/toon-reporter --includeTaskLocation
|
|
235
275
|
```
|
|
236
276
|
|
|
237
|
-
Without this option, skipped
|
|
277
|
+
Without this option, passing tests (with `timing: true`), skipped, and todo tests will only show the file path (not line:column). This is a Vitest limitation - test locations are only collected when this config is enabled before test collection.
|
|
278
|
+
|
|
279
|
+
**Note:** Failing tests always include line:column from the error stack trace, regardless of this setting.
|
|
238
280
|
|
|
239
281
|
### Playwright
|
|
240
282
|
|
package/dist/playwright.d.mts
CHANGED
|
@@ -3,6 +3,8 @@ import { FullConfig, FullResult, Reporter, Suite } from "@playwright/test/report
|
|
|
3
3
|
//#region src/toon-playwright-reporter.d.ts
|
|
4
4
|
interface ToonPlaywrightReporterOptions {
|
|
5
5
|
outputFile?: string;
|
|
6
|
+
/** When true, shows per-test timing in passing[N]{at,name,ms} format */
|
|
7
|
+
timing?: boolean;
|
|
6
8
|
/** @internal Used for testing to capture output */
|
|
7
9
|
_captureOutput?: (output: string) => void;
|
|
8
10
|
}
|
|
@@ -10,6 +12,7 @@ declare class ToonPlaywrightReporter implements Reporter {
|
|
|
10
12
|
private config;
|
|
11
13
|
private suite;
|
|
12
14
|
private options;
|
|
15
|
+
private testsDiscovered;
|
|
13
16
|
constructor(options?: ToonPlaywrightReporterOptions);
|
|
14
17
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
15
18
|
private formatLocation;
|
package/dist/playwright.mjs
CHANGED
|
@@ -3,13 +3,27 @@ import { dirname, relative, resolve } from "pathe";
|
|
|
3
3
|
import { encode } from "@toon-format/toon";
|
|
4
4
|
|
|
5
5
|
//#region src/toon-playwright-reporter.ts
|
|
6
|
+
function formatDuration(ms) {
|
|
7
|
+
const h = Math.floor(ms / 36e5);
|
|
8
|
+
const m = Math.floor(ms % 36e5 / 6e4);
|
|
9
|
+
const s = Math.floor(ms % 6e4 / 1e3);
|
|
10
|
+
const remaining = Math.round(ms % 1e3);
|
|
11
|
+
const parts = [];
|
|
12
|
+
if (h) parts.push(`${h}h`);
|
|
13
|
+
if (m) parts.push(`${m}m`);
|
|
14
|
+
if (s) parts.push(`${s}s`);
|
|
15
|
+
if (remaining || parts.length === 0) parts.push(`${remaining}ms`);
|
|
16
|
+
return parts.join("");
|
|
17
|
+
}
|
|
6
18
|
var ToonPlaywrightReporter = class {
|
|
7
19
|
constructor(options = {}) {
|
|
20
|
+
this.testsDiscovered = 0;
|
|
8
21
|
this.options = options;
|
|
9
22
|
}
|
|
10
23
|
onBegin(config, suite) {
|
|
11
24
|
this.config = config;
|
|
12
25
|
this.suite = suite;
|
|
26
|
+
this.testsDiscovered = suite.allTests().length;
|
|
13
27
|
}
|
|
14
28
|
formatLocation(filePath, line, column) {
|
|
15
29
|
const relPath = relative(process.cwd(), filePath);
|
|
@@ -39,6 +53,16 @@ var ToonPlaywrightReporter = class {
|
|
|
39
53
|
}
|
|
40
54
|
async onEnd(result) {
|
|
41
55
|
const allTests = this.suite.allTests();
|
|
56
|
+
if (allTests.length === 0) {
|
|
57
|
+
let errorMessage;
|
|
58
|
+
if (this.testsDiscovered > 0) errorMessage = `${this.testsDiscovered} test(s) discovered but not executed`;
|
|
59
|
+
else if (result.status === "interrupted") errorMessage = "Test run was interrupted";
|
|
60
|
+
else if (result.status === "timedout") errorMessage = "Test run timed out";
|
|
61
|
+
else errorMessage = "No test files found";
|
|
62
|
+
const report$1 = { error: errorMessage };
|
|
63
|
+
await this.writeReport(encode(report$1));
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
42
66
|
const passedTests = [];
|
|
43
67
|
const failedTests = [];
|
|
44
68
|
const skippedTests = [];
|
|
@@ -78,12 +102,26 @@ var ToonPlaywrightReporter = class {
|
|
|
78
102
|
name: test.title,
|
|
79
103
|
retries: test.results.length - 1
|
|
80
104
|
});
|
|
81
|
-
const
|
|
105
|
+
const mapToTiming = (test) => {
|
|
106
|
+
const lastResult = test.results[test.results.length - 1];
|
|
107
|
+
return {
|
|
108
|
+
at: this.formatLocation(test.location.file, test.location.line, test.location.column),
|
|
109
|
+
name: test.title,
|
|
110
|
+
ms: Math.round(lastResult?.duration ?? 0)
|
|
111
|
+
};
|
|
112
|
+
};
|
|
113
|
+
const report = {};
|
|
114
|
+
if (this.options.timing) {
|
|
115
|
+
report.duration = formatDuration(result.duration);
|
|
116
|
+
report.passing = passedTests.map(mapToTiming);
|
|
117
|
+
} else report.passing = passedTests.length;
|
|
82
118
|
if (flakyTests.length > 0) report.flaky = flakyTests.map(mapToFlaky);
|
|
83
119
|
if (failures.length > 0) report.failing = failures;
|
|
84
120
|
if (todoTests.length > 0) report.todo = todoTests.map(mapToSkipped);
|
|
85
121
|
if (skippedTests.length > 0) report.skipped = skippedTests.map(mapToSkipped);
|
|
86
|
-
|
|
122
|
+
let output = encode(report);
|
|
123
|
+
if (!!(this.config.grep || this.config.grepInvert)) output = output.replace(/^(passing: .+)$/m, "$1 (filtered)");
|
|
124
|
+
await this.writeReport(output);
|
|
87
125
|
}
|
|
88
126
|
async writeReport(report) {
|
|
89
127
|
if (this.options._captureOutput) {
|
|
@@ -92,7 +130,7 @@ var ToonPlaywrightReporter = class {
|
|
|
92
130
|
}
|
|
93
131
|
const outputFile = this.options.outputFile;
|
|
94
132
|
if (outputFile) {
|
|
95
|
-
const reportFile = resolve(this.rootDir, outputFile);
|
|
133
|
+
const reportFile = resolve(this.config.rootDir, outputFile);
|
|
96
134
|
const outputDirectory = dirname(reportFile);
|
|
97
135
|
if (!existsSync(outputDirectory)) await promises.mkdir(outputDirectory, { recursive: true });
|
|
98
136
|
await promises.writeFile(reportFile, report, "utf-8");
|