@tracelane/wdio 0.1.0-alpha.14 → 0.1.0-alpha.16
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 +32 -17
- package/dist/options.d.ts +30 -5
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js.map +1 -1
- package/dist/tracelane-session.d.ts.map +1 -1
- package/dist/tracelane-session.js +10 -6
- package/dist/tracelane-session.js.map +1 -1
- package/package.json +10 -4
- package/dist/inpage-bundle.d.ts +0 -9
- package/dist/inpage-bundle.d.ts.map +0 -1
- package/dist/inpage-bundle.js +0 -47
- package/dist/inpage-bundle.js.map +0 -1
- package/dist/network-capture.d.ts +0 -25
- package/dist/network-capture.d.ts.map +0 -1
- package/dist/network-capture.js +0 -56
- package/dist/network-capture.js.map +0 -1
- package/dist/report-writer.d.ts +0 -36
- package/dist/report-writer.d.ts.map +0 -1
- package/dist/report-writer.js +0 -56
- package/dist/report-writer.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
<img src="https://raw.githubusercontent.com/Cubenest/rrweb-stack/main/assets/brand/sub-tracelane.svg" height="40" alt="tracelane">
|
|
2
|
+
|
|
1
3
|
# @tracelane/wdio
|
|
2
4
|
|
|
3
|
-
> The reporter for your WebdriverIO
|
|
5
|
+
> The reporter for your WebdriverIO tests — Playwright and Cypress on the roadmap. Self-contained HTML for every run — replay failures, audit successes, attach to any bug tracker. No SaaS, no dashboard, no signup.
|
|
4
6
|
|
|
5
7
|
[](https://www.npmjs.com/package/@tracelane/wdio)
|
|
6
8
|
[](https://www.npmjs.com/package/@tracelane/wdio)
|
|
@@ -42,7 +44,7 @@ Same result — `npx @tracelane/cli init` is just the orchestration that does th
|
|
|
42
44
|
## What this is NOT
|
|
43
45
|
|
|
44
46
|
- Not Cypress Cloud, Replay.io, or Sentry Session Replay. There is no SaaS to host. There is no signup. There is no dashboard. There is no telemetry. The artifact is a single HTML file on your filesystem.
|
|
45
|
-
- Not a reporter (in the `@wdio/reporter` sense). `tracelane` is a WDIO **Service** because only a Service can attach to the live browser, inject the rrweb recorder, drain the in-page buffer, and use CDP
|
|
47
|
+
- Not a reporter (in the `@wdio/reporter` sense). `tracelane` is a WDIO **Service** because only a Service can attach to the live browser, inject the rrweb recorder + in-page network plugin, drain the in-page buffer, and (where available) use CDP to enrich network capture. A paired Allure **Reporter** shim is planned for v1.1.
|
|
46
48
|
- Not just for failures. `mode: 'all'` writes a report for every test — useful as a CI artifact, evidence in a PR, or a "what changed between green and red" diff.
|
|
47
49
|
|
|
48
50
|
## Full example
|
|
@@ -63,7 +65,10 @@ export const config: Options.Testrunner = {
|
|
|
63
65
|
},
|
|
64
66
|
],
|
|
65
67
|
services: [
|
|
66
|
-
|
|
68
|
+
// Optional: network is captured in-page by default (all browsers, no CDP).
|
|
69
|
+
// Add devtools only to enrich it with authoritative status + no-response
|
|
70
|
+
// failures on Chromium — see "Network capture" below.
|
|
71
|
+
['devtools', {}],
|
|
67
72
|
[
|
|
68
73
|
TraceLaneService,
|
|
69
74
|
{
|
|
@@ -82,7 +87,7 @@ export const config: Options.Testrunner = {
|
|
|
82
87
|
## How it works
|
|
83
88
|
|
|
84
89
|
- On the first test, the Service injects an rrweb recorder bundle into the page and installs an in-page event buffer (`window.__tracelane__events`).
|
|
85
|
-
- The Node side drains that buffer on a poll (default every 5 s) and on every `afterTest`, re-injecting
|
|
90
|
+
- The Node side drains that buffer on a poll (default every 5 s) and on every `afterTest`, re-injecting after each navigation.
|
|
86
91
|
- In `failed` mode (default) a passing test discards its buffer; a failing test's buffer is handed to [`@tracelane/report`](https://github.com/Cubenest/rrweb-stack/tree/main/packages/tracelane-report), which builds the single offline HTML (≤ 25 MB).
|
|
87
92
|
|
|
88
93
|
## Options
|
|
@@ -92,8 +97,9 @@ export const config: Options.Testrunner = {
|
|
|
92
97
|
| `mode` | `'failed' \| 'all'` | `'failed'` | `failed` writes a report only on failure; `all` on every test. `TRACELANE_MODE` env var overrides this. |
|
|
93
98
|
| `outDir` | `string` | `'./tracelane-reports'` | Report output directory (created if missing). |
|
|
94
99
|
| `capture.rrweb` | `boolean` | `true` | Record the rrweb session. |
|
|
95
|
-
| `capture.network` | `boolean` | `true` |
|
|
96
|
-
| `capture.
|
|
100
|
+
| `capture.network` | `boolean` | `true` | Capture network requests via the in-page `rrweb/network@1` plugin — all browsers, no CDP. Privacy-first: only URL/method/status/timing by default (headers + bodies off; opt in via `capture.networkOptions`). CDP is an optional fallback that adds authoritative status + true no-response failures where it's available. |
|
|
101
|
+
| `capture.networkOptions` | `NetworkRecordOptions` | plugin defaults | Forwarded to the in-page network plugin (`recordHeaders`, `recordBody`, `maskRequestFn`, `payloadHostDenyList`, …). Defaults are privacy-first (headers + bodies off). Ignored when `capture.network` is `false`. |
|
|
102
|
+
| `capture.console` | `boolean` | `true` | Capture `console.*` via the rrweb console plugin. Setting this `false` also drops any `[tracelane.net]` network-error lines from the CDP fallback path, since those surface through `console.error`. |
|
|
97
103
|
| `drainIntervalMs` | `number` | `5000` | Node-side drain poll interval. |
|
|
98
104
|
| `cooldownMs` | `number` | `250` | Re-injection cooldown guard (suppresses double-init on hash/HMR navigation). |
|
|
99
105
|
| `allure` | `boolean` | `false` | Reserved for the v1.1 Allure shim. No-op in v1. |
|
|
@@ -116,7 +122,7 @@ export const config: Options.Testrunner = {
|
|
|
116
122
|
before: tracelane.before,
|
|
117
123
|
beforeSuite: tracelane.beforeSuite,
|
|
118
124
|
beforeTest: tracelane.beforeTest,
|
|
119
|
-
|
|
125
|
+
afterCommand: tracelane.afterCommand, // re-injection after navigation
|
|
120
126
|
afterTest: tracelane.afterTest,
|
|
121
127
|
afterSuite: tracelane.afterSuite,
|
|
122
128
|
after: tracelane.after,
|
|
@@ -126,12 +132,21 @@ export const config: Options.Testrunner = {
|
|
|
126
132
|
|
|
127
133
|
## Network capture
|
|
128
134
|
|
|
129
|
-
|
|
135
|
+
By default network is captured **in-page** by the framework-agnostic `rrweb/network@1` plugin (shipped in `@cubenest/rrweb-core`). It works on **every browser** — Chrome, Edge, Firefox, Safari, and cloud Selenium — with **no CDP and no extra service**, because the plugin wraps `fetch`/`XHR` and reads `PerformanceObserver` entries from inside the page.
|
|
136
|
+
|
|
137
|
+
Privacy-first by default: only **URL, method, status, and timing** are captured. Request/response **headers and bodies are OFF** unless you opt in via `capture.networkOptions` (`recordHeaders` / `recordBody`, plus masking hooks like `maskRequestFn` and `payloadHostDenyList`). A couple of accuracy caveats of the default timing surface:
|
|
138
|
+
|
|
139
|
+
- For cross-origin **sub-resources** (images, scripts, fonts loaded from another origin), the Resource Timing spec reports `status: 0` and no method — these are not surfaced as failures.
|
|
140
|
+
- Accurate per-request **status** for `fetch`/`XHR` requires those wrappers, which are part of the default capture; the plugin only omits header/body payloads unless opted in.
|
|
141
|
+
|
|
142
|
+
### CDP fallback (optional enhancement)
|
|
143
|
+
|
|
144
|
+
When a CDP-capable session is present, `tracelane` *additionally* uses `browser.cdp(...)` to capture **authoritative HTTP status** for failed responses (`status >= 400`) and **true no-response failures** (CORS/DNS/offline/abort) that the page wrappers can't always see. These are routed into the report's console timeline (prefixed `[tracelane.net]`) and the report merges them over the in-page rows for the same request (real status wins). To enable it:
|
|
130
145
|
|
|
131
146
|
- **WDIO 8:** add `['devtools', {}]` via `@wdio/devtools-service@8`.
|
|
132
|
-
- **WDIO 9:** `@wdio/devtools-service` has no stable v9 line (it stabilized at v10); use the v10 service or a CDP-capable session.
|
|
147
|
+
- **WDIO 9:** `@wdio/devtools-service` has no stable v9 line (it stabilized at v10); use the v10 service or a CDP-capable session.
|
|
133
148
|
|
|
134
|
-
|
|
149
|
+
If CDP is unavailable (cloud Selenium, Firefox, Safari), nothing is lost beyond the CDP-only authoritative-status enhancement — the in-page plugin still populates the network panel and the report is still produced.
|
|
135
150
|
|
|
136
151
|
## Supported runners / browsers
|
|
137
152
|
|
|
@@ -141,12 +156,12 @@ Cloud Selenium vendors typically don't expose CDP; rrweb + console capture still
|
|
|
141
156
|
| Jasmine | Supported (same `afterTest` result shape) |
|
|
142
157
|
| Cucumber | Supported via `beforeScenario`/`afterScenario` |
|
|
143
158
|
|
|
144
|
-
| Browser | rrweb + console | Network (CDP) |
|
|
145
|
-
|
|
146
|
-
| Chrome / Chromium ≥ 116 | **Yes** | Yes (with a CDP-capable session) |
|
|
147
|
-
| Edge (Chromium) | Yes | Yes |
|
|
148
|
-
| Firefox | Yes | No (CDP is Chromium-only) |
|
|
149
|
-
| Safari | Yes | No |
|
|
159
|
+
| Browser | rrweb + console | Network (in-page plugin) | Authoritative status / no-response failures (CDP) |
|
|
160
|
+
|---|---|---|---|
|
|
161
|
+
| Chrome / Chromium ≥ 116 | **Yes** | Yes | Yes (with a CDP-capable session) |
|
|
162
|
+
| Edge (Chromium) | Yes | Yes | Yes |
|
|
163
|
+
| Firefox | Yes | Yes (in-page plugin) | No (CDP is Chromium-only) |
|
|
164
|
+
| Safari | Yes | Yes (in-page plugin) | No |
|
|
150
165
|
|
|
151
166
|
## Playwright + Cypress
|
|
152
167
|
|
|
@@ -161,7 +176,7 @@ Watch the [release notes](https://github.com/Cubenest/rrweb-stack/releases) on `
|
|
|
161
176
|
|
|
162
177
|
Semantic Versioning. Currently `0.1.0-alpha.x` (pre-release; the API may shift before `1.0.0`). See [SUPPORTED.md](https://github.com/Cubenest/rrweb-stack/blob/main/SUPPORTED.md) for the compatibility matrix.
|
|
163
178
|
|
|
164
|
-
**No telemetry.** `tracelane` collects and sends nothing; reports are written to your local `outDir` only. The generated HTML report includes a footer crediting the tool; you can suppress it with `report: { footer: false }
|
|
179
|
+
**No telemetry.** `tracelane` collects and sends nothing; reports are written to your local `outDir` only. The generated HTML report includes a footer crediting the tool; you can suppress it with `report: { footer: false }`.
|
|
165
180
|
|
|
166
181
|
## License
|
|
167
182
|
|
package/dist/options.d.ts
CHANGED
|
@@ -8,14 +8,28 @@ export interface CaptureOptions {
|
|
|
8
8
|
* Capture network requests. Default `true`.
|
|
9
9
|
*
|
|
10
10
|
* When enabled (default), the in-page rrweb network plugin
|
|
11
|
-
* (`rrweb/network@1`) is registered alongside the recorder —
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* but no longer the primary capture mechanism.
|
|
11
|
+
* (`rrweb/network@1`) is registered alongside the recorder —
|
|
12
|
+
* framework-agnostic, no CDP required, works on all browsers. The
|
|
13
|
+
* legacy CDP-based path is preserved as a fallback but no longer the
|
|
14
|
+
* primary capture mechanism.
|
|
16
15
|
*
|
|
17
16
|
* Privacy defaults (inherited from `@cubenest/rrweb-core`): headers and
|
|
18
17
|
* bodies are NOT captured unless opted in via {@link networkOptions}.
|
|
18
|
+
* Because the fetch/XHR wrappers are gated behind `recordHeaders` /
|
|
19
|
+
* `recordBody` (both off by default), the DEFAULT capture surface is
|
|
20
|
+
* PerformanceObserver timing only. That has two consequences worth
|
|
21
|
+
* knowing:
|
|
22
|
+
* - method and an accurate per-request status may be unavailable
|
|
23
|
+
* (PerformanceObserver entries don't carry the request method, and
|
|
24
|
+
* report `status` only on same-origin / Timing-Allow-Origin
|
|
25
|
+
* responses);
|
|
26
|
+
* - cross-origin sub-resources report `status: 0` per the Resource
|
|
27
|
+
* Timing spec even though they loaded fine — these are NOT treated
|
|
28
|
+
* as failures by the report.
|
|
29
|
+
* Enable the wrappers (and thus accurate per-request method + status,
|
|
30
|
+
* plus optional headers/bodies) via {@link networkOptions}. The CDP
|
|
31
|
+
* fallback, when present, separately supplies authoritative status and
|
|
32
|
+
* true no-response failures.
|
|
19
33
|
*/
|
|
20
34
|
network?: boolean;
|
|
21
35
|
/** Capture `console.*` via the rrweb console plugin. Default true. */
|
|
@@ -53,6 +67,17 @@ export interface TraceLaneOptions {
|
|
|
53
67
|
cooldownMs?: number;
|
|
54
68
|
/** Options forwarded to the in-page rrweb console plugin. */
|
|
55
69
|
consolePluginOptions?: ConsolePluginOptions;
|
|
70
|
+
/**
|
|
71
|
+
* Report-output tweaks.
|
|
72
|
+
*
|
|
73
|
+
* - `footer` — render the self-contained report's "Generated by tracelane"
|
|
74
|
+
* footer. Default `true`. Set `false` to suppress it (e.g. when embedding
|
|
75
|
+
* reports in an internal tool where the attribution isn't wanted).
|
|
76
|
+
*/
|
|
77
|
+
report?: {
|
|
78
|
+
/** Render the report footer. Default `true`. */
|
|
79
|
+
footer?: boolean;
|
|
80
|
+
};
|
|
56
81
|
}
|
|
57
82
|
/** The default output directory for reports. */
|
|
58
83
|
export declare const DEFAULT_OUT_DIR = "./tracelane-reports";
|
package/dist/options.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAElE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAElE,wDAAwD;AACxD,MAAM,WAAW,cAAc;IAC7B,8CAA8C;IAC9C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;OAQG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED,qFAAqF;AACrF,MAAM,WAAW,gBAAgB;IAC/B;;;;OAIG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,wEAAwE;IACxE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,gFAAgF;IAChF,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,mCAAmC;IACnC,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB,6EAA6E;IAC7E,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,oEAAoE;IACpE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,6DAA6D;IAC7D,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;IAC5C;;;;;;OAMG;IACH,MAAM,CAAC,EAAE;QACP,gDAAgD;QAChD,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;CACH;AAED,gDAAgD;AAChD,eAAO,MAAM,eAAe,wBAAwB,CAAC"}
|
package/dist/options.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,yEAAyE;
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,yEAAyE;AAsFzE,gDAAgD;AAChD,MAAM,CAAC,MAAM,eAAe,GAAG,qBAAqB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracelane-session.d.ts","sourceRoot":"","sources":["../src/tracelane-session.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"tracelane-session.d.ts","sourceRoot":"","sources":["../src/tracelane-session.ts"],"names":[],"mappings":"AAkBA,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,KAAK,WAAW,EAAsB,MAAM,oBAAoB,CAAC;AAQ1E,qFAAqF;AACrF,UAAU,mBAAmB;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,+EAA+E;AAC/E,KAAK,cAAc,GAAG,WAAW,GAAG;IAClC,YAAY,CAAC,EAAE,mBAAmB,CAAC;IACnC,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAmB;IAC3C,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAU;IACvC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAU;IACzC,OAAO,CAAC,SAAS,CAAiC;IAClD,OAAO,CAAC,GAAG,CAAqB;IAEhC,OAAO,CAAC,OAAO,CAA6B;IAC5C,OAAO,CAAC,QAAQ,CAAuB;IACvC,OAAO,CAAC,OAAO,CAA0B;IACzC,OAAO,CAAC,eAAe,CAAS;IAChC,6EAA6E;IAC7E,OAAO,CAAC,kBAAkB,CAAS;gBAEvB,OAAO,GAAE,gBAAqB,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,MAAM;IAW5E,iFAAiF;IACjF,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIjD,+EAA+E;IAC/E,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI;IAIrC;;;;OAIG;IACG,QAAQ,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAItD;;;;;;OAMG;IACH,OAAO,CAAC,2BAA2B;IAKnC,sEAAsE;IACtE,OAAO,CAAC,4BAA4B;IA6BpC,iFAAiF;IAC3E,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAW/D;;;;;OAKG;YACW,yBAAyB;IAevC;;;;;;;;OAQG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAKvC;;;;;;OAMG;IACG,WAAW,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IA6BnF;;;;;OAKG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAO9B,gFAAgF;IAChF,OAAO,CAAC,SAAS;CAclB"}
|
|
@@ -5,12 +5,10 @@
|
|
|
5
5
|
// the capture/inject/drain/report logic lives in exactly one place (ADR-0004:
|
|
6
6
|
// "the same logic exported as plain hook functions"). The session owns the
|
|
7
7
|
// recorder, the current-test metadata, and the report-write decision.
|
|
8
|
-
import { createRecorder, } from '@tracelane/core';
|
|
8
|
+
import { attachNetworkCapture, createRecorder, loadRrwebBundle, } from '@tracelane/core';
|
|
9
|
+
import { writeReport } from '@tracelane/report';
|
|
9
10
|
import { normalizeResult } from './framework-result.js';
|
|
10
|
-
import { loadRrwebBundle } from './inpage-bundle.js';
|
|
11
|
-
import { attachNetworkCapture } from './network-capture.js';
|
|
12
11
|
import { DEFAULT_OUT_DIR } from './options.js';
|
|
13
|
-
import { writeReport } from './report-writer.js';
|
|
14
12
|
import { createWdioExecutor } from './wdio-executor.js';
|
|
15
13
|
export class TraceLaneSession {
|
|
16
14
|
options;
|
|
@@ -70,7 +68,7 @@ export class TraceLaneSession {
|
|
|
70
68
|
createRecorderForCurrentTest(browser) {
|
|
71
69
|
const recorderOptions = {
|
|
72
70
|
executor: createWdioExecutor(browser),
|
|
73
|
-
rrwebBundle: loadRrwebBundle(),
|
|
71
|
+
rrwebBundle: loadRrwebBundle(import.meta.url),
|
|
74
72
|
};
|
|
75
73
|
if (this.options.drainIntervalMs !== undefined) {
|
|
76
74
|
recorderOptions.drainIntervalMs = this.options.drainIntervalMs;
|
|
@@ -165,7 +163,13 @@ export class TraceLaneSession {
|
|
|
165
163
|
return undefined;
|
|
166
164
|
}
|
|
167
165
|
const meta = this.buildMeta(normalized);
|
|
168
|
-
const path = writeReport({
|
|
166
|
+
const path = writeReport({
|
|
167
|
+
outDir: this.outDir,
|
|
168
|
+
cid: this.cid,
|
|
169
|
+
events,
|
|
170
|
+
meta,
|
|
171
|
+
footer: this.options.report?.footer,
|
|
172
|
+
});
|
|
169
173
|
this.current = undefined;
|
|
170
174
|
return path;
|
|
171
175
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tracelane-session.js","sourceRoot":"","sources":["../src/tracelane-session.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,EAAE;AACF,mEAAmE;AACnE,+EAA+E;AAC/E,8EAA8E;AAC9E,2EAA2E;AAC3E,sEAAsE;AAEtE,OAAO,EAIL,
|
|
1
|
+
{"version":3,"file":"tracelane-session.js","sourceRoot":"","sources":["../src/tracelane-session.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tracelane/wdio",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.16",
|
|
4
4
|
"description": "WebdriverIO Service that records rrweb sessions and writes a self-contained HTML report on failed tests. The user-facing tracelane integration for WDIO.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tracelane",
|
|
@@ -38,8 +38,8 @@
|
|
|
38
38
|
"README.md"
|
|
39
39
|
],
|
|
40
40
|
"dependencies": {
|
|
41
|
-
"@tracelane/
|
|
42
|
-
"@tracelane/
|
|
41
|
+
"@tracelane/core": "0.1.0-alpha.11",
|
|
42
|
+
"@tracelane/report": "0.1.0-alpha.13"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
45
|
"@wdio/types": "^9.0.0",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"esbuild": "^0.28.0",
|
|
56
56
|
"tsx": "^4.19.0",
|
|
57
57
|
"webdriverio": "^9.0.0",
|
|
58
|
-
"@cubenest/rrweb-core": "0.1.0-alpha.
|
|
58
|
+
"@cubenest/rrweb-core": "0.1.0-alpha.5"
|
|
59
59
|
},
|
|
60
60
|
"publishConfig": {
|
|
61
61
|
"access": "public",
|
|
@@ -66,7 +66,13 @@
|
|
|
66
66
|
"url": "https://github.com/Cubenest/rrweb-stack",
|
|
67
67
|
"directory": "packages/tracelane-wdio"
|
|
68
68
|
},
|
|
69
|
+
"bugs": {
|
|
70
|
+
"url": "https://github.com/Cubenest/rrweb-stack/issues"
|
|
71
|
+
},
|
|
69
72
|
"homepage": "https://tracelane.cubenest.in",
|
|
73
|
+
"engines": {
|
|
74
|
+
"node": ">=20.18.0"
|
|
75
|
+
},
|
|
70
76
|
"scripts": {
|
|
71
77
|
"build": "tsc -p tsconfig.json && node scripts/build-rrweb-bundle.mjs",
|
|
72
78
|
"typecheck": "tsc -p tsconfig.json --noEmit",
|
package/dist/inpage-bundle.d.ts
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* The rrweb in-page bundle source (defines `window.rrweb`). Read from
|
|
3
|
-
* `dist/rrweb-bundle.js`, cached after first read.
|
|
4
|
-
*
|
|
5
|
-
* @throws if the bundle is missing — that means the package was used without its
|
|
6
|
-
* build step (`pnpm --filter @tracelane/wdio build`) having run.
|
|
7
|
-
*/
|
|
8
|
-
export declare function loadRrwebBundle(): string;
|
|
9
|
-
//# sourceMappingURL=inpage-bundle.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inpage-bundle.d.ts","sourceRoot":"","sources":["../src/inpage-bundle.ts"],"names":[],"mappings":"AAgCA;;;;;;GAMG;AACH,wBAAgB,eAAe,IAAI,MAAM,CAWxC"}
|
package/dist/inpage-bundle.js
DELETED
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
// Loads the in-page rrweb bundle source string.
|
|
2
|
-
//
|
|
3
|
-
// The bundle (`dist/rrweb-bundle.js`) is produced at build time by
|
|
4
|
-
// `scripts/build-rrweb-bundle.mjs` (esbuild → IIFE that defines `window.rrweb`
|
|
5
|
-
// with `record` + `getRecordConsolePlugin`). @tracelane/core's recorder is
|
|
6
|
-
// bundle-source-agnostic (ADR-0006) and expects this source as a plain string,
|
|
7
|
-
// which it `window.eval`s in the page on every (re-)injection.
|
|
8
|
-
//
|
|
9
|
-
// We read it once and cache it. In the published package this module compiles
|
|
10
|
-
// to `dist/inpage-bundle.js`, sitting next to `dist/rrweb-bundle.js`; under
|
|
11
|
-
// vitest the source lives in `src/`, so we also probe a sibling `../dist/`. We
|
|
12
|
-
// derive the module directory from `fileURLToPath(import.meta.url)` directly
|
|
13
|
-
// (not via `new URL(rel, import.meta.url)`) because under the jsdom test env the
|
|
14
|
-
// global `URL` resolves relative inputs against the page origin, not the file.
|
|
15
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
16
|
-
import { dirname, join } from 'node:path';
|
|
17
|
-
import { fileURLToPath } from 'node:url';
|
|
18
|
-
let cached;
|
|
19
|
-
/** Candidate locations for the built bundle, in priority order. */
|
|
20
|
-
function candidatePaths() {
|
|
21
|
-
const moduleDir = dirname(fileURLToPath(import.meta.url));
|
|
22
|
-
return [
|
|
23
|
-
// Published layout: dist/inpage-bundle.js → dist/rrweb-bundle.js.
|
|
24
|
-
join(moduleDir, 'rrweb-bundle.js'),
|
|
25
|
-
// Source/test layout: src/inpage-bundle.ts → ../dist/rrweb-bundle.js.
|
|
26
|
-
join(moduleDir, '..', 'dist', 'rrweb-bundle.js'),
|
|
27
|
-
];
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* The rrweb in-page bundle source (defines `window.rrweb`). Read from
|
|
31
|
-
* `dist/rrweb-bundle.js`, cached after first read.
|
|
32
|
-
*
|
|
33
|
-
* @throws if the bundle is missing — that means the package was used without its
|
|
34
|
-
* build step (`pnpm --filter @tracelane/wdio build`) having run.
|
|
35
|
-
*/
|
|
36
|
-
export function loadRrwebBundle() {
|
|
37
|
-
if (cached !== undefined)
|
|
38
|
-
return cached;
|
|
39
|
-
const candidates = candidatePaths();
|
|
40
|
-
const found = candidates.find((p) => existsSync(p));
|
|
41
|
-
if (found === undefined) {
|
|
42
|
-
throw new Error(`@tracelane/wdio: in-page rrweb bundle not found (looked in ${candidates.join(', ')}). Run the package build (\`pnpm --filter @tracelane/wdio build\`) to generate it.`);
|
|
43
|
-
}
|
|
44
|
-
cached = readFileSync(found, 'utf8');
|
|
45
|
-
return cached;
|
|
46
|
-
}
|
|
47
|
-
//# sourceMappingURL=inpage-bundle.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"inpage-bundle.js","sourceRoot":"","sources":["../src/inpage-bundle.ts"],"names":[],"mappings":"AAAA,gDAAgD;AAChD,EAAE;AACF,mEAAmE;AACnE,+EAA+E;AAC/E,2EAA2E;AAC3E,+EAA+E;AAC/E,+DAA+D;AAC/D,EAAE;AACF,8EAA8E;AAC9E,4EAA4E;AAC5E,+EAA+E;AAC/E,6EAA6E;AAC7E,iFAAiF;AACjF,+EAA+E;AAE/E,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,IAAI,MAA0B,CAAC;AAE/B,mEAAmE;AACnE,SAAS,cAAc;IACrB,MAAM,SAAS,GAAG,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;IAC1D,OAAO;QACL,kEAAkE;QAClE,IAAI,CAAC,SAAS,EAAE,iBAAiB,CAAC;QAClC,sEAAsE;QACtE,IAAI,CAAC,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,iBAAiB,CAAC;KACjD,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,eAAe;IAC7B,IAAI,MAAM,KAAK,SAAS;QAAE,OAAO,MAAM,CAAC;IACxC,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;IACpC,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC;IACpD,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CACb,8DAA8D,UAAU,CAAC,IAAI,CAAC,IAAI,CAAC,oFAAoF,CACxK,CAAC;IACJ,CAAC;IACD,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;IACrC,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import type { BrowserExecutor } from '@tracelane/core';
|
|
2
|
-
/**
|
|
3
|
-
* Page-side logger. Self-contained (no Node closures) so it can be
|
|
4
|
-
* `.toString()`-serialized by `execute` (PRD §A.4). The console plugin captures
|
|
5
|
-
* this `console.error`; the `[tracelane.net]` prefix lets the report's network
|
|
6
|
-
* panel scrape it back out (PRD §E.2).
|
|
7
|
-
*/
|
|
8
|
-
declare function logNetworkErrorInPage(url: string, status: number, method: string): void;
|
|
9
|
-
/** Pull the request method out of CDP request headers, defaulting to GET. */
|
|
10
|
-
declare function methodOf(headers: Record<string, string> | undefined): string;
|
|
11
|
-
/**
|
|
12
|
-
* Attach CDP network capture to a BrowserExecutor (P1 PRD §E.2).
|
|
13
|
-
*
|
|
14
|
-
* Enables the Network domain and registers a `Network.responseReceived`
|
|
15
|
-
* subscriber that forwards 4xx/5xx responses into `console.error`. Resolves once
|
|
16
|
-
* `Network.enable` has been sent. The subscriber's own `execute` calls are
|
|
17
|
-
* fire-and-forget (their failures must not break the test).
|
|
18
|
-
*/
|
|
19
|
-
export declare function attachNetworkCapture(executor: BrowserExecutor): Promise<void>;
|
|
20
|
-
export declare const __internal: {
|
|
21
|
-
logNetworkErrorInPage: typeof logNetworkErrorInPage;
|
|
22
|
-
methodOf: typeof methodOf;
|
|
23
|
-
};
|
|
24
|
-
export {};
|
|
25
|
-
//# sourceMappingURL=network-capture.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"network-capture.d.ts","sourceRoot":"","sources":["../src/network-capture.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAWvD;;;;;GAKG;AACH,iBAAS,qBAAqB,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAEhF;AAED,6EAA6E;AAC7E,iBAAS,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,SAAS,GAAG,MAAM,CAKrE;AAED;;;;;;;GAOG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAgBnF;AAGD,eAAO,MAAM,UAAU;;;CAAsC,CAAC"}
|
package/dist/network-capture.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// CDP network capture wiring (Task 2.16 / P1 PRD §E.2).
|
|
2
|
-
//
|
|
3
|
-
// Enable the CDP Network domain, subscribe to `Network.responseReceived`, and
|
|
4
|
-
// route any response with `status >= 400` into the page's `console.error` via
|
|
5
|
-
// `executor.execute`. The rrweb console plugin (installed by @tracelane/core's
|
|
6
|
-
// recorder) then captures that console line, so failed responses show up in the
|
|
7
|
-
// report's console panel "for free" — no dedicated network transport in v1.
|
|
8
|
-
//
|
|
9
|
-
// The console line is prefixed `[tracelane.net]` so @tracelane/report's network
|
|
10
|
-
// panel can scrape it back out (NETWORK_CONSOLE_PREFIX in panels.ts).
|
|
11
|
-
/**
|
|
12
|
-
* Page-side logger. Self-contained (no Node closures) so it can be
|
|
13
|
-
* `.toString()`-serialized by `execute` (PRD §A.4). The console plugin captures
|
|
14
|
-
* this `console.error`; the `[tracelane.net]` prefix lets the report's network
|
|
15
|
-
* panel scrape it back out (PRD §E.2).
|
|
16
|
-
*/
|
|
17
|
-
function logNetworkErrorInPage(url, status, method) {
|
|
18
|
-
console.error(`[tracelane.net] ${method} ${status} ${url}`);
|
|
19
|
-
}
|
|
20
|
-
/** Pull the request method out of CDP request headers, defaulting to GET. */
|
|
21
|
-
function methodOf(headers) {
|
|
22
|
-
if (!headers)
|
|
23
|
-
return 'GET';
|
|
24
|
-
// CDP exposes the pseudo-header `:method` for HTTP/2/3; fall back to a plain
|
|
25
|
-
// `method` header, then GET.
|
|
26
|
-
return headers[':method'] ?? headers.method ?? 'GET';
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Attach CDP network capture to a BrowserExecutor (P1 PRD §E.2).
|
|
30
|
-
*
|
|
31
|
-
* Enables the Network domain and registers a `Network.responseReceived`
|
|
32
|
-
* subscriber that forwards 4xx/5xx responses into `console.error`. Resolves once
|
|
33
|
-
* `Network.enable` has been sent. The subscriber's own `execute` calls are
|
|
34
|
-
* fire-and-forget (their failures must not break the test).
|
|
35
|
-
*/
|
|
36
|
-
export async function attachNetworkCapture(executor) {
|
|
37
|
-
await executor.cdp('Network', 'enable');
|
|
38
|
-
executor.on('Network.responseReceived', (params) => {
|
|
39
|
-
const response = params?.response;
|
|
40
|
-
const status = response?.status;
|
|
41
|
-
if (typeof status !== 'number' || status < 400)
|
|
42
|
-
return;
|
|
43
|
-
const url = response?.url ?? '';
|
|
44
|
-
const method = methodOf(response?.requestHeaders);
|
|
45
|
-
// Fire-and-forget: a logging failure (e.g. page mid-navigation) must not
|
|
46
|
-
// surface as a test error.
|
|
47
|
-
void executor
|
|
48
|
-
.execute(logNetworkErrorInPage, url, status, method)
|
|
49
|
-
.catch(() => {
|
|
50
|
-
/* page may be navigating; drop this one line */
|
|
51
|
-
});
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
// Exposed for unit tests: the page-side logger + method resolver are pure.
|
|
55
|
-
export const __internal = { logNetworkErrorInPage, methodOf };
|
|
56
|
-
//# sourceMappingURL=network-capture.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"network-capture.js","sourceRoot":"","sources":["../src/network-capture.ts"],"names":[],"mappings":"AAAA,wDAAwD;AACxD,EAAE;AACF,8EAA8E;AAC9E,8EAA8E;AAC9E,+EAA+E;AAC/E,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,gFAAgF;AAChF,sEAAsE;AAatE;;;;;GAKG;AACH,SAAS,qBAAqB,CAAC,GAAW,EAAE,MAAc,EAAE,MAAc;IACxE,OAAO,CAAC,KAAK,CAAC,mBAAmB,MAAM,IAAI,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,6EAA6E;AAC7E,SAAS,QAAQ,CAAC,OAA2C;IAC3D,IAAI,CAAC,OAAO;QAAE,OAAO,KAAK,CAAC;IAC3B,6EAA6E;IAC7E,6BAA6B;IAC7B,OAAO,OAAO,CAAC,SAAS,CAAC,IAAI,OAAO,CAAC,MAAM,IAAI,KAAK,CAAC;AACvD,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,QAAyB;IAClE,MAAM,QAAQ,CAAC,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;IACxC,QAAQ,CAAC,EAAE,CAAC,0BAA0B,EAAE,CAAC,MAAe,EAAE,EAAE;QAC1D,MAAM,QAAQ,GAAI,MAAgC,EAAE,QAAQ,CAAC;QAC7D,MAAM,MAAM,GAAG,QAAQ,EAAE,MAAM,CAAC;QAChC,IAAI,OAAO,MAAM,KAAK,QAAQ,IAAI,MAAM,GAAG,GAAG;YAAE,OAAO;QACvD,MAAM,GAAG,GAAG,QAAQ,EAAE,GAAG,IAAI,EAAE,CAAC;QAChC,MAAM,MAAM,GAAG,QAAQ,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QAClD,yEAAyE;QACzE,2BAA2B;QAC3B,KAAK,QAAQ;aACV,OAAO,CAAC,qBAAqD,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC;aACnF,KAAK,CAAC,GAAG,EAAE;YACV,gDAAgD;QAClD,CAAC,CAAC,CAAC;IACP,CAAC,CAAC,CAAC;AACL,CAAC;AAED,2EAA2E;AAC3E,MAAM,CAAC,MAAM,UAAU,GAAG,EAAE,qBAAqB,EAAE,QAAQ,EAAE,CAAC"}
|
package/dist/report-writer.d.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import type { eventWithTime } from '@cubenest/rrweb-core';
|
|
2
|
-
import { type ReportMeta } from '@tracelane/report';
|
|
3
|
-
/** Inputs for {@link writeReport}. */
|
|
4
|
-
export interface WriteReportInput {
|
|
5
|
-
/** Output directory (created if missing). */
|
|
6
|
-
outDir: string;
|
|
7
|
-
/** Worker capability id (e.g. `0-0`); namespaces the filename for parallel safety. */
|
|
8
|
-
cid?: string | undefined;
|
|
9
|
-
/** Captured rrweb event stream. */
|
|
10
|
-
events: eventWithTime[];
|
|
11
|
-
/** Report metadata (spec, title, status, error, browser, ...). */
|
|
12
|
-
meta: ReportMeta;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Turn an arbitrary spec path / test title into a filesystem-safe slug:
|
|
16
|
-
* lowercase, non-alphanumerics collapsed to single dashes, trimmed, capped.
|
|
17
|
-
*/
|
|
18
|
-
export declare function slugify(input: string): string;
|
|
19
|
-
/**
|
|
20
|
-
* Build a unique-ish report filename: `<spec>--<title>--<cid>-<ts>.html`. The
|
|
21
|
-
* timestamp + cid keep retries and parallel workers from overwriting each other.
|
|
22
|
-
*/
|
|
23
|
-
export declare function reportFileName(meta: ReportMeta, cid?: string): string;
|
|
24
|
-
/**
|
|
25
|
-
* Slug for the spec segment: take the file's basename (so an absolute path like
|
|
26
|
-
* `/repo/test/login.spec.ts` doesn't bloat the filename), then strip the common
|
|
27
|
-
* test-file suffixes (`.spec.ts`, `.test.mts`, `.e2e.js`, …) so it slugifies to
|
|
28
|
-
* a clean `login`, not `login-spec`.
|
|
29
|
-
*/
|
|
30
|
-
export declare function specSlug(spec: string): string;
|
|
31
|
-
/**
|
|
32
|
-
* Build the report HTML and write it under `outDir`, namespaced by `cid`.
|
|
33
|
-
* Returns the absolute-or-relative path written (as `join` produced it).
|
|
34
|
-
*/
|
|
35
|
-
export declare function writeReport(input: WriteReportInput): string;
|
|
36
|
-
//# sourceMappingURL=report-writer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"report-writer.d.ts","sourceRoot":"","sources":["../src/report-writer.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,KAAK,UAAU,EAAe,MAAM,mBAAmB,CAAC;AAEjE,sCAAsC;AACtC,MAAM,WAAW,gBAAgB;IAC/B,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,sFAAsF;IACtF,GAAG,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,mCAAmC;IACnC,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,kEAAkE;IAClE,IAAI,EAAE,UAAU,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO7C;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,UAAU,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,MAAM,CAKrE;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG7C;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,gBAAgB,GAAG,MAAM,CAS3D"}
|
package/dist/report-writer.js
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
// Write a self-contained HTML report to disk (ADR-0005 / ADR-0006).
|
|
2
|
-
//
|
|
3
|
-
// Given the captured event stream + metadata, call @tracelane/report's
|
|
4
|
-
// buildReport to produce the offline HTML string, then write it under `outDir`.
|
|
5
|
-
// Files are namespaced by `cid` (the WDIO worker capability id) so parallel
|
|
6
|
-
// workers (`maxInstances > 1`) never collide (ADR-0006 action item #6).
|
|
7
|
-
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
8
|
-
import { join } from 'node:path';
|
|
9
|
-
import { buildReport } from '@tracelane/report';
|
|
10
|
-
/**
|
|
11
|
-
* Turn an arbitrary spec path / test title into a filesystem-safe slug:
|
|
12
|
-
* lowercase, non-alphanumerics collapsed to single dashes, trimmed, capped.
|
|
13
|
-
*/
|
|
14
|
-
export function slugify(input) {
|
|
15
|
-
const slug = input
|
|
16
|
-
.toLowerCase()
|
|
17
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
18
|
-
.replace(/^-+|-+$/g, '');
|
|
19
|
-
// Cap length so deeply-nested titles don't blow past filesystem name limits.
|
|
20
|
-
return (slug || 'report').slice(0, 120);
|
|
21
|
-
}
|
|
22
|
-
/**
|
|
23
|
-
* Build a unique-ish report filename: `<spec>--<title>--<cid>-<ts>.html`. The
|
|
24
|
-
* timestamp + cid keep retries and parallel workers from overwriting each other.
|
|
25
|
-
*/
|
|
26
|
-
export function reportFileName(meta, cid) {
|
|
27
|
-
const specPart = meta.spec ? specSlug(meta.spec) : 'spec';
|
|
28
|
-
const titlePart = slugify(meta.title);
|
|
29
|
-
const cidPart = cid ? `${slugify(cid)}-` : '';
|
|
30
|
-
return `${specPart}--${titlePart}--${cidPart}${Date.now()}.html`;
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Slug for the spec segment: take the file's basename (so an absolute path like
|
|
34
|
-
* `/repo/test/login.spec.ts` doesn't bloat the filename), then strip the common
|
|
35
|
-
* test-file suffixes (`.spec.ts`, `.test.mts`, `.e2e.js`, …) so it slugifies to
|
|
36
|
-
* a clean `login`, not `login-spec`.
|
|
37
|
-
*/
|
|
38
|
-
export function specSlug(spec) {
|
|
39
|
-
const base = spec.replace(/^.*[/\\]/, '');
|
|
40
|
-
return slugify(base.replace(/(\.(spec|test|e2e|cy))?\.[cm]?[jt]sx?$/, ''));
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Build the report HTML and write it under `outDir`, namespaced by `cid`.
|
|
44
|
-
* Returns the absolute-or-relative path written (as `join` produced it).
|
|
45
|
-
*/
|
|
46
|
-
export function writeReport(input) {
|
|
47
|
-
const { outDir, cid, events, meta } = input;
|
|
48
|
-
const html = buildReport(events, meta);
|
|
49
|
-
if (!existsSync(outDir)) {
|
|
50
|
-
mkdirSync(outDir, { recursive: true });
|
|
51
|
-
}
|
|
52
|
-
const filePath = join(outDir, reportFileName(meta, cid));
|
|
53
|
-
writeFileSync(filePath, html);
|
|
54
|
-
return filePath;
|
|
55
|
-
}
|
|
56
|
-
//# sourceMappingURL=report-writer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"report-writer.js","sourceRoot":"","sources":["../src/report-writer.ts"],"names":[],"mappings":"AAAA,oEAAoE;AACpE,EAAE;AACF,uEAAuE;AACvE,gFAAgF;AAChF,4EAA4E;AAC5E,wEAAwE;AAExE,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAmB,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAcjE;;;GAGG;AACH,MAAM,UAAU,OAAO,CAAC,KAAa;IACnC,MAAM,IAAI,GAAG,KAAK;SACf,WAAW,EAAE;SACb,OAAO,CAAC,aAAa,EAAE,GAAG,CAAC;SAC3B,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC3B,6EAA6E;IAC7E,OAAO,CAAC,IAAI,IAAI,QAAQ,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC1C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAgB,EAAE,GAAY;IAC3D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC;IAC1D,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtC,MAAM,OAAO,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9C,OAAO,GAAG,QAAQ,KAAK,SAAS,KAAK,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,OAAO,CAAC;AACnE,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,IAAY;IACnC,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,wCAAwC,EAAE,EAAE,CAAC,CAAC,CAAC;AAC7E,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,WAAW,CAAC,KAAuB;IACjD,MAAM,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,KAAK,CAAC;IAC5C,MAAM,IAAI,GAAG,WAAW,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IACvC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,CAAC;QACxB,SAAS,CAAC,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACzC,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE,cAAc,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,CAAC;IACzD,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC9B,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|