@tracelane/playwright 0.1.0-alpha.6 → 0.1.0-alpha.7
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 +37 -7
- package/dist/options.d.ts +80 -13
- package/dist/options.d.ts.map +1 -1
- package/dist/options.js +91 -13
- package/dist/options.js.map +1 -1
- package/dist/playwright-session.d.ts.map +1 -1
- package/dist/playwright-session.js +62 -7
- package/dist/playwright-session.js.map +1 -1
- package/dist/reporter.d.ts.map +1 -1
- package/dist/reporter.js +41 -9
- package/dist/reporter.js.map +1 -1
- package/dist/security-suppress.d.ts +10 -0
- package/dist/security-suppress.d.ts.map +1 -0
- package/dist/security-suppress.js +58 -0
- package/dist/security-suppress.js.map +1 -0
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -55,19 +55,49 @@ Run your suite. On a failing test you get one `.html` file at `./tracelane-repor
|
|
|
55
55
|
|
|
56
56
|
- **The fixture** owns the recording — and keeps it going across navigations, so the replay is one continuous session, not a set of per-page snapshots. It is the only place with a live `page` + `testInfo`, so it injects the rrweb bundle via `context.addInitScript` AND hooks `page.on('framenavigated')` to call `recorder.reinject` on every main-frame navigation (each navigation emits a `tracelane.nav` boundary marker in the replay). It starts the recorder before your test body, and — after it — builds + writes the report. It reuses `@tracelane/core`'s recorder and `@tracelane/report`'s HTML builder.
|
|
57
57
|
- **The reporter** owns config only: it validates options at startup and bridges them to the fixture via `TRACELANE_*` env vars. By design it never touches `page`, prints nothing, and produces no end-of-run summary (the fixture writes the per-test reports).
|
|
58
|
-
- **
|
|
58
|
+
- **Network capture** is captured **in-page** by the framework-agnostic `rrweb/network@1` plugin — it works on **every browser** (Chromium, Firefox, WebKit) with no CDP, because it wraps `fetch`/`XHR` and reads `PerformanceObserver` entries from inside the page. Privacy-first: only URL/method/status/timing (headers + bodies off). On **Chromium** tracelane *additionally* uses CDP to enrich the panel with authoritative HTTP status for failed responses (`4xx`/`5xx`) and true no-response failures (CORS/DNS/offline/abort); the report merges the CDP rows over the in-page rows for the same request (real status wins). Off entirely when `capture.network` is `false`.
|
|
59
59
|
- **Parallel-safe**: report filenames are namespaced by the Playwright **project name** and carry a millisecond timestamp. Different projects are isolated by name; parallel workers *within one project* share the project-name segment and rely on the timestamp (plus spec + title) to stay distinct.
|
|
60
60
|
- **Coexists** with Playwright's own `trace` — keep `trace: 'on-first-retry'` if you like; tracelane writes a separate, self-contained artifact.
|
|
61
61
|
|
|
62
62
|
## Options
|
|
63
63
|
|
|
64
|
-
| Option | Default | Notes |
|
|
65
|
-
| --- | --- | --- |
|
|
66
|
-
| `mode` | `'failed'` | `'failed'` writes a report only on failure; `'all'` writes one for every test.
|
|
67
|
-
| `outDir` | `'./tracelane-reports'` | Where reports are written.
|
|
68
|
-
| `
|
|
64
|
+
| Option | Type | Default | Notes |
|
|
65
|
+
| --- | --- | --- | --- |
|
|
66
|
+
| `mode` | `'failed' \| 'all'` | `'failed'` | `'failed'` writes a report only on failure; `'all'` writes one for every test. Env: `TRACELANE_MODE`. |
|
|
67
|
+
| `outDir` | `string` | `'./tracelane-reports'` | Where reports are written. Env: `TRACELANE_OUT_DIR`. |
|
|
68
|
+
| `capture.rrweb` | `boolean` | `true` | Record the rrweb session. When `false`, no recorder starts and **no report is written** at all. Env: `TRACELANE_CAPTURE_RRWEB`. |
|
|
69
|
+
| `capture.network` | `boolean` | `true` | Network capture: the in-page `rrweb/network@1` plugin on **all browsers**, plus CDP enrichment (authoritative status + no-response failures) on Chromium. Env: `TRACELANE_CAPTURE_NETWORK`. Supersedes the deprecated top-level `captureNetwork`. |
|
|
70
|
+
| `capture.console` | `boolean` | `true` | Capture `console.*` via the rrweb console plugin. When `false` the console plugin patches nothing (`{ level: [] }`). Env: `TRACELANE_CAPTURE_CONSOLE`. |
|
|
71
|
+
| `capture.networkOptions` | `NetworkRecordOptions` | plugin defaults | Forwarded to the in-page network plugin (`recordHeaders`, `recordBody`, `payloadHostDenyList`, …). Defaults are privacy-first (headers + bodies off). Ignored when `capture.network` is `false`. **Function-valued props (`maskRequestFn`, `maskResponseFn`) are NOT supported** via reporter config — see below. Env: `TRACELANE_NETWORK_OPTIONS` (JSON). |
|
|
72
|
+
| `consolePluginOptions` | `ConsolePluginOptions` | plugin defaults | Forwarded to the in-page console plugin. Env: `TRACELANE_CONSOLE_OPTIONS` (JSON). |
|
|
73
|
+
| `security` | `boolean` | `true` | Advisory security-hygiene signals in the report. `false` disables both the `[tracelane.sec]` capture and the report-side analysis (the report omits the "Security hygiene (advisory)" section). Env: `TRACELANE_SECURITY`. |
|
|
74
|
+
| `report.footer` | `boolean` | `true` | Render the report's "Generated by tracelane" footer. `false` suppresses it. Env: `TRACELANE_FOOTER`. |
|
|
75
|
+
| `drainIntervalMs` | `number` | `500` (core default) | Node-side drain poll interval. Env: `TRACELANE_DRAIN_INTERVAL_MS`. |
|
|
76
|
+
| `cooldownMs` | `number` | `250` (core default) | Re-injection cooldown guard (suppresses double-init on hash/HMR navigation). Env: `TRACELANE_COOLDOWN_MS`. |
|
|
77
|
+
| `captureNetwork` | `boolean` | `true` | **Deprecated** — use `capture.network`. Kept for back-compat; `capture.network` wins when both are set. Env: `TRACELANE_CAPTURE_NETWORK`. |
|
|
69
78
|
|
|
70
|
-
|
|
79
|
+
The options type is published — `import type { TraceLaneOptions } from '@tracelane/playwright'`.
|
|
80
|
+
|
|
81
|
+
> **The options→env bridge.** The Playwright fixture runs in a **separate worker process** and reads its configuration **only** from `TRACELANE_*` env vars. The reporter bridges its constructor options to those env vars at startup — but only when the env var is not already set, so an explicit env var (or CLI value) always wins. Boolean/number options bridge as strings; `capture.networkOptions` and `consolePluginOptions` bridge as JSON strings.
|
|
82
|
+
|
|
83
|
+
> **Mask functions are not supported via reporter config (worker-process limitation).** Because options cross to the fixture worker as env-var strings, the JSON bridge for `capture.networkOptions` cannot carry **function-valued** props — `maskRequestFn` / `maskResponseFn` are silently dropped (`JSON.stringify` strips them). Use only JSON-serializable masking options (`recordHeaders`, `recordBody`, `payloadHostDenyList`, …) when configuring through the reporter.
|
|
84
|
+
|
|
85
|
+
## Security hygiene (advisory)
|
|
86
|
+
|
|
87
|
+
By default the report includes an advisory security-hygiene section derived from privacy-safe main-document response metadata captured on Chromium (`[tracelane.sec]` — security-header **presence** + cookie-flag hygiene; never header or cookie **values**). Set `security: false` to disable both the capture and the report-side analysis.
|
|
88
|
+
|
|
89
|
+
You can silence known-acceptable signals by committing a `tracelane.security.suppress.json` file in the project working directory; it's loaded at report-write time (a missing/malformed file never throws — it degrades to no suppressions):
|
|
90
|
+
|
|
91
|
+
```json
|
|
92
|
+
{
|
|
93
|
+
"suppressions": [
|
|
94
|
+
{ "signal": "missing-csp", "evidence": "https://app.test" },
|
|
95
|
+
{ "signal": "insecure-cookie" }
|
|
96
|
+
]
|
|
97
|
+
}
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
It carries only advisory `{ signal?, evidence? }` rules — no secrets — so reading it from the working directory is safe.
|
|
71
101
|
|
|
72
102
|
## What this is NOT
|
|
73
103
|
|
package/dist/options.d.ts
CHANGED
|
@@ -1,7 +1,35 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { NetworkRecordOptions } from '@cubenest/rrweb-core';
|
|
2
|
+
import type { ConsolePluginOptions, Mode } from '@tracelane/core';
|
|
2
3
|
/** The default output directory for reports (matches @tracelane/wdio). */
|
|
3
4
|
export declare const DEFAULT_OUT_DIR = "./tracelane-reports";
|
|
4
5
|
type EnvLike = Record<string, string | undefined>;
|
|
6
|
+
/** Which capture channels are enabled (mirrors @tracelane/wdio §M.1). */
|
|
7
|
+
export interface CaptureOptions {
|
|
8
|
+
/** Record the rrweb session. Default `true`. When `false`, no recorder starts and no report is written. */
|
|
9
|
+
rrweb?: boolean;
|
|
10
|
+
/**
|
|
11
|
+
* Capture network requests. Default `true`. Supersedes the deprecated
|
|
12
|
+
* top-level {@link TraceLaneOptions.captureNetwork}. Captured in-page by the
|
|
13
|
+
* framework-agnostic `rrweb/network@1` plugin (all browsers, no CDP); on
|
|
14
|
+
* Chromium CDP additionally enriches failed responses.
|
|
15
|
+
*/
|
|
16
|
+
network?: boolean;
|
|
17
|
+
/** Capture `console.*` via the rrweb console plugin. Default `true`. */
|
|
18
|
+
console?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Options forwarded to the in-page rrweb network plugin
|
|
21
|
+
* (`getRecordNetworkPlugin`): `recordHeaders`, `recordBody`,
|
|
22
|
+
* `payloadHostDenyList`, etc. Defaults are privacy-first (headers + bodies
|
|
23
|
+
* off). Ignored when {@link network} is `false`.
|
|
24
|
+
*
|
|
25
|
+
* LIMITATION (worker-process bridge): function-valued props
|
|
26
|
+
* (`maskRequestFn`, `maskResponseFn`) are NOT supported via reporter config.
|
|
27
|
+
* The reporter bridges this object to the fixture as a JSON string, and
|
|
28
|
+
* functions cannot survive `JSON.stringify` / an env var — they are dropped
|
|
29
|
+
* silently. Use only JSON-serializable masking options here.
|
|
30
|
+
*/
|
|
31
|
+
networkOptions?: NetworkRecordOptions;
|
|
32
|
+
}
|
|
5
33
|
/** User-supplied options (all optional; passed to the reporter and/or fixture). */
|
|
6
34
|
export interface TraceLaneOptions {
|
|
7
35
|
/**
|
|
@@ -12,33 +40,72 @@ export interface TraceLaneOptions {
|
|
|
12
40
|
/** Directory to write reports into. Default `'./tracelane-reports'`. `TRACELANE_OUT_DIR` overrides. */
|
|
13
41
|
outDir?: string;
|
|
14
42
|
/**
|
|
15
|
-
* Capture
|
|
16
|
-
*
|
|
43
|
+
* Capture network requests. Default `true`.
|
|
44
|
+
*
|
|
45
|
+
* @deprecated Use {@link CaptureOptions.network} (`capture: { network }`)
|
|
46
|
+
* instead. Kept for back-compat; `capture.network` wins when both are set.
|
|
47
|
+
*
|
|
48
|
+
* Captured in-page by the framework-agnostic `rrweb/network@1` plugin, which
|
|
49
|
+
* works on ALL browsers (Chromium/Firefox/WebKit) with no CDP. On Chromium,
|
|
50
|
+
* CDP additionally enriches the report with authoritative status for failed
|
|
51
|
+
* responses and true no-response failures (the report merges the two). Set
|
|
52
|
+
* `false` to disable network capture entirely (both channels).
|
|
17
53
|
*
|
|
18
54
|
* The reporter bridges this option to `TRACELANE_CAPTURE_NETWORK` at startup
|
|
19
55
|
* (only when that env var is not already set), so the fixture honors it. An
|
|
20
56
|
* explicit `TRACELANE_CAPTURE_NETWORK` env var always wins over this option.
|
|
21
|
-
* To force-disable CDP capture regardless of reporter config, set:
|
|
22
|
-
* `TRACELANE_CAPTURE_NETWORK=false` before running Playwright.
|
|
23
57
|
*/
|
|
24
58
|
captureNetwork?: boolean;
|
|
59
|
+
/** Per-channel capture toggles (rrweb / network / console) + network masking. */
|
|
60
|
+
capture?: CaptureOptions;
|
|
61
|
+
/**
|
|
62
|
+
* Advisory security-hygiene signals in the report. Default `true`. Set
|
|
63
|
+
* `false` to disable both the `[tracelane.sec]` capture and the report-side
|
|
64
|
+
* analysis. Bridged via `TRACELANE_SECURITY` (`'false'` disables).
|
|
65
|
+
*/
|
|
66
|
+
security?: boolean;
|
|
67
|
+
/** Report-output tweaks. */
|
|
68
|
+
report?: {
|
|
69
|
+
/** Render the report's "Generated by tracelane" footer. Default `true`. */
|
|
70
|
+
footer?: boolean;
|
|
71
|
+
};
|
|
72
|
+
/** Node-side drain poll interval in ms (ADR-0006). Default from @tracelane/core when unset. */
|
|
73
|
+
drainIntervalMs?: number;
|
|
74
|
+
/** Re-injection cooldown guard in ms (ADR-0006). Default from @tracelane/core when unset. */
|
|
75
|
+
cooldownMs?: number;
|
|
76
|
+
/**
|
|
77
|
+
* Options forwarded to the in-page rrweb console plugin. Bridged to the
|
|
78
|
+
* fixture as a JSON string, so only JSON-serializable props survive.
|
|
79
|
+
*/
|
|
80
|
+
consolePluginOptions?: ConsolePluginOptions;
|
|
25
81
|
}
|
|
26
|
-
/** Fully-resolved options — every field present. */
|
|
82
|
+
/** Fully-resolved options — every always-present field present. */
|
|
27
83
|
export interface ResolvedOptions {
|
|
28
84
|
mode: Mode;
|
|
29
85
|
outDir: string;
|
|
30
86
|
captureNetwork: boolean;
|
|
87
|
+
captureRrweb: boolean;
|
|
88
|
+
captureConsole: boolean;
|
|
89
|
+
security: boolean;
|
|
90
|
+
footer: boolean;
|
|
91
|
+
drainIntervalMs?: number;
|
|
92
|
+
cooldownMs?: number;
|
|
93
|
+
networkOptions?: NetworkRecordOptions;
|
|
94
|
+
consolePluginOptions?: ConsolePluginOptions;
|
|
31
95
|
}
|
|
32
96
|
/**
|
|
33
97
|
* Resolve user options into a fully-resolved shape, applying defaults and the
|
|
34
|
-
* `
|
|
35
|
-
*
|
|
36
|
-
*
|
|
98
|
+
* `TRACELANE_*` env overrides (env wins over config, matching @tracelane/core's
|
|
99
|
+
* resolveMode). An invalid `TRACELANE_MODE` is ignored. `env` is injectable for
|
|
100
|
+
* testing.
|
|
37
101
|
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
102
|
+
* Env overrides:
|
|
103
|
+
* - `TRACELANE_MODE`, `TRACELANE_OUT_DIR`
|
|
104
|
+
* - `TRACELANE_CAPTURE_NETWORK` (`'false'` disables both the in-page plugin and
|
|
105
|
+
* the Chromium CDP enrichment) / `_RRWEB` / `_CONSOLE` (`'false'` disables)
|
|
106
|
+
* - `TRACELANE_SECURITY`, `TRACELANE_FOOTER` (`'false'` disables)
|
|
107
|
+
* - `TRACELANE_DRAIN_INTERVAL_MS`, `TRACELANE_COOLDOWN_MS` (parsed int; NaN ignored)
|
|
108
|
+
* - `TRACELANE_NETWORK_OPTIONS`, `TRACELANE_CONSOLE_OPTIONS` (JSON; bad JSON → undefined)
|
|
42
109
|
*/
|
|
43
110
|
export declare function resolveOptions(opts?: TraceLaneOptions, env?: EnvLike): ResolvedOptions;
|
|
44
111
|
export {};
|
package/dist/options.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,oBAAoB,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAElE,0EAA0E;AAC1E,eAAO,MAAM,eAAe,wBAAwB,CAAC;AAErD,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC,CAAC;AAElD,yEAAyE;AACzE,MAAM,WAAW,cAAc;IAC7B,2GAA2G;IAC3G,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wEAAwE;IACxE,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,oBAAoB,CAAC;CACvC;AAED,mFAAmF;AACnF,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;;;;;;;;;;;;OAeG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,iFAAiF;IACjF,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,4BAA4B;IAC5B,MAAM,CAAC,EAAE;QACP,2EAA2E;QAC3E,MAAM,CAAC,EAAE,OAAO,CAAC;KAClB,CAAC;IACF,+FAA+F;IAC/F,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,6FAA6F;IAC7F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AAED,mEAAmE;AACnE,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,IAAI,CAAC;IACX,MAAM,EAAE,MAAM,CAAC;IACf,cAAc,EAAE,OAAO,CAAC;IACxB,YAAY,EAAE,OAAO,CAAC;IACtB,cAAc,EAAE,OAAO,CAAC;IACxB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,oBAAoB,CAAC;IACtC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C;AA0CD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAC5B,IAAI,GAAE,gBAAqB,EAC3B,GAAG,GAAE,OAAsB,GAC1B,eAAe,CAkDjB"}
|
package/dist/options.js
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
// User-facing options for the Playwright reporter + fixture, and resolveOptions
|
|
2
2
|
// which normalizes them into the fully-resolved shape the session consumes.
|
|
3
3
|
//
|
|
4
|
-
// The option names mirror @tracelane/wdio (mode, outDir
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
//
|
|
4
|
+
// The option names mirror @tracelane/wdio (mode, outDir, capture.*, security,
|
|
5
|
+
// report.footer, drainIntervalMs, cooldownMs, consolePluginOptions) and the env
|
|
6
|
+
// contract mirrors @tracelane/core (TRACELANE_MODE) plus the TRACELANE_* bridge
|
|
7
|
+
// the reporter sets at startup.
|
|
8
|
+
//
|
|
9
|
+
// IMPORTANT — the worker-process bridge: the Playwright fixture runs in a
|
|
10
|
+
// SEPARATE worker process and reads configuration ONLY from TRACELANE_* env
|
|
11
|
+
// vars, which the Reporter (reporter.ts) bridges from its constructor options.
|
|
12
|
+
// Env vars are strings, so JSON-serializable options (networkOptions,
|
|
13
|
+
// consolePluginOptions) cross as JSON; FUNCTION-valued options (e.g. a network
|
|
14
|
+
// `maskRequestFn` / `maskResponseFn`) CANNOT cross this boundary and are NOT
|
|
15
|
+
// supported via reporter config — see the JSDoc on `capture.networkOptions`.
|
|
8
16
|
/** The default output directory for reports (matches @tracelane/wdio). */
|
|
9
17
|
export const DEFAULT_OUT_DIR = './tracelane-reports';
|
|
10
18
|
function isMode(value) {
|
|
@@ -14,24 +22,94 @@ function isMode(value) {
|
|
|
14
22
|
function defaultEnv() {
|
|
15
23
|
return globalThis.process?.env ?? {};
|
|
16
24
|
}
|
|
25
|
+
/** True unless the env var is present and (case-insensitively) `'false'`. */
|
|
26
|
+
function envDisables(value) {
|
|
27
|
+
return value !== undefined && value.toLowerCase() === 'false';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Resolve a boolean channel toggle: an env var (`'false'` disables, anything
|
|
31
|
+
* else enables) wins over the option, which defaults to `true`.
|
|
32
|
+
*/
|
|
33
|
+
function resolveBoolChannel(envValue, optValue) {
|
|
34
|
+
if (envValue !== undefined)
|
|
35
|
+
return envValue.toLowerCase() !== 'false';
|
|
36
|
+
return optValue ?? true;
|
|
37
|
+
}
|
|
38
|
+
/** Parse an integer env var; return undefined for missing / NaN values. */
|
|
39
|
+
function parseIntEnv(value) {
|
|
40
|
+
if (value === undefined)
|
|
41
|
+
return undefined;
|
|
42
|
+
const n = Number.parseInt(value, 10);
|
|
43
|
+
return Number.isNaN(n) ? undefined : n;
|
|
44
|
+
}
|
|
45
|
+
/** Parse a JSON env var into an object; return undefined on parse failure. */
|
|
46
|
+
function parseJsonEnv(value) {
|
|
47
|
+
if (value === undefined)
|
|
48
|
+
return undefined;
|
|
49
|
+
try {
|
|
50
|
+
return JSON.parse(value);
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
17
56
|
/**
|
|
18
57
|
* Resolve user options into a fully-resolved shape, applying defaults and the
|
|
19
|
-
* `
|
|
20
|
-
*
|
|
21
|
-
*
|
|
58
|
+
* `TRACELANE_*` env overrides (env wins over config, matching @tracelane/core's
|
|
59
|
+
* resolveMode). An invalid `TRACELANE_MODE` is ignored. `env` is injectable for
|
|
60
|
+
* testing.
|
|
22
61
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
26
|
-
*
|
|
62
|
+
* Env overrides:
|
|
63
|
+
* - `TRACELANE_MODE`, `TRACELANE_OUT_DIR`
|
|
64
|
+
* - `TRACELANE_CAPTURE_NETWORK` (`'false'` disables both the in-page plugin and
|
|
65
|
+
* the Chromium CDP enrichment) / `_RRWEB` / `_CONSOLE` (`'false'` disables)
|
|
66
|
+
* - `TRACELANE_SECURITY`, `TRACELANE_FOOTER` (`'false'` disables)
|
|
67
|
+
* - `TRACELANE_DRAIN_INTERVAL_MS`, `TRACELANE_COOLDOWN_MS` (parsed int; NaN ignored)
|
|
68
|
+
* - `TRACELANE_NETWORK_OPTIONS`, `TRACELANE_CONSOLE_OPTIONS` (JSON; bad JSON → undefined)
|
|
27
69
|
*/
|
|
28
70
|
export function resolveOptions(opts = {}, env = defaultEnv()) {
|
|
29
71
|
const mode = isMode(env.TRACELANE_MODE) ? env.TRACELANE_MODE : (opts.mode ?? 'failed');
|
|
30
72
|
const outDir = env.TRACELANE_OUT_DIR ?? opts.outDir ?? DEFAULT_OUT_DIR;
|
|
73
|
+
// Network-on: env wins; else capture.network ?? legacy captureNetwork ?? true.
|
|
31
74
|
const envCaptureNetwork = env.TRACELANE_CAPTURE_NETWORK;
|
|
32
75
|
const captureNetwork = envCaptureNetwork !== undefined
|
|
33
76
|
? envCaptureNetwork.toLowerCase() !== 'false'
|
|
34
|
-
: (opts.captureNetwork ?? true);
|
|
35
|
-
|
|
77
|
+
: (opts.capture?.network ?? opts.captureNetwork ?? true);
|
|
78
|
+
const captureRrweb = resolveBoolChannel(env.TRACELANE_CAPTURE_RRWEB, opts.capture?.rrweb);
|
|
79
|
+
const captureConsole = resolveBoolChannel(env.TRACELANE_CAPTURE_CONSOLE, opts.capture?.console);
|
|
80
|
+
const security = envDisables(env.TRACELANE_SECURITY)
|
|
81
|
+
? false
|
|
82
|
+
: env.TRACELANE_SECURITY !== undefined
|
|
83
|
+
? true
|
|
84
|
+
: (opts.security ?? true);
|
|
85
|
+
const footer = envDisables(env.TRACELANE_FOOTER)
|
|
86
|
+
? false
|
|
87
|
+
: env.TRACELANE_FOOTER !== undefined
|
|
88
|
+
? true
|
|
89
|
+
: (opts.report?.footer ?? true);
|
|
90
|
+
const drainIntervalMs = parseIntEnv(env.TRACELANE_DRAIN_INTERVAL_MS) ?? opts.drainIntervalMs;
|
|
91
|
+
const cooldownMs = parseIntEnv(env.TRACELANE_COOLDOWN_MS) ?? opts.cooldownMs;
|
|
92
|
+
// JSON-serializable masking options: env (JSON) wins over the option.
|
|
93
|
+
const networkOptions = parseJsonEnv(env.TRACELANE_NETWORK_OPTIONS) ??
|
|
94
|
+
opts.capture?.networkOptions;
|
|
95
|
+
const consolePluginOptions = parseJsonEnv(env.TRACELANE_CONSOLE_OPTIONS) ?? opts.consolePluginOptions;
|
|
96
|
+
const resolved = {
|
|
97
|
+
mode,
|
|
98
|
+
outDir,
|
|
99
|
+
captureNetwork,
|
|
100
|
+
captureRrweb,
|
|
101
|
+
captureConsole,
|
|
102
|
+
security,
|
|
103
|
+
footer,
|
|
104
|
+
};
|
|
105
|
+
if (drainIntervalMs !== undefined)
|
|
106
|
+
resolved.drainIntervalMs = drainIntervalMs;
|
|
107
|
+
if (cooldownMs !== undefined)
|
|
108
|
+
resolved.cooldownMs = cooldownMs;
|
|
109
|
+
if (networkOptions !== undefined)
|
|
110
|
+
resolved.networkOptions = networkOptions;
|
|
111
|
+
if (consolePluginOptions !== undefined)
|
|
112
|
+
resolved.consolePluginOptions = consolePluginOptions;
|
|
113
|
+
return resolved;
|
|
36
114
|
}
|
|
37
115
|
//# sourceMappingURL=options.js.map
|
package/dist/options.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AAAA,gFAAgF;AAChF,4EAA4E;AAC5E,EAAE;AACF,8EAA8E;AAC9E,+EAA+E;AAC/E,
|
|
1
|
+
{"version":3,"file":"options.js","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-session.d.ts","sourceRoot":"","sources":["../src/playwright-session.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"playwright-session.d.ts","sourceRoot":"","sources":["../src/playwright-session.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC1E,OAAO,EAEL,KAAK,QAAQ,EAGd,MAAM,iBAAiB,CAAC;AAEzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAKpD,yCAAyC;AACzC,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,IAAI,CAAC;IACX,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,4EAA4E;AAC5E,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,QAAQ,CAAC;IACnB,oFAAoF;IACpF,GAAG,CAAC,EAAE,UAAU,CAAC;IACjB,6EAA6E;IAC7E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iFAAiF;IACjF,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,oFAAoF;IACpF,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;IAC/B,+EAA+E;IAC/E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAWD,yDAAyD;AACzD,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,IAAI,CAAC;IACX,QAAQ,EAAE,QAAQ,CAAC;IACnB,OAAO,EAAE,eAAe,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;CACrB;AAeD,sFAAsF;AACtF,wBAAsB,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,OAAO,CAAC,cAAc,CAAC,CAyHzE;AAoBD,qFAAqF;AACrF,wBAAsB,WAAW,CAAC,OAAO,EAAE,cAAc,EAAE,KAAK,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAqC9F;AAED,mFAAmF;AACnF,wBAAsB,mBAAmB,CACvC,KAAK,EAAE,aAAa,GAAG;IAAE,WAAW,EAAE,MAAM,CAAA;CAAE,GAC7C,OAAO,CAAC,IAAI,CAAC,CAOf"}
|
|
@@ -10,10 +10,12 @@
|
|
|
10
10
|
// recorder's own start() (which evals the bundle + installs the in-page buffer
|
|
11
11
|
// in the CURRENT document). The recorder then drains events Node-side on its
|
|
12
12
|
// poll loop and at finalize (ADR-0006).
|
|
13
|
-
import {
|
|
13
|
+
import { cwd } from 'node:process';
|
|
14
|
+
import { attachNetworkCapture, createRecorder, } from '@tracelane/core';
|
|
14
15
|
import { writeReport } from '@tracelane/report';
|
|
15
16
|
import { createPlaywrightExecutor } from './playwright-executor.js';
|
|
16
17
|
import { isPassed, mapStatus } from './result-status.js';
|
|
18
|
+
import { loadSecuritySuppressions } from './security-suppress.js';
|
|
17
19
|
/** The Playwright browser-type name, when resolvable from the page's context. */
|
|
18
20
|
function browserNameOf(page) {
|
|
19
21
|
try {
|
|
@@ -23,9 +25,29 @@ function browserNameOf(page) {
|
|
|
23
25
|
return undefined;
|
|
24
26
|
}
|
|
25
27
|
}
|
|
28
|
+
/**
|
|
29
|
+
* Resolve the console-plugin options: when `captureConsole` is false, pass
|
|
30
|
+
* `{ level: [] }` so the rrweb console plugin patches no `console.*` methods
|
|
31
|
+
* and installs no error/rejection listeners — i.e. console capture is off
|
|
32
|
+
* (mirrors @tracelane/wdio). Otherwise forward any user-supplied
|
|
33
|
+
* `consolePluginOptions` (the core recorder applies its defaults when none are
|
|
34
|
+
* given, so `undefined` is returned to keep that behavior).
|
|
35
|
+
*/
|
|
36
|
+
function resolveConsolePluginOptions(options) {
|
|
37
|
+
if (options.captureConsole === false)
|
|
38
|
+
return { level: [] };
|
|
39
|
+
return options.consolePluginOptions;
|
|
40
|
+
}
|
|
26
41
|
/** Inject the rrweb bundle on the context, build the executor, start the recorder. */
|
|
27
42
|
export async function runStart(input) {
|
|
28
43
|
const { page, options, rrwebBundle } = input;
|
|
44
|
+
// rrweb opt-out: when capture.rrweb is false, no recorder starts and no
|
|
45
|
+
// report is written (mirrors @tracelane/wdio `capture.rrweb:false`). We skip
|
|
46
|
+
// the context injection + CDP entirely. The returned session is disabled, so
|
|
47
|
+
// runFinalize writes nothing. Default-on: only an explicit `false` disables.
|
|
48
|
+
if (options.captureRrweb === false) {
|
|
49
|
+
return { recorder: undefined, disabled: true };
|
|
50
|
+
}
|
|
29
51
|
const context = page.context();
|
|
30
52
|
// Inject on the context so newly-created / navigated documents get rrweb
|
|
31
53
|
// before any app script runs.
|
|
@@ -48,30 +70,56 @@ export async function runStart(input) {
|
|
|
48
70
|
}
|
|
49
71
|
catch {
|
|
50
72
|
cdp = undefined; // no CDP; rrweb+console still work
|
|
51
|
-
console.warn('[tracelane] could not open a CDP session; network
|
|
73
|
+
console.warn('[tracelane] could not open a CDP session; CDP network enrichment unavailable, continuing with in-page rrweb network capture.');
|
|
52
74
|
}
|
|
53
75
|
}
|
|
54
76
|
// ONE executor for both network capture and the recorder (recorder uses only
|
|
55
77
|
// execute(); cdp/on are used solely by attachNetworkCapture).
|
|
56
78
|
const executor = createPlaywrightExecutor(page, cdp);
|
|
57
|
-
const
|
|
79
|
+
const recorderOptions = {
|
|
58
80
|
executor,
|
|
59
81
|
rrwebBundle,
|
|
60
82
|
mode: options.mode,
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
83
|
+
};
|
|
84
|
+
// Drain/cooldown tuning (ADR-0006): forward only when set, else the core
|
|
85
|
+
// recorder applies its defaults.
|
|
86
|
+
if (options.drainIntervalMs !== undefined) {
|
|
87
|
+
recorderOptions.drainIntervalMs = options.drainIntervalMs;
|
|
88
|
+
}
|
|
89
|
+
if (options.cooldownMs !== undefined) {
|
|
90
|
+
recorderOptions.cooldownMs = options.cooldownMs;
|
|
91
|
+
}
|
|
92
|
+
// Console capture: `{ level: [] }` disables it; otherwise the user's options
|
|
93
|
+
// (or undefined → core defaults).
|
|
94
|
+
const consolePluginOptions = resolveConsolePluginOptions(options);
|
|
95
|
+
if (consolePluginOptions !== undefined) {
|
|
96
|
+
recorderOptions.consolePluginOptions = consolePluginOptions;
|
|
97
|
+
}
|
|
98
|
+
// In-page rrweb network plugin (`rrweb/network@1`): the framework-agnostic
|
|
99
|
+
// network channel that works on ALL browsers (Chromium/Firefox/WebKit) with
|
|
100
|
+
// no CDP — it wraps fetch/XHR + reads PerformanceObserver from inside the
|
|
101
|
+
// page. Privacy-first defaults (`{}`): URL/method/status/timing only, headers
|
|
102
|
+
// + bodies off. On Chromium the CDP path above ALSO runs and enriches these
|
|
103
|
+
// rows with authoritative status + true no-response failures; the report
|
|
104
|
+
// merges the two (real status wins). Mirrors @tracelane/wdio. Off entirely
|
|
105
|
+
// when `captureNetwork` is false.
|
|
106
|
+
if (options.captureNetwork) {
|
|
107
|
+
recorderOptions.networkPluginOptions =
|
|
108
|
+
options.networkOptions ?? {};
|
|
109
|
+
}
|
|
110
|
+
const recorder = createRecorder(recorderOptions);
|
|
64
111
|
// Network capture is best-effort: if it fails, detach CDP and continue.
|
|
65
112
|
if (cdp) {
|
|
66
113
|
try {
|
|
67
114
|
await attachNetworkCapture(executor, {
|
|
115
|
+
security: options.security,
|
|
68
116
|
onSecurityMeta: (m) => recorder.addCustomEvent('tracelane.sec', m),
|
|
69
117
|
});
|
|
70
118
|
}
|
|
71
119
|
catch {
|
|
72
120
|
await cdp.detach().catch(() => { });
|
|
73
121
|
cdp = undefined;
|
|
74
|
-
console.warn('[tracelane] network
|
|
122
|
+
console.warn('[tracelane] CDP network enrichment unavailable; continuing with in-page rrweb network capture.');
|
|
75
123
|
}
|
|
76
124
|
}
|
|
77
125
|
// Capture start is best-effort: a CSP / injection failure must NOT fail the
|
|
@@ -144,6 +192,10 @@ export async function runFinalize(session, input) {
|
|
|
144
192
|
});
|
|
145
193
|
if (!shouldBuildReport)
|
|
146
194
|
return;
|
|
195
|
+
// Load the optional suppression file at report-write time. The loader never
|
|
196
|
+
// throws and falls back to `[]`, so a missing/malformed file can't break the
|
|
197
|
+
// report. Skip the read entirely when security is off.
|
|
198
|
+
const securitySuppress = options.security ? loadSecuritySuppressions(cwd()) : [];
|
|
147
199
|
writeReport({
|
|
148
200
|
outDir: options.outDir,
|
|
149
201
|
// project.name namespaces the filename so parallel projects/workers never
|
|
@@ -151,6 +203,9 @@ export async function runFinalize(session, input) {
|
|
|
151
203
|
cid: testInfo.project?.name,
|
|
152
204
|
events,
|
|
153
205
|
meta: buildMeta(testInfo, session),
|
|
206
|
+
footer: options.footer,
|
|
207
|
+
security: options.security,
|
|
208
|
+
securitySuppress,
|
|
154
209
|
});
|
|
155
210
|
}
|
|
156
211
|
finally {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"playwright-session.js","sourceRoot":"","sources":["../src/playwright-session.ts"],"names":[],"mappings":"AAAA,8EAA8E;AAC9E,6EAA6E;AAC7E,wEAAwE;AACxE,0EAA0E;AAC1E,2EAA2E;AAC3E,4EAA4E;AAC5E,EAAE;AACF,0EAA0E;AAC1E,sEAAsE;AACtE,+EAA+E;AAC/E,6EAA6E;AAC7E,wCAAwC;
|
|
1
|
+
{"version":3,"file":"playwright-session.js","sourceRoot":"","sources":["../src/playwright-session.ts"],"names":[],"mappings":"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"}
|
package/dist/reporter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,gBAAgB,EAAkB,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"reporter.d.ts","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAiBA,OAAO,KAAK,EACV,UAAU,EACV,UAAU,EACV,QAAQ,EACR,KAAK,EACL,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,KAAK,gBAAgB,EAAkB,MAAM,cAAc,CAAC;AAoBrE;;;;;GAKG;AACH,qBAAa,iBAAkB,YAAW,QAAQ;gBACpC,IAAI,GAAE,gBAAqB;IAiEvC,4EAA4E;IAC5E,aAAa,IAAI,OAAO;IAIxB,OAAO,CAAC,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,GAAG,IAAI;IAIjD,WAAW,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAIvD,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,UAAU,GAAG,IAAI;IAKrD,KAAK,CAAC,OAAO,EAAE,UAAU,GAAG,IAAI;CAKjC;AAED,eAAe,iBAAiB,CAAC"}
|
package/dist/reporter.js
CHANGED
|
@@ -15,6 +15,25 @@
|
|
|
15
15
|
// The reporter does NOT tally pass/fail counts and does NOT print any
|
|
16
16
|
// end-of-run summary. All per-test reporting is handled by the fixture.
|
|
17
17
|
import { resolveOptions } from './options.js';
|
|
18
|
+
/**
|
|
19
|
+
* Serialize a JSON-bridgeable option to a string for the env bridge, or
|
|
20
|
+
* `undefined` when there's nothing to bridge. `JSON.stringify` silently drops
|
|
21
|
+
* function-valued props (e.g. network `maskRequestFn` / `maskResponseFn`) —
|
|
22
|
+
* those cannot survive the worker-process env boundary, a documented
|
|
23
|
+
* limitation. Returns `undefined` if the result is empty (`{}`) or fails.
|
|
24
|
+
*/
|
|
25
|
+
function bridgeJson(value) {
|
|
26
|
+
if (value === undefined || value === null)
|
|
27
|
+
return undefined;
|
|
28
|
+
try {
|
|
29
|
+
const json = JSON.stringify(value);
|
|
30
|
+
// `{}` carries no information once functions are stripped — skip it.
|
|
31
|
+
return json === undefined || json === '{}' ? undefined : json;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return undefined;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
18
37
|
/**
|
|
19
38
|
* tracelane's Playwright reporter. Pair it with the fixture
|
|
20
39
|
* (`import { test } from '@tracelane/playwright/fixture'`) — the fixture records
|
|
@@ -27,15 +46,28 @@ export class TraceLaneReporter {
|
|
|
27
46
|
resolveOptions(opts);
|
|
28
47
|
const env = globalThis.process?.env;
|
|
29
48
|
if (env !== undefined) {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
49
|
+
// Each option is bridged only when its env var is unset, so an explicit
|
|
50
|
+
// env var / CLI value always wins.
|
|
51
|
+
const set = (key, value) => {
|
|
52
|
+
if (value !== undefined && env[key] === undefined)
|
|
53
|
+
env[key] = value;
|
|
54
|
+
};
|
|
55
|
+
set('TRACELANE_MODE', opts.mode);
|
|
56
|
+
set('TRACELANE_OUT_DIR', opts.outDir);
|
|
57
|
+
// capture.network (preferred) wins over the deprecated top-level captureNetwork.
|
|
58
|
+
const network = opts.capture?.network ?? opts.captureNetwork;
|
|
59
|
+
set('TRACELANE_CAPTURE_NETWORK', network !== undefined ? String(network) : undefined);
|
|
60
|
+
set('TRACELANE_CAPTURE_RRWEB', opts.capture?.rrweb !== undefined ? String(opts.capture.rrweb) : undefined);
|
|
61
|
+
set('TRACELANE_CAPTURE_CONSOLE', opts.capture?.console !== undefined ? String(opts.capture.console) : undefined);
|
|
62
|
+
set('TRACELANE_SECURITY', opts.security !== undefined ? String(opts.security) : undefined);
|
|
63
|
+
set('TRACELANE_FOOTER', opts.report?.footer !== undefined ? String(opts.report.footer) : undefined);
|
|
64
|
+
set('TRACELANE_DRAIN_INTERVAL_MS', opts.drainIntervalMs !== undefined ? String(opts.drainIntervalMs) : undefined);
|
|
65
|
+
set('TRACELANE_COOLDOWN_MS', opts.cooldownMs !== undefined ? String(opts.cooldownMs) : undefined);
|
|
66
|
+
// JSON-serializable masking options. JSON.stringify naturally drops
|
|
67
|
+
// function-valued props (maskRequestFn / maskResponseFn) — they cannot
|
|
68
|
+
// cross the worker-process env bridge, a documented limitation.
|
|
69
|
+
set('TRACELANE_NETWORK_OPTIONS', bridgeJson(opts.capture?.networkOptions));
|
|
70
|
+
set('TRACELANE_CONSOLE_OPTIONS', bridgeJson(opts.consolePluginOptions));
|
|
39
71
|
}
|
|
40
72
|
}
|
|
41
73
|
/** The fixture + Playwright own the run output; this reporter is silent. */
|
package/dist/reporter.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"AAAA,6EAA6E;AAC7E,6DAA6D;AAC7D,EAAE;AACF,iFAAiF;AACjF,gFAAgF;AAChF,iEAAiE;AACjE,8EAA8E;AAC9E,8CAA8C;AAC9C,2EAA2E;AAC3E,8EAA8E;AAC9E,iFAAiF;AACjF,+EAA+E;AAC/E,0EAA0E;AAC1E,EAAE;AACF,sEAAsE;AACtE,wEAAwE;AAUxE,OAAO,EAAyB,cAAc,EAAE,MAAM,cAAc,CAAC;AAErE;;;;;GAKG;AACH,MAAM,OAAO,iBAAiB;IAC5B,YAAY,OAAyB,EAAE;QACrC,yDAAyD;QACzD,cAAc,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Suppression } from '@tracelane/report';
|
|
2
|
+
/** The fixed filename looked up in the project cwd. */
|
|
3
|
+
export declare const SUPPRESS_FILE_NAME = "tracelane.security.suppress.json";
|
|
4
|
+
/**
|
|
5
|
+
* Load `tracelane.security.suppress.json` from `cwd`, if present. Returns the
|
|
6
|
+
* parsed suppression rules, or `[]` when the file is missing/unreadable/
|
|
7
|
+
* malformed/wrong-shaped. Never throws.
|
|
8
|
+
*/
|
|
9
|
+
export declare function loadSecuritySuppressions(cwd: string): Suppression[];
|
|
10
|
+
//# sourceMappingURL=security-suppress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-suppress.d.ts","sourceRoot":"","sources":["../src/security-suppress.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAErD,uDAAuD;AACvD,eAAO,MAAM,kBAAkB,qCAAqC,CAAC;AA0BrE;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,EAAE,CAUnE"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Optional security-suppression file loader (ported from @tracelane/wdio).
|
|
2
|
+
//
|
|
3
|
+
// tracelane's advisory security analyzer (`@tracelane/security`) accepts a list
|
|
4
|
+
// of `Suppression` rules to silence known-acceptable signals. To let teams
|
|
5
|
+
// commit those rules alongside their suite, the playwright adapter looks for a
|
|
6
|
+
// `tracelane.security.suppress.json` in the project cwd at report-write time.
|
|
7
|
+
//
|
|
8
|
+
// This loader is deliberately defensive: a missing, unreadable, malformed, or
|
|
9
|
+
// wrong-shaped file MUST NEVER throw and MUST NEVER break the report. Any of
|
|
10
|
+
// those cases degrade to `[]` (no suppressions). The file only ever carries
|
|
11
|
+
// advisory `{ signal?, evidence? }` rules — no secrets — so reading it from cwd
|
|
12
|
+
// is safe (P1 security MVP privacy invariant).
|
|
13
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
14
|
+
import { join } from 'node:path';
|
|
15
|
+
/** The fixed filename looked up in the project cwd. */
|
|
16
|
+
export const SUPPRESS_FILE_NAME = 'tracelane.security.suppress.json';
|
|
17
|
+
/**
|
|
18
|
+
* Coerce arbitrary parsed JSON into a `Suppression[]`, leniently:
|
|
19
|
+
* - a bare array → used as-is;
|
|
20
|
+
* - an object with a `suppressions` array → that array;
|
|
21
|
+
* - anything else → `[]`.
|
|
22
|
+
*
|
|
23
|
+
* Element shape is not validated beyond "is an object" — the analyzer reads
|
|
24
|
+
* only `signal` / `evidence` and ignores the rest, so a loose pass-through is
|
|
25
|
+
* both safe and forgiving of hand-edited files.
|
|
26
|
+
*/
|
|
27
|
+
function coerceSuppressions(parsed) {
|
|
28
|
+
const arr = Array.isArray(parsed)
|
|
29
|
+
? parsed
|
|
30
|
+
: isRecord(parsed) && Array.isArray(parsed.suppressions)
|
|
31
|
+
? parsed.suppressions
|
|
32
|
+
: undefined;
|
|
33
|
+
if (!arr)
|
|
34
|
+
return [];
|
|
35
|
+
return arr.filter(isRecord);
|
|
36
|
+
}
|
|
37
|
+
function isRecord(v) {
|
|
38
|
+
return typeof v === 'object' && v !== null;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Load `tracelane.security.suppress.json` from `cwd`, if present. Returns the
|
|
42
|
+
* parsed suppression rules, or `[]` when the file is missing/unreadable/
|
|
43
|
+
* malformed/wrong-shaped. Never throws.
|
|
44
|
+
*/
|
|
45
|
+
export function loadSecuritySuppressions(cwd) {
|
|
46
|
+
const filePath = join(cwd, SUPPRESS_FILE_NAME);
|
|
47
|
+
try {
|
|
48
|
+
if (!existsSync(filePath))
|
|
49
|
+
return [];
|
|
50
|
+
const raw = readFileSync(filePath, 'utf8');
|
|
51
|
+
return coerceSuppressions(JSON.parse(raw));
|
|
52
|
+
}
|
|
53
|
+
catch {
|
|
54
|
+
// Missing/unreadable/malformed file must never break the report.
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=security-suppress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"security-suppress.js","sourceRoot":"","sources":["../src/security-suppress.ts"],"names":[],"mappings":"AAAA,2EAA2E;AAC3E,EAAE;AACF,gFAAgF;AAChF,2EAA2E;AAC3E,+EAA+E;AAC/E,8EAA8E;AAC9E,EAAE;AACF,8EAA8E;AAC9E,6EAA6E;AAC7E,4EAA4E;AAC5E,gFAAgF;AAChF,+CAA+C;AAE/C,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,uDAAuD;AACvD,MAAM,CAAC,MAAM,kBAAkB,GAAG,kCAAkC,CAAC;AAErE;;;;;;;;;GASG;AACH,SAAS,kBAAkB,CAAC,MAAe;IACzC,MAAM,GAAG,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC;QAC/B,CAAC,CAAC,MAAM;QACR,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC;YACtD,CAAC,CAAC,MAAM,CAAC,YAAY;YACrB,CAAC,CAAC,SAAS,CAAC;IAChB,IAAI,CAAC,GAAG;QAAE,OAAO,EAAE,CAAC;IACpB,OAAO,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAkB,CAAC;AAC/C,CAAC;AAED,SAAS,QAAQ,CAAC,CAAU;IAC1B,OAAO,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,KAAK,IAAI,CAAC;AAC7C,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,wBAAwB,CAAC,GAAW;IAClD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,kBAAkB,CAAC,CAAC;IAC/C,IAAI,CAAC;QACH,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;YAAE,OAAO,EAAE,CAAC;QACrC,MAAM,GAAG,GAAG,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC3C,OAAO,kBAAkB,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC7C,CAAC;IAAC,MAAM,CAAC;QACP,iEAAiE;QACjE,OAAO,EAAE,CAAC;IACZ,CAAC;AACH,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tracelane/playwright",
|
|
3
|
-
"version": "0.1.0-alpha.
|
|
3
|
+
"version": "0.1.0-alpha.7",
|
|
4
4
|
"description": "Playwright reporter + auto-fixture that writes a self-contained replayable HTML report on test failure (rrweb + console + failed-network). No SaaS.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"tracelane",
|
|
@@ -41,8 +41,8 @@
|
|
|
41
41
|
"README.md"
|
|
42
42
|
],
|
|
43
43
|
"dependencies": {
|
|
44
|
-
"@tracelane/core": "0.1.0-alpha.
|
|
45
|
-
"@tracelane/report": "0.1.0-alpha.
|
|
44
|
+
"@tracelane/core": "0.1.0-alpha.16",
|
|
45
|
+
"@tracelane/report": "0.1.0-alpha.19"
|
|
46
46
|
},
|
|
47
47
|
"peerDependencies": {
|
|
48
48
|
"@playwright/test": ">=1.40.0"
|