@reportforge/playwright-pdf 0.28.0 → 0.30.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.
@@ -26,6 +26,8 @@
26
26
 
27
27
  {{#if sections.runDiff}}{{> run-diff variant='full'}}{{/if}}
28
28
 
29
+ {{#if sections.browserMatrix}}{{> browser-matrix}}{{/if}}
30
+
29
31
  {{#if sections.requirementsMatrix}}{{> requirements-matrix }}{{/if}}
30
32
 
31
33
  {{#if sections.ciEnvironment}}{{> ci-environment options=(obj showFullEnvironment=sections.fullEnvironment) }}{{/if}}
@@ -26,6 +26,8 @@
26
26
 
27
27
  {{#if sections.charts}}{{> charts options=(obj showCharts=true showTrend=sections.trend heroTrend=true) }}{{/if}}
28
28
 
29
+ {{#if sections.browserMatrix}}{{> browser-matrix}}{{/if}}
30
+
29
31
  {{#if sections.requirementsMatrix}}{{> requirements-matrix }}{{/if}}
30
32
 
31
33
  {{#if sections.defectLog}}{{> defect-log }}{{/if}}
@@ -26,6 +26,8 @@
26
26
 
27
27
  {{#if sections.charts}}{{> charts options=(obj showCharts=true showTrend=sections.trend) }}{{/if}}
28
28
 
29
+ {{#if sections.browserMatrix}}{{> browser-matrix}}{{/if}}
30
+
29
31
  {{#if sections.requirementsMatrix}}{{> requirements-matrix }}{{/if}}
30
32
 
31
33
  {{#if sections.suiteBreakdown}}{{> suite-breakdown options=(obj showRetries=sections.retries showFullFailures=sections.fullFailures showStackTraces=sections.stackTraces) }}{{/if}}
@@ -0,0 +1,34 @@
1
+ {{#if browserMatrix}}
2
+ <div class="section section--browser-matrix">
3
+ <h2>Cross-Browser Comparison</h2>
4
+ {{#if browserMatrix.rows.length}}
5
+ <p class="browser-matrix__intro">{{browserMatrix.divergentCount}} {{pluralise browserMatrix.divergentCount "test behaves" "tests behave"}} differently across the run's {{browserMatrix.projects.length}} projects.{{#if browserMatrix.hasFailures}} Failures first.{{/if}}</p>
6
+ <table class="browser-matrix__table">
7
+ <thead>
8
+ <tr>
9
+ <th>Test</th>
10
+ {{#each browserMatrix.projects}}<th class="browser-matrix__col">{{this}}</th>{{/each}}
11
+ </tr>
12
+ </thead>
13
+ <tbody>
14
+ {{#each browserMatrix.rows}}
15
+ <tr class="no-break">
16
+ <td class="browser-matrix__title">{{title}}</td>
17
+ {{#each statuses}}
18
+ <td class="browser-matrix__cell">{{#if this}}{{statusBadge this}}{{else}}<span class="browser-matrix__na">n/a</span>{{/if}}</td>
19
+ {{/each}}
20
+ </tr>
21
+ {{/each}}
22
+ </tbody>
23
+ </table>
24
+ {{#if browserMatrix.truncated}}
25
+ <div class="browser-matrix__note">+ {{subtract browserMatrix.divergentCount browserMatrix.rows.length}} more divergent {{pluralise (subtract browserMatrix.divergentCount browserMatrix.rows.length) "test" "tests"}} not shown</div>
26
+ {{/if}}
27
+ {{#if browserMatrix.identicalCount}}
28
+ <div class="browser-matrix__note">{{browserMatrix.identicalCount}} {{pluralise browserMatrix.identicalCount "test behaves" "tests behave"}} the same in every project.</div>
29
+ {{/if}}
30
+ {{else}}
31
+ <p class="browser-matrix__intro">All {{browserMatrix.identicalCount}} comparable {{pluralise browserMatrix.identicalCount "test behaves" "tests behave"}} the same across the run's {{browserMatrix.projects.length}} projects.</p>
32
+ {{/if}}
33
+ </div>
34
+ {{/if}}
@@ -13,21 +13,29 @@
13
13
  <div class="flakiness-card__header">
14
14
  <span class="flakiness-card__title">Top flaky tests &mdash; last {{charts.flakinessTable.[0].totalRuns}} runs</span>
15
15
  </div>
16
+ {{#if charts.flakinessQuarantineTotal}}
17
+ <div class="flakiness-quarantine-callout">
18
+ <strong>{{charts.flakinessQuarantineTotal}} {{pluralise charts.flakinessQuarantineTotal "test exceeds" "tests exceed"}} the {{#if charts.flakinessQuarantineThreshold}}{{charts.flakinessQuarantineThreshold}}% {{/if}}quarantine threshold.</strong>
19
+ Quarantine or stabilize {{pluralise charts.flakinessQuarantineTotal "it before it masks" "them before they mask"}} real regressions.
20
+ </div>
21
+ {{/if}}
16
22
  <table class="flakiness-table">
17
23
  <thead>
18
24
  <tr>
19
25
  <th class="flakiness-table__test">Test</th>
20
26
  <th class="flakiness-table__rate">Flake rate</th>
21
27
  <th class="flakiness-table__count">Runs flaky</th>
28
+ <th class="flakiness-table__streak">Streak</th>
22
29
  <th class="flakiness-table__spark">History</th>
23
30
  </tr>
24
31
  </thead>
25
32
  <tbody>
26
33
  {{#each charts.flakinessTable}}
27
34
  <tr>
28
- <td class="flakiness-table__test" title="{{this.title}}">{{this.title}}</td>
35
+ <td class="flakiness-table__test" title="{{this.title}}"><span class="flakiness-table__test-inner"><span class="flakiness-table__test-title">{{this.title}}</span>{{#if this.quarantine}}<span class="quarantine-chip">QUARANTINE</span>{{/if}}</span></td>
29
36
  <td class="flakiness-table__rate">{{this.flakeRate}}%</td>
30
37
  <td class="flakiness-table__count">{{this.flakeCount}} / {{this.totalRuns}}</td>
38
+ <td class="flakiness-table__streak">{{#if this.streak}}{{this.streak}}&times;{{else}}&ndash;{{/if}}</td>
31
39
  <td class="flakiness-table__spark">
32
40
  <span class="flaky-spark">
33
41
  {{#each this.sparkline}}
@@ -64,8 +64,8 @@
64
64
 
65
65
  {{#if execution.evidence}}
66
66
  <div class="evidence-links">
67
- {{#if execution.evidence.tracePath}}<span class="evidence-link"><span class="evidence-link__tag">Trace</span><span class="evidence-link__path">{{execution.evidence.tracePath}}</span></span>{{/if}}
68
- {{#if execution.evidence.videoPath}}<span class="evidence-link"><span class="evidence-link__tag">Video</span><span class="evidence-link__path">{{execution.evidence.videoPath}}</span></span>{{/if}}
67
+ {{#if execution.evidence.tracePath}}<span class="evidence-link"><span class="evidence-link__tag">Trace</span>{{#if execution.evidence.traceUrl}}<a class="evidence-link__path evidence-link__path--url" href="{{execution.evidence.traceUrl}}">{{execution.evidence.traceUrl}}</a>{{#if execution.evidence.traceQr}}<img class="evidence-link__qr" src="{{execution.evidence.traceQr}}" alt="Trace QR" />{{/if}}{{else}}<span class="evidence-link__path">{{execution.evidence.tracePath}}</span>{{/if}}</span>{{/if}}
68
+ {{#if execution.evidence.videoPath}}<span class="evidence-link"><span class="evidence-link__tag">Video</span>{{#if execution.evidence.videoUrl}}<a class="evidence-link__path evidence-link__path--url" href="{{execution.evidence.videoUrl}}">{{execution.evidence.videoUrl}}</a>{{#if execution.evidence.videoQr}}<img class="evidence-link__qr" src="{{execution.evidence.videoQr}}" alt="Video QR" />{{/if}}{{else}}<span class="evidence-link__path">{{execution.evidence.videoPath}}</span>{{/if}}</span>{{/if}}
69
69
  </div>
70
70
  {{/if}}
71
71
  </div>
@@ -3,7 +3,7 @@
3
3
  <h2>Since Last Run</h2>
4
4
  <div class="run-diff__head">
5
5
  <div class="run-diff__strip">
6
- <span class="run-diff__count run-diff__count--new"><strong>{{runDiff.newFailures.length}}</strong> {{pluralise runDiff.newFailures.length "new failure" "new failures"}}</span>&nbsp;&middot;&nbsp;<span class="run-diff__count run-diff__count--fixed"><strong>{{runDiff.fixed.length}}</strong> fixed</span>&nbsp;&middot;&nbsp;<span class="run-diff__count run-diff__count--still"><strong>{{runDiff.stillFailing.length}}</strong> still failing</span>{{#if runDiff.noLongerRun.length}}&nbsp;&middot;&nbsp;<span class="run-diff__count run-diff__count--neutral"><strong>{{runDiff.noLongerRun.length}}</strong> no longer run</span>{{/if}}
6
+ <span class="run-diff__count run-diff__count--new"><strong>{{runDiff.newFailures.length}}</strong> {{pluralise runDiff.newFailures.length "new failure" "new failures"}}</span>&nbsp;&middot;&nbsp;<span class="run-diff__count run-diff__count--fixed"><strong>{{runDiff.fixed.length}}</strong> fixed</span>&nbsp;&middot;&nbsp;<span class="run-diff__count run-diff__count--still"><strong>{{runDiff.stillFailing.length}}</strong> still failing</span>{{#if runDiff.newlyFlaky.length}}&nbsp;&middot;&nbsp;<span class="run-diff__count run-diff__count--flaky"><strong>{{runDiff.newlyFlaky.length}}</strong> newly flaky</span>{{/if}}{{#if runDiff.noLongerRun.length}}&nbsp;&middot;&nbsp;<span class="run-diff__count run-diff__count--neutral"><strong>{{runDiff.noLongerRun.length}}</strong> no longer run</span>{{/if}}
7
7
  </div>
8
8
  <div class="run-diff__baseline">vs. previous run on {{formatDate runDiff.baselineTimestamp}}</div>
9
9
  {{#if runDiff.baselineTruncated}}
@@ -13,6 +13,7 @@
13
13
 
14
14
  {{#if (eq variant 'full')}}
15
15
  {{> run-diff-list items=runDiff.newFailures label="New Failures" }}
16
+ {{> run-diff-list items=runDiff.newlyFlaky label="Newly Flaky" }}
16
17
  {{> run-diff-list items=runDiff.fixed label="Fixed" }}
17
18
  {{> run-diff-list items=runDiff.stillFailing label="Still Failing" }}
18
19
  {{#if runDiff.noLongerRun.length}}
@@ -954,3 +954,58 @@ code {
954
954
  /* The aggregate failure-analysis cluster section was retired — root causes now
955
955
  render as inline per-test chips in the suite breakdown (.root-cause-chip above).
956
956
  The .analysis-oneliner one-liner lives further up and is still used by executive. */
957
+
958
+ /* ── Cross-browser comparison matrix (sections.browserMatrix) ───────────── */
959
+ /* Divergent tests only; identical tests collapse into the note line. Cells
960
+ reuse the .badge--* status palette so the grid reads like the rest of the
961
+ report. Lives here (not a template css) because the section is toggle-able
962
+ across all three layouts. */
963
+ .browser-matrix__intro {
964
+ font-size: 10px; color: var(--text-3); margin-bottom: 12px;
965
+ }
966
+ .browser-matrix__table th.browser-matrix__col {
967
+ width: 88px; text-align: center; text-transform: capitalize;
968
+ }
969
+ .browser-matrix__title {
970
+ font-size: 9.5px; color: var(--text-1); word-break: break-word;
971
+ }
972
+ .browser-matrix__cell { text-align: center; }
973
+ .browser-matrix__cell .badge { min-width: 0; font-size: 7.5px; }
974
+ .browser-matrix__na {
975
+ font-family: 'JetBrains Mono', monospace;
976
+ font-size: 8.5px; color: var(--text-3);
977
+ }
978
+ .browser-matrix__note {
979
+ font-size: 9px; color: var(--text-3); margin-top: 6px;
980
+ }
981
+
982
+ /* ── Flaky intelligence (quarantine + streak) ───────────────────────────── */
983
+ /* The title cell ellipsizes; the chip must never be the part that clips, so
984
+ the cell content is a flex row: shrinking title span + fixed chip. */
985
+ .flakiness-table__test-inner { display: flex; align-items: center; gap: 5px; min-width: 0; }
986
+ .flakiness-table__test-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
987
+ .flakiness-quarantine-callout {
988
+ font-size: 9.5px; color: var(--text-2);
989
+ background: var(--flaky-dim);
990
+ border: 1px solid var(--flaky);
991
+ border-radius: 6px; padding: 7px 10px; margin-bottom: 10px;
992
+ }
993
+ .quarantine-chip {
994
+ flex: none;
995
+ font-family: 'Inter', system-ui, sans-serif; font-weight: 700;
996
+ font-size: 6.5px; letter-spacing: 0.7px;
997
+ color: var(--flaky); border: 1px solid var(--flaky);
998
+ border-radius: 3px; padding: 1px 4px;
999
+ }
1000
+ .flakiness-table__streak { text-align: center; width: 52px; }
1001
+ .run-diff__count--flaky strong { color: var(--flaky); }
1002
+
1003
+ /* ── Evidence deep links + QR (evidenceUrlTemplate) ─────────────────────── */
1004
+ .evidence-link__path--url {
1005
+ color: var(--accent); text-decoration: none;
1006
+ word-break: break-all;
1007
+ }
1008
+ .evidence-link__qr {
1009
+ width: 44px; height: 44px; margin-left: 8px; vertical-align: middle;
1010
+ border: 1px solid var(--border); border-radius: 3px; background: #fff;
1011
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reportforge/playwright-pdf",
3
- "version": "0.28.0",
3
+ "version": "0.30.0",
4
4
  "description": "Playwright Test reporter that generates designed PDF reports: minimal, detailed, and executive templates with CI/CD integrations",
5
5
  "license": "Elastic-2.0",
6
6
  "author": "ReportForge",
@@ -91,6 +91,7 @@
91
91
  "chrome-finder": "^1.0.7",
92
92
  "glob": "^13.0.6",
93
93
  "handlebars": "^4.7.8",
94
+ "qrcode-generator": "^2.0.4",
94
95
  "sharp": "^0.35.3",
95
96
  "zod": "^3.23.8"
96
97
  },