@reportforge/playwright-pdf 0.6.1 → 0.6.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/index.js CHANGED
@@ -12030,6 +12030,9 @@ function formatDuration(ms) {
12030
12030
  function jsonForInlineScript(value) {
12031
12031
  return JSON.stringify(value).replace(/<\//g, "<\\/");
12032
12032
  }
12033
+ function isUnknownValue(v) {
12034
+ return !v || v === "unknown";
12035
+ }
12033
12036
  function deriveOptions(s) {
12034
12037
  return {
12035
12038
  showCharts: s.charts,
@@ -12250,6 +12253,9 @@ var TemplateEngine = class {
12250
12253
  );
12251
12254
  this.handlebars.registerHelper("lt", (a, b) => a < b);
12252
12255
  this.handlebars.registerHelper("or", (a, b) => Boolean(a) || Boolean(b));
12256
+ this.handlebars.registerHelper("and", (a, b) => Boolean(a) && Boolean(b));
12257
+ this.handlebars.registerHelper("isUnknown", (v) => isUnknownValue(v));
12258
+ this.handlebars.registerHelper("gitVal", (v) => isUnknownValue(v) ? "n/a" : v);
12253
12259
  this.handlebars.registerHelper("categoryLabel", (cat) => CATEGORY_LABEL[cat] ?? cat);
12254
12260
  this.handlebars.registerHelper("strengthLabel", (s) => s ? s.charAt(0).toUpperCase() + s.slice(1) : "");
12255
12261
  this.handlebars.registerHelper("pluralise", (n, one, many) => n === 1 ? one : many);
@@ -13231,7 +13237,7 @@ var PdfReporter = class {
13231
13237
  this.dataCollector = new DataCollector();
13232
13238
  this.pdfGenerator = new PdfGenerator();
13233
13239
  this.options = parseOptions(rawOptions);
13234
- const version = true ? "0.6.1" : "0.x";
13240
+ const version = true ? "0.6.3" : "0.x";
13235
13241
  logger.info(`@reportforge/playwright-pdf v${version} initialised`);
13236
13242
  this.licenseClient = new LicenseClient({
13237
13243
  licenseKey: this.options.licenseKey,
package/dist/index.mjs CHANGED
@@ -12031,6 +12031,9 @@ function formatDuration(ms) {
12031
12031
  function jsonForInlineScript(value) {
12032
12032
  return JSON.stringify(value).replace(/<\//g, "<\\/");
12033
12033
  }
12034
+ function isUnknownValue(v) {
12035
+ return !v || v === "unknown";
12036
+ }
12034
12037
  function deriveOptions(s) {
12035
12038
  return {
12036
12039
  showCharts: s.charts,
@@ -12251,6 +12254,9 @@ var TemplateEngine = class {
12251
12254
  );
12252
12255
  this.handlebars.registerHelper("lt", (a, b) => a < b);
12253
12256
  this.handlebars.registerHelper("or", (a, b) => Boolean(a) || Boolean(b));
12257
+ this.handlebars.registerHelper("and", (a, b) => Boolean(a) && Boolean(b));
12258
+ this.handlebars.registerHelper("isUnknown", (v) => isUnknownValue(v));
12259
+ this.handlebars.registerHelper("gitVal", (v) => isUnknownValue(v) ? "n/a" : v);
12254
12260
  this.handlebars.registerHelper("categoryLabel", (cat) => CATEGORY_LABEL[cat] ?? cat);
12255
12261
  this.handlebars.registerHelper("strengthLabel", (s) => s ? s.charAt(0).toUpperCase() + s.slice(1) : "");
12256
12262
  this.handlebars.registerHelper("pluralise", (n, one, many) => n === 1 ? one : many);
@@ -13232,7 +13238,7 @@ var PdfReporter = class {
13232
13238
  this.dataCollector = new DataCollector();
13233
13239
  this.pdfGenerator = new PdfGenerator();
13234
13240
  this.options = parseOptions(rawOptions);
13235
- const version = true ? "0.6.1" : "0.x";
13241
+ const version = true ? "0.6.3" : "0.x";
13236
13242
  logger.info(`@reportforge/playwright-pdf v${version} initialised`);
13237
13243
  this.licenseClient = new LicenseClient({
13238
13244
  licenseKey: this.options.licenseKey,
@@ -37,8 +37,8 @@
37
37
  {{#if sections.defectLog}}{{> defect-log }}{{/if}}
38
38
 
39
39
  <div class="report-footer">
40
- <span>Generated by ReportForge</span>
41
- <span>{{formatDate meta.generatedAt}}</span>
40
+ <span>{{meta.projectName}} &nbsp;·&nbsp; Playwright Test Report</span>
41
+ <span>Generated by ReportForge &nbsp;·&nbsp; {{formatDate meta.generatedAt}}</span>
42
42
  </div>
43
43
 
44
44
  </body>
@@ -4,8 +4,6 @@
4
4
 
5
5
  {{#if sections.coverPage}}{{> cover-page}}{{/if}}
6
6
 
7
- {{#if sections.analysisOneliner}}{{> analysis-oneliner}}{{/if}}
8
-
9
7
  <div class="report-header">
10
8
  <div class="report-header__left">
11
9
  {{#if meta.branding.logoBase64}}
@@ -16,6 +14,8 @@
16
14
  <div class="report-header__date">{{formatDate meta.generatedAt}}</div>
17
15
  </div>
18
16
 
17
+ {{#if sections.analysisOneliner}}{{> analysis-oneliner}}{{/if}}
18
+
19
19
  {{#if sections.releaseGate}}{{> release-gate}}{{/if}}
20
20
 
21
21
  {{#if sections.summary}}{{> executive-summary options=(obj showPassRate=sections.passRate) }}{{/if}}
@@ -37,8 +37,8 @@
37
37
  {{#if sections.ciEnvironment}}{{> ci-environment options=(obj showFullEnvironment=sections.fullEnvironment) }}{{/if}}
38
38
 
39
39
  <div class="report-footer">
40
- <span>Generated by ReportForge</span>
41
- <span>{{formatDate meta.generatedAt}}</span>
40
+ <span>{{meta.projectName}} &nbsp;·&nbsp; Playwright Test Report</span>
41
+ <span>Generated by ReportForge &nbsp;·&nbsp; {{formatDate meta.generatedAt}}</span>
42
42
  </div>
43
43
 
44
44
  </body>
@@ -166,14 +166,14 @@
166
166
  y: {
167
167
  stacked: true,
168
168
  // Widen the label gutter so the longest label isn't clipped at the canvas edge.
169
- afterFit: function(scale) { if (!scale.$rfPadded) { scale.$rfPadded = true; scale.width += 8; } },
169
+ afterFit: function(scale) { if (!scale.$rfPadded) { scale.$rfPadded = true; scale.width += 12; } },
170
170
  ticks: {
171
171
  font: { size: 9, family: 'Inter, sans-serif' }, color: '#1F1F1C', autoSkip: false,
172
172
  // Truncate long suite / describe labels so they fit the axis gutter
173
173
  // instead of clipping at the canvas edge.
174
174
  callback: function(value) {
175
175
  var l = this.getLabelForValue(value);
176
- return l.length > 24 ? l.slice(0, 23) + '…' : l;
176
+ return l.length > 18 ? l.slice(0, 17) + '…' : l;
177
177
  }
178
178
  },
179
179
  grid: { display: false },
@@ -4,8 +4,8 @@
4
4
  <table class="env-table">
5
5
  <tbody>
6
6
  <tr><td>CI Provider</td><td class="env-value">{{environment.ciProvider}}</td></tr>
7
- <tr><td>Branch</td> <td class="env-value">{{environment.branch}}</td></tr>
8
- <tr><td>Commit</td> <td class="env-value">{{environment.commit}}</td></tr>
7
+ <tr><td>Branch</td> <td class="env-value">{{gitVal environment.branch}}</td></tr>
8
+ <tr><td>Commit</td> <td class="env-value">{{gitVal environment.commit}}</td></tr>
9
9
  {{#if environment.buildUrl}}
10
10
  <tr><td>Build URL</td> <td class="env-value"><a href="{{environment.buildUrl}}" style="color:var(--primary,#CC785C);">{{environment.buildUrl}}</a></td></tr>
11
11
  {{/if}}
@@ -21,11 +21,11 @@
21
21
  <div class="env-grid">
22
22
  <div class="env-item">
23
23
  <span class="env-item__key">Branch</span>
24
- <span class="env-item__val">{{environment.branch}}</span>
24
+ <span class="env-item__val">{{gitVal environment.branch}}</span>
25
25
  </div>
26
26
  <div class="env-item">
27
27
  <span class="env-item__key">Commit</span>
28
- <span class="env-item__val">{{environment.commit}}</span>
28
+ <span class="env-item__val">{{gitVal environment.commit}}</span>
29
29
  </div>
30
30
  <div class="env-item">
31
31
  <span class="env-item__key">Browsers</span>
@@ -38,9 +38,11 @@
38
38
  <div class="cover-page__meta">
39
39
  Generated {{formatDate meta.generatedAt}}
40
40
  </div>
41
+ {{#unless (and (isUnknown environment.branch) (isUnknown environment.commit))}}
41
42
  <div class="cover-page__meta">
42
- Branch: <strong style="color:var(--text-1);">{{environment.branch}}</strong>
43
- &nbsp;·&nbsp;
44
- Commit: <strong style="color:var(--text-1);">{{environment.commit}}</strong>
43
+ {{#unless (isUnknown environment.branch)}}Branch: <strong style="color:var(--text-1);">{{environment.branch}}</strong>{{/unless}}
44
+ {{#unless (or (isUnknown environment.branch) (isUnknown environment.commit))}}&nbsp;·&nbsp;{{/unless}}
45
+ {{#unless (isUnknown environment.commit)}}Commit: <strong style="color:var(--text-1);">{{environment.commit}}</strong>{{/unless}}
45
46
  </div>
47
+ {{/unless}}
46
48
  </div>
@@ -22,12 +22,13 @@
22
22
  </span>
23
23
  </div>
24
24
 
25
- <div class="failure-item__message">{{error.message}}</div>
26
-
27
- {{#if error.stack}}
28
- {{#if ../options.showStackTraces}}
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
29
  <pre>{{error.stack}}</pre>
30
- {{/if}}
30
+ {{else}}
31
+ <div class="failure-item__message">{{error.message}}</div>
31
32
  {{/if}}
32
33
 
33
34
  {{#if screenshotBase64}}
@@ -39,7 +40,7 @@
39
40
  Retry history:&nbsp;
40
41
  {{#each retryHistory}}
41
42
  <span class="retry-attempt retry-attempt--{{status}}">
42
- #{{attempt}} {{status}} · {{formatDuration duration}}
43
+ #{{addOne attempt}} {{status}} · {{formatDuration duration}}
43
44
  </span>
44
45
  {{/each}}
45
46
  </div>
@@ -12,9 +12,8 @@
12
12
  </div>
13
13
  </div>
14
14
  <div class="release-gate__meta">
15
- <span>{{environment.branch}}</span>
16
- <span class="release-gate__sep">&middot;</span>
17
- <span>{{environment.commit}}</span>
15
+ {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
16
+ {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
18
17
  </div>
19
18
  </div>
20
19
  {{else}}
@@ -31,9 +30,8 @@
31
30
  </div>
32
31
  </div>
33
32
  <div class="release-gate__meta">
34
- <span>{{environment.branch}}</span>
35
- <span class="release-gate__sep">&middot;</span>
36
- <span>{{environment.commit}}</span>
33
+ {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
34
+ {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
37
35
  </div>
38
36
  </div>
39
37
  {{else}}
@@ -50,11 +48,9 @@
50
48
  </div>
51
49
  </div>
52
50
  <div class="release-gate__meta">
53
- <span>{{environment.branch}}</span>
54
- <span class="release-gate__sep">&middot;</span>
55
- <span>{{environment.commit}}</span>
51
+ {{#unless (isUnknown environment.branch)}}<span>{{environment.branch}}</span>{{/unless}}
52
+ {{#unless (isUnknown environment.commit)}}<span>{{environment.commit}}</span>{{/unless}}
56
53
  {{#if stats.flaky}}
57
- <span class="release-gate__sep">&middot;</span>
58
54
  <span class="release-gate__flaky-warn">&#9888; {{stats.flaky}} flaky</span>
59
55
  {{/if}}
60
56
  </div>
@@ -13,7 +13,7 @@
13
13
  <div class="suite-group__header">
14
14
  <span class="suite-title">{{title}}</span>
15
15
  <span class="suite-stats">
16
- <span class="badge badge--passed">{{stats.passed}}</span>
16
+ {{#if stats.passed}}<span class="badge badge--passed">{{stats.passed}}</span>{{/if}}
17
17
  {{#if stats.failed}}<span class="badge badge--failed">{{stats.failed}}</span>{{/if}}
18
18
  {{#if stats.timedOut}}<span class="badge badge--timedout">{{stats.timedOut}}</span>{{/if}}
19
19
  {{#if stats.skipped}}<span class="badge badge--skipped">{{stats.skipped}}</span>{{/if}}
@@ -43,6 +43,9 @@ html, body {
43
43
  color: var(--text-1);
44
44
  background: var(--bg);
45
45
  }
46
+ /* Establish a stacking context on the body so the fixed watermark (z-index:-1)
47
+ paints behind all content instead of on top of cards, charts and screenshots. */
48
+ body { position: relative; z-index: 0; }
46
49
 
47
50
  /* ── Typography ─────────────────────────────────────────────────────────── */
48
51
  h1 {
@@ -143,7 +146,7 @@ tr:last-child td { border-bottom: none; }
143
146
  position: fixed; top: 50%; left: 50%;
144
147
  transform: translate(-50%, -50%) rotate(-30deg);
145
148
  font-family: 'Source Serif Pro', Georgia, serif; font-size: 60px; font-weight: 700;
146
- color: rgba(31,31,28,0.04); pointer-events: none; z-index: 0;
149
+ color: rgba(31,31,28,0.04); pointer-events: none; z-index: -1;
147
150
  white-space: nowrap; letter-spacing: 8px; text-transform: uppercase;
148
151
  user-select: none;
149
152
  }
@@ -239,7 +242,10 @@ code {
239
242
  }
240
243
 
241
244
  /* ── Failure-analysis one-liner — shared by detailed + executive templates ── */
242
- .analysis-oneliner { font-size: 0.9em; opacity: 0.9; margin-top: 4px; }
245
+ .analysis-oneliner {
246
+ font-size: 0.9em; opacity: 0.9; margin-top: 4px;
247
+ page-break-after: avoid; break-after: avoid;
248
+ }
243
249
 
244
250
  /* ── Release gate — ship/hold banner, shared by all templates ────────────── */
245
251
  .release-gate {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reportforge/playwright-pdf",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
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
- "reportforge-demo": "./dist/demo/cli.js",
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
- });