@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 +33 -13
- package/dist/index.d.ts +2 -1
- package/dist/index.js +3 -3
- package/dist/internal/capture/attachments.d.ts +3 -2
- package/dist/internal/capture/attachments.js +3 -2
- package/dist/internal/capture/capture-fixtures.d.ts +123 -7
- package/dist/internal/capture/capture-fixtures.js +528 -57
- package/dist/internal/capture/locator-healing.d.ts +63 -3
- package/dist/internal/capture/locator-healing.js +134 -26
- package/dist/internal/config/env.d.ts +1 -0
- package/dist/internal/config/env.js +9 -0
- package/dist/internal/files/file-handler.js +9 -0
- package/dist/internal/submit/serializer.js +1 -0
- package/dist/public/options.d.ts +8 -0
- package/dist/types/collected.d.ts +1 -0
- package/dist/types/wire.d.ts +2 -0
- package/package.json +3 -2
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
|
-
##
|
|
139
|
+
## Capture fixtures
|
|
127
140
|
|
|
128
|
-
|
|
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 {
|
|
146
|
+
import { piwiFixtures } from '@piwitests/reporter'
|
|
134
147
|
|
|
135
|
-
export const test = base.extend(
|
|
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 `
|
|
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 {
|
|
156
|
+
import { extendPiwiFixtures } from '@piwitests/reporter'
|
|
144
157
|
|
|
145
|
-
export const test =
|
|
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
|
-
|
|
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.
|
|
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
|
-
###
|
|
252
|
+
### Fixture data not appearing (network, Web Vitals, console, ARIA, locator healing)
|
|
233
253
|
|
|
234
|
-
- Extend your `test` with `
|
|
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 {
|
|
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.
|
|
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, "
|
|
24
|
-
Object.defineProperty(exports, "
|
|
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
|
|
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
|
|
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,
|
|
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
|
|
168
|
+
export declare const piwiFixtures: Fixtures<PiwiFixtures, {}, PlaywrightTestArgs & PlaywrightTestOptions, PlaywrightWorkerArgs & PlaywrightWorkerOptions>;
|
|
54
169
|
/**
|
|
55
|
-
* Extend a Playwright `test` object with Piwi
|
|
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 {
|
|
180
|
+
* import { extendPiwiFixtures } from '@piwitests/reporter';
|
|
65
181
|
*
|
|
66
|
-
* export const test =
|
|
182
|
+
* export const test = extendPiwiFixtures(base);
|
|
67
183
|
* ```
|
|
68
184
|
*/
|
|
69
|
-
export declare function
|
|
185
|
+
export declare function extendPiwiFixtures<TestArgs extends FixtureArgs, WorkerArgs extends FixtureArgs>(test: TestType<TestArgs, WorkerArgs>): TestType<TestArgs & PiwiFixtures, WorkerArgs>;
|
|
70
186
|
export {};
|