@reportforge/playwright-pdf 0.2.0 → 0.2.2
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 +26 -23
- package/dist/index.d.mts +10 -1
- package/dist/index.d.ts +10 -1
- package/dist/index.js +55 -1
- package/dist/index.mjs +55 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -165,31 +165,34 @@ Currency is auto-detected by region; toggle on the pricing page.
|
|
|
165
165
|
|
|
166
166
|
All options are the second element of the reporter tuple.
|
|
167
167
|
|
|
168
|
+
<!-- AUTOGEN:options-table START — edit scripts/docs/options-meta.ts, then run npm run docs:gen -->
|
|
168
169
|
| Option | Type | Default | Description |
|
|
169
170
|
|---|---|---|---|
|
|
170
|
-
| `
|
|
171
|
-
| `
|
|
172
|
-
| `
|
|
173
|
-
| `
|
|
174
|
-
| `
|
|
175
|
-
| `
|
|
176
|
-
| `
|
|
177
|
-
| `
|
|
178
|
-
| `
|
|
179
|
-
| `
|
|
180
|
-
| `
|
|
181
|
-
| `
|
|
182
|
-
| `
|
|
183
|
-
| `
|
|
184
|
-
| `
|
|
185
|
-
| `
|
|
186
|
-
| `
|
|
187
|
-
| `
|
|
188
|
-
| `
|
|
189
|
-
| `
|
|
190
|
-
| `
|
|
191
|
-
| `
|
|
192
|
-
| `
|
|
171
|
+
| `outputFile` | `string` | `'playwright-report/{date}-report.pdf'` | Output path. Supports tokens: `{date}`, `{datetime}`, `{branch}`, `{status}`, `{total}`, `{passed}`, `{failed}`, `{project}`. Directory is created if absent. |
|
|
172
|
+
| `template` | `'minimal' \| 'detailed' \| 'executive'` or array | `'minimal'` | Template(s) to generate. Pass an array to produce one PDF per template in one run — the template name is appended to each output filename automatically (e.g. `report-detailed.pdf`). |
|
|
173
|
+
| `licenseKey` | `string` | env `RF_LICENSE_KEY` | Subscription key (format `RFSU-…`). Falls back to the `RF_LICENSE_KEY` environment variable. PDF generation is skipped when absent or invalid. |
|
|
174
|
+
| `logo` | `string` | — | Path to a logo image (PNG, JPG, or SVG) to embed in the report header. Supports absolute and relative paths. |
|
|
175
|
+
| `primaryColor` | `string` | `'#CC785C'` | Primary brand colour (3-, 6-, or 8-digit hex). Used for headers and accent bars. |
|
|
176
|
+
| `accentColor` | `string` | `'#3F7D58'` | Accent brand colour (hex). Used for highlights and badges. |
|
|
177
|
+
| `watermark` | `string` | — | Text to render as a diagonal watermark overlay on every page — e.g. `'CONFIDENTIAL'`, `'DRAFT'`. |
|
|
178
|
+
| `pdfPassword` | `string` | — | Password-protect the generated PDF. Requires `qpdf` installed on the system. |
|
|
179
|
+
| `reportTitle` | `string` | `'Playwright Test Report'` | Custom title for the report cover page and running header. |
|
|
180
|
+
| `projectName` | `string` | from `package.json` | Project or application name. Inferred from the `package.json` `name` field if absent. |
|
|
181
|
+
| `open` | `boolean` | `false` | Open the generated PDF automatically after generation. For local use only — ignored in CI. |
|
|
182
|
+
| `puppeteerExecutablePath` | `string` | auto-detect | Full path to a Chrome or Chromium binary. Falls back to `PUPPETEER_EXECUTABLE_PATH` env, then system Chrome discovery. |
|
|
183
|
+
| `serverUrl` | `string` | `'https://reportforge.org'` | Base URL for the ReportForge licensing server. Override only for self-hosting or local development. |
|
|
184
|
+
| `compressionLevel` | `'auto' \| 'none' \| 'balanced' \| 'max'` | `'auto'` | Screenshot JPEG quality preset. `'auto'` picks based on failure volume; `'none'` keeps original PNGs; `'balanced'` uses JPEG q85; `'max'` uses JPEG q70. |
|
|
185
|
+
| `includeScreenshots` | `boolean` | `true` | Embed Playwright screenshots in the PDF. Set to `false` to omit images — useful for exec-audience reports or reducing file size. |
|
|
186
|
+
| `maxInlineFailures` | `number` | derived from `compressionLevel` | Cap on failure entries rendered inline in the PDF. Overflow is written to a sibling `{basename}-failures.json` sidecar file. |
|
|
187
|
+
| `maxFileSizeMb` | `number` | `8` | Soft cap on the final PDF size in MB. If exceeded, the report is re-rendered once with the `'max'` compression preset. |
|
|
188
|
+
| `shardResults` | `string \| string[]` | — | Glob or path array of Playwright JSON shard report files to merge into one PDF. See the Shard Merging docs. |
|
|
189
|
+
| `notify` | `object` | — | Notification channels: `slack`, `teams`, `discord` (each `{ url, enabled, on }`), `email` (`{ to, enabled, on, attachPdf }`). See the Notifications docs. |
|
|
190
|
+
| `historyFile` | `string` | `~/.reportforge/{key}/history.json` | Path to the history JSON file. Relative paths resolve from `cwd`. Enables pass-rate trending charts in the `detailed` template. |
|
|
191
|
+
| `historySize` | `number` | `10` | Maximum number of test runs to keep in the history file (integer ≥ 2). Older runs are pruned on append. |
|
|
192
|
+
| `showTrend` | `boolean` | `true` | Show the pass-rate sparkline and delta badge in the `detailed` template. Set to `false` to disable history tracking entirely. |
|
|
193
|
+
| `flakinessTopN` | `number` | `5` | Maximum flaky tests to show in the flakiness table (`detailed` template). Set to `0` to disable the table entirely. |
|
|
194
|
+
| `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. |
|
|
195
|
+
<!-- AUTOGEN:options-table END -->
|
|
193
196
|
|
|
194
197
|
Full reference: [reportforge.org/docs#configuration](https://reportforge.org/docs#configuration).
|
|
195
198
|
|
package/dist/index.d.mts
CHANGED
|
@@ -136,9 +136,12 @@ interface ReporterOptions {
|
|
|
136
136
|
*/
|
|
137
137
|
shardResults?: string | string[];
|
|
138
138
|
/**
|
|
139
|
-
*
|
|
139
|
+
* Notification channels. Post a pass/fail summary after each test run.
|
|
140
140
|
* Each channel must have `enabled: true` to fire (default is `false`).
|
|
141
141
|
* `on` controls the trigger: `'always'` (default), `'failure'`, or `'success'`.
|
|
142
|
+
*
|
|
143
|
+
* - `slack` / `teams` / `discord`: webhook URL channels
|
|
144
|
+
* - `email`: sends via Resend; `to` is required; `attachPdf: true` attaches the PDF
|
|
142
145
|
*/
|
|
143
146
|
notify?: {
|
|
144
147
|
slack?: {
|
|
@@ -156,6 +159,12 @@ interface ReporterOptions {
|
|
|
156
159
|
on?: 'always' | 'failure' | 'success';
|
|
157
160
|
enabled?: boolean;
|
|
158
161
|
};
|
|
162
|
+
email?: {
|
|
163
|
+
to: string[];
|
|
164
|
+
on?: 'always' | 'failure' | 'success';
|
|
165
|
+
enabled?: boolean;
|
|
166
|
+
attachPdf?: boolean;
|
|
167
|
+
};
|
|
159
168
|
};
|
|
160
169
|
/**
|
|
161
170
|
* Path to the history JSON file. Defaults to
|
package/dist/index.d.ts
CHANGED
|
@@ -136,9 +136,12 @@ interface ReporterOptions {
|
|
|
136
136
|
*/
|
|
137
137
|
shardResults?: string | string[];
|
|
138
138
|
/**
|
|
139
|
-
*
|
|
139
|
+
* Notification channels. Post a pass/fail summary after each test run.
|
|
140
140
|
* Each channel must have `enabled: true` to fire (default is `false`).
|
|
141
141
|
* `on` controls the trigger: `'always'` (default), `'failure'`, or `'success'`.
|
|
142
|
+
*
|
|
143
|
+
* - `slack` / `teams` / `discord`: webhook URL channels
|
|
144
|
+
* - `email`: sends via Resend; `to` is required; `attachPdf: true` attaches the PDF
|
|
142
145
|
*/
|
|
143
146
|
notify?: {
|
|
144
147
|
slack?: {
|
|
@@ -156,6 +159,12 @@ interface ReporterOptions {
|
|
|
156
159
|
on?: 'always' | 'failure' | 'success';
|
|
157
160
|
enabled?: boolean;
|
|
158
161
|
};
|
|
162
|
+
email?: {
|
|
163
|
+
to: string[];
|
|
164
|
+
on?: 'always' | 'failure' | 'success';
|
|
165
|
+
enabled?: boolean;
|
|
166
|
+
attachPdf?: boolean;
|
|
167
|
+
};
|
|
159
168
|
};
|
|
160
169
|
/**
|
|
161
170
|
* Path to the history JSON file. Defaults to
|
package/dist/index.js
CHANGED
|
@@ -10971,6 +10971,18 @@ function detectCiEnv() {
|
|
|
10971
10971
|
ciProvider: "CircleCI"
|
|
10972
10972
|
};
|
|
10973
10973
|
}
|
|
10974
|
+
if (env.BITBUCKET_BUILD_NUMBER) {
|
|
10975
|
+
const workspace = env.BITBUCKET_WORKSPACE;
|
|
10976
|
+
const repoSlug = env.BITBUCKET_REPO_SLUG;
|
|
10977
|
+
const buildNumber = env.BITBUCKET_BUILD_NUMBER;
|
|
10978
|
+
const buildUrl = workspace && repoSlug && buildNumber ? `https://bitbucket.org/${workspace}/${repoSlug}/pipelines/results/${buildNumber}` : "";
|
|
10979
|
+
return {
|
|
10980
|
+
branch: env.BITBUCKET_BRANCH || "unknown",
|
|
10981
|
+
commit: env.BITBUCKET_COMMIT?.slice(0, COMMIT_HASH_LENGTH) || "unknown",
|
|
10982
|
+
buildUrl,
|
|
10983
|
+
ciProvider: "Bitbucket Pipelines"
|
|
10984
|
+
};
|
|
10985
|
+
}
|
|
10974
10986
|
return {
|
|
10975
10987
|
branch: gitBranch(),
|
|
10976
10988
|
commit: gitCommit(),
|
|
@@ -11021,6 +11033,13 @@ var DataCollector = class {
|
|
|
11021
11033
|
if (existing >= 0) this.failureRecords.splice(existing, 1);
|
|
11022
11034
|
}
|
|
11023
11035
|
}
|
|
11036
|
+
/**
|
|
11037
|
+
* Builds the final `ReportData` payload from all accumulated hook data.
|
|
11038
|
+
*
|
|
11039
|
+
* @param fullResult - Playwright's `FullResult` (contains `duration` + `status`).
|
|
11040
|
+
* @returns Structured data for the PDF generator, including projects tree,
|
|
11041
|
+
* aggregated stats, failures, environment info, and chart inputs.
|
|
11042
|
+
*/
|
|
11024
11043
|
finalize(fullResult) {
|
|
11025
11044
|
const walker = new SuiteWalker();
|
|
11026
11045
|
const projects = walker.build(this.rootSuite, this.resultMap);
|
|
@@ -11955,6 +11974,15 @@ var import_path6 = __toESM(require("path"));
|
|
|
11955
11974
|
var import_fs9 = __toESM(require("fs"));
|
|
11956
11975
|
var MAX_BRANCH_LENGTH = 50;
|
|
11957
11976
|
var FilenameResolver = class {
|
|
11977
|
+
/**
|
|
11978
|
+
* Expands `{date}`, `{branch}`, and `{status}` tokens in `template`, then
|
|
11979
|
+
* resolves to an absolute path and creates the parent directory.
|
|
11980
|
+
*
|
|
11981
|
+
* @param template - Output path, possibly relative to `cwd` and possibly
|
|
11982
|
+
* containing `{date}`, `{branch}`, or `{status}` tokens.
|
|
11983
|
+
* @param ctx - Runtime values for token expansion.
|
|
11984
|
+
* @returns Absolute path with parent directories created.
|
|
11985
|
+
*/
|
|
11958
11986
|
resolve(template, ctx) {
|
|
11959
11987
|
const date = this.formatDate(ctx.date);
|
|
11960
11988
|
const branch = this.sanitiseBranch(ctx.branch);
|
|
@@ -11964,6 +11992,14 @@ var FilenameResolver = class {
|
|
|
11964
11992
|
import_fs9.default.mkdirSync(import_path6.default.dirname(absolute), { recursive: true });
|
|
11965
11993
|
return absolute;
|
|
11966
11994
|
}
|
|
11995
|
+
/**
|
|
11996
|
+
* Inserts `suffix` before the file extension so multi-template runs produce
|
|
11997
|
+
* unique filenames from the same base path.
|
|
11998
|
+
*
|
|
11999
|
+
* @example
|
|
12000
|
+
* injectTemplateSuffix('/out/2026-report.pdf', 'executive')
|
|
12001
|
+
* // → '/out/2026-report-executive.pdf'
|
|
12002
|
+
*/
|
|
11967
12003
|
injectTemplateSuffix(outputPath, suffix) {
|
|
11968
12004
|
const ext = import_path6.default.extname(outputPath);
|
|
11969
12005
|
const base = outputPath.slice(0, outputPath.length - ext.length);
|
|
@@ -12086,6 +12122,24 @@ var PdfGenerator = class {
|
|
|
12086
12122
|
} catch {
|
|
12087
12123
|
}
|
|
12088
12124
|
}
|
|
12125
|
+
/**
|
|
12126
|
+
* Generates one PDF per template source and returns their absolute output paths.
|
|
12127
|
+
*
|
|
12128
|
+
* Launches a single Puppeteer browser instance shared across all template
|
|
12129
|
+
* renders to avoid the 2–5 s startup cost per template. Each render pass goes
|
|
12130
|
+
* through: failure pagination → screenshot embedding → Handlebars render →
|
|
12131
|
+
* temp HTML write → `page.pdf()`.
|
|
12132
|
+
*
|
|
12133
|
+
* Size cap enforcement: if the first-pass PDF exceeds `options.maxFileSizeMb`,
|
|
12134
|
+
* compression settings are tightened (quality 55, maxWidth 800, inline cap
|
|
12135
|
+
* scaled to fit) and the PDF is re-rendered once. A second overrun warns but
|
|
12136
|
+
* is not re-rendered.
|
|
12137
|
+
*
|
|
12138
|
+
* @param data - Assembled `ReportData` (projects, stats, failures, charts).
|
|
12139
|
+
* @param options - Validated reporter options.
|
|
12140
|
+
* @param _licenseInfo - Kept for API symmetry; unused in this method.
|
|
12141
|
+
* @returns Absolute paths of written PDF files.
|
|
12142
|
+
*/
|
|
12089
12143
|
async generateAll(data, options, _licenseInfo) {
|
|
12090
12144
|
const sources = this.resolveTemplateSources(options);
|
|
12091
12145
|
const isMulti = sources.length > 1;
|
|
@@ -12616,7 +12670,7 @@ var PdfReporter = class {
|
|
|
12616
12670
|
this.dataCollector = new DataCollector();
|
|
12617
12671
|
this.pdfGenerator = new PdfGenerator();
|
|
12618
12672
|
this.options = parseOptions(rawOptions);
|
|
12619
|
-
const version = true ? "0.2.
|
|
12673
|
+
const version = true ? "0.2.2" : "0.x";
|
|
12620
12674
|
logger.info(`@reportforge/playwright-pdf v${version} initialised`);
|
|
12621
12675
|
this.licenseClient = new LicenseClient({
|
|
12622
12676
|
licenseKey: this.options.licenseKey,
|
package/dist/index.mjs
CHANGED
|
@@ -10972,6 +10972,18 @@ function detectCiEnv() {
|
|
|
10972
10972
|
ciProvider: "CircleCI"
|
|
10973
10973
|
};
|
|
10974
10974
|
}
|
|
10975
|
+
if (env.BITBUCKET_BUILD_NUMBER) {
|
|
10976
|
+
const workspace = env.BITBUCKET_WORKSPACE;
|
|
10977
|
+
const repoSlug = env.BITBUCKET_REPO_SLUG;
|
|
10978
|
+
const buildNumber = env.BITBUCKET_BUILD_NUMBER;
|
|
10979
|
+
const buildUrl = workspace && repoSlug && buildNumber ? `https://bitbucket.org/${workspace}/${repoSlug}/pipelines/results/${buildNumber}` : "";
|
|
10980
|
+
return {
|
|
10981
|
+
branch: env.BITBUCKET_BRANCH || "unknown",
|
|
10982
|
+
commit: env.BITBUCKET_COMMIT?.slice(0, COMMIT_HASH_LENGTH) || "unknown",
|
|
10983
|
+
buildUrl,
|
|
10984
|
+
ciProvider: "Bitbucket Pipelines"
|
|
10985
|
+
};
|
|
10986
|
+
}
|
|
10975
10987
|
return {
|
|
10976
10988
|
branch: gitBranch(),
|
|
10977
10989
|
commit: gitCommit(),
|
|
@@ -11022,6 +11034,13 @@ var DataCollector = class {
|
|
|
11022
11034
|
if (existing >= 0) this.failureRecords.splice(existing, 1);
|
|
11023
11035
|
}
|
|
11024
11036
|
}
|
|
11037
|
+
/**
|
|
11038
|
+
* Builds the final `ReportData` payload from all accumulated hook data.
|
|
11039
|
+
*
|
|
11040
|
+
* @param fullResult - Playwright's `FullResult` (contains `duration` + `status`).
|
|
11041
|
+
* @returns Structured data for the PDF generator, including projects tree,
|
|
11042
|
+
* aggregated stats, failures, environment info, and chart inputs.
|
|
11043
|
+
*/
|
|
11025
11044
|
finalize(fullResult) {
|
|
11026
11045
|
const walker = new SuiteWalker();
|
|
11027
11046
|
const projects = walker.build(this.rootSuite, this.resultMap);
|
|
@@ -11956,6 +11975,15 @@ import path7 from "path";
|
|
|
11956
11975
|
import fs8 from "fs";
|
|
11957
11976
|
var MAX_BRANCH_LENGTH = 50;
|
|
11958
11977
|
var FilenameResolver = class {
|
|
11978
|
+
/**
|
|
11979
|
+
* Expands `{date}`, `{branch}`, and `{status}` tokens in `template`, then
|
|
11980
|
+
* resolves to an absolute path and creates the parent directory.
|
|
11981
|
+
*
|
|
11982
|
+
* @param template - Output path, possibly relative to `cwd` and possibly
|
|
11983
|
+
* containing `{date}`, `{branch}`, or `{status}` tokens.
|
|
11984
|
+
* @param ctx - Runtime values for token expansion.
|
|
11985
|
+
* @returns Absolute path with parent directories created.
|
|
11986
|
+
*/
|
|
11959
11987
|
resolve(template, ctx) {
|
|
11960
11988
|
const date = this.formatDate(ctx.date);
|
|
11961
11989
|
const branch = this.sanitiseBranch(ctx.branch);
|
|
@@ -11965,6 +11993,14 @@ var FilenameResolver = class {
|
|
|
11965
11993
|
fs8.mkdirSync(path7.dirname(absolute), { recursive: true });
|
|
11966
11994
|
return absolute;
|
|
11967
11995
|
}
|
|
11996
|
+
/**
|
|
11997
|
+
* Inserts `suffix` before the file extension so multi-template runs produce
|
|
11998
|
+
* unique filenames from the same base path.
|
|
11999
|
+
*
|
|
12000
|
+
* @example
|
|
12001
|
+
* injectTemplateSuffix('/out/2026-report.pdf', 'executive')
|
|
12002
|
+
* // → '/out/2026-report-executive.pdf'
|
|
12003
|
+
*/
|
|
11968
12004
|
injectTemplateSuffix(outputPath, suffix) {
|
|
11969
12005
|
const ext = path7.extname(outputPath);
|
|
11970
12006
|
const base = outputPath.slice(0, outputPath.length - ext.length);
|
|
@@ -12087,6 +12123,24 @@ var PdfGenerator = class {
|
|
|
12087
12123
|
} catch {
|
|
12088
12124
|
}
|
|
12089
12125
|
}
|
|
12126
|
+
/**
|
|
12127
|
+
* Generates one PDF per template source and returns their absolute output paths.
|
|
12128
|
+
*
|
|
12129
|
+
* Launches a single Puppeteer browser instance shared across all template
|
|
12130
|
+
* renders to avoid the 2–5 s startup cost per template. Each render pass goes
|
|
12131
|
+
* through: failure pagination → screenshot embedding → Handlebars render →
|
|
12132
|
+
* temp HTML write → `page.pdf()`.
|
|
12133
|
+
*
|
|
12134
|
+
* Size cap enforcement: if the first-pass PDF exceeds `options.maxFileSizeMb`,
|
|
12135
|
+
* compression settings are tightened (quality 55, maxWidth 800, inline cap
|
|
12136
|
+
* scaled to fit) and the PDF is re-rendered once. A second overrun warns but
|
|
12137
|
+
* is not re-rendered.
|
|
12138
|
+
*
|
|
12139
|
+
* @param data - Assembled `ReportData` (projects, stats, failures, charts).
|
|
12140
|
+
* @param options - Validated reporter options.
|
|
12141
|
+
* @param _licenseInfo - Kept for API symmetry; unused in this method.
|
|
12142
|
+
* @returns Absolute paths of written PDF files.
|
|
12143
|
+
*/
|
|
12090
12144
|
async generateAll(data, options, _licenseInfo) {
|
|
12091
12145
|
const sources = this.resolveTemplateSources(options);
|
|
12092
12146
|
const isMulti = sources.length > 1;
|
|
@@ -12617,7 +12671,7 @@ var PdfReporter = class {
|
|
|
12617
12671
|
this.dataCollector = new DataCollector();
|
|
12618
12672
|
this.pdfGenerator = new PdfGenerator();
|
|
12619
12673
|
this.options = parseOptions(rawOptions);
|
|
12620
|
-
const version = true ? "0.2.
|
|
12674
|
+
const version = true ? "0.2.2" : "0.x";
|
|
12621
12675
|
logger.info(`@reportforge/playwright-pdf v${version} initialised`);
|
|
12622
12676
|
this.licenseClient = new LicenseClient({
|
|
12623
12677
|
licenseKey: this.options.licenseKey,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reportforge/playwright-pdf",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.2",
|
|
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",
|
|
@@ -60,7 +60,9 @@
|
|
|
60
60
|
"typecheck": "tsc --noEmit",
|
|
61
61
|
"prepublishOnly": "npm run typecheck && npm run test && npm run build",
|
|
62
62
|
"generate-license": "tsx scripts/generate-license.ts",
|
|
63
|
-
"snapshot-png": "tsx scripts/snapshot-png.ts"
|
|
63
|
+
"snapshot-png": "tsx scripts/snapshot-png.ts",
|
|
64
|
+
"docs:gen": "tsx scripts/docs/sync-docs.ts --write",
|
|
65
|
+
"check:docs": "tsx scripts/docs/sync-docs.ts --check"
|
|
64
66
|
},
|
|
65
67
|
"peerDependencies": {
|
|
66
68
|
"@playwright/test": ">=1.40.0",
|