@reportforge/playwright-pdf 0.26.1 → 0.27.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/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Playwright PDF reporter: generate branded PDF reports from [Playwright Test](https://playwright.dev/) results. Three templates (minimal, detailed, executive) for developers, QA teams, and stakeholders. CI-ready, self-contained, with embedded screenshots and charts.
4
4
 
5
- Drop it into any Playwright project and get a PDF report on every CI run. No changes to your tests.
5
+ Drop it into any Playwright project and get a PDF report on every CI run. No changes to your tests. Testing with Python? The same reports ship as [`reportforge-playwright-pdf` on PyPI](https://pypi.org/project/reportforge-playwright-pdf/) for pytest-playwright, on the same license key (see [Python](#python)).
6
6
 
7
7
  [![npm](https://img.shields.io/npm/v/@reportforge/playwright-pdf.svg)](https://www.npmjs.com/package/@reportforge/playwright-pdf)
8
8
  [![Node](https://img.shields.io/node/v/@reportforge/playwright-pdf.svg)](https://nodejs.org/)
@@ -67,6 +67,7 @@ Three templates, one reporter. Every PDF is fully offline: fonts, charts, and sc
67
67
  - **Executive report brief band**: leads with a plain-language brief (trend + root causes) and one hero pass-rate number instead of a flat KPI grid
68
68
  - **Data protection**: AES-256 password-protected PDFs (no external tools) and opt-in redaction of credentials/PII in report content and live streams
69
69
  - **Hybrid licensing**: one short key unlocks it; reports keep rendering offline via a locally cached license between refreshes
70
+ - **Python support**: the same reports for pytest-playwright via the `reportforge-playwright-pdf` PyPI package; one subscription key covers both ecosystems (live runs and step trees are Node.js-only today)
70
71
  - **Update notice**: when a newer reporter version is published, each run ends with one info line naming it and the update command; no extra network calls
71
72
 
72
73
  ---
@@ -157,6 +158,31 @@ reporter: [['@reportforge/playwright-pdf', {
157
158
 
158
159
  ---
159
160
 
161
+ ## Python
162
+
163
+ The same reports for [pytest-playwright](https://playwright.dev/python/) runs, from the `reportforge-playwright-pdf` PyPI package. One subscription key covers Node.js and Python, and both count against the same machine allowance.
164
+
165
+ ```bash
166
+ pip install reportforge-playwright-pdf
167
+ python -m playwright install chromium
168
+
169
+ export RF_LICENSE_KEY=RFSU-XXXX-XXXX-XXXX-XXXX
170
+ pytest --pdf
171
+ ```
172
+
173
+ Project defaults go in `pyproject.toml` with the same camelCase option names as this reporter:
174
+
175
+ ```toml
176
+ [tool.reportforge]
177
+ enabled = true
178
+ template = "detailed"
179
+ outputFile = "reports/{date}-{branch}-report.pdf"
180
+ ```
181
+
182
+ The plugin reuses the Node runtime and Chromium that Playwright for Python already ships, so there is nothing else to install. pytest-xdist and pytest-rerunfailures are supported; failure screenshots from `--screenshot only-on-failure` are embedded. Live runs and step trees are Node.js-only today. Full guide: [reportforge.org/docs/python](https://reportforge.org/docs/python).
183
+
184
+ ---
185
+
160
186
  ## Pricing
161
187
 
162
188
  | Plan | INR | USD | Includes |
@@ -270,7 +296,9 @@ reporter: [
270
296
 
271
297
  The merge step can run against a dummy test file with zero tests: `shardResults` takes precedence over live results.
272
298
 
273
- > **Note:** Timed-out tests are counted as failures in shard mode (Playwright JSON stats do not distinguish `timedOut` from `failed`).
299
+ Timed-out tests keep their own count in shard mode: the aggregate JSON stats fold them into failures, but the per-test result status carries `timedOut` and the report recovers it for the KPI cards and charts.
300
+
301
+ The merger also honors two optional fields in the JSON report's `config` block when the emitting run provides them: `workers` (shown as the worker count in the environment panel, defaults to 1) and `metadata.playwrightVersion` (shown as the Playwright version when the merge runs where `@playwright/test` is not installed).
274
302
 
275
303
  ### Notifications
276
304
 
@@ -676,6 +704,7 @@ ReportForge ships a **hybrid offline-first** model:
676
704
  3. Every subsequent run is **fully offline**: the cached license is validated on-device. No network call.
677
705
  4. The reporter refreshes the cache automatically in the background before it expires.
678
706
  5. Each subscription allows up to **25 active machines**. Machines that haven't run in 30 days free up automatically.
707
+ 6. The key is **language-neutral**: the same subscription activates the Node.js reporter and the Python plugin, and a machine running both uses one slot.
679
708
 
680
709
  Cancel the subscription and the reporter keeps working until the end of the current billing period, then stops generating PDFs until you restart. **A license issue never aborts your test run**: the reporter logs a warning and skips PDF generation; your Playwright tests still pass.
681
710
 
@@ -736,6 +765,7 @@ Full guide: [reportforge.org/docs/troubleshooting](https://reportforge.org/docs/
736
765
  Full documentation at [reportforge.org/docs](https://reportforge.org/docs):
737
766
 
738
767
  - [Quick start](https://reportforge.org/docs/quickstart): install, configure, activate
768
+ - [Python](https://reportforge.org/docs/python): the pytest-playwright plugin, same license key
739
769
  - [Chrome setup](https://reportforge.org/docs/running-tests#chrome-setup): Windows, Linux, macOS
740
770
  - [CI/CD integration](https://reportforge.org/docs/ci-cd): GitHub Actions, GitLab CI, Bitbucket Pipelines, Jenkins, Azure DevOps
741
771
  - [Configuration reference](https://reportforge.org/docs/configuration): all options
package/dist/cli/index.js CHANGED
@@ -6952,13 +6952,25 @@ var init_engine = __esm({
6952
6952
  });
6953
6953
 
6954
6954
  // src/screenshots/ScreenshotEmbedder.ts
6955
- var import_fs2, import_sharp, ScreenshotEmbedder;
6955
+ function loadSharp() {
6956
+ if (sharpModule === void 0) {
6957
+ try {
6958
+ sharpModule = require("sharp");
6959
+ } catch {
6960
+ sharpModule = null;
6961
+ logger.warn(
6962
+ "sharp is not available \u2014 screenshots are embedded at original size. Reports with many screenshots may exceed maxFileSizeMb more often."
6963
+ );
6964
+ }
6965
+ }
6966
+ return sharpModule;
6967
+ }
6968
+ var import_fs2, sharpModule, ScreenshotEmbedder;
6956
6969
  var init_ScreenshotEmbedder = __esm({
6957
6970
  "src/screenshots/ScreenshotEmbedder.ts"() {
6958
6971
  "use strict";
6959
6972
  init_cjs_shims();
6960
6973
  import_fs2 = __toESM(require("fs"));
6961
- import_sharp = __toESM(require("sharp"));
6962
6974
  init_logger();
6963
6975
  ScreenshotEmbedder = class {
6964
6976
  async embedAll(failures, opts) {
@@ -6995,7 +7007,11 @@ var init_ScreenshotEmbedder = __esm({
6995
7007
  if (this.looksLikeSvg(buf)) {
6996
7008
  return { buffer: buf, mime: "image/svg+xml" };
6997
7009
  }
6998
- const img = (0, import_sharp.default)(buf, { failOn: "none" });
7010
+ const sharp = loadSharp();
7011
+ if (!sharp) {
7012
+ return { buffer: buf, mime: this.detectMimeType(buf) };
7013
+ }
7014
+ const img = sharp(buf, { failOn: "none" });
6999
7015
  const meta = await img.metadata();
7000
7016
  const needsResize = (meta.width ?? 0) > opts.maxWidth;
7001
7017
  let pipeline = img.rotate();
package/dist/index.d.mts CHANGED
@@ -432,43 +432,6 @@ interface ReporterOptions {
432
432
  sections?: SectionsOption;
433
433
  }
434
434
 
435
- /**
436
- * The resolved license for the current reporter run.
437
- *
438
- * The mere existence of a LicenseInfo means the subscription is active —
439
- * "no license" is represented by `LicenseClient.resolve()` returning null,
440
- * not by a sentinel plan or `valid: false` flag.
441
- *
442
- * `source` surfaces how the license was obtained so the reporter can log
443
- * actionable diagnostics:
444
- * - 'jwt-online' → freshly activated/refreshed against the server
445
- * - 'jwt-cache' → served from ~/.reportforge/license.json (offline)
446
- */
447
- interface LicenseInfo {
448
- plan: LicensePlan;
449
- source: 'jwt-online' | 'jwt-cache' | 'demo';
450
- expiry: Date;
451
- key: string;
452
- /** Raw Ed25519-signed JWT from the server. Absent only in demo mode. */
453
- jwt?: string;
454
- machinesUsed?: number;
455
- machineLimit?: number;
456
- /**
457
- * Entitlement flags carried on the signed JWT (`features` claim). Surfaces
458
- * server-gated capabilities (e.g. `'live'`) to the reporter without a second
459
- * network trip. Undefined for caches written by reporter versions before this
460
- * field was persisted — treat absence as "no extra features".
461
- */
462
- features?: string[];
463
- /**
464
- * Latest published npm version of the reporter, as last reported by the
465
- * license server (activate/refresh piggyback). Absent when the server
466
- * could not reach the registry or the cache predates this field. Used
467
- * only for the non-blocking run-end update notice.
468
- */
469
- latestVersion?: string;
470
- }
471
-
472
435
  /**
473
436
  * PdfReporter — Playwright custom reporter that generates professional PDF reports.
474
437
  *
@@ -526,9 +489,6 @@ declare class PdfReporter implements Reporter {
526
489
  onExit(): Promise<void>;
527
490
  onEnd(result: FullResult): Promise<void>;
528
491
  private _collectData;
529
- private _appendTrend;
530
- private _populateHistoryCharts;
531
- private _buildReportData;
532
492
  printsToStdio(): boolean;
533
493
  /**
534
494
  * Memoized license resolution shared by the live stream (kicked early in
@@ -566,8 +526,43 @@ declare class PdfReporter implements Reporter {
566
526
  * posts one message, not N. The banner still prints from every shard's own log.
567
527
  */
568
528
  private announceLive;
569
- private resolveProjectName;
570
- private openPdf;
529
+ }
530
+
531
+ /**
532
+ * The resolved license for the current reporter run.
533
+ *
534
+ * The mere existence of a LicenseInfo means the subscription is active —
535
+ * "no license" is represented by `LicenseClient.resolve()` returning null,
536
+ * not by a sentinel plan or `valid: false` flag.
537
+ *
538
+ * `source` surfaces how the license was obtained so the reporter can log
539
+ * actionable diagnostics:
540
+ * - 'jwt-online' → freshly activated/refreshed against the server
541
+ * - 'jwt-cache' → served from ~/.reportforge/license.json (offline)
542
+ */
543
+ interface LicenseInfo {
544
+ plan: LicensePlan;
545
+ source: 'jwt-online' | 'jwt-cache' | 'demo';
546
+ expiry: Date;
547
+ key: string;
548
+ /** Raw Ed25519-signed JWT from the server. Absent only in demo mode. */
549
+ jwt?: string;
550
+ machinesUsed?: number;
551
+ machineLimit?: number;
552
+ /**
553
+ * Entitlement flags carried on the signed JWT (`features` claim). Surfaces
554
+ * server-gated capabilities (e.g. `'live'`) to the reporter without a second
555
+ * network trip. Undefined for caches written by reporter versions before this
556
+ * field was persisted — treat absence as "no extra features".
557
+ */
558
+ features?: string[];
559
+ /**
560
+ * Latest published npm version of the reporter, as last reported by the
561
+ * license server (activate/refresh piggyback). Absent when the server
562
+ * could not reach the registry or the cache predates this field. Used
563
+ * only for the non-blocking run-end update notice.
564
+ */
565
+ latestVersion?: string;
571
566
  }
572
567
 
573
568
  /**
package/dist/index.d.ts CHANGED
@@ -432,43 +432,6 @@ interface ReporterOptions {
432
432
  sections?: SectionsOption;
433
433
  }
434
434
 
435
- /**
436
- * The resolved license for the current reporter run.
437
- *
438
- * The mere existence of a LicenseInfo means the subscription is active —
439
- * "no license" is represented by `LicenseClient.resolve()` returning null,
440
- * not by a sentinel plan or `valid: false` flag.
441
- *
442
- * `source` surfaces how the license was obtained so the reporter can log
443
- * actionable diagnostics:
444
- * - 'jwt-online' → freshly activated/refreshed against the server
445
- * - 'jwt-cache' → served from ~/.reportforge/license.json (offline)
446
- */
447
- interface LicenseInfo {
448
- plan: LicensePlan;
449
- source: 'jwt-online' | 'jwt-cache' | 'demo';
450
- expiry: Date;
451
- key: string;
452
- /** Raw Ed25519-signed JWT from the server. Absent only in demo mode. */
453
- jwt?: string;
454
- machinesUsed?: number;
455
- machineLimit?: number;
456
- /**
457
- * Entitlement flags carried on the signed JWT (`features` claim). Surfaces
458
- * server-gated capabilities (e.g. `'live'`) to the reporter without a second
459
- * network trip. Undefined for caches written by reporter versions before this
460
- * field was persisted — treat absence as "no extra features".
461
- */
462
- features?: string[];
463
- /**
464
- * Latest published npm version of the reporter, as last reported by the
465
- * license server (activate/refresh piggyback). Absent when the server
466
- * could not reach the registry or the cache predates this field. Used
467
- * only for the non-blocking run-end update notice.
468
- */
469
- latestVersion?: string;
470
- }
471
-
472
435
  /**
473
436
  * PdfReporter — Playwright custom reporter that generates professional PDF reports.
474
437
  *
@@ -526,9 +489,6 @@ declare class PdfReporter implements Reporter {
526
489
  onExit(): Promise<void>;
527
490
  onEnd(result: FullResult): Promise<void>;
528
491
  private _collectData;
529
- private _appendTrend;
530
- private _populateHistoryCharts;
531
- private _buildReportData;
532
492
  printsToStdio(): boolean;
533
493
  /**
534
494
  * Memoized license resolution shared by the live stream (kicked early in
@@ -566,8 +526,43 @@ declare class PdfReporter implements Reporter {
566
526
  * posts one message, not N. The banner still prints from every shard's own log.
567
527
  */
568
528
  private announceLive;
569
- private resolveProjectName;
570
- private openPdf;
529
+ }
530
+
531
+ /**
532
+ * The resolved license for the current reporter run.
533
+ *
534
+ * The mere existence of a LicenseInfo means the subscription is active —
535
+ * "no license" is represented by `LicenseClient.resolve()` returning null,
536
+ * not by a sentinel plan or `valid: false` flag.
537
+ *
538
+ * `source` surfaces how the license was obtained so the reporter can log
539
+ * actionable diagnostics:
540
+ * - 'jwt-online' → freshly activated/refreshed against the server
541
+ * - 'jwt-cache' → served from ~/.reportforge/license.json (offline)
542
+ */
543
+ interface LicenseInfo {
544
+ plan: LicensePlan;
545
+ source: 'jwt-online' | 'jwt-cache' | 'demo';
546
+ expiry: Date;
547
+ key: string;
548
+ /** Raw Ed25519-signed JWT from the server. Absent only in demo mode. */
549
+ jwt?: string;
550
+ machinesUsed?: number;
551
+ machineLimit?: number;
552
+ /**
553
+ * Entitlement flags carried on the signed JWT (`features` claim). Surfaces
554
+ * server-gated capabilities (e.g. `'live'`) to the reporter without a second
555
+ * network trip. Undefined for caches written by reporter versions before this
556
+ * field was persisted — treat absence as "no extra features".
557
+ */
558
+ features?: string[];
559
+ /**
560
+ * Latest published npm version of the reporter, as last reported by the
561
+ * license server (activate/refresh piggyback). Absent when the server
562
+ * could not reach the registry or the cache predates this field. Used
563
+ * only for the non-blocking run-end update notice.
564
+ */
565
+ latestVersion?: string;
571
566
  }
572
567
 
573
568
  /**