@andrii_kremlovskyi/playwright-traces-reader 1.0.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 ADDED
@@ -0,0 +1,164 @@
1
+ # playwright-traces-reader
2
+
3
+ Parse [Playwright](https://playwright.dev) trace files into structured data — useful for AI agents, custom reporters, and post-run analysis tooling.
4
+
5
+ ## Features
6
+
7
+ - Extract test steps with timings and errors
8
+ - Extract failed steps with full error messages
9
+ - Extract API and browser network traffic (with resolved request/response bodies)
10
+ - Save screenshots from screencasts
11
+ - Extract full DOM snapshots (before / during / after each action) with back-reference resolution
12
+ - Support for multi-test reports (many SHA1 trace entries in one `data/` directory)
13
+ - GitHub Copilot skill scaffold via `init-skills` CLI command
14
+
15
+ ## Installation
16
+
17
+ ```bash
18
+ npm install @kremlovskyi/playwright-traces-reader
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ```typescript
24
+ import {
25
+ prepareTraceDir,
26
+ getFailedTests,
27
+ getNetworkTraffic,
28
+ getTestSteps,
29
+ extractScreenshots,
30
+ getDomSnapshots,
31
+ } from '@kremlovskyi/playwright-traces-reader';
32
+
33
+ // Point at a single extracted trace directory (or a .zip)
34
+ const ctx = await prepareTraceDir('/path/to/playwright-report/data/<sha1>');
35
+
36
+ const failures = await getFailedTests(ctx);
37
+ const traffic = await getNetworkTraffic(ctx);
38
+ const steps = await getTestSteps(ctx);
39
+ ```
40
+
41
+ ### Multi-test reports
42
+
43
+ A Playwright HTML report stores one trace entry per test inside `playwright-report/data/`. Use `listTraces()` to iterate all of them:
44
+
45
+ ```typescript
46
+ import { listTraces, getFailedTests } from '@kremlovskyi/playwright-traces-reader';
47
+
48
+ const traces = await listTraces('/path/to/playwright-report/data');
49
+
50
+ for (const ctx of traces) {
51
+ const failures = await getFailedTests(ctx);
52
+ // ... process per-test results
53
+ }
54
+ ```
55
+
56
+ Both extracted directories and `.zip` archives are handled automatically. Non-trace files (`.md`, `.png`, etc.) are ignored.
57
+
58
+ ## API
59
+
60
+ ### `listTraces(reportDataDir)`
61
+
62
+ Discovers all trace contexts inside a `data/` directory. Returns `TraceContext[]`.
63
+
64
+ ### `prepareTraceDir(tracePath)`
65
+
66
+ Takes a single path (extracted directory or `.zip`) and returns a `TraceContext`.
67
+
68
+ ### `getTestSteps(ctx)`
69
+
70
+ Returns the full step tree from `test.trace` as `TestStep[]`. Each step has:
71
+
72
+ | Field | Type | Description |
73
+ |---|---|---|
74
+ | `callId` | `string` | Unique step identifier |
75
+ | `title` | `string` | Human-readable step name |
76
+ | `method` | `string \| undefined` | API method if applicable |
77
+ | `startTime` | `number` | Unix ms timestamp |
78
+ | `endTime` | `number \| null` | Unix ms timestamp |
79
+ | `durationMs` | `number \| null` | Wall-clock duration |
80
+ | `error` | `TraceError \| null` | Error details if the step failed |
81
+ | `children` | `TestStep[]` | Nested child steps |
82
+
83
+ ### `getFailedTests(ctx)`
84
+
85
+ Walks the step tree and returns a flat `FailedStep[]` for every step that has an `error`. Useful as a quick failure summary.
86
+
87
+ ### `getNetworkTraffic(ctx)`
88
+
89
+ Returns `NetworkEntry[]` from all `*.network` trace files. Each entry includes:
90
+
91
+ | Field | Type | Description |
92
+ |---|---|---|
93
+ | `url` | `string` | Request URL |
94
+ | `method` | `string` | HTTP method |
95
+ | `status` | `number` | HTTP response status |
96
+ | `source` | `'api' \| 'browser'` | `'api'` = Playwright `APIRequestContext`; `'browser'` = XHR / navigation |
97
+ | `requestBody` | `string \| null` | Resolved request body |
98
+ | `responseBody` | `string \| null` | Resolved response body (binary → `[binary: ...]` placeholder) |
99
+ | `contentType` | `string \| null` | Response content-type |
100
+ | `pageref` | `string \| undefined` | Browser page ID (browser traffic only) |
101
+
102
+ ### `extractScreenshots(ctx, outDir)`
103
+
104
+ Writes screencast frames from all `[N]-trace.trace` files to `outDir` as numbered `.jpeg` files. Returns `Screenshot[]` with `savedPath`, `timestamp`, `pageId`, `width`, and `height`.
105
+
106
+ ### `getDomSnapshots(ctx)`
107
+
108
+ Returns `ActionDomSnapshots[]` — one entry per browser action that has DOM snapshots. Each entry groups three phases:
109
+
110
+ | Field | Type | Description |
111
+ |---|---|---|
112
+ | `callId` | `string` | Action identifier |
113
+ | `before` | `DomSnapshot \| null` | DOM before the action |
114
+ | `action` | `DomSnapshot \| null` | DOM during the action (mid-interaction) |
115
+ | `after` | `DomSnapshot \| null` | DOM after the action completed |
116
+
117
+ Each `DomSnapshot` contains:
118
+
119
+ | Field | Type | Description |
120
+ |---|---|---|
121
+ | `html` | `string` | Full serialized HTML (back-references resolved, `<script>` stripped) |
122
+ | `phase` | `'before' \| 'action' \| 'after'` | Snapshot phase |
123
+ | `frameUrl` | `string` | URL of the frame at snapshot time |
124
+ | `targetElement` | `string \| null` | `callId` of the action that targeted an element, or `null` |
125
+ | `viewport` | `{ width: number; height: number }` | Viewport dimensions |
126
+ | `timestamp` | `number` | Unix ms timestamp |
127
+
128
+ ### `getResourceBuffer(ctx, sha1)`
129
+
130
+ Low-level helper. Resolves a SHA1 filename to a raw `Buffer` from `resources/`. Returns `null` if not found.
131
+
132
+ ### `readNdjson<T>(filePath)`
133
+
134
+ Low-level async generator that streams and parses an NDJSON file line by line. Silently skips malformed lines.
135
+
136
+ ## GitHub Copilot Skill
137
+
138
+ Install a ready-made GitHub Copilot skill scaffold into your project:
139
+
140
+ ```bash
141
+ npx @kremlovskyi/playwright-traces-reader init-skills
142
+ # or into a custom target directory:
143
+ npx @kremlovskyi/playwright-traces-reader init-skills ./my-project
144
+ ```
145
+
146
+ This copies a `SKILL.md` template to `.github/skills/analyze-playwright-traces/SKILL.md` with code examples for all extractor functions. Once in place, GitHub Copilot will use the skill automatically when answering questions about your Playwright test runs.
147
+
148
+ ## Trace Format
149
+
150
+ Playwright HTML reports store traces in `playwright-report/data/<sha1>/`:
151
+
152
+ ```
153
+ <sha1>/
154
+ ├── test.trace ← step tree (getTestSteps / getFailedTests)
155
+ ├── 0-trace.trace ← browser actions, screenshots, DOM snapshots
156
+ ├── 0-trace.network ← network HAR entries
157
+ └── resources/ ← binary blobs (bodies, images) addressed by SHA1
158
+ ```
159
+
160
+ All trace files use **Newline-Delimited JSON (NDJSON)**.
161
+
162
+ ## License
163
+
164
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=cli.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":""}
package/dist/cli.js ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
4
+ if (k2 === undefined) k2 = k;
5
+ var desc = Object.getOwnPropertyDescriptor(m, k);
6
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
7
+ desc = { enumerable: true, get: function() { return m[k]; } };
8
+ }
9
+ Object.defineProperty(o, k2, desc);
10
+ }) : (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ o[k2] = m[k];
13
+ }));
14
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
15
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
16
+ }) : function(o, v) {
17
+ o["default"] = v;
18
+ });
19
+ var __importStar = (this && this.__importStar) || (function () {
20
+ var ownKeys = function(o) {
21
+ ownKeys = Object.getOwnPropertyNames || function (o) {
22
+ var ar = [];
23
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
24
+ return ar;
25
+ };
26
+ return ownKeys(o);
27
+ };
28
+ return function (mod) {
29
+ if (mod && mod.__esModule) return mod;
30
+ var result = {};
31
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
32
+ __setModuleDefault(result, mod);
33
+ return result;
34
+ };
35
+ })();
36
+ Object.defineProperty(exports, "__esModule", { value: true });
37
+ const fs = __importStar(require("fs"));
38
+ const path = __importStar(require("path"));
39
+ const [, , command, ...args] = process.argv;
40
+ async function initSkills(targetDir) {
41
+ const skillDir = path.join(targetDir, '.github', 'skills', 'analyze-playwright-traces');
42
+ await fs.promises.mkdir(skillDir, { recursive: true });
43
+ // Copy the template SKILL.md from this package
44
+ const templatePath = path.resolve(__dirname, '..', 'templates', 'skills', 'analyze-playwright-traces', 'SKILL.md');
45
+ const destPath = path.join(skillDir, 'SKILL.md');
46
+ if (!fs.existsSync(templatePath)) {
47
+ console.error(`Template not found at ${templatePath}`);
48
+ process.exit(1);
49
+ }
50
+ await fs.promises.copyFile(templatePath, destPath);
51
+ console.log(`✔ Skill scaffolded at ${destPath}`);
52
+ }
53
+ async function main() {
54
+ if (command === 'init-skills') {
55
+ const targetDir = args[0] ?? process.cwd();
56
+ await initSkills(targetDir);
57
+ return;
58
+ }
59
+ console.log(`
60
+ playwright-traces-reader — CLI for parsing Playwright trace files
61
+
62
+ Usage:
63
+ npx playwright-traces-reader init-skills [targetDir]
64
+ Scaffolds the GitHub Copilot skill template into <targetDir>/.github/skills/analyze-playwright-traces/SKILL.md
65
+ Defaults targetDir to the current working directory.
66
+ `);
67
+ }
68
+ main().catch(err => {
69
+ console.error(err);
70
+ process.exit(1);
71
+ });
72
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AACA,uCAAyB;AACzB,2CAA6B;AAE7B,MAAM,CAAC,EAAE,AAAD,EAAG,OAAO,EAAE,GAAG,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;AAE5C,KAAK,UAAU,UAAU,CAAC,SAAiB;IACzC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,2BAA2B,CAAC,CAAC;IACxF,MAAM,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAEvD,+CAA+C;IAC/C,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,2BAA2B,EAAE,UAAU,CAAC,CAAC;IACnH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAC;IAEjD,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QACjC,OAAO,CAAC,KAAK,CAAC,yBAAyB,YAAY,EAAE,CAAC,CAAC;QACvD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAClB,CAAC;IAED,MAAM,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAC;IACnD,OAAO,CAAC,GAAG,CAAC,yBAAyB,QAAQ,EAAE,CAAC,CAAC;AACnD,CAAC;AAED,KAAK,UAAU,IAAI;IACjB,IAAI,OAAO,KAAK,aAAa,EAAE,CAAC;QAC9B,MAAM,SAAS,GAAG,IAAI,CAAC,CAAC,CAAC,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;QAC3C,MAAM,UAAU,CAAC,SAAS,CAAC,CAAC;QAC5B,OAAO;IACT,CAAC;IAED,OAAO,CAAC,GAAG,CAAC;;;;;;;CAOb,CAAC,CAAC;AACH,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,131 @@
1
+ import { type TraceContext } from './parseTrace';
2
+ export interface TestStep {
3
+ callId: string;
4
+ parentId: string | null;
5
+ title: string;
6
+ method: string;
7
+ startTime: number;
8
+ endTime: number | null;
9
+ durationMs: number | null;
10
+ error: TraceError | null;
11
+ children: TestStep[];
12
+ }
13
+ export interface TraceError {
14
+ name: string;
15
+ message: string;
16
+ stack?: string;
17
+ }
18
+ export interface FailedStep {
19
+ callId: string;
20
+ title: string;
21
+ error: TraceError;
22
+ durationMs: number | null;
23
+ }
24
+ export interface NetworkEntry {
25
+ source: 'browser' | 'api';
26
+ method: string;
27
+ url: string;
28
+ status: number;
29
+ statusText: string;
30
+ requestHeaders: Array<{
31
+ name: string;
32
+ value: string;
33
+ }>;
34
+ responseHeaders: Array<{
35
+ name: string;
36
+ value: string;
37
+ }>;
38
+ requestBody: string | null;
39
+ responseBody: string | null;
40
+ mimeType: string;
41
+ startedDateTime: string;
42
+ durationMs: number;
43
+ }
44
+ export interface Screenshot {
45
+ sha1: string;
46
+ timestamp: number;
47
+ pageId: string;
48
+ width: number;
49
+ height: number;
50
+ savedPath: string;
51
+ }
52
+ /**
53
+ * Reconstructs the test step tree from a test.trace file.
54
+ * Returns top-level steps (roots), each with nested children.
55
+ */
56
+ export declare function getTestSteps(traceContext: TraceContext): Promise<TestStep[]>;
57
+ /**
58
+ * Scans test.trace for steps that have errors.
59
+ * Returns a flat list of all steps (at any nesting level) that failed,
60
+ * with the error details and duration.
61
+ */
62
+ export declare function getFailedTests(traceContext: TraceContext): Promise<FailedStep[]>;
63
+ /**
64
+ * Reads all *.network files in the trace directory and returns structured
65
+ * network entries. Each entry is tagged as 'browser' (has pageref) or
66
+ * 'api' (has _apiRequest, no pageref). Response and request bodies are
67
+ * resolved from the resources/ directory when a _sha1 reference exists.
68
+ */
69
+ export declare function getNetworkTraffic(traceContext: TraceContext): Promise<NetworkEntry[]>;
70
+ /**
71
+ * Finds all screencast-frame entries across all *-trace.trace files,
72
+ * copies the referenced JPEG/PNG blobs from resources/ into outDir as
73
+ * numbered files, and returns metadata including the saved file path.
74
+ */
75
+ export declare function extractScreenshots(traceContext: TraceContext, outDir: string): Promise<Screenshot[]>;
76
+ /**
77
+ * A single serialized DOM snapshot for one phase of one action.
78
+ *
79
+ * `phase`:
80
+ * - `"before"` — DOM state before the action started (default recorded point)
81
+ * - `"action"` — DOM state during the action (corresponds to Trace Viewer's "Action" tab)
82
+ * - `"after"` — DOM state after the action completed
83
+ *
84
+ * `html` is the snapshot serialized to an HTML string. Back-references inside
85
+ * Playwright's compact snapshot format (`[[offset, nodeIdx]]`) are resolved using
86
+ * the per-frame snapshot history: `offset` is how many snapshots to go back (from
87
+ * the current snapshot's index) within this frame's history, and `nodeIdx` is the
88
+ * index in the post-order DFS traversal of that historical snapshot.
89
+ *
90
+ * `targetElement` is the value of the `__playwright_target__` attribute on
91
+ * the interacted element (the `callId` of the action that targeted it), if any.
92
+ */
93
+ export interface DomSnapshot {
94
+ callId: string;
95
+ phase: 'before' | 'action' | 'after';
96
+ snapshotName: string;
97
+ frameId: string;
98
+ frameUrl: string;
99
+ pageId: string;
100
+ timestamp: number;
101
+ viewport: {
102
+ width: number;
103
+ height: number;
104
+ };
105
+ html: string;
106
+ targetElement: string | null;
107
+ }
108
+ /**
109
+ * A complete set of DOM snapshots for a single browser action, grouping
110
+ * the before/action/after phases together.
111
+ */
112
+ export interface ActionDomSnapshots {
113
+ callId: string;
114
+ before: DomSnapshot | null;
115
+ action: DomSnapshot | null;
116
+ after: DomSnapshot | null;
117
+ }
118
+ /**
119
+ * Extracts DOM snapshots from all browser context trace files (`[N]-trace.trace`
120
+ * where the context is a browser context, i.e. has frame-snapshot entries).
121
+ *
122
+ * Returns one `ActionDomSnapshots` per unique `callId` found, with all three
123
+ * phases (`before`, `action`, `after`) populated when available.
124
+ *
125
+ * Back-references in Playwright's compact snapshot format are resolved using
126
+ * the per-frame snapshot history: `[[offset, nodeIdx]]` means "go back `offset`
127
+ * snapshots in this frame's history, find the `nodeIdx`-th node in post-order
128
+ * DFS of that snapshot".
129
+ */
130
+ export declare function getDomSnapshots(traceContext: TraceContext): Promise<ActionDomSnapshots[]>;
131
+ //# sourceMappingURL=extractors.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractors.d.ts","sourceRoot":"","sources":["../src/extractors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,429 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.getTestSteps = getTestSteps;
37
+ exports.getFailedTests = getFailedTests;
38
+ exports.getNetworkTraffic = getNetworkTraffic;
39
+ exports.extractScreenshots = extractScreenshots;
40
+ exports.getDomSnapshots = getDomSnapshots;
41
+ const fs = __importStar(require("fs"));
42
+ const path = __importStar(require("path"));
43
+ const parseTrace_1 = require("./parseTrace");
44
+ // ---------- getTestSteps ----------
45
+ /**
46
+ * Reconstructs the test step tree from a test.trace file.
47
+ * Returns top-level steps (roots), each with nested children.
48
+ */
49
+ async function getTestSteps(traceContext) {
50
+ const testTracePath = path.join(traceContext.traceDir, 'test.trace');
51
+ const stepMap = new Map();
52
+ const roots = [];
53
+ for await (const event of (0, parseTrace_1.readNdjson)(testTracePath)) {
54
+ if (event.type === 'before') {
55
+ const step = {
56
+ callId: event.callId,
57
+ parentId: event.parentId ?? null,
58
+ title: event.title,
59
+ method: event.method,
60
+ startTime: event.startTime,
61
+ endTime: null,
62
+ durationMs: null,
63
+ error: null,
64
+ children: [],
65
+ };
66
+ stepMap.set(event.callId, step);
67
+ if (event.parentId) {
68
+ const parent = stepMap.get(event.parentId);
69
+ if (parent) {
70
+ parent.children.push(step);
71
+ }
72
+ else {
73
+ roots.push(step);
74
+ }
75
+ }
76
+ else {
77
+ roots.push(step);
78
+ }
79
+ }
80
+ else if (event.type === 'after') {
81
+ const step = stepMap.get(event.callId);
82
+ if (step) {
83
+ step.endTime = event.endTime;
84
+ step.durationMs = event.endTime - step.startTime;
85
+ if (event.error) {
86
+ step.error = event.error;
87
+ }
88
+ }
89
+ }
90
+ }
91
+ return roots;
92
+ }
93
+ // ---------- getFailedTests ----------
94
+ /**
95
+ * Scans test.trace for steps that have errors.
96
+ * Returns a flat list of all steps (at any nesting level) that failed,
97
+ * with the error details and duration.
98
+ */
99
+ async function getFailedTests(traceContext) {
100
+ const roots = await getTestSteps(traceContext);
101
+ const failed = [];
102
+ function collect(steps) {
103
+ for (const step of steps) {
104
+ if (step.error) {
105
+ failed.push({
106
+ callId: step.callId,
107
+ title: step.title,
108
+ error: step.error,
109
+ durationMs: step.durationMs,
110
+ });
111
+ }
112
+ collect(step.children);
113
+ }
114
+ }
115
+ collect(roots);
116
+ return failed;
117
+ }
118
+ // ---------- getNetworkTraffic ----------
119
+ /**
120
+ * Reads all *.network files in the trace directory and returns structured
121
+ * network entries. Each entry is tagged as 'browser' (has pageref) or
122
+ * 'api' (has _apiRequest, no pageref). Response and request bodies are
123
+ * resolved from the resources/ directory when a _sha1 reference exists.
124
+ */
125
+ async function getNetworkTraffic(traceContext) {
126
+ const entries = [];
127
+ const files = await fs.promises.readdir(traceContext.traceDir);
128
+ const networkFiles = files.filter(f => f.endsWith('.network')).sort();
129
+ for (const networkFile of networkFiles) {
130
+ const filePath = path.join(traceContext.traceDir, networkFile);
131
+ for await (const event of (0, parseTrace_1.readNdjson)(filePath)) {
132
+ if (event.type !== 'resource-snapshot')
133
+ continue;
134
+ const snap = event.snapshot;
135
+ const isBrowser = Boolean(snap.pageref);
136
+ const source = isBrowser ? 'browser' : 'api';
137
+ // Resolve request body
138
+ let requestBody = null;
139
+ const postData = snap.request.postData;
140
+ if (postData) {
141
+ if (postData.text) {
142
+ requestBody = postData.text;
143
+ }
144
+ else if (postData._sha1) {
145
+ const sha1 = postData._sha1.replace(/\.bin$/, '');
146
+ const buf = await (0, parseTrace_1.getResourceBuffer)(traceContext, postData._sha1) ??
147
+ await (0, parseTrace_1.getResourceBuffer)(traceContext, sha1);
148
+ if (buf)
149
+ requestBody = buf.toString('utf8');
150
+ }
151
+ }
152
+ // Resolve response body
153
+ let responseBody = null;
154
+ const content = snap.response.content;
155
+ if (content._sha1) {
156
+ const buf = await (0, parseTrace_1.getResourceBuffer)(traceContext, content._sha1);
157
+ if (buf) {
158
+ if (content.mimeType.includes('json') || content.mimeType.includes('text')) {
159
+ responseBody = buf.toString('utf8');
160
+ }
161
+ else {
162
+ responseBody = `[binary: ${content.mimeType}, ${buf.length} bytes]`;
163
+ }
164
+ }
165
+ }
166
+ else if (content.text) {
167
+ responseBody = content.text;
168
+ }
169
+ entries.push({
170
+ source,
171
+ method: snap.request.method,
172
+ url: snap.request.url,
173
+ status: snap.response.status,
174
+ statusText: snap.response.statusText,
175
+ requestHeaders: snap.request.headers,
176
+ responseHeaders: snap.response.headers,
177
+ requestBody,
178
+ responseBody,
179
+ mimeType: content.mimeType,
180
+ startedDateTime: snap.startedDateTime,
181
+ durationMs: snap.time,
182
+ });
183
+ }
184
+ }
185
+ return entries;
186
+ }
187
+ // ---------- extractScreenshots ----------
188
+ /**
189
+ * Finds all screencast-frame entries across all *-trace.trace files,
190
+ * copies the referenced JPEG/PNG blobs from resources/ into outDir as
191
+ * numbered files, and returns metadata including the saved file path.
192
+ */
193
+ async function extractScreenshots(traceContext, outDir) {
194
+ const screenshots = [];
195
+ await fs.promises.mkdir(outDir, { recursive: true });
196
+ const files = await fs.promises.readdir(traceContext.traceDir);
197
+ const traceFiles = files.filter(f => f.endsWith('.trace') && f !== 'test.trace').sort();
198
+ let index = 0;
199
+ for (const traceFile of traceFiles) {
200
+ const filePath = path.join(traceContext.traceDir, traceFile);
201
+ for await (const event of (0, parseTrace_1.readNdjson)(filePath)) {
202
+ if (event.type !== 'screencast-frame')
203
+ continue;
204
+ const buf = await (0, parseTrace_1.getResourceBuffer)(traceContext, event.sha1);
205
+ if (!buf)
206
+ continue;
207
+ const ext = event.sha1.endsWith('.jpeg') ? 'jpeg' : 'png';
208
+ const outFileName = `screenshot-${String(index).padStart(4, '0')}.${ext}`;
209
+ const savedPath = path.join(outDir, outFileName);
210
+ await fs.promises.writeFile(savedPath, buf);
211
+ screenshots.push({
212
+ sha1: event.sha1,
213
+ timestamp: event.timestamp,
214
+ pageId: event.pageId,
215
+ width: event.width,
216
+ height: event.height,
217
+ savedPath,
218
+ });
219
+ index++;
220
+ }
221
+ }
222
+ return screenshots;
223
+ }
224
+ /**
225
+ * Builds the post-order DFS node list for a snapshot tree (mirroring
226
+ * Playwright's `snapshotNodes()` in snapshotRenderer.ts). Only "real"
227
+ * element/text nodes are added — subtree refs are not in the list.
228
+ * This list is used for back-reference resolution.
229
+ *
230
+ * Results are memoized in `cache` (keyed by the html object reference) to avoid
231
+ * recomputing the same large trees when many back-references point to the same
232
+ * historical snapshot.
233
+ */
234
+ function buildSnapshotNodeList(html, cache) {
235
+ const cached = cache.get(html);
236
+ if (cached)
237
+ return cached;
238
+ const nodes = [];
239
+ function visit(n) {
240
+ if (typeof n === 'string') {
241
+ nodes.push(n);
242
+ return;
243
+ }
244
+ if (!Array.isArray(n))
245
+ return;
246
+ // Subtree ref: [[offset, idx]] — skip (not a real node)
247
+ if (Array.isArray(n[0]))
248
+ return;
249
+ // Element: [tagName, attrs, ...children] — recurse children first (post-order)
250
+ const [, , ...children] = n;
251
+ for (const child of children)
252
+ visit(child);
253
+ nodes.push(n);
254
+ }
255
+ visit(html);
256
+ cache.set(html, nodes);
257
+ return nodes;
258
+ }
259
+ const VOID_TAGS = new Set([
260
+ 'area', 'base', 'br', 'col', 'embed', 'hr', 'img', 'input',
261
+ 'keygen', 'link', 'menuitem', 'meta', 'param', 'source', 'track', 'wbr',
262
+ ]);
263
+ /**
264
+ * Resolves a snapshot node tree into an HTML string.
265
+ *
266
+ * `snapshotIndex` is the 0-based index of the current snapshot in `frameHistory`.
267
+ * `frameHistory` is the ordered list of all raw snapshot HTMLs seen so far for
268
+ * this frame (current snapshot is already appended before this call).
269
+ * `dfsCache` is a memoization cache shared across the entire trace-file pass to
270
+ * avoid rebuilding the same DFS node lists repeatedly.
271
+ *
272
+ * Back-references `[[offset, nodeIdx]]` are resolved as:
273
+ * refIndex = snapshotIndex - offset
274
+ * node = snapshotNodes(frameHistory[refIndex])[nodeIdx]
275
+ */
276
+ function resolveSnapshotNode(node, snapshotIndex, frameHistory, dfsCache) {
277
+ if (typeof node === 'string') {
278
+ return escapeHtml(node);
279
+ }
280
+ if (!Array.isArray(node))
281
+ return '';
282
+ // Subtree reference: [[offset, nodeIdx]]
283
+ if (Array.isArray(node[0]) &&
284
+ node[0].length === 2 &&
285
+ typeof node[0][0] === 'number' &&
286
+ typeof node[0][1] === 'number') {
287
+ const [offset, nodeIdx] = node[0];
288
+ const refIndex = snapshotIndex - offset;
289
+ if (refIndex >= 0 && refIndex < frameHistory.length) {
290
+ const refHtml = frameHistory[refIndex];
291
+ const nodes = buildSnapshotNodeList(refHtml, dfsCache);
292
+ if (nodeIdx >= 0 && nodeIdx < nodes.length) {
293
+ return resolveSnapshotNode(nodes[nodeIdx], refIndex, frameHistory, dfsCache);
294
+ }
295
+ }
296
+ return `<!-- ref: [${offset}, ${nodeIdx}] unresolved -->`;
297
+ }
298
+ // Element node: [tagName, attrs, ...children]
299
+ const [tagName, attrs, ...children] = node;
300
+ if (typeof tagName !== 'string')
301
+ return '';
302
+ const tag = tagName.toLowerCase();
303
+ if (tag === 'script')
304
+ return ''; // not useful for AI analysis
305
+ const attrStr = attrs && typeof attrs === 'object' && !Array.isArray(attrs)
306
+ ? Object.entries(attrs)
307
+ .filter(([k]) => !k.startsWith('__playwright'))
308
+ .map(([k, v]) => ` ${k}="${escapeAttr(String(v))}"`)
309
+ .join('')
310
+ : '';
311
+ if (VOID_TAGS.has(tag)) {
312
+ return `<${tag}${attrStr}>`;
313
+ }
314
+ const inner = children.map(c => resolveSnapshotNode(c, snapshotIndex, frameHistory, dfsCache)).join('');
315
+ return `<${tag}${attrStr}>${inner}</${tag}>`;
316
+ }
317
+ function escapeHtml(s) {
318
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
319
+ }
320
+ function escapeAttr(s) {
321
+ return s.replace(/&/g, '&amp;').replace(/"/g, '&quot;');
322
+ }
323
+ /**
324
+ * Extracts DOM snapshots from all browser context trace files (`[N]-trace.trace`
325
+ * where the context is a browser context, i.e. has frame-snapshot entries).
326
+ *
327
+ * Returns one `ActionDomSnapshots` per unique `callId` found, with all three
328
+ * phases (`before`, `action`, `after`) populated when available.
329
+ *
330
+ * Back-references in Playwright's compact snapshot format are resolved using
331
+ * the per-frame snapshot history: `[[offset, nodeIdx]]` means "go back `offset`
332
+ * snapshots in this frame's history, find the `nodeIdx`-th node in post-order
333
+ * DFS of that snapshot".
334
+ */
335
+ async function getDomSnapshots(traceContext) {
336
+ const files = await fs.promises.readdir(traceContext.traceDir);
337
+ const traceFiles = files.filter(f => f.endsWith('.trace') && f !== 'test.trace').sort();
338
+ // Per-frame snapshot history: frameId → ordered list of raw html nodes
339
+ const frameHistory = new Map();
340
+ // Memoization cache for buildSnapshotNodeList — keyed by html object reference
341
+ const dfsCache = new Map();
342
+ // Collect resolved snapshots keyed by callId → phase
343
+ const rawByCallId = new Map();
344
+ for (const traceFile of traceFiles) {
345
+ const filePath = path.join(traceContext.traceDir, traceFile);
346
+ for await (const event of (0, parseTrace_1.readNdjson)(filePath)) {
347
+ if (event.type !== 'frame-snapshot')
348
+ continue;
349
+ const snap = event.snapshot;
350
+ const { snapshotName, callId, frameId, frameUrl, pageId, viewport, timestamp, html } = snap;
351
+ // Determine phase from snapshotName prefix
352
+ let phase;
353
+ if (snapshotName.startsWith('before@')) {
354
+ phase = 'before';
355
+ }
356
+ else if (snapshotName.startsWith('after@')) {
357
+ phase = 'after';
358
+ }
359
+ else if (snapshotName.startsWith('input@')) {
360
+ phase = 'action';
361
+ }
362
+ else {
363
+ continue; // unknown phase, skip
364
+ }
365
+ // Append to per-frame history BEFORE resolving (so refs can point back to it)
366
+ if (!frameHistory.has(frameId))
367
+ frameHistory.set(frameId, []);
368
+ const history = frameHistory.get(frameId);
369
+ history.push(html);
370
+ const snapshotIndex = history.length - 1;
371
+ // Resolve the full html to a string using the correct ref algorithm
372
+ const resolvedHtml = resolveSnapshotNode(html, snapshotIndex, history, dfsCache);
373
+ // Extract the __playwright_target__ attribute to identify the targeted element
374
+ const targetElement = findPlaywrightTarget(html);
375
+ const domSnapshot = {
376
+ callId,
377
+ phase,
378
+ snapshotName,
379
+ frameId,
380
+ frameUrl,
381
+ pageId,
382
+ timestamp,
383
+ viewport,
384
+ html: resolvedHtml,
385
+ targetElement,
386
+ };
387
+ if (!rawByCallId.has(callId))
388
+ rawByCallId.set(callId, new Map());
389
+ rawByCallId.get(callId).set(phase, domSnapshot);
390
+ }
391
+ }
392
+ // Assemble into ActionDomSnapshots
393
+ const result = [];
394
+ for (const [callId, phases] of rawByCallId) {
395
+ result.push({
396
+ callId,
397
+ before: phases.get('before') ?? null,
398
+ action: phases.get('action') ?? null,
399
+ after: phases.get('after') ?? null,
400
+ });
401
+ }
402
+ result.sort((a, b) => {
403
+ const ta = (a.before ?? a.action ?? a.after)?.timestamp ?? 0;
404
+ const tb = (b.before ?? b.action ?? b.after)?.timestamp ?? 0;
405
+ return ta - tb;
406
+ });
407
+ return result;
408
+ }
409
+ /** Recursively searches the snapshot tree for a node with __playwright_target__ */
410
+ function findPlaywrightTarget(node) {
411
+ if (!Array.isArray(node) || node.length < 2)
412
+ return null;
413
+ const attrs = node[1];
414
+ if (attrs && typeof attrs === 'object' && !Array.isArray(attrs)) {
415
+ const target = attrs['__playwright_target__'];
416
+ if (target)
417
+ return target;
418
+ }
419
+ for (let i = 2; i < node.length; i++) {
420
+ const child = node[i];
421
+ if (Array.isArray(child)) {
422
+ const found = findPlaywrightTarget(child);
423
+ if (found)
424
+ return found;
425
+ }
426
+ }
427
+ return null;
428
+ }
429
+ //# sourceMappingURL=extractors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"extractors.js","sourceRoot":"","sources":["../src/extractors.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export { prepareTraceDir, readNdjson, getResourceBuffer, listTraces } from './parseTrace';
2
+ export type { TraceContext } from './parseTrace';
3
+ export { getTestSteps, getFailedTests, getNetworkTraffic, extractScreenshots, getDomSnapshots, } from './extractors';
4
+ export type { TestStep, TraceError, FailedStep, NetworkEntry, Screenshot, DomSnapshot, ActionDomSnapshots, } from './extractors';
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,UAAU,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1F,YAAY,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAEjD,OAAO,EACL,YAAY,EACZ,cAAc,EACd,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,GAChB,MAAM,cAAc,CAAC;AAEtB,YAAY,EACV,QAAQ,EACR,UAAU,EACV,UAAU,EACV,YAAY,EACZ,UAAU,EACV,WAAW,EACX,kBAAkB,GACnB,MAAM,cAAc,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDomSnapshots = exports.extractScreenshots = exports.getNetworkTraffic = exports.getFailedTests = exports.getTestSteps = exports.listTraces = exports.getResourceBuffer = exports.readNdjson = exports.prepareTraceDir = void 0;
4
+ var parseTrace_1 = require("./parseTrace");
5
+ Object.defineProperty(exports, "prepareTraceDir", { enumerable: true, get: function () { return parseTrace_1.prepareTraceDir; } });
6
+ Object.defineProperty(exports, "readNdjson", { enumerable: true, get: function () { return parseTrace_1.readNdjson; } });
7
+ Object.defineProperty(exports, "getResourceBuffer", { enumerable: true, get: function () { return parseTrace_1.getResourceBuffer; } });
8
+ Object.defineProperty(exports, "listTraces", { enumerable: true, get: function () { return parseTrace_1.listTraces; } });
9
+ var extractors_1 = require("./extractors");
10
+ Object.defineProperty(exports, "getTestSteps", { enumerable: true, get: function () { return extractors_1.getTestSteps; } });
11
+ Object.defineProperty(exports, "getFailedTests", { enumerable: true, get: function () { return extractors_1.getFailedTests; } });
12
+ Object.defineProperty(exports, "getNetworkTraffic", { enumerable: true, get: function () { return extractors_1.getNetworkTraffic; } });
13
+ Object.defineProperty(exports, "extractScreenshots", { enumerable: true, get: function () { return extractors_1.extractScreenshots; } });
14
+ Object.defineProperty(exports, "getDomSnapshots", { enumerable: true, get: function () { return extractors_1.getDomSnapshots; } });
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,2CAA0F;AAAjF,6GAAA,eAAe,OAAA;AAAE,wGAAA,UAAU,OAAA;AAAE,+GAAA,iBAAiB,OAAA;AAAE,wGAAA,UAAU,OAAA;AAGnE,2CAMsB;AALpB,0GAAA,YAAY,OAAA;AACZ,4GAAA,cAAc,OAAA;AACd,+GAAA,iBAAiB,OAAA;AACjB,gHAAA,kBAAkB,OAAA;AAClB,6GAAA,eAAe,OAAA"}
@@ -0,0 +1,30 @@
1
+ export interface TraceContext {
2
+ traceDir: string;
3
+ }
4
+ /**
5
+ * Ensures the trace is available as a directory.
6
+ * If a .zip file is provided, it extracts it to a temporary directory.
7
+ */
8
+ export declare function prepareTraceDir(tracePath: string): Promise<TraceContext>;
9
+ /**
10
+ * Reads a Newline Delimited JSON (NDJSON) file line by line and yields parsed objects.
11
+ */
12
+ export declare function readNdjson<T = any>(filePath: string): AsyncGenerator<T>;
13
+ /**
14
+ * Returns raw file buffer from the resources/ directory of a trace
15
+ */
16
+ export declare function getResourceBuffer(traceContext: TraceContext, sha1: string): Promise<Buffer | null>;
17
+ /**
18
+ * Discovers all individual test traces inside a Playwright report data directory.
19
+ *
20
+ * A report's `data/` directory can contain many SHA1-named entries — one per test
21
+ * execution. Each entry is either a directory or a `.zip` archive. Non-trace files
22
+ * (`.md`, `.png`, `.json`, etc.) are ignored.
23
+ *
24
+ * Returns a `TraceContext` for every trace found, with `.zip` archives extracted
25
+ * in-place (same directory, no suffix).
26
+ *
27
+ * @param reportDataDir Path to the `playwright-report/data/` directory.
28
+ */
29
+ export declare function listTraces(reportDataDir: string): Promise<TraceContext[]>;
30
+ //# sourceMappingURL=parseTrace.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseTrace.d.ts","sourceRoot":"","sources":["../src/parseTrace.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAsB,eAAe,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAgB9E;AAED;;GAEG;AACH,wBAAuB,UAAU,CAAC,CAAC,GAAG,GAAG,EAAE,QAAQ,EAAE,MAAM,GAAG,cAAc,CAAC,CAAC,CAAC,CAmB9E;AAED;;GAEG;AACH,wBAAsB,iBAAiB,CAAC,YAAY,EAAE,YAAY,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAMxG;AAED;;;;;;;;;;;GAWG;AACH,wBAAsB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,EAAE,CAAC,CA6B/E"}
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.prepareTraceDir = prepareTraceDir;
40
+ exports.readNdjson = readNdjson;
41
+ exports.getResourceBuffer = getResourceBuffer;
42
+ exports.listTraces = listTraces;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const readline = __importStar(require("readline"));
46
+ const adm_zip_1 = __importDefault(require("adm-zip"));
47
+ /**
48
+ * Ensures the trace is available as a directory.
49
+ * If a .zip file is provided, it extracts it to a temporary directory.
50
+ */
51
+ async function prepareTraceDir(tracePath) {
52
+ const stat = await fs.promises.stat(tracePath);
53
+ if (stat.isDirectory()) {
54
+ return { traceDir: tracePath };
55
+ }
56
+ if (tracePath.endsWith('.zip')) {
57
+ const outDir = tracePath.replace(/\.zip$/, '');
58
+ if (!fs.existsSync(outDir)) {
59
+ const zip = new adm_zip_1.default(tracePath);
60
+ zip.extractAllTo(outDir, true);
61
+ }
62
+ return { traceDir: outDir };
63
+ }
64
+ throw new Error(`Invalid trace path: ${tracePath}. Must be a directory or .zip file.`);
65
+ }
66
+ /**
67
+ * Reads a Newline Delimited JSON (NDJSON) file line by line and yields parsed objects.
68
+ */
69
+ async function* readNdjson(filePath) {
70
+ if (!fs.existsSync(filePath)) {
71
+ return;
72
+ }
73
+ const fileStream = fs.createReadStream(filePath);
74
+ const rl = readline.createInterface({
75
+ input: fileStream,
76
+ crlfDelay: Infinity
77
+ });
78
+ for await (const line of rl) {
79
+ if (line.trim()) {
80
+ try {
81
+ yield JSON.parse(line);
82
+ }
83
+ catch (e) {
84
+ // Ignore malformed lines
85
+ }
86
+ }
87
+ }
88
+ }
89
+ /**
90
+ * Returns raw file buffer from the resources/ directory of a trace
91
+ */
92
+ async function getResourceBuffer(traceContext, sha1) {
93
+ const resourcePath = path.join(traceContext.traceDir, 'resources', sha1);
94
+ if (fs.existsSync(resourcePath)) {
95
+ return fs.promises.readFile(resourcePath);
96
+ }
97
+ return null;
98
+ }
99
+ /**
100
+ * Discovers all individual test traces inside a Playwright report data directory.
101
+ *
102
+ * A report's `data/` directory can contain many SHA1-named entries — one per test
103
+ * execution. Each entry is either a directory or a `.zip` archive. Non-trace files
104
+ * (`.md`, `.png`, `.json`, etc.) are ignored.
105
+ *
106
+ * Returns a `TraceContext` for every trace found, with `.zip` archives extracted
107
+ * in-place (same directory, no suffix).
108
+ *
109
+ * @param reportDataDir Path to the `playwright-report/data/` directory.
110
+ */
111
+ async function listTraces(reportDataDir) {
112
+ const entries = await fs.promises.readdir(reportDataDir, { withFileTypes: true });
113
+ const tracePaths = [];
114
+ for (const entry of entries) {
115
+ const fullPath = path.join(reportDataDir, entry.name);
116
+ // Resolve symlinks so we can reliably check if it's a directory
117
+ const isDir = entry.isDirectory() ||
118
+ (entry.isSymbolicLink() && (await fs.promises.stat(fullPath)).isDirectory());
119
+ if (isDir) {
120
+ // A directory is a trace if it contains a test.trace file
121
+ const testTracePath = path.join(fullPath, 'test.trace');
122
+ if (fs.existsSync(testTracePath)) {
123
+ tracePaths.push(fullPath);
124
+ }
125
+ }
126
+ else if (entry.isFile() && entry.name.endsWith('.zip')) {
127
+ const extractedDir = fullPath.replace(/\.zip$/, '');
128
+ // Only include if the extracted form is not already represented as a directory entry
129
+ if (!fs.existsSync(extractedDir)) {
130
+ tracePaths.push(fullPath);
131
+ }
132
+ // If the directory already exists (previously extracted), it was already added above
133
+ }
134
+ }
135
+ return Promise.all(tracePaths.map(p => prepareTraceDir(p)));
136
+ }
137
+ //# sourceMappingURL=parseTrace.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parseTrace.js","sourceRoot":"","sources":["../src/parseTrace.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "@andrii_kremlovskyi/playwright-traces-reader",
3
+ "version": "1.0.0",
4
+ "description": "Parse Playwright trace files into structured data for AI agents and tooling",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "bin": {
8
+ "playwright-traces-reader": "dist/cli.js"
9
+ },
10
+ "files": [
11
+ "dist/",
12
+ "templates/"
13
+ ],
14
+ "scripts": {
15
+ "build": "tsc",
16
+ "prepublishOnly": "npm run build",
17
+ "test": "jest"
18
+ },
19
+ "keywords": [
20
+ "playwright",
21
+ "traces",
22
+ "github-copilot",
23
+ "llm",
24
+ "skills"
25
+ ],
26
+ "author": "Andrii Kremlovskyi",
27
+ "license": "MIT",
28
+ "devDependencies": {
29
+ "@types/adm-zip": "^0.5.8",
30
+ "@types/jest": "^30.0.0",
31
+ "@types/node": "^25.5.0",
32
+ "jest": "^30.3.0",
33
+ "ts-jest": "^29.4.6",
34
+ "tsx": "^4.21.0",
35
+ "typescript": "^5.9.3"
36
+ },
37
+ "dependencies": {
38
+ "adm-zip": "^0.5.16"
39
+ },
40
+ "jest": {
41
+ "testEnvironment": "node",
42
+ "testMatch": [
43
+ "**/tests/**/*.test.ts"
44
+ ],
45
+ "transform": {
46
+ "^.+\\.tsx?$": [
47
+ "ts-jest",
48
+ {
49
+ "tsconfig": {
50
+ "module": "commonjs",
51
+ "moduleResolution": "node",
52
+ "esModuleInterop": true,
53
+ "types": [
54
+ "node",
55
+ "jest"
56
+ ]
57
+ }
58
+ }
59
+ ]
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: analyze-playwright-traces
3
+ description: Analyze Playwright test traces to find test failures, extract network request/response payloads, identify slow steps, locate UI screenshots, and inspect DOM state before/during/after each browser action.
4
+ ---
5
+
6
+ # Analyze Playwright Traces
7
+
8
+ Use this skill when the user asks about test failures, API payloads in tests, slow steps, or UI screenshots from Playwright test runs.
9
+
10
+ ## Prerequisites
11
+
12
+ This skill requires the `@kremlovskyi/playwright-traces-reader` npm package in the project:
13
+ ```bash
14
+ npm install @kremlovskyi/playwright-traces-reader
15
+ ```
16
+
17
+ And a built trace directory under `playwright-report/data/<sha1>/` (or a `.zip` of the same).
18
+
19
+ ## How to Use
20
+
21
+ ### 1. Find failed tests and error details
22
+
23
+ ```typescript
24
+ import { prepareTraceDir, getFailedTests } from 'playwright-traces-reader';
25
+
26
+ const ctx = await prepareTraceDir('/path/to/playwright-report/data/<sha1>');
27
+ const failures = await getFailedTests(ctx);
28
+
29
+ for (const f of failures) {
30
+ console.log(`FAILED: ${f.title}`);
31
+ console.log(`Error: ${f.error.message}`);
32
+ console.log(`Duration: ${f.durationMs}ms`);
33
+ }
34
+ ```
35
+
36
+ ### 2. Find slow steps
37
+
38
+ ```typescript
39
+ import { prepareTraceDir, getTestSteps } from 'playwright-traces-reader';
40
+
41
+ const ctx = await prepareTraceDir('/path/to/trace-dir');
42
+ const steps = await getTestSteps(ctx);
43
+
44
+ function flatten(steps: TestStep[], out: TestStep[] = []): TestStep[] {
45
+ for (const s of steps) { out.push(s); flatten(s.children, out); }
46
+ return out;
47
+ }
48
+
49
+ const allSteps = flatten(steps);
50
+ const sorted = allSteps
51
+ .filter(s => s.durationMs !== null)
52
+ .sort((a, b) => (b.durationMs ?? 0) - (a.durationMs ?? 0));
53
+
54
+ console.log('Top 5 slowest steps:');
55
+ sorted.slice(0, 5).forEach(s => console.log(` ${s.durationMs}ms — ${s.title}`));
56
+ ```
57
+
58
+ ### 3. Extract API request/response bodies
59
+
60
+ ```typescript
61
+ import { prepareTraceDir, getNetworkTraffic } from 'playwright-traces-reader';
62
+
63
+ const ctx = await prepareTraceDir('/path/to/trace-dir');
64
+ const traffic = await getNetworkTraffic(ctx);
65
+
66
+ // Filter only API (Node.js APIRequestContext) calls
67
+ const apiCalls = traffic.filter(e => e.source === 'api');
68
+ for (const call of apiCalls) {
69
+ console.log(`${call.method} ${call.url} → ${call.status}`);
70
+ if (call.responseBody) console.log('Response:', call.responseBody.slice(0, 500));
71
+ }
72
+ ```
73
+
74
+ ### 4. Extract screenshots for visual inspection
75
+
76
+ ```typescript
77
+ import { prepareTraceDir, extractScreenshots } from 'playwright-traces-reader';
78
+ import * as os from 'os';
79
+
80
+ const ctx = await prepareTraceDir('/path/to/trace-dir');
81
+ const screenshots = await extractScreenshots(ctx, `${os.tmpdir()}/pw-screenshots`);
82
+
83
+ for (const s of screenshots) {
84
+ console.log(`Screenshot at ${s.timestamp}ms: ${s.savedPath}`);
85
+ }
86
+ // Pass savedPath to your image viewer or AI model for visual inspection
87
+ ```
88
+
89
+ ### 5. Inspect DOM state before/during/after each action
90
+
91
+ ```typescript
92
+ import { prepareTraceDir, getDomSnapshots } from 'playwright-traces-reader';
93
+
94
+ const ctx = await prepareTraceDir('/path/to/trace-dir');
95
+ const snapshots = await getDomSnapshots(ctx);
96
+
97
+ for (const action of snapshots) {
98
+ // action.before — DOM before the action
99
+ // action.action — DOM during the action (mid-interaction state)
100
+ // action.after — DOM after the action
101
+ const snap = action.after ?? action.before;
102
+ if (!snap) continue;
103
+
104
+ console.log(`callId: ${action.callId} | url: ${snap.frameUrl}`);
105
+ if (snap.targetElement) {
106
+ console.log(` targeted element (callId attr): ${snap.targetElement}`);
107
+ }
108
+ console.log(` DOM snippet: ${snap.html.slice(0, 300)}`);
109
+ }
110
+
111
+ // Find the DOM snapshot for a specific action by its callId
112
+ const target = snapshots.find(s => s.callId === 'call@42');
113
+ if (target?.after) {
114
+ console.log('DOM after action 42:', target.after.html);
115
+ }
116
+ ```
117
+
118
+ Each `DomSnapshot` includes:
119
+ - `html` — full serialized HTML string (back-references resolved, `<script>` tags stripped)
120
+ - `phase` — `"before"` | `"action"` | `"after"`
121
+ - `frameUrl` — URL of the page frame at snapshot time
122
+ - `targetElement` — `callId` of the action that targeted an element (from `__playwright_target__` attr), or `null`
123
+ - `viewport` — `{ width, height }` at snapshot time
124
+
125
+ ## Locating Trace Directories
126
+
127
+ A Playwright report's `data/` directory holds **one SHA1 entry per test**. A test suite run with multiple tests will have multiple entries. Use `listTraces()` to iterate all of them:
128
+
129
+ ```typescript
130
+ import { listTraces, getFailedTests } from 'playwright-traces-reader';
131
+
132
+ // Works for 1 test or many — automatically discovers all SHA1 trace entries
133
+ const traces = await listTraces('/path/to/playwright-report/data');
134
+
135
+ for (const ctx of traces) {
136
+ const failures = await getFailedTests(ctx);
137
+ // ... process results per test
138
+ }
139
+ ```
140
+
141
+ Each entry is either a directory (already extracted) or a `.zip` (auto-extracted). Non-trace files in `data/` (`.md`, `.png`, etc.) are ignored automatically.
142
+
143
+ ## Low-Level Utilities
144
+
145
+ ### `prepareTraceDir(tracePath)`
146
+
147
+ Takes a single path (directory or `.zip`) and returns a `TraceContext`. Use this when you have a specific trace path rather than a whole `data/` directory.
148
+
149
+ ```typescript
150
+ import { prepareTraceDir } from '@kremlovskyi/playwright-traces-reader';
151
+
152
+ const ctx = await prepareTraceDir('/path/to/playwright-report/data/<sha1>');
153
+ // or a zip:
154
+ const ctx = await prepareTraceDir('/path/to/playwright-report/data/<sha1>.zip');
155
+ ```
156
+
157
+ ### `getResourceBuffer(ctx, sha1)`
158
+
159
+ Resolves a SHA1 filename to its raw `Buffer` from the trace's `resources/` directory. Returns `null` if the blob is not found. Useful when you need the raw bytes of a request/response body or a screenshot blob.
160
+
161
+ ```typescript
162
+ import { prepareTraceDir, getResourceBuffer } from '@kremlovskyi/playwright-traces-reader';
163
+
164
+ const ctx = await prepareTraceDir('/path/to/trace-dir');
165
+ const buf = await getResourceBuffer(ctx, 'a1b2c3d4...');
166
+ if (buf) {
167
+ // e.g. write to disk, pass to an image model, etc.
168
+ console.log(`blob size: ${buf.byteLength} bytes`);
169
+ }
170
+ ```
171
+
172
+ ### `readNdjson<T>(filePath)`
173
+
174
+ Async generator that streams and parses an NDJSON file line by line. Silently skips malformed lines. Useful for reading raw trace events when the built-in extractors don't cover your use case.
175
+
176
+ ```typescript
177
+ import { prepareTraceDir, readNdjson } from '@kremlovskyi/playwright-traces-reader';
178
+ import * as path from 'path';
179
+
180
+ const ctx = await prepareTraceDir('/path/to/trace-dir');
181
+ const traceFile = path.join(ctx.traceDir, '0-trace.trace');
182
+
183
+ for await (const event of readNdjson<{ type: string }>(traceFile)) {
184
+ if (event.type === 'screencast-frame') {
185
+ console.log('screencast frame:', event);
186
+ }
187
+ }
188
+ ```
189
+
190
+
191
+
192
+ - **Failures**: Start with `getFailedTests()` to see the error message and which step failed. The `error.message` contains the full diff for assertion failures.
193
+ - **Network issues**: Use `getNetworkTraffic()` filtering by `source === 'api'` to see all HTTP calls made by the test. Check `status >= 400` for HTTP errors.
194
+ - **Slow tests**: Use `getTestSteps()` and sort by `durationMs` to find bottlenecks.
195
+ - **Visual state**: Use `extractScreenshots()` and look at the last screenshot before a failure to understand the UI state at the time of failure.
196
+ - **DOM inspection**: Use `getDomSnapshots()` to get the full HTML page state at each action. The `after` snapshot is the most useful for debugging — it shows exactly what the page looked like after Playwright performed an action. Combine with `targetElement` to identify which element Playwright interacted with.