@reportforge/playwright-pdf 0.6.2 → 0.7.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 +15 -0
- package/README.md +15 -13
- package/dist/{demo/cli.js → cli/index.js} +5726 -5267
- package/dist/index.d.mts +8 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.js +67 -13
- package/dist/index.mjs +67 -13
- package/dist/templates/layouts/detailed.hbs +1 -7
- package/dist/templates/layouts/executive.hbs +1 -7
- package/dist/templates/layouts/minimal.hbs +1 -7
- package/dist/templates/partials/failure-card.hbs +27 -0
- package/dist/templates/partials/suite-breakdown.hbs +30 -4
- package/dist/templates/styles/base.css +87 -26
- package/dist/templates/styles/detailed.css +1 -44
- package/dist/templates/styles/executive.css +2 -32
- package/dist/templates/styles/minimal.css +1 -46
- package/package.json +2 -3
- package/dist/cli/export-feedback.js +0 -87
- package/dist/templates/partials/failure-analysis.hbs +0 -42
- package/dist/templates/partials/failure-deep-dive.hbs +0 -62
- package/dist/templates/partials/slow-tests.hbs +0 -27
|
@@ -292,12 +292,90 @@ code {
|
|
|
292
292
|
.release-gate__sep { color: var(--border-3); }
|
|
293
293
|
.release-gate__flaky-warn { color: var(--flaky); font-weight: 600; }
|
|
294
294
|
|
|
295
|
-
/* ──
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
.
|
|
299
|
-
.failure-
|
|
300
|
-
.failure-
|
|
295
|
+
/* ── Inline failure detail — ONE block hanging off the failed test row ────────
|
|
296
|
+
A single severity-coloured left rail + hairline dividers tie the error,
|
|
297
|
+
retry history, and root cause together as the failed test's detail (not
|
|
298
|
+
three separate boxes). Lives here so every template's breakdown can use it. */
|
|
299
|
+
.failure-row > td { padding: 0; border: none; background: transparent; }
|
|
300
|
+
.failure-detail {
|
|
301
|
+
border-left: 3px solid var(--fail);
|
|
302
|
+
margin: 2px 10px 10px 12px; /* slight indent under the test row + gap before next row */
|
|
303
|
+
padding: 11px 0 11px 16px;
|
|
304
|
+
page-break-inside: avoid;
|
|
305
|
+
}
|
|
306
|
+
.failure-detail--critical { border-left-color: var(--fail); }
|
|
307
|
+
.failure-detail--high { border-left-color: #A35E1E; }
|
|
308
|
+
.failure-detail--medium { border-left-color: var(--timeout); }
|
|
309
|
+
.failure-detail--low { border-left-color: var(--text-3); }
|
|
310
|
+
|
|
311
|
+
/* error — flat on the rail, no inner box (override the base pre panel) */
|
|
312
|
+
.failure-detail pre {
|
|
313
|
+
margin: 0; padding: 0;
|
|
314
|
+
background: transparent; border: none; border-radius: 0; max-height: none;
|
|
315
|
+
}
|
|
316
|
+
.failure-detail .failure-item__message {
|
|
317
|
+
background: transparent; border: none; border-radius: 0; padding: 0; margin: 0;
|
|
318
|
+
font-family: 'JetBrains Mono', monospace;
|
|
319
|
+
font-size: 9.5px; color: #8A2E1E; word-break: break-word; line-height: 1.6;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/* retry history — divider above, same block */
|
|
323
|
+
.retry-history {
|
|
324
|
+
font-size: 9px; color: var(--text-3); margin-top: 11px;
|
|
325
|
+
padding-top: 10px; border-top: 1px solid var(--border);
|
|
326
|
+
font-family: 'Inter', system-ui, sans-serif; font-weight: 600;
|
|
327
|
+
letter-spacing: 0.5px; text-transform: uppercase;
|
|
328
|
+
}
|
|
329
|
+
.retry-attempt {
|
|
330
|
+
display: inline-block; padding: 2px 7px;
|
|
331
|
+
border-radius: 3px; margin-right: 4px;
|
|
332
|
+
font-size: 8.5px; font-family: 'Inter', system-ui, sans-serif; font-weight: 600;
|
|
333
|
+
-webkit-print-color-adjust: exact;
|
|
334
|
+
print-color-adjust: exact;
|
|
335
|
+
}
|
|
336
|
+
.retry-attempt--failed { background: var(--fail-dim); color: var(--fail); border: 1px solid #E5C9BD; }
|
|
337
|
+
.retry-attempt--passed { background: var(--pass-dim); color: var(--pass); border: 1px solid #C5DBC9; }
|
|
338
|
+
|
|
339
|
+
/* Slow-test badge — a compact tag inline beside the status badge (min-width:0
|
|
340
|
+
overrides the .badge 50px floor so it doesn't wrap onto its own line). */
|
|
341
|
+
.badge--slow {
|
|
342
|
+
background: var(--timeout-dim); color: var(--timeout);
|
|
343
|
+
border: 1px solid #E5D2A8;
|
|
344
|
+
min-width: 0; margin-left: 5px;
|
|
345
|
+
padding: 1px 5px; font-size: 7.5px; letter-spacing: 0.4px;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/* Root cause — footer of the same failure block: a divider above, no box and no
|
|
349
|
+
second rail (the block already carries one). */
|
|
350
|
+
.root-cause-chip {
|
|
351
|
+
display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px;
|
|
352
|
+
margin-top: 11px; padding-top: 10px;
|
|
353
|
+
border-top: 1px solid var(--border);
|
|
354
|
+
font-family: 'Inter', system-ui, sans-serif; font-size: 9.5px;
|
|
355
|
+
}
|
|
356
|
+
.root-cause-chip__tag {
|
|
357
|
+
font-size: 8px; font-weight: 700; letter-spacing: 0.8px; text-transform: uppercase;
|
|
358
|
+
color: var(--text-3);
|
|
359
|
+
}
|
|
360
|
+
.root-cause-chip__label { font-weight: 700; color: var(--text-1); }
|
|
361
|
+
.root-cause-chip__msg {
|
|
362
|
+
font-family: 'JetBrains Mono', monospace; color: var(--text-2);
|
|
363
|
+
word-break: break-word;
|
|
364
|
+
}
|
|
365
|
+
.root-cause-chip__strength {
|
|
366
|
+
margin-left: auto; font-size: 8.5px; font-weight: 600;
|
|
367
|
+
letter-spacing: 0.5px; text-transform: uppercase; color: var(--text-3);
|
|
368
|
+
}
|
|
369
|
+
/* Subtle category cue — tint only the category name (no box, no second rail), so
|
|
370
|
+
the chip stays "quiet" but failures are still scannable by root-cause type. */
|
|
371
|
+
.root-cause-chip--assertion .root-cause-chip__label,
|
|
372
|
+
.root-cause-chip--locator-not-found .root-cause-chip__label { color: oklch(52% 0.19 25); }
|
|
373
|
+
.root-cause-chip--timeout .root-cause-chip__label { color: oklch(50% 0.13 65); }
|
|
374
|
+
.root-cause-chip--network .root-cause-chip__label,
|
|
375
|
+
.root-cause-chip--navigation .root-cause-chip__label { color: oklch(50% 0.15 260); }
|
|
376
|
+
.root-cause-chip--flaky-by-retry .root-cause-chip__label { color: oklch(50% 0.15 290); }
|
|
377
|
+
.root-cause-chip--env-config .root-cause-chip__label { color: oklch(48% 0.14 145); }
|
|
378
|
+
.root-cause-chip--unknown .root-cause-chip__label { color: var(--text-2); }
|
|
301
379
|
|
|
302
380
|
/* ── History trend card — shared by detailed + executive (charts.hbs) ─────── */
|
|
303
381
|
.trend-card {
|
|
@@ -656,23 +734,6 @@ code {
|
|
|
656
734
|
font-size: 9.5px; color: var(--text-1);
|
|
657
735
|
}
|
|
658
736
|
|
|
659
|
-
/*
|
|
660
|
-
|
|
661
|
-
.
|
|
662
|
-
.failure-analysis .oneliner { font-weight: 600; }
|
|
663
|
-
.analysis-truncated { font-size: 0.85em; opacity: 0.8; }
|
|
664
|
-
|
|
665
|
-
.cluster { border-left: 3px solid; padding-left: 12px; margin-bottom: 16px; }
|
|
666
|
-
.cluster-header { display: flex; gap: 12px; align-items: baseline; }
|
|
667
|
-
.cluster-strength--strong { color: oklch(55% 0.15 145); }
|
|
668
|
-
.cluster-strength--moderate { color: oklch(60% 0.18 65); }
|
|
669
|
-
.cluster-strength--weak { color: oklch(55% 0.20 25); }
|
|
670
|
-
.cluster--timeout { border-color: oklch(60% 0.18 65); }
|
|
671
|
-
.cluster--assertion { border-color: oklch(55% 0.20 25); }
|
|
672
|
-
.cluster--locator-not-found { border-color: oklch(55% 0.20 25); }
|
|
673
|
-
.cluster--network { border-color: oklch(55% 0.15 260); }
|
|
674
|
-
.cluster--navigation { border-color: oklch(55% 0.15 260); }
|
|
675
|
-
.cluster--flaky-by-retry { border-color: oklch(55% 0.15 290); }
|
|
676
|
-
.cluster--env-config { border-color: oklch(55% 0.15 145); }
|
|
677
|
-
.cluster--unknown { border-color: oklch(60% 0.00 0); }
|
|
678
|
-
.confidence-warning { font-size: 0.8em; opacity: 0.75; }
|
|
737
|
+
/* The aggregate failure-analysis cluster section was retired — root causes now
|
|
738
|
+
render as inline per-test chips in the suite breakdown (.root-cause-chip above).
|
|
739
|
+
The .analysis-oneliner one-liner lives further up and is still used by executive. */
|
|
@@ -130,50 +130,7 @@
|
|
|
130
130
|
|
|
131
131
|
/* ── Suite breakdown · styles live in base.css (shared across templates) ──── */
|
|
132
132
|
|
|
133
|
-
/* ── Failure
|
|
134
|
-
.failure-item {
|
|
135
|
-
background: var(--surface);
|
|
136
|
-
border: 1px solid var(--border-2);
|
|
137
|
-
border-left: 3px solid var(--fail);
|
|
138
|
-
border-radius: 6px; padding: 16px 18px;
|
|
139
|
-
margin-bottom: 12px; page-break-inside: avoid;
|
|
140
|
-
}
|
|
141
|
-
.failure-item__header {
|
|
142
|
-
display: flex; justify-content: space-between;
|
|
143
|
-
align-items: flex-start; margin-bottom: 10px;
|
|
144
|
-
}
|
|
145
|
-
.failure-item__title {
|
|
146
|
-
font-family: 'Inter', system-ui, sans-serif;
|
|
147
|
-
font-weight: 600; font-size: 12px; color: var(--text-1);
|
|
148
|
-
}
|
|
149
|
-
.failure-item__path {
|
|
150
|
-
font-family: 'JetBrains Mono', monospace;
|
|
151
|
-
font-size: 8.5px; color: var(--text-3); margin-bottom: 10px;
|
|
152
|
-
}
|
|
153
|
-
.failure-item__message {
|
|
154
|
-
background: var(--fail-dim);
|
|
155
|
-
border: 1px solid #E5C9BD;
|
|
156
|
-
border-left: 2px solid var(--fail);
|
|
157
|
-
border-radius: 4px; padding: 10px 14px; margin-bottom: 12px;
|
|
158
|
-
font-family: 'JetBrains Mono', monospace;
|
|
159
|
-
font-size: 9.5px; color: #8A2E1E;
|
|
160
|
-
word-break: break-word; line-height: 1.6;
|
|
161
|
-
}
|
|
162
|
-
.retry-history {
|
|
163
|
-
font-size: 9px; color: var(--text-3); margin-top: 10px;
|
|
164
|
-
padding-top: 10px; border-top: 1px solid var(--border);
|
|
165
|
-
font-family: 'Inter', system-ui, sans-serif; font-weight: 600;
|
|
166
|
-
letter-spacing: 0.5px; text-transform: uppercase;
|
|
167
|
-
}
|
|
168
|
-
.retry-attempt {
|
|
169
|
-
display: inline-block; padding: 2px 7px;
|
|
170
|
-
border-radius: 3px; margin-right: 4px;
|
|
171
|
-
font-size: 8.5px; font-family: 'Inter', system-ui, sans-serif; font-weight: 600;
|
|
172
|
-
-webkit-print-color-adjust: exact;
|
|
173
|
-
print-color-adjust: exact;
|
|
174
|
-
}
|
|
175
|
-
.retry-attempt--failed { background: var(--fail-dim); color: var(--fail); border: 1px solid #E5C9BD; }
|
|
176
|
-
.retry-attempt--passed { background: var(--pass-dim); color: var(--pass); border: 1px solid #C5DBC9; }
|
|
133
|
+
/* ── Failure card · styles live in base.css (rendered inside the breakdown) ── */
|
|
177
134
|
|
|
178
135
|
/* ── Defect log, coverage bar, environment table, failure analysis ───────── */
|
|
179
136
|
/* All moved to base.css so executive/minimal can toggle these sections on too. */
|
|
@@ -132,35 +132,5 @@
|
|
|
132
132
|
letter-spacing: 1.3px;
|
|
133
133
|
}
|
|
134
134
|
|
|
135
|
-
/*
|
|
136
|
-
.
|
|
137
|
-
display: flex; align-items: center; justify-content: space-between;
|
|
138
|
-
padding: 12px 16px;
|
|
139
|
-
border-bottom: 1px solid var(--border);
|
|
140
|
-
background: var(--surface);
|
|
141
|
-
border-left: 3px solid var(--fail);
|
|
142
|
-
}
|
|
143
|
-
.failure-summary-item:first-child {
|
|
144
|
-
border-radius: 6px 6px 0 0;
|
|
145
|
-
border-top: 1px solid var(--border-2);
|
|
146
|
-
border-right: 1px solid var(--border-2);
|
|
147
|
-
}
|
|
148
|
-
.failure-summary-item:last-child {
|
|
149
|
-
border-radius: 0 0 6px 6px;
|
|
150
|
-
border-bottom: 1px solid var(--border-2);
|
|
151
|
-
border-right: 1px solid var(--border-2);
|
|
152
|
-
}
|
|
153
|
-
.failure-summary-item:only-child {
|
|
154
|
-
border-radius: 6px;
|
|
155
|
-
border: 1px solid var(--border-2);
|
|
156
|
-
border-left: 3px solid var(--fail);
|
|
157
|
-
}
|
|
158
|
-
.failure-summary-item__title {
|
|
159
|
-
font-size: 11px; font-weight: 600; color: var(--text-1);
|
|
160
|
-
padding-left: 6px;
|
|
161
|
-
}
|
|
162
|
-
.failure-summary-item__suite {
|
|
163
|
-
font-size: 9px; color: var(--text-3); margin-top: 3px;
|
|
164
|
-
font-family: 'JetBrains Mono', monospace;
|
|
165
|
-
padding-left: 6px;
|
|
166
|
-
}
|
|
135
|
+
/* Executive failures now render inline in the suite breakdown (compact, no
|
|
136
|
+
screenshots) — card styles live in base.css. */
|
|
@@ -143,49 +143,4 @@
|
|
|
143
143
|
}
|
|
144
144
|
.suite-stats { gap: 6px; }
|
|
145
145
|
|
|
146
|
-
/* ── Failure
|
|
147
|
-
.failure-item {
|
|
148
|
-
background: var(--surface);
|
|
149
|
-
border: 1px solid var(--border-2);
|
|
150
|
-
border-left: 3px solid var(--fail);
|
|
151
|
-
border-radius: 6px;
|
|
152
|
-
padding: 14px 16px; margin-bottom: 10px;
|
|
153
|
-
page-break-inside: avoid;
|
|
154
|
-
}
|
|
155
|
-
.failure-item__header {
|
|
156
|
-
display: flex; justify-content: space-between;
|
|
157
|
-
align-items: flex-start; margin-bottom: 8px;
|
|
158
|
-
}
|
|
159
|
-
.failure-item__title {
|
|
160
|
-
font-family: 'Inter', system-ui, sans-serif;
|
|
161
|
-
font-weight: 600; font-size: 12px;
|
|
162
|
-
color: var(--text-1); margin-bottom: 4px;
|
|
163
|
-
}
|
|
164
|
-
.failure-item__path {
|
|
165
|
-
font-family: 'JetBrains Mono', monospace;
|
|
166
|
-
font-size: 8.5px; color: var(--text-3); margin-bottom: 10px;
|
|
167
|
-
}
|
|
168
|
-
.failure-item__message {
|
|
169
|
-
background: var(--fail-dim);
|
|
170
|
-
border: 1px solid #E5C9BD;
|
|
171
|
-
border-left: 2px solid var(--fail);
|
|
172
|
-
border-radius: 4px;
|
|
173
|
-
padding: 9px 12px; margin-bottom: 10px;
|
|
174
|
-
font-family: 'JetBrains Mono', monospace;
|
|
175
|
-
font-size: 9.5px; color: #8A2E1E;
|
|
176
|
-
word-break: break-word; line-height: 1.6;
|
|
177
|
-
}
|
|
178
|
-
.retry-history {
|
|
179
|
-
font-size: 9px; color: var(--text-3); margin-top: 10px;
|
|
180
|
-
padding-top: 8px; border-top: 1px solid var(--border);
|
|
181
|
-
}
|
|
182
|
-
.retry-attempt {
|
|
183
|
-
display: inline-block; padding: 2px 7px;
|
|
184
|
-
border-radius: 3px; margin-right: 4px; font-size: 8.5px;
|
|
185
|
-
font-family: 'Inter', system-ui, sans-serif; font-weight: 600;
|
|
186
|
-
letter-spacing: 0.4px; text-transform: uppercase;
|
|
187
|
-
-webkit-print-color-adjust: exact;
|
|
188
|
-
print-color-adjust: exact;
|
|
189
|
-
}
|
|
190
|
-
.retry-attempt--failed { background: var(--fail-dim); color: var(--fail); border: 1px solid #E5C9BD; }
|
|
191
|
-
.retry-attempt--passed { background: var(--pass-dim); color: var(--pass); border: 1px solid #C5DBC9; }
|
|
146
|
+
/* ── Failure card · styles live in base.css (rendered inside the breakdown) ── */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reportforge/playwright-pdf",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Enterprise-ready PDF reports for Playwright Test — minimal, detailed, and executive templates with CI/CD integrations",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "ReportForge",
|
|
@@ -34,8 +34,7 @@
|
|
|
34
34
|
}
|
|
35
35
|
},
|
|
36
36
|
"bin": {
|
|
37
|
-
"
|
|
38
|
-
"reportforge-export-feedback": "./dist/cli/export-feedback.js"
|
|
37
|
+
"playwright-pdf": "./dist/cli/index.js"
|
|
39
38
|
},
|
|
40
39
|
"publishConfig": {
|
|
41
40
|
"access": "public"
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
"use strict";
|
|
3
|
-
var __create = Object.create;
|
|
4
|
-
var __defProp = Object.defineProperty;
|
|
5
|
-
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
-
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
-
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
-
var __export = (target, all) => {
|
|
10
|
-
for (var name in all)
|
|
11
|
-
__defProp(target, name, { get: all[name], enumerable: true });
|
|
12
|
-
};
|
|
13
|
-
var __copyProps = (to, from, except, desc) => {
|
|
14
|
-
if (from && typeof from === "object" || typeof from === "function") {
|
|
15
|
-
for (let key of __getOwnPropNames(from))
|
|
16
|
-
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
17
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
18
|
-
}
|
|
19
|
-
return to;
|
|
20
|
-
};
|
|
21
|
-
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
22
|
-
// If the importer is in node compatibility mode or this is not an ESM
|
|
23
|
-
// file that has been converted to a CommonJS file using a Babel-
|
|
24
|
-
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
25
|
-
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
26
|
-
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
27
|
-
mod
|
|
28
|
-
));
|
|
29
|
-
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
|
-
|
|
31
|
-
// src/cli/export-feedback.ts
|
|
32
|
-
var export_feedback_exports = {};
|
|
33
|
-
__export(export_feedback_exports, {
|
|
34
|
-
exportFeedback: () => exportFeedback
|
|
35
|
-
});
|
|
36
|
-
module.exports = __toCommonJS(export_feedback_exports);
|
|
37
|
-
var import_node_fs = __toESM(require("fs"));
|
|
38
|
-
var import_node_os = __toESM(require("os"));
|
|
39
|
-
var import_node_path = __toESM(require("path"));
|
|
40
|
-
|
|
41
|
-
// src/analysis/feedback-csv.ts
|
|
42
|
-
var FEEDBACK_HEADER = "category,margin,text";
|
|
43
|
-
function parseFeedbackRows(content) {
|
|
44
|
-
return content.trim().split("\n").slice(1).filter(Boolean).map((line) => {
|
|
45
|
-
const parts = line.split(",");
|
|
46
|
-
return { category: parts[0], margin: parts[1], text: parts.slice(2).join(","), raw: line };
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// src/cli/export-feedback.ts
|
|
51
|
-
function exportFeedback(deps = {}) {
|
|
52
|
-
const home = deps.home ?? import_node_os.default.homedir();
|
|
53
|
-
const cwd = deps.cwd ?? process.cwd();
|
|
54
|
-
const root = import_node_path.default.join(home, ".reportforge");
|
|
55
|
-
const rows = [];
|
|
56
|
-
if (import_node_fs.default.existsSync(root)) {
|
|
57
|
-
for (const entry of import_node_fs.default.readdirSync(root)) {
|
|
58
|
-
const file = import_node_path.default.join(root, entry, "unclassified.csv");
|
|
59
|
-
if (!import_node_fs.default.existsSync(file)) continue;
|
|
60
|
-
for (const r of parseFeedbackRows(import_node_fs.default.readFileSync(file, "utf8"))) {
|
|
61
|
-
rows.push(r.raw);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
if (rows.length === 0) return { rowCount: 0, outFile: null };
|
|
66
|
-
const outFile = import_node_path.default.join(cwd, "reportforge-feedback.csv");
|
|
67
|
-
import_node_fs.default.writeFileSync(outFile, `${FEEDBACK_HEADER}
|
|
68
|
-
${rows.join("\n")}
|
|
69
|
-
`, "utf8");
|
|
70
|
-
return { rowCount: rows.length, outFile };
|
|
71
|
-
}
|
|
72
|
-
if (process.argv[1] && process.argv[1].endsWith("export-feedback.js")) {
|
|
73
|
-
const res = exportFeedback();
|
|
74
|
-
if (res.rowCount === 0) {
|
|
75
|
-
process.stdout.write("No locally collected failures found (~/.reportforge/*/unclassified.csv).\n");
|
|
76
|
-
} else {
|
|
77
|
-
process.stdout.write(
|
|
78
|
-
`Saved ${res.rowCount} row(s) to ${res.outFile} - tokenized + redacted, LOCAL only.
|
|
79
|
-
Review it before sharing; there is no upload.
|
|
80
|
-
`
|
|
81
|
-
);
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
// Annotate the CommonJS export names for ESM import in node:
|
|
85
|
-
0 && (module.exports = {
|
|
86
|
-
exportFeedback
|
|
87
|
-
});
|
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
{{#if analysis.clusters.length}}
|
|
2
|
-
<section class="failure-analysis">
|
|
3
|
-
<h2>Failure Analysis</h2>
|
|
4
|
-
<p class="oneliner">{{analysis.oneLiner}}</p>
|
|
5
|
-
|
|
6
|
-
{{#if analysis.truncated}}
|
|
7
|
-
<p class="analysis-truncated">
|
|
8
|
-
Showing analysis of the first {{analysis.analysedCount}} of {{analysis.totalFailures}} failures.
|
|
9
|
-
Raise <code>failureAnalysis.maxFailuresToAnalyse</code> for full coverage.
|
|
10
|
-
</p>
|
|
11
|
-
{{/if}}
|
|
12
|
-
|
|
13
|
-
{{#each analysis.clusters}}
|
|
14
|
-
<div class="cluster cluster--{{category}}">
|
|
15
|
-
<div class="cluster-header">
|
|
16
|
-
<span class="cluster-category">{{categoryLabel category}}</span>
|
|
17
|
-
<span class="cluster-count">{{count}} {{pluralise count "test" "tests"}}</span>
|
|
18
|
-
<span class="cluster-strength cluster-strength--{{strength}}">{{strengthLabel strength}} match</span>
|
|
19
|
-
</div>
|
|
20
|
-
|
|
21
|
-
{{#if stackFrame}}
|
|
22
|
-
<div class="cluster-frame"><code>{{stackFrame}}</code></div>
|
|
23
|
-
{{/if}}
|
|
24
|
-
|
|
25
|
-
<div class="cluster-message">{{representativeMessage}}</div>
|
|
26
|
-
|
|
27
|
-
<ul class="cluster-tests">
|
|
28
|
-
{{#each (take tests 5)}}
|
|
29
|
-
<li>{{this}}</li>
|
|
30
|
-
{{/each}}
|
|
31
|
-
{{#if (gt testsTotal 5)}}
|
|
32
|
-
<li class="more">+{{subtract testsTotal 5}} more</li>
|
|
33
|
-
{{/if}}
|
|
34
|
-
</ul>
|
|
35
|
-
|
|
36
|
-
{{#if (eq strength "weak")}}
|
|
37
|
-
<p class="confidence-warning">* Weak match — manual review recommended</p>
|
|
38
|
-
{{/if}}
|
|
39
|
-
</div>
|
|
40
|
-
{{/each}}
|
|
41
|
-
</section>
|
|
42
|
-
{{/if}}
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
{{#if failures.length}}
|
|
2
|
-
<div class="section">
|
|
3
|
-
<h2>Failures ({{failures.length}}{{#if overflowFailureCount}} shown · {{overflowFailureCount}} more in sidecar{{/if}})</h2>
|
|
4
|
-
{{#if overflowFailureCount}}
|
|
5
|
-
<div class="overflow-banner">
|
|
6
|
-
Showing the first {{failures.length}} failures. {{overflowFailureCount}} additional
|
|
7
|
-
failure{{#if (gt overflowFailureCount 1)}}s are{{else}} is{{/if}} available in
|
|
8
|
-
<code>{{overflowSidecarName}}</code> alongside this report.
|
|
9
|
-
</div>
|
|
10
|
-
{{/if}}
|
|
11
|
-
{{#each failures}}
|
|
12
|
-
<div class="failure-item failure-item--{{severity}} no-break">
|
|
13
|
-
{{#if ../options.showFullFailures}}
|
|
14
|
-
|
|
15
|
-
<div class="failure-item__header">
|
|
16
|
-
<div>
|
|
17
|
-
<div class="failure-item__title">{{testTitle}}</div>
|
|
18
|
-
<div class="failure-item__path">{{join suitePath " › "}}</div>
|
|
19
|
-
</div>
|
|
20
|
-
<span class="badge badge--failed severity--{{severity}}" style="white-space:nowrap;">
|
|
21
|
-
{{upperCase severity}}
|
|
22
|
-
</span>
|
|
23
|
-
</div>
|
|
24
|
-
|
|
25
|
-
{{!-- Playwright's error.stack already begins with the message, so rendering
|
|
26
|
-
both duplicates it. Show the formatted stack when available; otherwise
|
|
27
|
-
fall back to the bare message. --}}
|
|
28
|
-
{{#if (and error.stack ../options.showStackTraces)}}
|
|
29
|
-
<pre>{{error.stack}}</pre>
|
|
30
|
-
{{else}}
|
|
31
|
-
<div class="failure-item__message">{{error.message}}</div>
|
|
32
|
-
{{/if}}
|
|
33
|
-
|
|
34
|
-
{{#if screenshotBase64}}
|
|
35
|
-
<img class="screenshot" src="{{screenshotBase64}}" alt="Screenshot at failure">
|
|
36
|
-
{{/if}}
|
|
37
|
-
|
|
38
|
-
{{#if retryHistory.length}}
|
|
39
|
-
<div class="retry-history">
|
|
40
|
-
Retry history:
|
|
41
|
-
{{#each retryHistory}}
|
|
42
|
-
<span class="retry-attempt retry-attempt--{{status}}">
|
|
43
|
-
#{{addOne attempt}} {{status}} · {{formatDuration duration}}
|
|
44
|
-
</span>
|
|
45
|
-
{{/each}}
|
|
46
|
-
</div>
|
|
47
|
-
{{/if}}
|
|
48
|
-
|
|
49
|
-
{{else}}
|
|
50
|
-
{{!-- Executive: title + suite only --}}
|
|
51
|
-
<div class="failure-summary-item">
|
|
52
|
-
<div>
|
|
53
|
-
<div class="failure-summary-item__title">{{testTitle}}</div>
|
|
54
|
-
<div class="failure-summary-item__suite">{{join suitePath " › "}}</div>
|
|
55
|
-
</div>
|
|
56
|
-
<span class="badge badge--failed">FAILED</span>
|
|
57
|
-
</div>
|
|
58
|
-
{{/if}}
|
|
59
|
-
</div>
|
|
60
|
-
{{/each}}
|
|
61
|
-
</div>
|
|
62
|
-
{{/if}}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
{{#if slowTests.length}}
|
|
2
|
-
<div class="section">
|
|
3
|
-
<h2>Slowest Tests</h2>
|
|
4
|
-
<table>
|
|
5
|
-
<thead>
|
|
6
|
-
<tr>
|
|
7
|
-
<th>Test</th>
|
|
8
|
-
<th style="width:130px;">Suite</th>
|
|
9
|
-
{{#if showProject}}<th style="width:80px;">Project</th>{{/if}}
|
|
10
|
-
<th style="width:76px;text-align:right;">Duration</th>
|
|
11
|
-
<th style="width:68px;">Status</th>
|
|
12
|
-
</tr>
|
|
13
|
-
</thead>
|
|
14
|
-
<tbody>
|
|
15
|
-
{{#each (take slowTests limit)}}
|
|
16
|
-
<tr class="no-break">
|
|
17
|
-
<td style="font-weight:500;">{{title}}</td>
|
|
18
|
-
<td style="font-size:9px;color:var(--text-3);font-family:'JetBrains Mono',monospace;">{{suiteTitle}}</td>
|
|
19
|
-
{{#if ../showProject}}<td style="font-size:9px;color:var(--text-3);white-space:nowrap;">{{project}}</td>{{/if}}
|
|
20
|
-
<td style="font-family:'JetBrains Mono',monospace;font-size:9.5px;color:var(--text-2);text-align:right;">{{durationFormatted}}</td>
|
|
21
|
-
<td>{{statusBadge status}}</td>
|
|
22
|
-
</tr>
|
|
23
|
-
{{/each}}
|
|
24
|
-
</tbody>
|
|
25
|
-
</table>
|
|
26
|
-
</div>
|
|
27
|
-
{{/if}}
|