@piwitests/reporter 0.9.1 → 0.12.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
@@ -39,6 +39,19 @@ Run your tests — results are uploaded automatically:
39
39
  npx playwright test
40
40
  ```
41
41
 
42
+ **Recommended: enable the [capture fixtures](#capture-fixtures)** — one small file unlocks the dashboard's richest features (locator healing, slow-endpoint analysis, Web Vitals, console capture, failure-time ARIA snapshots):
43
+
44
+ ```typescript
45
+ // tests/fixtures.ts
46
+ import { test as base, expect } from '@playwright/test'
47
+ import { piwiFixtures } from '@piwitests/reporter'
48
+
49
+ export const test = base.extend(piwiFixtures)
50
+ export { expect }
51
+ ```
52
+
53
+ Import `test` from this file in your specs instead of `@playwright/test` — see [Capture fixtures](#capture-fixtures) below.
54
+
42
55
  Prefer to wire it up by hand? Add the reporter to the `reporter` array instead:
43
56
 
44
57
  ```typescript
@@ -123,35 +136,42 @@ export default defineConfig({
123
136
  })
124
137
  ```
125
138
 
126
- ## Performance Metrics & Web Vitals
139
+ ## Capture fixtures
127
140
 
128
- To capture network request timing and browser Web Vitals, use the provided fixtures:
141
+ The reporter works without any test-code changes, but the **capture fixtures** observe your tests from the inside and unlock the dashboard's richest features. Extend your `test` with them:
129
142
 
130
143
  ```typescript
131
144
  // tests/fixtures.ts
132
145
  import { test as base, expect } from '@playwright/test'
133
- import { dashboardFixtures } from '@piwitests/reporter'
146
+ import { piwiFixtures } from '@piwitests/reporter'
134
147
 
135
- export const test = base.extend(dashboardFixtures)
148
+ export const test = base.extend(piwiFixtures)
136
149
  export { expect }
137
150
  ```
138
151
 
139
- Or extend the base `test` in one line with `extendDashboardFixtures`:
152
+ Or extend the base `test` in one line with `extendPiwiFixtures`:
140
153
 
141
154
  ```typescript
142
155
  import { test as base } from '@playwright/test'
143
- import { extendDashboardFixtures } from '@piwitests/reporter'
156
+ import { extendPiwiFixtures } from '@piwitests/reporter'
144
157
 
145
- export const test = extendDashboardFixtures(base)
158
+ export const test = extendPiwiFixtures(base)
146
159
  export { expect } from '@playwright/test'
147
160
  ```
148
161
 
162
+ Then import `test` from your fixtures file in every spec — a spec that imports `test` from `@playwright/test` directly still runs and reports fine, it just isn't captured.
163
+
149
164
  ### What gets captured
150
165
 
151
- - **Network requests** — method, URL, status, duration, resource type. Aggregated on the dashboard into a *Slow API Endpoints* table grouped by `METHOD + normalized route`.
166
+ - **Network requests** — method, URL, status, duration, resource type (API/document traffic only). Aggregated on the dashboard into a *Slow API Endpoints* table grouped by `METHOD + normalized route`.
167
+ - **Console entries** — `warning`, `error`, and `assert` messages with their source location.
152
168
  - **Browser Web Vitals** — TTFB, DOM Interactive, DOMContentLoaded, Load Complete, First Paint, First Contentful Paint — displayed with color-coded thresholds.
169
+ - **ARIA snapshot** — captured automatically when a test fails, shown as failure evidence and fed to the AI diagnosis.
170
+ - **Locator snapshots** — for each acted-on element, its attributes plus ranked alternative locators, stamped with the call site. These power locator healing; when a failing locator matches nothing, a fresh suggestion is attached as a Playwright annotation.
171
+
172
+ Capture works for the `page` fixture, `browser.newPage()`, `browser.newContext().newPage()`, and popups. Everything is only collected when `collectPerformanceMetrics` is `true` (the default); locator snapshots can be disabled separately with `captureLocators: false`.
153
173
 
154
- Both are only collected when `collectPerformanceMetrics` is `true` (the default).
174
+ Without the fixtures you still get full run history, statuses, errors, traces, reports, streaming, and clustering — the fixtures add the slow-endpoint, Web Vitals, console, ARIA, and locator-healing layers. See the [capture fixtures guide](https://piwitests.github.io/capture-fixtures) for the full feature matrix and composition patterns.
155
175
 
156
176
  ## Authentication
157
177
 
@@ -195,7 +215,7 @@ When `collectCiInfo` is enabled (default), the reporter auto-detects:
195
215
  2. As tests complete, results are streamed in batches to the server
196
216
  3. After all tests finish, HTML reports are compressed and uploaded
197
217
  4. Trace files from test attachments are uploaded
198
- 5. Network request and web vitals data (from fixtures) are included per test case
218
+ 5. Data from the capture fixtures (network requests, console entries, web vitals, ARIA snapshots, locator snapshots) is included per test case
199
219
  6. The server stores everything and makes it available in the dashboard UI
200
220
 
201
221
  ## Requirements
@@ -229,10 +249,10 @@ Everything public — the reporter, config helpers, and the capture fixtures —
229
249
  - Ensure traces are enabled: `use: { trace: 'retain-on-failure' }`
230
250
  - Check the dashboard server is running and accessible at `serverUrl`
231
251
 
232
- ### Network/Web Vitals not appearing
252
+ ### Fixture data not appearing (network, Web Vitals, console, ARIA, locator healing)
233
253
 
234
- - Extend your `test` with `dashboardFixtures` / `extendDashboardFixtures` from `@piwitests/reporter`
235
- - Verify `collectPerformanceMetrics` is not set to `false`
254
+ - Extend your `test` with `piwiFixtures` / `extendPiwiFixtures` from `@piwitests/reporter`, and import `test` from your fixtures file in every spec — not from `@playwright/test` directly
255
+ - Verify `collectPerformanceMetrics` is not set to `false` (and `captureLocators` for locator healing)
236
256
  - Ensure tests navigate to at least one page (`await page.goto(...)`)
237
257
 
238
258
  ### Connection errors
package/dist/index.d.ts CHANGED
@@ -11,5 +11,6 @@ export default PiwiDashboardReporter;
11
11
  export { PiwiDashboardReporter };
12
12
  export { wrapConfig } from './public/config-wrapper.js';
13
13
  export { createGlobalSetup } from './public/global-setup.js';
14
- export { dashboardFixtures, extendDashboardFixtures } from './internal/capture/capture-fixtures.js';
14
+ export { piwiFixtures, extendPiwiFixtures } from './internal/capture/capture-fixtures.js';
15
+ export type { PiwiFixtures } from './internal/capture/capture-fixtures.js';
15
16
  export type { PiwiDashboardOptions, PlaywrightTestConfig } from './public/options.js';
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.extendDashboardFixtures = exports.dashboardFixtures = exports.createGlobalSetup = exports.wrapConfig = exports.PiwiDashboardReporter = void 0;
3
+ exports.extendPiwiFixtures = exports.piwiFixtures = exports.createGlobalSetup = exports.wrapConfig = exports.PiwiDashboardReporter = void 0;
4
4
  /**
5
5
  * Public API of `@piwitests/reporter`.
6
6
  *
@@ -20,5 +20,5 @@ var global_setup_js_1 = require("./public/global-setup.js");
20
20
  Object.defineProperty(exports, "createGlobalSetup", { enumerable: true, get: function () { return global_setup_js_1.createGlobalSetup; } });
21
21
  // ── Capture fixtures ─────────────────────────────────────────────────────────
22
22
  var capture_fixtures_js_1 = require("./internal/capture/capture-fixtures.js");
23
- Object.defineProperty(exports, "dashboardFixtures", { enumerable: true, get: function () { return capture_fixtures_js_1.dashboardFixtures; } });
24
- Object.defineProperty(exports, "extendDashboardFixtures", { enumerable: true, get: function () { return capture_fixtures_js_1.extendDashboardFixtures; } });
23
+ Object.defineProperty(exports, "piwiFixtures", { enumerable: true, get: function () { return capture_fixtures_js_1.piwiFixtures; } });
24
+ Object.defineProperty(exports, "extendPiwiFixtures", { enumerable: true, get: function () { return capture_fixtures_js_1.extendPiwiFixtures; } });
@@ -1,8 +1,8 @@
1
1
  /**
2
2
  * Names of the `testInfo` attachments the dashboard fixtures produce and the
3
3
  * reporter parses. Single source of truth — imported by the producer
4
- * (`fixtures.ts`), the consumers (`reporter.ts` / `file-handler.ts`), and the
5
- * dogfooding `application/tests/fixtures.ts`, so producer and consumer can
4
+ * (`capture-fixtures.ts`), the consumers (`reporter.ts` / `file-handler.ts`), and
5
+ * the dogfooding `application/tests/fixtures.ts`, so producer and consumer can
6
6
  * never drift on a name.
7
7
  */
8
8
  export declare const ATTACHMENT_NAMES: {
@@ -12,6 +12,7 @@ export declare const ATTACHMENT_NAMES: {
12
12
  readonly network: "piwi-network";
13
13
  readonly webVitals: "piwi-web-vitals";
14
14
  readonly locatorSuggestion: "piwi-locator-suggestion";
15
+ readonly pageState: "piwi-page-state";
15
16
  };
16
17
  /** Set of every internal attachment name — used to skip them when collecting user attachments. */
17
18
  export declare const INTERNAL_ATTACHMENT_NAMES: ReadonlySet<string>;
@@ -4,8 +4,8 @@ exports.LOCATOR_SUGGESTION_ANNOTATION = exports.INTERNAL_ATTACHMENT_NAMES = expo
4
4
  /**
5
5
  * Names of the `testInfo` attachments the dashboard fixtures produce and the
6
6
  * reporter parses. Single source of truth — imported by the producer
7
- * (`fixtures.ts`), the consumers (`reporter.ts` / `file-handler.ts`), and the
8
- * dogfooding `application/tests/fixtures.ts`, so producer and consumer can
7
+ * (`capture-fixtures.ts`), the consumers (`reporter.ts` / `file-handler.ts`), and
8
+ * the dogfooding `application/tests/fixtures.ts`, so producer and consumer can
9
9
  * never drift on a name.
10
10
  */
11
11
  exports.ATTACHMENT_NAMES = {
@@ -15,6 +15,7 @@ exports.ATTACHMENT_NAMES = {
15
15
  network: 'piwi-network',
16
16
  webVitals: 'piwi-web-vitals',
17
17
  locatorSuggestion: 'piwi-locator-suggestion',
18
+ pageState: 'piwi-page-state',
18
19
  };
19
20
  /** Set of every internal attachment name — used to skip them when collecting user attachments. */
20
21
  exports.INTERNAL_ATTACHMENT_NAMES = new Set(Object.values(exports.ATTACHMENT_NAMES));
@@ -1,4 +1,7 @@
1
- import type { Fixtures, Locator } from '@playwright/test';
1
+ import type { Fixtures, Locator, PlaywrightTestArgs, PlaywrightTestOptions, PlaywrightWorkerArgs, PlaywrightWorkerOptions, TestType } from '@playwright/test';
2
+ type FixtureArgs = {
3
+ [key: string]: any;
4
+ };
2
5
  /** Shape returned by the in-page element probe (see `wrapLocator`). */
3
6
  interface CapturedAttrs {
4
7
  tagName: string;
@@ -17,7 +20,108 @@ interface CapturedAttrs {
17
20
  name?: number;
18
21
  classes?: Record<string, number>;
19
22
  };
23
+ /** Position among same-role elements, document-wide (null when the element has no role). */
24
+ rolePosition: {
25
+ role: string;
26
+ count: number;
27
+ index: number;
28
+ levelCount?: number;
29
+ } | null;
30
+ /** Anchor-worthy ancestors, nearest first (empty when none found or probing failed). */
31
+ ancestors: Array<{
32
+ tag: string;
33
+ depth: number;
34
+ testId: string | null;
35
+ id: string | null;
36
+ role: string | null;
37
+ ariaLabel: string | null;
38
+ scopedRoleCount?: number;
39
+ testIdCount?: number;
40
+ idCount?: number;
41
+ roleCount?: number;
42
+ }>;
20
43
  }
44
+ /** Plain-object projection of a performance entry, shipped out of the page. */
45
+ export interface RawVitalEntry {
46
+ startTime?: number;
47
+ value?: number;
48
+ hadRecentInput?: boolean;
49
+ interactionId?: number;
50
+ duration?: number;
51
+ }
52
+ /**
53
+ * Aggregate buffered performance entries into LCP/CLS/INP. Pure and Node-side
54
+ * so it is unit-testable; the in-page evaluate only ships raw entry projections.
55
+ * A null entry list means the entry type is unsupported (non-Chromium) — the
56
+ * metric is null rather than 0 so absence is distinguishable from "no shifts".
57
+ */
58
+ export declare function computeCoreVitals(lcpEntries: RawVitalEntry[] | null, shiftEntries: RawVitalEntry[] | null, eventEntries: RawVitalEntry[] | null): {
59
+ lcp: number | null;
60
+ cls: number | null;
61
+ inp: number | null;
62
+ } | null;
63
+ /** Page state captured at test end. Storage values and cookie values are NEVER included. */
64
+ export interface PageState {
65
+ url: string;
66
+ hash: string | null;
67
+ /** `history.state` as JSON, capped and token-masked. */
68
+ historyState: string | null;
69
+ /** Key names + value lengths only. */
70
+ localStorage: Array<{
71
+ key: string;
72
+ length: number;
73
+ }>;
74
+ sessionStorage: Array<{
75
+ key: string;
76
+ length: number;
77
+ }>;
78
+ /** Cookie names + flags only (values are never read). */
79
+ cookies: Array<{
80
+ name: string;
81
+ domain: string;
82
+ path: string;
83
+ httpOnly: boolean;
84
+ secure: boolean;
85
+ sameSite?: string;
86
+ expires?: number;
87
+ }>;
88
+ }
89
+ /** Raw in-page reads shipped out of the evaluate (see `readPageState`). */
90
+ export interface RawPageState {
91
+ url: string;
92
+ hash: string | null;
93
+ historyState: string | null;
94
+ localStorage: Array<{
95
+ key: string;
96
+ length: number;
97
+ }>;
98
+ sessionStorage: Array<{
99
+ key: string;
100
+ length: number;
101
+ }>;
102
+ }
103
+ /**
104
+ * Assemble the wire page-state from the in-page reads and the context cookies.
105
+ * Pure and Node-side so the sanitization (token masking, caps, value-free
106
+ * cookies) is unit-testable.
107
+ */
108
+ export declare function buildPageState(raw: RawPageState, cookies: Array<Record<string, unknown>> | null): PageState;
109
+ /**
110
+ * Everything the in-page probe needs, serialized into the browser on every
111
+ * action. `tagRoles`/`inputRoles` are the shared role maps (single source of
112
+ * truth in `locator-healing.ts`), and `roleSources` is the CSS selector for
113
+ * every element the probe can resolve a role for — all derived from the map so
114
+ * nothing is hand-maintained twice. Exported so the dogfood mirror
115
+ * (`application/tests/fixtures.ts`) reuses the same assembled object.
116
+ */
117
+ export interface ProbeArg {
118
+ keep: string[];
119
+ tagRoles: Record<string, string>;
120
+ inputRoles: Record<string, string>;
121
+ roleSources: string;
122
+ }
123
+ /** Built once — passed verbatim into evaluate() on every action. */
124
+ export declare const CAPTURED_ATTRS_ARG: ProbeArg;
21
125
  /**
22
126
  * ARIA snapshot that tolerates every Playwright version the reporter supports,
23
127
  * returning null instead of throwing so a capture can never fail the test. The
@@ -40,7 +144,18 @@ export declare function ariaSnapshotBestEffort(target: Locator, timeout?: number
40
144
  * `el` is browser-context (no DOM lib in this Node package), hence `any`.
41
145
  * Exported for unit testing; still passed directly to `evaluate()` below.
42
146
  */
43
- export declare function probeElementAttrs(el: any, keep: string[]): CapturedAttrs;
147
+ export declare function probeElementAttrs(el: any, arg: ProbeArg): CapturedAttrs;
148
+ /**
149
+ * The fixtures `piwiFixtures` / `extendPiwiFixtures` contribute. The single
150
+ * added fixture is `piwiCapture`: an auto, test-scoped teardown hook that
151
+ * attaches the collected `piwi-*` data. Its name is **reserved** — a user
152
+ * fixture of the same name replaces the capture teardown and silently disables
153
+ * all capture. Exported so `piwiFixtures` and the extended `test` carry it in
154
+ * their types (and a collision surfaces to the type checker).
155
+ */
156
+ export interface PiwiFixtures {
157
+ piwiCapture: void;
158
+ }
44
159
  /**
45
160
  * Playwright fixtures that collect network requests, console entries,
46
161
  * web vitals, ARIA snapshots, and locator interaction data during a test.
@@ -50,9 +165,10 @@ export declare function probeElementAttrs(el: any, keep: string[]): CapturedAttr
50
165
  * `browser.newContext()`. Collected data is attached as `piwi-*`
51
166
  * test-info attachments which the Piwi Dashboard reporter parses on `onTestEnd`.
52
167
  */
53
- export declare const dashboardFixtures: Fixtures;
168
+ export declare const piwiFixtures: Fixtures<PiwiFixtures, {}, PlaywrightTestArgs & PlaywrightTestOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
54
169
  /**
55
- * Extend a Playwright `test` object with Piwi Dashboard fixtures.
170
+ * Extend a Playwright `test` object with the Piwi capture fixtures. The
171
+ * returned `test` carries the existing fixtures plus {@link PiwiFixtures}.
56
172
  *
57
173
  * Use this instead of importing `@playwright/test` directly from this package
58
174
  * to avoid the "Requiring @playwright/test second time" error caused by
@@ -61,10 +177,10 @@ export declare const dashboardFixtures: Fixtures;
61
177
  * @example
62
178
  * ```ts
63
179
  * import { test as base } from '@playwright/test';
64
- * import { extendDashboardFixtures } from '@piwitests/reporter';
180
+ * import { extendPiwiFixtures } from '@piwitests/reporter';
65
181
  *
66
- * export const test = extendDashboardFixtures(base);
182
+ * export const test = extendPiwiFixtures(base);
67
183
  * ```
68
184
  */
69
- export declare function extendDashboardFixtures<T>(test: T): T;
185
+ export declare function extendPiwiFixtures<TestArgs extends FixtureArgs, WorkerArgs extends FixtureArgs>(test: TestType<TestArgs, WorkerArgs>): TestType<TestArgs & PiwiFixtures, WorkerArgs>;
70
186
  export {};