@reportforge/playwright-pdf 0.7.0 → 0.7.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/README.md +1 -1
- package/dist/cli/index.js +7 -3
- package/dist/index.d.mts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +7 -3
- package/dist/index.mjs +7 -3
- package/dist/templates/layouts/detailed.hbs +1 -1
- package/dist/templates/layouts/executive.hbs +1 -1
- package/dist/templates/layouts/minimal.hbs +1 -1
- package/dist/templates/partials/charts.hbs +13 -7
- package/dist/templates/partials/executive-summary.hbs +18 -4
- package/dist/templates/partials/suite-breakdown.hbs +1 -1
- package/dist/templates/styles/base.css +11 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -198,7 +198,7 @@ All options are the second element of the reporter tuple.
|
|
|
198
198
|
| `historySize` | `number` | `10` | Maximum number of test runs to keep in the history file (integer ≥ 2). Older runs are pruned on append. |
|
|
199
199
|
| `showTrend` | `boolean` | `true` | Show the pass-rate sparkline and delta badge in the `detailed` template. Set to `false` to disable history tracking entirely. |
|
|
200
200
|
| `flakinessTopN` | `number` | `5` | Maximum flaky tests to show in the flakiness table (`detailed` template). Set to `0` to disable the table entirely. |
|
|
201
|
-
| `slowTestThreshold` | `number` | `10` | Minimum timed-test count before `SLOW` badges appear in the suite breakdown
|
|
201
|
+
| `slowTestThreshold` | `number` | `10` | Minimum timed-test count before `SLOW` badges appear in the suite breakdown — below this, every test is trivially the "slowest" so badges stay hidden. On larger runs only genuine outliers (at least 2× the median test duration) are badged, so the badge stays rare. Set to `0` to drop the run-size gate (outliers still required). |
|
|
202
202
|
| `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. |
|
|
203
203
|
| `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. |
|
|
204
204
|
<!-- AUTOGEN:options-table END -->
|
package/dist/cli/index.js
CHANGED
|
@@ -6473,7 +6473,7 @@ function deriveOptions(s) {
|
|
|
6473
6473
|
showFullFailures: s.fullFailures
|
|
6474
6474
|
};
|
|
6475
6475
|
}
|
|
6476
|
-
var import_handlebars, import_fs, import_path, TemplateEngine;
|
|
6476
|
+
var import_handlebars, import_fs, import_path, SLOW_MEDIAN_FACTOR, TemplateEngine;
|
|
6477
6477
|
var init_engine = __esm({
|
|
6478
6478
|
"src/templates/engine.ts"() {
|
|
6479
6479
|
"use strict";
|
|
@@ -6485,6 +6485,7 @@ var init_engine = __esm({
|
|
|
6485
6485
|
init_duration();
|
|
6486
6486
|
init_SummaryWriter();
|
|
6487
6487
|
init_sections();
|
|
6488
|
+
SLOW_MEDIAN_FACTOR = 2;
|
|
6488
6489
|
TemplateEngine = class {
|
|
6489
6490
|
constructor() {
|
|
6490
6491
|
this.chartjsInline = "";
|
|
@@ -6612,8 +6613,10 @@ var init_engine = __esm({
|
|
|
6612
6613
|
}
|
|
6613
6614
|
}
|
|
6614
6615
|
}
|
|
6616
|
+
const sortedDurations = tests.map((t) => t.durationMs).sort((a, b) => a - b);
|
|
6617
|
+
const medianMs = sortedDurations.length ? sortedDurations[Math.floor((sortedDurations.length - 1) / 2)] : 0;
|
|
6615
6618
|
const ranked = tests.sort((a, b) => b.durationMs - a.durationMs).slice(0, 10);
|
|
6616
|
-
return { ranked, timedTotal: tests.length };
|
|
6619
|
+
return { ranked, timedTotal: tests.length, medianMs };
|
|
6617
6620
|
}
|
|
6618
6621
|
/**
|
|
6619
6622
|
* Project the suite tree into its render form: attach each test's failure detail
|
|
@@ -6628,7 +6631,8 @@ var init_engine = __esm({
|
|
|
6628
6631
|
const chipById = sections.failureAnalysis ? new Map((data.classifications ?? []).map((c) => [c.testId, c])) : /* @__PURE__ */ new Map();
|
|
6629
6632
|
const slowRankById = /* @__PURE__ */ new Map();
|
|
6630
6633
|
if (sections.slowTests && slow.timedTotal > slowTestThreshold) {
|
|
6631
|
-
|
|
6634
|
+
const floorMs = slow.medianMs * SLOW_MEDIAN_FACTOR;
|
|
6635
|
+
slow.ranked.filter((t) => t.durationMs >= floorMs).forEach((t, i) => slowRankById.set(t.id, i + 1));
|
|
6632
6636
|
}
|
|
6633
6637
|
const enrichTest = (t) => ({
|
|
6634
6638
|
...t,
|
package/dist/index.d.mts
CHANGED
|
@@ -236,9 +236,10 @@ interface ReporterOptions {
|
|
|
236
236
|
flakinessTopN?: number;
|
|
237
237
|
/**
|
|
238
238
|
* Minimum number of timed tests before `SLOW` badges appear in the suite
|
|
239
|
-
* breakdown
|
|
240
|
-
*
|
|
241
|
-
*
|
|
239
|
+
* breakdown — below this, every test is trivially the "slowest", so badges
|
|
240
|
+
* stay hidden. On larger runs only genuine outliers (at least 2× the median
|
|
241
|
+
* test duration) are badged, so the badge stays rare. Set to 0 to drop the
|
|
242
|
+
* run-size gate (outliers are still required). Must be a non-negative integer.
|
|
242
243
|
* @default 10
|
|
243
244
|
*/
|
|
244
245
|
slowTestThreshold?: number;
|
package/dist/index.d.ts
CHANGED
|
@@ -236,9 +236,10 @@ interface ReporterOptions {
|
|
|
236
236
|
flakinessTopN?: number;
|
|
237
237
|
/**
|
|
238
238
|
* Minimum number of timed tests before `SLOW` badges appear in the suite
|
|
239
|
-
* breakdown
|
|
240
|
-
*
|
|
241
|
-
*
|
|
239
|
+
* breakdown — below this, every test is trivially the "slowest", so badges
|
|
240
|
+
* stay hidden. On larger runs only genuine outliers (at least 2× the median
|
|
241
|
+
* test duration) are badged, so the badge stays rare. Set to 0 to drop the
|
|
242
|
+
* run-size gate (outliers are still required). Must be a non-negative integer.
|
|
242
243
|
* @default 10
|
|
243
244
|
*/
|
|
244
245
|
slowTestThreshold?: number;
|
package/dist/index.js
CHANGED
|
@@ -12048,6 +12048,7 @@ function jsonForInlineScript(value) {
|
|
|
12048
12048
|
function isUnknownValue(v) {
|
|
12049
12049
|
return !v || v === "unknown";
|
|
12050
12050
|
}
|
|
12051
|
+
var SLOW_MEDIAN_FACTOR = 2;
|
|
12051
12052
|
function deriveOptions(s) {
|
|
12052
12053
|
return {
|
|
12053
12054
|
showCharts: s.charts,
|
|
@@ -12186,8 +12187,10 @@ var TemplateEngine = class {
|
|
|
12186
12187
|
}
|
|
12187
12188
|
}
|
|
12188
12189
|
}
|
|
12190
|
+
const sortedDurations = tests.map((t) => t.durationMs).sort((a, b) => a - b);
|
|
12191
|
+
const medianMs = sortedDurations.length ? sortedDurations[Math.floor((sortedDurations.length - 1) / 2)] : 0;
|
|
12189
12192
|
const ranked = tests.sort((a, b) => b.durationMs - a.durationMs).slice(0, 10);
|
|
12190
|
-
return { ranked, timedTotal: tests.length };
|
|
12193
|
+
return { ranked, timedTotal: tests.length, medianMs };
|
|
12191
12194
|
}
|
|
12192
12195
|
/**
|
|
12193
12196
|
* Project the suite tree into its render form: attach each test's failure detail
|
|
@@ -12202,7 +12205,8 @@ var TemplateEngine = class {
|
|
|
12202
12205
|
const chipById = sections.failureAnalysis ? new Map((data.classifications ?? []).map((c) => [c.testId, c])) : /* @__PURE__ */ new Map();
|
|
12203
12206
|
const slowRankById = /* @__PURE__ */ new Map();
|
|
12204
12207
|
if (sections.slowTests && slow.timedTotal > slowTestThreshold) {
|
|
12205
|
-
|
|
12208
|
+
const floorMs = slow.medianMs * SLOW_MEDIAN_FACTOR;
|
|
12209
|
+
slow.ranked.filter((t) => t.durationMs >= floorMs).forEach((t, i) => slowRankById.set(t.id, i + 1));
|
|
12206
12210
|
}
|
|
12207
12211
|
const enrichTest = (t) => ({
|
|
12208
12212
|
...t,
|
|
@@ -13291,7 +13295,7 @@ var PdfReporter = class {
|
|
|
13291
13295
|
this.dataCollector = new DataCollector();
|
|
13292
13296
|
this.pdfGenerator = new PdfGenerator();
|
|
13293
13297
|
this.options = parseOptions(rawOptions);
|
|
13294
|
-
const version = true ? "0.7.
|
|
13298
|
+
const version = true ? "0.7.1" : "0.x";
|
|
13295
13299
|
logger.info(`@reportforge/playwright-pdf v${version} initialised`);
|
|
13296
13300
|
this.licenseClient = new LicenseClient({
|
|
13297
13301
|
licenseKey: this.options.licenseKey,
|
package/dist/index.mjs
CHANGED
|
@@ -12049,6 +12049,7 @@ function jsonForInlineScript(value) {
|
|
|
12049
12049
|
function isUnknownValue(v) {
|
|
12050
12050
|
return !v || v === "unknown";
|
|
12051
12051
|
}
|
|
12052
|
+
var SLOW_MEDIAN_FACTOR = 2;
|
|
12052
12053
|
function deriveOptions(s) {
|
|
12053
12054
|
return {
|
|
12054
12055
|
showCharts: s.charts,
|
|
@@ -12187,8 +12188,10 @@ var TemplateEngine = class {
|
|
|
12187
12188
|
}
|
|
12188
12189
|
}
|
|
12189
12190
|
}
|
|
12191
|
+
const sortedDurations = tests.map((t) => t.durationMs).sort((a, b) => a - b);
|
|
12192
|
+
const medianMs = sortedDurations.length ? sortedDurations[Math.floor((sortedDurations.length - 1) / 2)] : 0;
|
|
12190
12193
|
const ranked = tests.sort((a, b) => b.durationMs - a.durationMs).slice(0, 10);
|
|
12191
|
-
return { ranked, timedTotal: tests.length };
|
|
12194
|
+
return { ranked, timedTotal: tests.length, medianMs };
|
|
12192
12195
|
}
|
|
12193
12196
|
/**
|
|
12194
12197
|
* Project the suite tree into its render form: attach each test's failure detail
|
|
@@ -12203,7 +12206,8 @@ var TemplateEngine = class {
|
|
|
12203
12206
|
const chipById = sections.failureAnalysis ? new Map((data.classifications ?? []).map((c) => [c.testId, c])) : /* @__PURE__ */ new Map();
|
|
12204
12207
|
const slowRankById = /* @__PURE__ */ new Map();
|
|
12205
12208
|
if (sections.slowTests && slow.timedTotal > slowTestThreshold) {
|
|
12206
|
-
|
|
12209
|
+
const floorMs = slow.medianMs * SLOW_MEDIAN_FACTOR;
|
|
12210
|
+
slow.ranked.filter((t) => t.durationMs >= floorMs).forEach((t, i) => slowRankById.set(t.id, i + 1));
|
|
12207
12211
|
}
|
|
12208
12212
|
const enrichTest = (t) => ({
|
|
12209
12213
|
...t,
|
|
@@ -13292,7 +13296,7 @@ var PdfReporter = class {
|
|
|
13292
13296
|
this.dataCollector = new DataCollector();
|
|
13293
13297
|
this.pdfGenerator = new PdfGenerator();
|
|
13294
13298
|
this.options = parseOptions(rawOptions);
|
|
13295
|
-
const version = true ? "0.7.
|
|
13299
|
+
const version = true ? "0.7.1" : "0.x";
|
|
13296
13300
|
logger.info(`@reportforge/playwright-pdf v${version} initialised`);
|
|
13297
13301
|
this.licenseClient = new LicenseClient({
|
|
13298
13302
|
licenseKey: this.options.licenseKey,
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
{{#if sections.releaseGate}}{{> release-gate}}{{/if}}
|
|
20
20
|
|
|
21
|
-
{{#if sections.summary}}{{> executive-summary options=(obj showPassRate=sections.passRate) }}{{/if}}
|
|
21
|
+
{{#if sections.summary}}{{> executive-summary options=(obj showPassRate=sections.passRate releaseGateShown=sections.releaseGate) }}{{/if}}
|
|
22
22
|
|
|
23
23
|
{{#if sections.charts}}{{> charts options=(obj showCharts=true showTrend=sections.trend) }}{{/if}}
|
|
24
24
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
{{#if sections.releaseGate}}{{> release-gate}}{{/if}}
|
|
20
20
|
|
|
21
|
-
{{#if sections.summary}}{{> executive-summary options=(obj showPassRate=sections.passRate) }}{{/if}}
|
|
21
|
+
{{#if sections.summary}}{{> executive-summary options=(obj showPassRate=sections.passRate releaseGateShown=sections.releaseGate) }}{{/if}}
|
|
22
22
|
|
|
23
23
|
{{#if sections.charts}}{{> charts options=(obj showCharts=true showTrend=sections.trend) }}{{/if}}
|
|
24
24
|
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
{{#if sections.releaseGate}}{{> release-gate}}{{/if}}
|
|
20
20
|
|
|
21
|
-
{{#if sections.summary}}{{> executive-summary options=(obj showPassRate=sections.passRate) }}{{/if}}
|
|
21
|
+
{{#if sections.summary}}{{> executive-summary options=(obj showPassRate=sections.passRate releaseGateShown=sections.releaseGate) }}{{/if}}
|
|
22
22
|
|
|
23
23
|
{{#if sections.charts}}{{> charts options=(obj showCharts=true showTrend=sections.trend) }}{{/if}}
|
|
24
24
|
|
|
@@ -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 +=
|
|
169
|
+
afterFit: function(scale) { if (!scale.$rfPadded) { scale.$rfPadded = true; scale.width += 30; } },
|
|
170
170
|
ticks: {
|
|
171
171
|
font: { size: 9, family: 'Inter, sans-serif' }, color: '#1F1F1C', autoSkip: false,
|
|
172
|
-
// Truncate long suite / describe labels
|
|
173
|
-
// instead of clipping at the canvas edge.
|
|
172
|
+
// Truncate only very long suite / describe labels; the wider gutter above
|
|
173
|
+
// lets most labels show in full instead of clipping at the canvas edge.
|
|
174
174
|
callback: function(value) {
|
|
175
175
|
var l = this.getLabelForValue(value);
|
|
176
|
-
return l.length >
|
|
176
|
+
return l.length > 24 ? l.slice(0, 23) + '…' : l;
|
|
177
177
|
}
|
|
178
178
|
},
|
|
179
179
|
grid: { display: false },
|
|
@@ -215,11 +215,17 @@
|
|
|
215
215
|
|
|
216
216
|
// Smart y-axis: pad above/below the actual data range so variation is readable
|
|
217
217
|
// rather than a flat line at the top of a 0–100 axis when all runs pass at 96%.
|
|
218
|
+
// Pick the step FIRST, then snap the bounds to multiples of that step, so every
|
|
219
|
+
// tick (min, min+step, … max) is a round value — never 45% / 70% from a min that
|
|
220
|
+
// isn't itself a multiple of the step. Clamps to 0/100 keep the bounds round too.
|
|
218
221
|
var minRate = Math.min.apply(null, trendRates);
|
|
219
222
|
var maxRate = Math.max.apply(null, trendRates);
|
|
220
223
|
var yPad = Math.max(8, Math.round((maxRate - minRate) * 0.4));
|
|
221
|
-
var
|
|
222
|
-
var
|
|
224
|
+
var rawMin = Math.max(0, minRate - yPad);
|
|
225
|
+
var rawMax = Math.min(100, maxRate + yPad);
|
|
226
|
+
var yStep = (rawMax - rawMin) <= 20 ? 5 : (rawMax - rawMin) <= 50 ? 10 : 25;
|
|
227
|
+
var yMin = Math.max(0, Math.floor(rawMin / yStep) * yStep);
|
|
228
|
+
var yMax = Math.min(100, Math.ceil(rawMax / yStep) * yStep);
|
|
223
229
|
|
|
224
230
|
// Data-label plugin: renders pass-rate % above each point.
|
|
225
231
|
// Canvas ID guard prevents firing on the doughnut and bar charts.
|
|
@@ -279,7 +285,7 @@
|
|
|
279
285
|
callback: function(v) { return v + '%'; },
|
|
280
286
|
font: { size: 8.5, family: 'Inter, sans-serif' },
|
|
281
287
|
color: '#8C8B82',
|
|
282
|
-
|
|
288
|
+
stepSize: yStep,
|
|
283
289
|
},
|
|
284
290
|
grid: { color: 'rgba(31,31,28,0.07)' },
|
|
285
291
|
border: { color: 'transparent' },
|
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
<div class="section">
|
|
2
|
+
{{!-- The release gate (when the layout renders it) already states the verdict
|
|
3
|
+
prominently, so the chip would just repeat it — suppress it then. The layout
|
|
4
|
+
passes options.releaseGateShown; it is absent for custom templates, so the
|
|
5
|
+
chip renders by default there (a custom template may not include a gate). --}}
|
|
6
|
+
{{#unless options.releaseGateShown}}
|
|
2
7
|
<span class="verdict verdict--{{stats.verdict}}">
|
|
3
8
|
{{verdictLabel stats.verdict}}
|
|
4
9
|
</span>
|
|
10
|
+
{{/unless}}
|
|
5
11
|
|
|
6
12
|
<div class="kpi-strip">
|
|
7
13
|
<div class="kpi-card kpi-card--total">
|
|
@@ -15,19 +21,21 @@
|
|
|
15
21
|
<div class="kpi-card__rate">{{stats.passRate}}%</div>
|
|
16
22
|
{{/if}}
|
|
17
23
|
</div>
|
|
18
|
-
|
|
24
|
+
{{!-- Zero-value status cards are dimmed: a coloured "0" for an absent status
|
|
25
|
+
draws the eye to nothing and dilutes the counts that matter. --}}
|
|
26
|
+
<div class="kpi-card kpi-card--fail{{#unless stats.failed}} kpi-card--zero{{/unless}}">
|
|
19
27
|
<div class="kpi-card__value">{{stats.failed}}</div>
|
|
20
28
|
<div class="kpi-card__label">Failed</div>
|
|
21
29
|
</div>
|
|
22
|
-
<div class="kpi-card kpi-card--timedout">
|
|
30
|
+
<div class="kpi-card kpi-card--timedout{{#unless stats.timedOut}} kpi-card--zero{{/unless}}">
|
|
23
31
|
<div class="kpi-card__value">{{stats.timedOut}}</div>
|
|
24
32
|
<div class="kpi-card__label">Timed Out</div>
|
|
25
33
|
</div>
|
|
26
|
-
<div class="kpi-card kpi-card--skip">
|
|
34
|
+
<div class="kpi-card kpi-card--skip{{#unless stats.skipped}} kpi-card--zero{{/unless}}">
|
|
27
35
|
<div class="kpi-card__value">{{stats.skipped}}</div>
|
|
28
36
|
<div class="kpi-card__label">Skipped</div>
|
|
29
37
|
</div>
|
|
30
|
-
<div class="kpi-card kpi-card--flaky">
|
|
38
|
+
<div class="kpi-card kpi-card--flaky{{#unless stats.flaky}} kpi-card--zero{{/unless}}">
|
|
31
39
|
<div class="kpi-card__value">{{stats.flaky}}</div>
|
|
32
40
|
<div class="kpi-card__label">Flaky</div>
|
|
33
41
|
</div>
|
|
@@ -38,10 +46,16 @@
|
|
|
38
46
|
<span class="summary-row__label">Duration</span>
|
|
39
47
|
<span class="summary-row__value">{{formatDuration stats.duration}}</span>
|
|
40
48
|
</div>
|
|
49
|
+
{{!-- Pass rate normally shows on the Passed KPI card and the doughnut centre, so
|
|
50
|
+
it is omitted here to avoid a third restatement on one page. Fallback: if
|
|
51
|
+
BOTH of those are off (passRate modifier off AND charts off, e.g. a stripped
|
|
52
|
+
minimal), show it here so the headline metric never disappears entirely. --}}
|
|
53
|
+
{{#unless (or options.showPassRate @root.sections.charts)}}
|
|
41
54
|
<div class="summary-row__item">
|
|
42
55
|
<span class="summary-row__label">Pass Rate</span>
|
|
43
56
|
<span class="summary-row__value" style="color:var(--pass);">{{stats.passRate}}%</span>
|
|
44
57
|
</div>
|
|
58
|
+
{{/unless}}
|
|
45
59
|
<div class="summary-row__item">
|
|
46
60
|
<span class="summary-row__label">Generated</span>
|
|
47
61
|
<span class="summary-row__value">{{formatDate meta.generatedAt}}</span>
|
|
@@ -153,6 +153,12 @@ tr:last-child td { border-bottom: none; }
|
|
|
153
153
|
|
|
154
154
|
/* ── Page breaks ────────────────────────────────────────────────────────── */
|
|
155
155
|
.no-break { page-break-inside: avoid; }
|
|
156
|
+
/* Keep a failed test row glued to its inline failure detail: the detail block is
|
|
157
|
+
page-break-inside:avoid, so when it can't fit the remaining space it moves to the
|
|
158
|
+
next page — these pull the test row along so the title never orphans above it. */
|
|
159
|
+
.test-row--has-detail { page-break-after: avoid; break-after: avoid; }
|
|
160
|
+
.test-row--has-detail > td { border-bottom: none; }
|
|
161
|
+
.failure-row { page-break-before: avoid; break-before: avoid; }
|
|
156
162
|
|
|
157
163
|
/* ── Section wrapper ────────────────────────────────────────────────────── */
|
|
158
164
|
/* Large sections (suite breakdown, failures, tables) MAY flow across pages —
|
|
@@ -664,6 +670,11 @@ code {
|
|
|
664
670
|
print-color-adjust: exact;
|
|
665
671
|
}
|
|
666
672
|
|
|
673
|
+
/* Dimmed zero-value KPI card — a coloured "0" for an absent status (no timeouts,
|
|
674
|
+
no flakes) draws the eye to nothing; muting it lets the real counts lead. Shared
|
|
675
|
+
modifier so every template's kpi-strip gets it. */
|
|
676
|
+
.kpi-card--zero { opacity: 0.42; }
|
|
677
|
+
|
|
667
678
|
/* ── Suite breakdown — shared so any template (incl. executive) can toggle it on ─ */
|
|
668
679
|
/* Values match the detailed template; minimal.css keeps its own tuned overrides
|
|
669
680
|
for .suite-group (margin) and .suite-stats (gap), which win via templateCSS order. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reportforge/playwright-pdf",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.1",
|
|
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",
|