@reportforge/playwright-pdf 0.5.1 → 0.6.1
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 +23 -0
- package/README.md +93 -13
- package/dist/demo/cli.js +154 -49
- package/dist/index.d.mts +40 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.js +155 -50
- package/dist/index.mjs +155 -50
- package/dist/templates/layouts/detailed.hbs +14 -10
- package/dist/templates/layouts/executive.hbs +16 -8
- package/dist/templates/layouts/minimal.hbs +19 -5
- package/dist/templates/styles/base.css +198 -1
- package/dist/templates/styles/detailed.css +3 -104
- package/dist/templates/styles/executive.css +4 -99
- package/dist/templates/styles/minimal.css +4 -21
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,29 @@
|
|
|
3
3
|
All notable changes to this project will be documented in this file.
|
|
4
4
|
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
|
|
5
5
|
|
|
6
|
+
## [0.6.1] — 2026-06-22
|
|
7
|
+
|
|
8
|
+
### Documentation
|
|
9
|
+
|
|
10
|
+
- **Expanded the Report Sections reference** — per-section descriptions for all 13 block toggles, the 5 display modifiers, a per-template defaults matrix, and the dependency rules (`trend` needs `charts`, data-gated sections, render-layer only) in both the README and the docs site.
|
|
11
|
+
- Fixed the README documentation links to point at the real `/docs/<page>` routes instead of non-existent `/docs#<anchor>` anchors.
|
|
12
|
+
|
|
13
|
+
_No runtime changes — `@reportforge/playwright-pdf@0.6.1` is identical in behaviour to `0.6.0`._
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## [0.6.0] — 2026-06-22
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- **Configurable report sections** — a new `sections` option adds or removes any section in any built-in template (`minimal` / `detailed` / `executive`) straight from `playwright.config`. Flat keys set a baseline for every chosen template; per-template keys override per template. Covers every block (cover page, charts, trend, requirements traceability, CI/environment, suite breakdown, failures, failure analysis, slow tests, defect log, release gate) plus display modifiers (pass-rate, full environment table, retries, failure and stack-trace depth). Unknown keys are rejected with a clear configuration error. Reports render identically to before when the option is omitted.
|
|
22
|
+
|
|
23
|
+
### Changed
|
|
24
|
+
|
|
25
|
+
- Shared section styles (cover page, charts, defect log, coverage bars, environment table, failure analysis, suite breakdown) moved to the base stylesheet, so a section toggled into a template that didn't ship it before now renders fully styled.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
6
29
|
## [0.5.1] — 2026-06-19
|
|
7
30
|
|
|
8
31
|
### Security
|
package/README.md
CHANGED
|
@@ -58,6 +58,7 @@ Three templates, one reporter. Every PDF is fully offline — fonts, charts, and
|
|
|
58
58
|
- **Flakiness trend** — top-N flakiest tests table with per-test dot sparkline across stored history runs in the `detailed` template; `flakinessTopN` option (default 5, `0` disables)
|
|
59
59
|
- **Offline failure analysis** — sorts failures into 7 root-cause buckets and clusters them, right inside the `detailed` PDF (one-liner in `executive`). No network, no AI service — a small embedded classifier that updates itself safely over your existing license refresh; pin it any time. Full details at [reportforge.org/docs/advanced/failure-analysis](https://reportforge.org/docs/advanced/failure-analysis).
|
|
60
60
|
- `reportforge-export-feedback` — exports locally-collected unrecognized failures (tokenized, local-only) for optional manual sharing.
|
|
61
|
+
- **Configurable sections** — add or remove any report section per template via `sections`.
|
|
61
62
|
- **Hybrid licensing** — one short key unlocks it; reports keep rendering offline via a cached JWT between refreshes
|
|
62
63
|
|
|
63
64
|
---
|
|
@@ -108,7 +109,7 @@ npx playwright test
|
|
|
108
109
|
|
|
109
110
|
### Chrome setup
|
|
110
111
|
|
|
111
|
-
The reporter auto-detects Chrome via `PUPPETEER_EXECUTABLE_PATH`, then system Chrome, then `chrome-finder`. Full per-OS instructions: [reportforge.org/docs#chrome-setup](https://reportforge.org/docs#chrome-setup).
|
|
112
|
+
The reporter auto-detects Chrome via `PUPPETEER_EXECUTABLE_PATH`, then system Chrome, then `chrome-finder`. Full per-OS instructions: [reportforge.org/docs/running-tests#chrome-setup](https://reportforge.org/docs/running-tests#chrome-setup).
|
|
112
113
|
|
|
113
114
|
**Windows**
|
|
114
115
|
|
|
@@ -198,9 +199,10 @@ All options are the second element of the reporter tuple.
|
|
|
198
199
|
| `showTrend` | `boolean` | `true` | Show the pass-rate sparkline and delta badge in the `detailed` template. Set to `false` to disable history tracking entirely. |
|
|
199
200
|
| `flakinessTopN` | `number` | `5` | Maximum flaky tests to show in the flakiness table (`detailed` template). Set to `0` to disable the table entirely. |
|
|
200
201
|
| `templatePath` | `string \| string[]` | — | Path to a custom Handlebars (`.hbs`) template file. Takes precedence over `template`. Pass an array to generate one PDF per custom template. See the Custom Templates docs. |
|
|
202
|
+
| `sections` | `object` | per-template | Override which report sections appear, on top of the chosen template's defaults. Flat keys are the baseline for every chosen template; per-template keys (`minimal`\|`detailed`\|`executive`) override per template. Block toggles: `coverPage`, `analysisOneliner`, `releaseGate`, `summary`, `charts`, `trend`, `requirementsMatrix`, `ciEnvironment`, `suiteBreakdown`, `failureDeepDive`, `failureAnalysis`, `slowTests`, `defectLog`. Display modifiers: `passRate`, `fullEnvironment`, `retries`, `fullFailures`, `stackTraces`. See the Report Sections docs. |
|
|
201
203
|
<!-- AUTOGEN:options-table END -->
|
|
202
204
|
|
|
203
|
-
Full reference: [reportforge.org/docs
|
|
205
|
+
Full reference: [reportforge.org/docs/configuration](https://reportforge.org/docs/configuration).
|
|
204
206
|
|
|
205
207
|
### Shard Merging
|
|
206
208
|
|
|
@@ -360,11 +362,88 @@ reporter: [
|
|
|
360
362
|
|
|
361
363
|
One license check, one data-collection pass — faster than multiple reporter instances. Duplicate entries are silently deduplicated.
|
|
362
364
|
|
|
365
|
+
### Report Sections
|
|
366
|
+
|
|
367
|
+
Each built-in template ships a curated set of sections. The `sections` option lets you **add a section a template hides or remove one it shows — per template** — without writing a custom template.
|
|
368
|
+
|
|
369
|
+
```ts
|
|
370
|
+
reporter: [['@reportforge/playwright-pdf', {
|
|
371
|
+
template: ['minimal', 'detailed'],
|
|
372
|
+
sections: {
|
|
373
|
+
defectLog: false, // flat key: baseline for EVERY chosen template
|
|
374
|
+
minimal: { charts: true }, // per-template key: overrides the baseline
|
|
375
|
+
detailed: { ciEnvironment: false },
|
|
376
|
+
},
|
|
377
|
+
}]]
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
**Resolution order** (lowest → highest): the template's defaults → flat keys (baseline for all chosen templates) → per-template keys (`minimal` / `detailed` / `executive`). An unknown key throws a configuration error (typo-safe).
|
|
381
|
+
|
|
382
|
+
#### Block toggles — which sections appear
|
|
383
|
+
|
|
384
|
+
| Key | Renders |
|
|
385
|
+
|---|---|
|
|
386
|
+
| `coverPage` | Full-page cover: title, verdict badge, KPI stats strip, branch + commit |
|
|
387
|
+
| `analysisOneliner` | One-line failure-analysis summary banner |
|
|
388
|
+
| `releaseGate` | Ship/hold recommendation banner (APPROVED TO SHIP / HOLD) |
|
|
389
|
+
| `summary` | KPI strip (total · passed · failed · timed-out · skipped · flaky), verdict, duration, pass rate, flaky callout |
|
|
390
|
+
| `charts` | Pass-rate doughnut + stacked suite-results bar |
|
|
391
|
+
| `trend` | Pass-rate trend line, run-history table, and flakiness table (rendered inside `charts`) |
|
|
392
|
+
| `requirementsMatrix` | Tests grouped by `@tag('REQ-…')` with coverage bars (requirements traceability) |
|
|
393
|
+
| `ciEnvironment` | Branch, commit, browsers, CI provider, OS, Node, Playwright, projects, workers |
|
|
394
|
+
| `suiteBreakdown` | Per-suite → per-test table (status, duration, tags, retries) |
|
|
395
|
+
| `failureDeepDive` | Failure cards: error message, stack trace, screenshot, retry history |
|
|
396
|
+
| `failureAnalysis` | Clustered root-cause buckets from the offline classifier |
|
|
397
|
+
| `slowTests` | Slowest tests, duration-ranked |
|
|
398
|
+
| `defectLog` | `DEF-####` numbered failure table (severity, retries) |
|
|
399
|
+
|
|
400
|
+
#### Display modifiers — tune a section that's on
|
|
401
|
+
|
|
402
|
+
| Key | Effect |
|
|
403
|
+
|---|---|
|
|
404
|
+
| `passRate` | Show the pass-rate % on the Passed KPI card (`summary`) |
|
|
405
|
+
| `fullEnvironment` | Full environment table instead of the compact grid (`ciEnvironment`) |
|
|
406
|
+
| `retries` | Retries column in the suite breakdown (`suiteBreakdown`) |
|
|
407
|
+
| `fullFailures` | Full failure cards instead of one-line summaries (`failureDeepDive`) |
|
|
408
|
+
| `stackTraces` | Include `<pre>` stack traces in the failure cards (`failureDeepDive`) |
|
|
409
|
+
|
|
410
|
+
#### Per-template defaults
|
|
411
|
+
|
|
412
|
+
Omit `sections` entirely and each template renders exactly this (✓ = on):
|
|
413
|
+
|
|
414
|
+
| Section | minimal | detailed | executive |
|
|
415
|
+
|---|:-:|:-:|:-:|
|
|
416
|
+
| `coverPage` | – | – | ✓ |
|
|
417
|
+
| `analysisOneliner` | – | – | ✓ |
|
|
418
|
+
| `releaseGate` | ✓ | ✓ | ✓ |
|
|
419
|
+
| `summary` | ✓ | ✓ | ✓ |
|
|
420
|
+
| `charts` | – | ✓ | ✓ |
|
|
421
|
+
| `trend` | – | ✓ | ✓ |
|
|
422
|
+
| `requirementsMatrix` | – | ✓ | – |
|
|
423
|
+
| `ciEnvironment` | ✓ | ✓ | ✓ |
|
|
424
|
+
| `suiteBreakdown` | ✓ | ✓ | – |
|
|
425
|
+
| `failureDeepDive` | ✓ | ✓ | ✓ |
|
|
426
|
+
| `failureAnalysis` | – | ✓ | – |
|
|
427
|
+
| `slowTests` | – | ✓ | ✓ |
|
|
428
|
+
| `defectLog` | – | ✓ | – |
|
|
429
|
+
| `passRate` | ✓ | ✓ | ✓ |
|
|
430
|
+
| `fullEnvironment` | – | ✓ | – |
|
|
431
|
+
| `retries` | ✓ | ✓ | – |
|
|
432
|
+
| `fullFailures` | ✓ | ✓ | – |
|
|
433
|
+
| `stackTraces` | ✓ | ✓ | – |
|
|
434
|
+
|
|
435
|
+
#### Dependencies & gotchas
|
|
436
|
+
|
|
437
|
+
- **`trend` needs `charts`.** The trend line, run-history, and flakiness table live inside the charts block, so `trend: true` does nothing with `charts: false` (it's coerced off). It also needs history data — keep the top-level `showTrend` option on.
|
|
438
|
+
- **Data-gated sections** — `failureAnalysis`, `requirementsMatrix`, `slowTests`, `defectLog` render only when there's matching data (clusters, tagged tests, timed tests, failures). Toggling them on with no data shows nothing.
|
|
439
|
+
- **Render-layer only.** `sections` controls what's drawn, never what's collected — the data switches stay separate: `showTrend` (history), `flakinessTopN` (flaky-table size), `failureAnalysis.enabled` (classifier), `includeScreenshots` (images).
|
|
440
|
+
- **Custom templates.** `sections` applies to the built-in templates; a custom `templatePath` controls its own layout (every section available) and ignores `sections` — you'll get a warning if both are set.
|
|
441
|
+
|
|
363
442
|
---
|
|
364
443
|
|
|
365
444
|
## CI/CD Integration
|
|
366
445
|
|
|
367
|
-
Full workflows for all four CI providers: [reportforge.org/docs
|
|
446
|
+
Full workflows for all four CI providers: [reportforge.org/docs/ci-cd](https://reportforge.org/docs/ci-cd).
|
|
368
447
|
|
|
369
448
|
### GitHub Actions
|
|
370
449
|
|
|
@@ -500,7 +579,7 @@ export RF_LICENSE_KEY=RFSU-XXXX-XXXX-XXXX-XXXX
|
|
|
500
579
|
npx playwright test
|
|
501
580
|
```
|
|
502
581
|
|
|
503
|
-
Full lifecycle + offline failure modes: [reportforge.org/docs
|
|
582
|
+
Full lifecycle + offline failure modes: [reportforge.org/docs/licensing](https://reportforge.org/docs/licensing).
|
|
504
583
|
|
|
505
584
|
---
|
|
506
585
|
|
|
@@ -532,7 +611,7 @@ Common issues:
|
|
|
532
611
|
- **`pdfPassword` ignored** → requires `qpdf` on `PATH` (`apt install qpdf` / `brew install qpdf` / `choco install qpdf`).
|
|
533
612
|
- **No PDF generated** → verify `RF_LICENSE_KEY` is set and your subscription is active. `RF_DEBUG=1` will show the activation result; missing key, expired subscription, network failure, or no JWT cache all skip PDF generation.
|
|
534
613
|
|
|
535
|
-
Full guide: [reportforge.org/docs
|
|
614
|
+
Full guide: [reportforge.org/docs/troubleshooting](https://reportforge.org/docs/troubleshooting).
|
|
536
615
|
|
|
537
616
|
---
|
|
538
617
|
|
|
@@ -540,14 +619,15 @@ Full guide: [reportforge.org/docs#troubleshooting](https://reportforge.org/docs#
|
|
|
540
619
|
|
|
541
620
|
Full documentation at [reportforge.org/docs](https://reportforge.org/docs):
|
|
542
621
|
|
|
543
|
-
- [Quick start](https://reportforge.org/docs
|
|
544
|
-
- [Chrome setup](https://reportforge.org/docs#chrome-setup) — Windows, Linux, macOS
|
|
545
|
-
- [CI/CD integration](https://reportforge.org/docs
|
|
546
|
-
- [Configuration reference](https://reportforge.org/docs
|
|
547
|
-
- [Filename tokens](https://reportforge.org/docs#tokens) — `{date}`, `{branch}`, `{status}`
|
|
548
|
-
- [Templates](https://reportforge.org/docs
|
|
549
|
-
- [
|
|
550
|
-
- [
|
|
622
|
+
- [Quick start](https://reportforge.org/docs/quickstart) — install, configure, activate
|
|
623
|
+
- [Chrome setup](https://reportforge.org/docs/running-tests#chrome-setup) — Windows, Linux, macOS
|
|
624
|
+
- [CI/CD integration](https://reportforge.org/docs/ci-cd) — GitHub Actions, GitLab CI, Bitbucket Pipelines, Jenkins, Azure DevOps
|
|
625
|
+
- [Configuration reference](https://reportforge.org/docs/configuration) — all options
|
|
626
|
+
- [Filename tokens](https://reportforge.org/docs/configuration#tokens) — `{date}`, `{branch}`, `{status}`
|
|
627
|
+
- [Templates](https://reportforge.org/docs/templates) — minimal, detailed, executive
|
|
628
|
+
- [Report Sections](https://reportforge.org/docs/configuration#report-sections) — add or remove sections per template
|
|
629
|
+
- [License & offline behaviour](https://reportforge.org/docs/licensing) — JWT cache, machine cap, cancellation
|
|
630
|
+
- [Troubleshooting](https://reportforge.org/docs/troubleshooting) — common issues and debug flags
|
|
551
631
|
- **Changelog** — [CHANGELOG.md](./CHANGELOG.md)
|
|
552
632
|
|
|
553
633
|
---
|
package/dist/demo/cli.js
CHANGED
|
@@ -6531,15 +6531,134 @@ function dominantCategory(clusters) {
|
|
|
6531
6531
|
return clusters[0]?.category ?? "unknown";
|
|
6532
6532
|
}
|
|
6533
6533
|
|
|
6534
|
+
// src/templates/sections.ts
|
|
6535
|
+
init_cjs_shims();
|
|
6536
|
+
var SECTION_KEYS = [
|
|
6537
|
+
// block toggles
|
|
6538
|
+
"coverPage",
|
|
6539
|
+
"analysisOneliner",
|
|
6540
|
+
"releaseGate",
|
|
6541
|
+
"summary",
|
|
6542
|
+
"charts",
|
|
6543
|
+
"trend",
|
|
6544
|
+
"requirementsMatrix",
|
|
6545
|
+
"ciEnvironment",
|
|
6546
|
+
"suiteBreakdown",
|
|
6547
|
+
"failureDeepDive",
|
|
6548
|
+
"failureAnalysis",
|
|
6549
|
+
"slowTests",
|
|
6550
|
+
"defectLog",
|
|
6551
|
+
// display modifiers
|
|
6552
|
+
"passRate",
|
|
6553
|
+
"fullEnvironment",
|
|
6554
|
+
"retries",
|
|
6555
|
+
"fullFailures",
|
|
6556
|
+
"stackTraces"
|
|
6557
|
+
];
|
|
6558
|
+
var SECTION_DEFAULTS = {
|
|
6559
|
+
minimal: {
|
|
6560
|
+
coverPage: false,
|
|
6561
|
+
analysisOneliner: false,
|
|
6562
|
+
releaseGate: true,
|
|
6563
|
+
summary: true,
|
|
6564
|
+
charts: false,
|
|
6565
|
+
trend: false,
|
|
6566
|
+
requirementsMatrix: false,
|
|
6567
|
+
ciEnvironment: true,
|
|
6568
|
+
suiteBreakdown: true,
|
|
6569
|
+
failureDeepDive: true,
|
|
6570
|
+
failureAnalysis: false,
|
|
6571
|
+
slowTests: false,
|
|
6572
|
+
defectLog: false,
|
|
6573
|
+
passRate: true,
|
|
6574
|
+
fullEnvironment: false,
|
|
6575
|
+
retries: true,
|
|
6576
|
+
fullFailures: true,
|
|
6577
|
+
stackTraces: true
|
|
6578
|
+
},
|
|
6579
|
+
detailed: {
|
|
6580
|
+
coverPage: false,
|
|
6581
|
+
analysisOneliner: false,
|
|
6582
|
+
releaseGate: true,
|
|
6583
|
+
summary: true,
|
|
6584
|
+
charts: true,
|
|
6585
|
+
trend: true,
|
|
6586
|
+
requirementsMatrix: true,
|
|
6587
|
+
ciEnvironment: true,
|
|
6588
|
+
suiteBreakdown: true,
|
|
6589
|
+
failureDeepDive: true,
|
|
6590
|
+
failureAnalysis: true,
|
|
6591
|
+
slowTests: true,
|
|
6592
|
+
defectLog: true,
|
|
6593
|
+
passRate: true,
|
|
6594
|
+
fullEnvironment: true,
|
|
6595
|
+
retries: true,
|
|
6596
|
+
fullFailures: true,
|
|
6597
|
+
stackTraces: true
|
|
6598
|
+
},
|
|
6599
|
+
executive: {
|
|
6600
|
+
coverPage: true,
|
|
6601
|
+
analysisOneliner: true,
|
|
6602
|
+
releaseGate: true,
|
|
6603
|
+
summary: true,
|
|
6604
|
+
charts: true,
|
|
6605
|
+
trend: true,
|
|
6606
|
+
requirementsMatrix: false,
|
|
6607
|
+
ciEnvironment: true,
|
|
6608
|
+
suiteBreakdown: false,
|
|
6609
|
+
failureDeepDive: true,
|
|
6610
|
+
failureAnalysis: false,
|
|
6611
|
+
slowTests: true,
|
|
6612
|
+
defectLog: false,
|
|
6613
|
+
passRate: true,
|
|
6614
|
+
fullEnvironment: false,
|
|
6615
|
+
retries: false,
|
|
6616
|
+
fullFailures: false,
|
|
6617
|
+
stackTraces: false
|
|
6618
|
+
}
|
|
6619
|
+
};
|
|
6620
|
+
var SECTION_KEY_SET = new Set(SECTION_KEYS);
|
|
6621
|
+
function resolveSections(template, userSections) {
|
|
6622
|
+
const base = SECTION_DEFAULTS[template];
|
|
6623
|
+
if (!userSections) return { ...base };
|
|
6624
|
+
const flat = {};
|
|
6625
|
+
for (const [k, v] of Object.entries(userSections)) {
|
|
6626
|
+
if (v !== void 0 && SECTION_KEY_SET.has(k)) flat[k] = v;
|
|
6627
|
+
}
|
|
6628
|
+
const perTemplateRaw = userSections[template] ?? {};
|
|
6629
|
+
const perTemplate = {};
|
|
6630
|
+
for (const [k, v] of Object.entries(perTemplateRaw)) {
|
|
6631
|
+
if (v !== void 0) perTemplate[k] = v;
|
|
6632
|
+
}
|
|
6633
|
+
const result = { ...base, ...flat, ...perTemplate };
|
|
6634
|
+
if (!result.charts) result.trend = false;
|
|
6635
|
+
return result;
|
|
6636
|
+
}
|
|
6637
|
+
function allSectionsOn() {
|
|
6638
|
+
return Object.fromEntries(SECTION_KEYS.map((k) => [k, true]));
|
|
6639
|
+
}
|
|
6640
|
+
|
|
6534
6641
|
// src/templates/engine.ts
|
|
6535
6642
|
function jsonForInlineScript(value) {
|
|
6536
6643
|
return JSON.stringify(value).replace(/<\//g, "<\\/");
|
|
6537
6644
|
}
|
|
6645
|
+
function deriveOptions(s) {
|
|
6646
|
+
return {
|
|
6647
|
+
showCharts: s.charts,
|
|
6648
|
+
showTrend: s.trend,
|
|
6649
|
+
showStackTraces: s.stackTraces,
|
|
6650
|
+
showFullEnvironment: s.fullEnvironment,
|
|
6651
|
+
showPassRate: s.passRate,
|
|
6652
|
+
showRetries: s.retries,
|
|
6653
|
+
showFullFailures: s.fullFailures
|
|
6654
|
+
};
|
|
6655
|
+
}
|
|
6538
6656
|
var TemplateEngine = class {
|
|
6539
6657
|
constructor() {
|
|
6540
6658
|
this.chartjsInline = "";
|
|
6541
6659
|
this.fontsBundleCss = "";
|
|
6542
6660
|
this.customTemplateCache = /* @__PURE__ */ new Map();
|
|
6661
|
+
this.styleCache = /* @__PURE__ */ new Map();
|
|
6543
6662
|
this.handlebars = import_handlebars.default.create();
|
|
6544
6663
|
this.templatesDir = import_path.default.resolve(__dirname, "templates");
|
|
6545
6664
|
if (!import_fs.default.existsSync(this.templatesDir)) {
|
|
@@ -6559,14 +6678,14 @@ var TemplateEngine = class {
|
|
|
6559
6678
|
setChartjsBundle(inline) {
|
|
6560
6679
|
this.chartjsInline = inline;
|
|
6561
6680
|
}
|
|
6562
|
-
render(data, template) {
|
|
6681
|
+
render(data, template, userSections) {
|
|
6563
6682
|
const layoutPath = import_path.default.join(this.templatesDir, "layouts", `${template}.hbs`);
|
|
6564
6683
|
if (!import_fs.default.existsSync(layoutPath)) {
|
|
6565
6684
|
throw new Error(`[reportforge] Template layout not found: ${layoutPath}`);
|
|
6566
6685
|
}
|
|
6567
6686
|
const layoutSource = import_fs.default.readFileSync(layoutPath, "utf8");
|
|
6568
6687
|
const compiledLayout = this.handlebars.compile(layoutSource);
|
|
6569
|
-
const ctx = this.buildContext(data, template);
|
|
6688
|
+
const ctx = this.buildContext(data, template, userSections);
|
|
6570
6689
|
return compiledLayout(ctx);
|
|
6571
6690
|
}
|
|
6572
6691
|
renderFromPath(data, filePath) {
|
|
@@ -6581,17 +6700,11 @@ var TemplateEngine = class {
|
|
|
6581
6700
|
return compiledFn(this.buildCustomContext(data));
|
|
6582
6701
|
}
|
|
6583
6702
|
buildCustomContext(data) {
|
|
6703
|
+
const sections = allSectionsOn();
|
|
6584
6704
|
return {
|
|
6585
6705
|
...data,
|
|
6586
|
-
options:
|
|
6587
|
-
|
|
6588
|
-
showTrend: true,
|
|
6589
|
-
showStackTraces: true,
|
|
6590
|
-
showFullEnvironment: true,
|
|
6591
|
-
showPassRate: true,
|
|
6592
|
-
showRetries: true,
|
|
6593
|
-
showFullFailures: true
|
|
6594
|
-
},
|
|
6706
|
+
options: deriveOptions(sections),
|
|
6707
|
+
sections,
|
|
6595
6708
|
tagGroups: this.buildTagGroups(data),
|
|
6596
6709
|
slowTests: this.buildSlowTests(data),
|
|
6597
6710
|
baseCSS: this.fontsBundleCss + "\n" + this.readStyle("base.css"),
|
|
@@ -6599,47 +6712,20 @@ var TemplateEngine = class {
|
|
|
6599
6712
|
chartjsScript: this.buildChartjsScript()
|
|
6600
6713
|
};
|
|
6601
6714
|
}
|
|
6602
|
-
buildContext(data, template) {
|
|
6715
|
+
buildContext(data, template, userSections) {
|
|
6603
6716
|
const baseCSS = this.fontsBundleCss + "\n" + this.readStyle("base.css");
|
|
6604
6717
|
const templateCSS = this.readStyle(`${template}.css`);
|
|
6605
|
-
const
|
|
6606
|
-
|
|
6607
|
-
showCharts: false,
|
|
6608
|
-
showTrend: false,
|
|
6609
|
-
showStackTraces: true,
|
|
6610
|
-
showFullEnvironment: false,
|
|
6611
|
-
showPassRate: true,
|
|
6612
|
-
showRetries: true,
|
|
6613
|
-
showFullFailures: true
|
|
6614
|
-
},
|
|
6615
|
-
detailed: {
|
|
6616
|
-
showCharts: true,
|
|
6617
|
-
showTrend: true,
|
|
6618
|
-
showStackTraces: true,
|
|
6619
|
-
showFullEnvironment: true,
|
|
6620
|
-
showPassRate: true,
|
|
6621
|
-
showRetries: true,
|
|
6622
|
-
showFullFailures: true
|
|
6623
|
-
},
|
|
6624
|
-
executive: {
|
|
6625
|
-
showCharts: true,
|
|
6626
|
-
showTrend: true,
|
|
6627
|
-
showStackTraces: false,
|
|
6628
|
-
showFullEnvironment: false,
|
|
6629
|
-
showPassRate: true,
|
|
6630
|
-
showRetries: false,
|
|
6631
|
-
showFullFailures: false
|
|
6632
|
-
}
|
|
6633
|
-
};
|
|
6634
|
-
const chartjsScript = this.buildChartjsScript();
|
|
6718
|
+
const sections = resolveSections(template, userSections);
|
|
6719
|
+
const options = deriveOptions(sections);
|
|
6635
6720
|
return {
|
|
6636
6721
|
...data,
|
|
6637
|
-
options
|
|
6722
|
+
options,
|
|
6723
|
+
sections,
|
|
6638
6724
|
tagGroups: this.buildTagGroups(data),
|
|
6639
6725
|
slowTests: this.buildSlowTests(data),
|
|
6640
6726
|
baseCSS,
|
|
6641
6727
|
templateCSS,
|
|
6642
|
-
chartjsScript
|
|
6728
|
+
chartjsScript: this.buildChartjsScript()
|
|
6643
6729
|
};
|
|
6644
6730
|
}
|
|
6645
6731
|
buildChartjsScript() {
|
|
@@ -6687,9 +6773,12 @@ var TemplateEngine = class {
|
|
|
6687
6773
|
return tests.sort((a, b) => b.durationMs - a.durationMs).slice(0, 10);
|
|
6688
6774
|
}
|
|
6689
6775
|
readStyle(filename) {
|
|
6776
|
+
const cached = this.styleCache.get(filename);
|
|
6777
|
+
if (cached !== void 0) return cached;
|
|
6690
6778
|
const stylePath = import_path.default.join(this.templatesDir, "styles", filename);
|
|
6691
|
-
|
|
6692
|
-
|
|
6779
|
+
const css = import_fs.default.existsSync(stylePath) ? import_fs.default.readFileSync(stylePath, "utf8") : "";
|
|
6780
|
+
this.styleCache.set(filename, css);
|
|
6781
|
+
return css;
|
|
6693
6782
|
}
|
|
6694
6783
|
registerPartials() {
|
|
6695
6784
|
const partialsDir = import_path.default.join(this.templatesDir, "partials");
|
|
@@ -7274,6 +7363,12 @@ var PdfGenerator = class {
|
|
|
7274
7363
|
`Compression: level=${compression.effective} jpeg-q=${compression.quality} max-w=${compression.maxWidth}px inline-failures\u2264${compression.maxInlineFailures}`
|
|
7275
7364
|
);
|
|
7276
7365
|
const outputPaths = [];
|
|
7366
|
+
if (options.sections !== void 0) {
|
|
7367
|
+
const hasCustom = sources.some((s) => s.kind !== "builtin");
|
|
7368
|
+
if (hasCustom) {
|
|
7369
|
+
logger.warn('[reportforge] "sections" applies to built-in templates only; it is ignored for custom templatePath templates.');
|
|
7370
|
+
}
|
|
7371
|
+
}
|
|
7277
7372
|
const { page } = await this.browserManager.launch(options.puppeteerExecutablePath);
|
|
7278
7373
|
try {
|
|
7279
7374
|
for (const source of sources) {
|
|
@@ -7381,13 +7476,13 @@ var PdfGenerator = class {
|
|
|
7381
7476
|
}
|
|
7382
7477
|
const templateLabel = source.kind === "builtin" ? source.id : source.label;
|
|
7383
7478
|
logger.info(`Rendering template: ${templateLabel}`);
|
|
7384
|
-
const html = source.kind === "builtin" ? this.templateEngine.render(renderData, source.id) : this.templateEngine.renderFromPath(renderData, source.absolutePath);
|
|
7479
|
+
const html = source.kind === "builtin" ? this.templateEngine.render(renderData, source.id, options.sections) : this.templateEngine.renderFromPath(renderData, source.absolutePath);
|
|
7385
7480
|
const tempHtmlPath = await this.htmlWriter.write(html);
|
|
7386
7481
|
const fileUrl = this.htmlWriter.toFileUrl(tempHtmlPath);
|
|
7387
7482
|
try {
|
|
7388
7483
|
logger.info(`Navigating to temp HTML (${Math.round(html.length / 1024)} KB)...`);
|
|
7389
7484
|
await page.goto(fileUrl, { waitUntil: "domcontentloaded", timeout: 6e4 });
|
|
7390
|
-
const showCharts = source.kind === "builtin" ? source.id
|
|
7485
|
+
const showCharts = source.kind === "builtin" ? resolveSections(source.id, options.sections).charts : true;
|
|
7391
7486
|
await this.chartRenderer.waitForCharts(page, showCharts);
|
|
7392
7487
|
logger.info(`Generating PDF \u2192 ${import_path6.default.relative(process.cwd(), outputPath)}`);
|
|
7393
7488
|
await page.pdf({
|
|
@@ -11516,6 +11611,15 @@ var failureAnalysisConfigSchema = external_exports.object({
|
|
|
11516
11611
|
collectUnclassified: external_exports.boolean().default(true),
|
|
11517
11612
|
autoUpdateModel: external_exports.boolean().default(true)
|
|
11518
11613
|
}).default({});
|
|
11614
|
+
var sectionFlagsShape = Object.fromEntries(
|
|
11615
|
+
SECTION_KEYS.map((k) => [k, external_exports.boolean().optional()])
|
|
11616
|
+
);
|
|
11617
|
+
var sectionFlagsSchema = external_exports.object(sectionFlagsShape).strict();
|
|
11618
|
+
var sectionsSchema = sectionFlagsSchema.extend({
|
|
11619
|
+
minimal: sectionFlagsSchema.optional(),
|
|
11620
|
+
detailed: sectionFlagsSchema.optional(),
|
|
11621
|
+
executive: sectionFlagsSchema.optional()
|
|
11622
|
+
}).strict();
|
|
11519
11623
|
var ReporterOptionsSchema = external_exports.object({
|
|
11520
11624
|
outputFile: external_exports.string().optional().default(DEFAULT_OPTIONS.outputFile),
|
|
11521
11625
|
template: external_exports.union([
|
|
@@ -11553,7 +11657,8 @@ var ReporterOptionsSchema = external_exports.object({
|
|
|
11553
11657
|
).min(1, "templatePath array must not be empty")
|
|
11554
11658
|
]).optional().transform(
|
|
11555
11659
|
(v) => v === void 0 ? void 0 : Array.isArray(v) ? v : [v]
|
|
11556
|
-
)
|
|
11660
|
+
),
|
|
11661
|
+
sections: sectionsSchema.optional()
|
|
11557
11662
|
});
|
|
11558
11663
|
function parseOptions(raw) {
|
|
11559
11664
|
const result = ReporterOptionsSchema.safeParse(raw ?? {});
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { Reporter, FullConfig, Suite, TestCase, TestResult, FullResult } from '@playwright/test/reporter';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Canonical section catalog. 13 block toggles (section present/absent) followed
|
|
5
|
+
* by 5 display modifiers (tweak a section that is on). Single source of truth —
|
|
6
|
+
* the Zod schema in src/config/schema.ts builds its flag shape from this array.
|
|
7
|
+
*/
|
|
8
|
+
declare const SECTION_KEYS: readonly ["coverPage", "analysisOneliner", "releaseGate", "summary", "charts", "trend", "requirementsMatrix", "ciEnvironment", "suiteBreakdown", "failureDeepDive", "failureAnalysis", "slowTests", "defectLog", "passRate", "fullEnvironment", "retries", "fullFailures", "stackTraces"];
|
|
9
|
+
type SectionKey = (typeof SECTION_KEYS)[number];
|
|
10
|
+
/** A partial map of section flags — used both as the flat baseline and per-template. */
|
|
11
|
+
type SectionFlags = Partial<Record<SectionKey, boolean>>;
|
|
12
|
+
/** The public `sections` option shape: flat flags + optional per-template overrides. */
|
|
13
|
+
type SectionsOption = SectionFlags & {
|
|
14
|
+
minimal?: SectionFlags;
|
|
15
|
+
detailed?: SectionFlags;
|
|
16
|
+
executive?: SectionFlags;
|
|
17
|
+
};
|
|
18
|
+
|
|
3
19
|
declare const TEMPLATE_IDS: readonly ["minimal", "detailed", "executive"];
|
|
4
20
|
type TemplateId = (typeof TEMPLATE_IDS)[number];
|
|
5
21
|
/**
|
|
@@ -230,6 +246,30 @@ interface ReporterOptions {
|
|
|
230
246
|
* templatePath: ['./templates/exec.hbs', './templates/eng.hbs']
|
|
231
247
|
*/
|
|
232
248
|
templatePath?: string | string[];
|
|
249
|
+
/**
|
|
250
|
+
* Per-section override of which report sections appear, on top of the chosen
|
|
251
|
+
* template's defaults. Both-layered: flat keys are the baseline applied to
|
|
252
|
+
* every chosen template; per-template keys (`minimal` / `detailed` /
|
|
253
|
+
* `executive`) override the baseline for that template only.
|
|
254
|
+
*
|
|
255
|
+
* Block toggles: `coverPage`, `analysisOneliner`, `releaseGate`, `summary`,
|
|
256
|
+
* `charts`, `trend`, `requirementsMatrix`, `ciEnvironment`, `suiteBreakdown`,
|
|
257
|
+
* `failureDeepDive`, `failureAnalysis`, `slowTests`, `defectLog`.
|
|
258
|
+
* Display modifiers: `passRate`, `fullEnvironment`, `retries`, `fullFailures`,
|
|
259
|
+
* `stackTraces`.
|
|
260
|
+
*
|
|
261
|
+
* `trend` renders only when `charts` is also on and history data exists (`showTrend` enabled). Sections gated on data
|
|
262
|
+
* (`failureAnalysis`, `requirementsMatrix`, `slowTests`, `defectLog`) appear
|
|
263
|
+
* only when matching data exists — this option never changes data collection.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* sections: {
|
|
267
|
+
* defectLog: false, // baseline for all chosen templates
|
|
268
|
+
* minimal: { charts: true }, // add charts to the minimal report
|
|
269
|
+
* detailed: { ciEnvironment: false },
|
|
270
|
+
* }
|
|
271
|
+
*/
|
|
272
|
+
sections?: SectionsOption;
|
|
233
273
|
}
|
|
234
274
|
|
|
235
275
|
/**
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
import { Reporter, FullConfig, Suite, TestCase, TestResult, FullResult } from '@playwright/test/reporter';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Canonical section catalog. 13 block toggles (section present/absent) followed
|
|
5
|
+
* by 5 display modifiers (tweak a section that is on). Single source of truth —
|
|
6
|
+
* the Zod schema in src/config/schema.ts builds its flag shape from this array.
|
|
7
|
+
*/
|
|
8
|
+
declare const SECTION_KEYS: readonly ["coverPage", "analysisOneliner", "releaseGate", "summary", "charts", "trend", "requirementsMatrix", "ciEnvironment", "suiteBreakdown", "failureDeepDive", "failureAnalysis", "slowTests", "defectLog", "passRate", "fullEnvironment", "retries", "fullFailures", "stackTraces"];
|
|
9
|
+
type SectionKey = (typeof SECTION_KEYS)[number];
|
|
10
|
+
/** A partial map of section flags — used both as the flat baseline and per-template. */
|
|
11
|
+
type SectionFlags = Partial<Record<SectionKey, boolean>>;
|
|
12
|
+
/** The public `sections` option shape: flat flags + optional per-template overrides. */
|
|
13
|
+
type SectionsOption = SectionFlags & {
|
|
14
|
+
minimal?: SectionFlags;
|
|
15
|
+
detailed?: SectionFlags;
|
|
16
|
+
executive?: SectionFlags;
|
|
17
|
+
};
|
|
18
|
+
|
|
3
19
|
declare const TEMPLATE_IDS: readonly ["minimal", "detailed", "executive"];
|
|
4
20
|
type TemplateId = (typeof TEMPLATE_IDS)[number];
|
|
5
21
|
/**
|
|
@@ -230,6 +246,30 @@ interface ReporterOptions {
|
|
|
230
246
|
* templatePath: ['./templates/exec.hbs', './templates/eng.hbs']
|
|
231
247
|
*/
|
|
232
248
|
templatePath?: string | string[];
|
|
249
|
+
/**
|
|
250
|
+
* Per-section override of which report sections appear, on top of the chosen
|
|
251
|
+
* template's defaults. Both-layered: flat keys are the baseline applied to
|
|
252
|
+
* every chosen template; per-template keys (`minimal` / `detailed` /
|
|
253
|
+
* `executive`) override the baseline for that template only.
|
|
254
|
+
*
|
|
255
|
+
* Block toggles: `coverPage`, `analysisOneliner`, `releaseGate`, `summary`,
|
|
256
|
+
* `charts`, `trend`, `requirementsMatrix`, `ciEnvironment`, `suiteBreakdown`,
|
|
257
|
+
* `failureDeepDive`, `failureAnalysis`, `slowTests`, `defectLog`.
|
|
258
|
+
* Display modifiers: `passRate`, `fullEnvironment`, `retries`, `fullFailures`,
|
|
259
|
+
* `stackTraces`.
|
|
260
|
+
*
|
|
261
|
+
* `trend` renders only when `charts` is also on and history data exists (`showTrend` enabled). Sections gated on data
|
|
262
|
+
* (`failureAnalysis`, `requirementsMatrix`, `slowTests`, `defectLog`) appear
|
|
263
|
+
* only when matching data exists — this option never changes data collection.
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* sections: {
|
|
267
|
+
* defectLog: false, // baseline for all chosen templates
|
|
268
|
+
* minimal: { charts: true }, // add charts to the minimal report
|
|
269
|
+
* detailed: { ciEnvironment: false },
|
|
270
|
+
* }
|
|
271
|
+
*/
|
|
272
|
+
sections?: SectionsOption;
|
|
233
273
|
}
|
|
234
274
|
|
|
235
275
|
/**
|