pw-sanitizer 0.1.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 +415 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +113 -0
- package/dist/cli.js.map +1 -0
- package/dist/config/loader.d.ts +30 -0
- package/dist/config/loader.d.ts.map +1 -0
- package/dist/config/loader.js +173 -0
- package/dist/config/loader.js.map +1 -0
- package/dist/config/types.d.ts +432 -0
- package/dist/config/types.d.ts.map +1 -0
- package/dist/config/types.js +6 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/validator.d.ts +34 -0
- package/dist/config/validator.d.ts.map +1 -0
- package/dist/config/validator.js +95 -0
- package/dist/config/validator.js.map +1 -0
- package/dist/index.d.ts +74 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +190 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +82 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +122 -0
- package/dist/logger.js.map +1 -0
- package/dist/processors/html-report.d.ts +27 -0
- package/dist/processors/html-report.d.ts.map +1 -0
- package/dist/processors/html-report.js +220 -0
- package/dist/processors/html-report.js.map +1 -0
- package/dist/processors/screenshot.d.ts +37 -0
- package/dist/processors/screenshot.d.ts.map +1 -0
- package/dist/processors/screenshot.js +52 -0
- package/dist/processors/screenshot.js.map +1 -0
- package/dist/processors/trace-file.d.ts +29 -0
- package/dist/processors/trace-file.d.ts.map +1 -0
- package/dist/processors/trace-file.js +250 -0
- package/dist/processors/trace-file.js.map +1 -0
- package/dist/redact/json-walker.d.ts +28 -0
- package/dist/redact/json-walker.d.ts.map +1 -0
- package/dist/redact/json-walker.js +164 -0
- package/dist/redact/json-walker.js.map +1 -0
- package/dist/redact/matcher.d.ts +25 -0
- package/dist/redact/matcher.d.ts.map +1 -0
- package/dist/redact/matcher.js +130 -0
- package/dist/redact/matcher.js.map +1 -0
- package/dist/redact/pattern-loader.d.ts +25 -0
- package/dist/redact/pattern-loader.d.ts.map +1 -0
- package/dist/redact/pattern-loader.js +111 -0
- package/dist/redact/pattern-loader.js.map +1 -0
- package/dist/redact/pattern-registry.d.ts +17 -0
- package/dist/redact/pattern-registry.d.ts.map +1 -0
- package/dist/redact/pattern-registry.js +58 -0
- package/dist/redact/pattern-registry.js.map +1 -0
- package/dist/remove/detector.d.ts +22 -0
- package/dist/remove/detector.d.ts.map +1 -0
- package/dist/remove/detector.js +152 -0
- package/dist/remove/detector.js.map +1 -0
- package/dist/remove/remover.d.ts +18 -0
- package/dist/remove/remover.d.ts.map +1 -0
- package/dist/remove/remover.js +72 -0
- package/dist/remove/remover.js.map +1 -0
- package/dist/remove/rule-loader.d.ts +25 -0
- package/dist/remove/rule-loader.d.ts.map +1 -0
- package/dist/remove/rule-loader.js +110 -0
- package/dist/remove/rule-loader.js.map +1 -0
- package/dist/remove/rule-registry.d.ts +17 -0
- package/dist/remove/rule-registry.d.ts.map +1 -0
- package/dist/remove/rule-registry.js +58 -0
- package/dist/remove/rule-registry.js.map +1 -0
- package/dist/remove/timestamp-repair.d.ts +28 -0
- package/dist/remove/timestamp-repair.d.ts.map +1 -0
- package/dist/remove/timestamp-repair.js +157 -0
- package/dist/remove/timestamp-repair.js.map +1 -0
- package/dist/reporter.d.ts +44 -0
- package/dist/reporter.d.ts.map +1 -0
- package/dist/reporter.js +180 -0
- package/dist/reporter.js.map +1 -0
- package/dist/teardown.d.ts +27 -0
- package/dist/teardown.d.ts.map +1 -0
- package/dist/teardown.js +42 -0
- package/dist/teardown.js.map +1 -0
- package/dist/utils.d.ts +36 -0
- package/dist/utils.d.ts.map +1 -0
- package/dist/utils.js +112 -0
- package/dist/utils.js.map +1 -0
- package/package.json +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
# playwright-sanitizer
|
|
2
|
+
|
|
3
|
+
Post-process Playwright HTML reports and trace files to **redact secrets** and **remove noisy steps** before sharing or archiving.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why
|
|
8
|
+
|
|
9
|
+
Playwright's HTML reports and `.zip` trace files can contain sensitive data — auth headers, API keys, tokens, passwords — embedded in network request logs, action parameters, and step metadata. `playwright-sanitizer` scans those files after your test run and replaces or strips the sensitive content so they are safe to store in CI artifacts, share with teammates, or upload to a dashboard.
|
|
10
|
+
|
|
11
|
+
It also lets you delete repetitive or internal steps (e.g. health-check pings, internal logging actions) that create noise in the trace viewer.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Features
|
|
16
|
+
|
|
17
|
+
- Redact secrets in HTML reports and trace `.zip` files by key name, value pattern, or both
|
|
18
|
+
- Remove entire steps from traces with configurable timestamp repair
|
|
19
|
+
- Three output modes: `copy` (safe default), `in-place`, `side-by-side`
|
|
20
|
+
- Zero built-in patterns — you declare exactly what gets touched, nothing else runs
|
|
21
|
+
- Dry-run mode to preview changes without writing files
|
|
22
|
+
- Integrates as a Playwright `globalTeardown` so it runs automatically after every test run
|
|
23
|
+
- Programmatic API for custom pipelines
|
|
24
|
+
- Summary table printed to the console (and optionally written as JSON)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## Requirements
|
|
29
|
+
|
|
30
|
+
- Node.js >= 18
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## Installation
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
npm install --save-dev playwright-sanitizer
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Screenshot redaction inside trace files requires the optional `sharp` dependency:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install --save-dev sharp
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## Quick start
|
|
49
|
+
|
|
50
|
+
### 1. Create a config file
|
|
51
|
+
|
|
52
|
+
```ts
|
|
53
|
+
// playwright-sanitizer.config.ts
|
|
54
|
+
import type { SanitizerConfig } from 'playwright-sanitizer';
|
|
55
|
+
|
|
56
|
+
const config: SanitizerConfig = {
|
|
57
|
+
redact: {
|
|
58
|
+
patterns: [
|
|
59
|
+
{
|
|
60
|
+
id: 'auth-header',
|
|
61
|
+
description: 'Authorization header value',
|
|
62
|
+
key: 'authorization',
|
|
63
|
+
severity: 'critical',
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
id: 'api-key',
|
|
67
|
+
key: /x-api-key/i,
|
|
68
|
+
severity: 'high',
|
|
69
|
+
},
|
|
70
|
+
],
|
|
71
|
+
},
|
|
72
|
+
remove: {
|
|
73
|
+
rules: [
|
|
74
|
+
{
|
|
75
|
+
label: 'health-check pings',
|
|
76
|
+
url: /\/health$/,
|
|
77
|
+
},
|
|
78
|
+
],
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
export default config;
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 2. Run the CLI
|
|
86
|
+
|
|
87
|
+
```bash
|
|
88
|
+
npx playwright-sanitizer
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Sanitized files are written to `./sanitized-report` by default. Originals are untouched.
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Configuration
|
|
96
|
+
|
|
97
|
+
Config is auto-discovered in this priority order:
|
|
98
|
+
|
|
99
|
+
| Priority | Source |
|
|
100
|
+
|----------|--------|
|
|
101
|
+
| 1 | `--config <path>` CLI flag |
|
|
102
|
+
| 2 | `playwright-sanitizer.config.ts` |
|
|
103
|
+
| 3 | `playwright-sanitizer.config.js` |
|
|
104
|
+
| 4 | `playwright-sanitizer.config.json` |
|
|
105
|
+
| 5 | `sanitizer` key inside `playwright.config.ts` |
|
|
106
|
+
|
|
107
|
+
### Full config reference
|
|
108
|
+
|
|
109
|
+
```ts
|
|
110
|
+
import type { SanitizerConfig } from 'playwright-sanitizer';
|
|
111
|
+
|
|
112
|
+
const config: SanitizerConfig = {
|
|
113
|
+
redact: {
|
|
114
|
+
// Inline patterns (merged with patternFiles)
|
|
115
|
+
patterns: [
|
|
116
|
+
{
|
|
117
|
+
id: 'my-token', // REQUIRED — unique identifier shown in summary
|
|
118
|
+
description: 'API token', // optional, shown in summary output
|
|
119
|
+
key: 'x-api-token', // match field/header name (string = case-insensitive exact match)
|
|
120
|
+
valuePattern: /^ey/, // match field value (RegExp) — when set alongside key, BOTH must match
|
|
121
|
+
severity: 'high', // 'low' | 'medium' | 'high' | 'critical' (informational only)
|
|
122
|
+
},
|
|
123
|
+
],
|
|
124
|
+
|
|
125
|
+
// External pattern files (.ts, .js, or .json) — merged with inline patterns
|
|
126
|
+
patternFiles: ['./secrets/patterns.ts'],
|
|
127
|
+
|
|
128
|
+
// Replacement string. Default: '[REDACTED]'
|
|
129
|
+
placeholder: '[REDACTED]',
|
|
130
|
+
|
|
131
|
+
// Partial redaction: keep first N and last N characters, mask the middle with '***'
|
|
132
|
+
// Example: { prefix: 4, suffix: 4 } on 'Bearer eyJhbGci...' => 'Bear***..ci'
|
|
133
|
+
// Takes priority over placeholder when set.
|
|
134
|
+
partialRedaction: { prefix: 4, suffix: 4 },
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
remove: {
|
|
138
|
+
// Inline rules (merged with ruleFiles)
|
|
139
|
+
rules: [
|
|
140
|
+
{
|
|
141
|
+
label: 'noisy health checks', // REQUIRED — shown in summary and dry-run log
|
|
142
|
+
stepName: /health/i, // match step name shown in the HTML report
|
|
143
|
+
selector: '[data-testid="spinner"]', // match CSS selector or XPath locator
|
|
144
|
+
url: /\/internal\//, // match network request URL
|
|
145
|
+
actionType: 'waitForTimeout', // match Playwright internal action type
|
|
146
|
+
|
|
147
|
+
// Safety guard: only remove if this step appears at least N times
|
|
148
|
+
// consecutively within a test. Logs a warning and skips removal if not met.
|
|
149
|
+
minConsecutiveOccurrences: 3,
|
|
150
|
+
},
|
|
151
|
+
],
|
|
152
|
+
|
|
153
|
+
// External rule files (.ts, .js, or .json) — merged with inline rules
|
|
154
|
+
ruleFiles: ['./rules/remove-rules.ts'],
|
|
155
|
+
|
|
156
|
+
// How to handle time after a step is deleted.
|
|
157
|
+
// 'absorb-into-prev' (default): preceding step absorbs the deleted duration
|
|
158
|
+
// 'absorb-into-next': following step's start time shifts back
|
|
159
|
+
// 'gap': no adjustment; a gap appears in the timeline
|
|
160
|
+
timestampStrategy: 'absorb-into-prev',
|
|
161
|
+
|
|
162
|
+
// What to do with child steps when a parent is removed.
|
|
163
|
+
// 'remove-children' (default): children are also removed
|
|
164
|
+
// 'keep-shell': parent is kept as an empty container
|
|
165
|
+
orphanStrategy: 'remove-children',
|
|
166
|
+
|
|
167
|
+
// Preview mode — log what would be removed but don't write any files
|
|
168
|
+
dryRun: false,
|
|
169
|
+
},
|
|
170
|
+
|
|
171
|
+
output: {
|
|
172
|
+
reportDir: './playwright-report', // where to find HTML reports. Default: './playwright-report'
|
|
173
|
+
traceDir: './test-results', // where to find trace zips. Default: './test-results'
|
|
174
|
+
|
|
175
|
+
// Output mode:
|
|
176
|
+
// 'copy' (default): write sanitized files to dir, originals untouched
|
|
177
|
+
// 'in-place': overwrite original files (ensure they are version-controlled!)
|
|
178
|
+
// 'side-by-side': write '<name>.sanitized.<ext>' next to each original
|
|
179
|
+
mode: 'copy',
|
|
180
|
+
|
|
181
|
+
dir: './sanitized-report', // destination for 'copy' mode. Default: './sanitized-report'
|
|
182
|
+
|
|
183
|
+
processReports: true, // set to false to skip HTML report processing
|
|
184
|
+
processTraces: true, // set to false to skip trace file processing
|
|
185
|
+
redactScreenshots: false, // redact screenshots in traces (requires 'sharp')
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
reporting: {
|
|
189
|
+
summary: true, // print summary table after processing. Default: true
|
|
190
|
+
summaryFile: './sanitization-summary.json', // optional — write summary as JSON to this path
|
|
191
|
+
logLevel: 'normal', // 'silent' | 'normal' | 'verbose'
|
|
192
|
+
},
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
export default config;
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
---
|
|
199
|
+
|
|
200
|
+
## External pattern and rule files
|
|
201
|
+
|
|
202
|
+
For large teams, keep patterns and rules in dedicated files so they can be versioned and shared across projects:
|
|
203
|
+
|
|
204
|
+
```ts
|
|
205
|
+
// secrets/patterns.ts
|
|
206
|
+
import type { RedactPattern } from 'playwright-sanitizer';
|
|
207
|
+
|
|
208
|
+
const patterns: RedactPattern[] = [
|
|
209
|
+
{ id: 'auth-header', key: 'authorization', severity: 'critical' },
|
|
210
|
+
{ id: 'cookie', key: 'cookie', severity: 'high' },
|
|
211
|
+
{ id: 'set-cookie', key: 'set-cookie', severity: 'high' },
|
|
212
|
+
];
|
|
213
|
+
|
|
214
|
+
export default patterns;
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
```ts
|
|
218
|
+
// playwright-sanitizer.config.ts
|
|
219
|
+
export default {
|
|
220
|
+
redact: { patternFiles: ['./secrets/patterns.ts'] },
|
|
221
|
+
};
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
JSON files are also supported. Note that RegExp is not available in JSON — strings are matched as case-insensitive exact key names:
|
|
225
|
+
|
|
226
|
+
```json
|
|
227
|
+
[
|
|
228
|
+
{ "id": "auth-header", "key": "authorization", "severity": "critical" },
|
|
229
|
+
{ "id": "api-key", "key": "x-api-key", "severity": "high" }
|
|
230
|
+
]
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
---
|
|
234
|
+
|
|
235
|
+
## CLI reference
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
Usage: playwright-sanitizer [options]
|
|
239
|
+
|
|
240
|
+
Options:
|
|
241
|
+
-c, --config <path> Path to config file
|
|
242
|
+
-r, --report <path> HTML report directory (default: "./playwright-report")
|
|
243
|
+
-t, --traces <path> Trace directory (default: "./test-results")
|
|
244
|
+
-o, --output <path> Output directory (copy mode)
|
|
245
|
+
--in-place Overwrite original files
|
|
246
|
+
--patterns <path...> One or more pattern files
|
|
247
|
+
--placeholder <string> Redaction placeholder (default: "[REDACTED]")
|
|
248
|
+
--dry-run Log changes without writing files
|
|
249
|
+
--no-traces Skip trace file processing
|
|
250
|
+
--no-reports Skip HTML report processing
|
|
251
|
+
--summary-output <path> Write JSON summary to file
|
|
252
|
+
--log-level <level> silent | normal | verbose (default: "normal")
|
|
253
|
+
-V, --version Display version number
|
|
254
|
+
-h, --help Display help
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Examples
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
# Auto-discover config and run with defaults
|
|
261
|
+
npx playwright-sanitizer
|
|
262
|
+
|
|
263
|
+
# Use a specific config file
|
|
264
|
+
npx playwright-sanitizer --config ./ci/sanitizer.config.ts
|
|
265
|
+
|
|
266
|
+
# Overwrite originals (ensure files are version-controlled)
|
|
267
|
+
npx playwright-sanitizer --in-place
|
|
268
|
+
|
|
269
|
+
# Preview without writing any files
|
|
270
|
+
npx playwright-sanitizer --dry-run --log-level verbose
|
|
271
|
+
|
|
272
|
+
# Point to non-default directories
|
|
273
|
+
npx playwright-sanitizer --report ./reports --traces ./artifacts/traces --output ./sanitized
|
|
274
|
+
|
|
275
|
+
# Write a machine-readable summary for CI artifact ingestion
|
|
276
|
+
npx playwright-sanitizer --summary-output ./sanitization-summary.json
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Playwright globalTeardown integration
|
|
282
|
+
|
|
283
|
+
Register the sanitizer as a Playwright `globalTeardown` and it runs automatically after every test suite:
|
|
284
|
+
|
|
285
|
+
```ts
|
|
286
|
+
// playwright.config.ts
|
|
287
|
+
import { defineConfig } from '@playwright/test';
|
|
288
|
+
|
|
289
|
+
export default defineConfig({
|
|
290
|
+
globalTeardown: require.resolve('playwright-sanitizer/teardown'),
|
|
291
|
+
// ...rest of your config
|
|
292
|
+
});
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
Config is auto-discovered as described above. Teardown failures are caught and logged — they will never mask your actual test results.
|
|
296
|
+
|
|
297
|
+
### Embedding config in playwright.config.ts
|
|
298
|
+
|
|
299
|
+
If you prefer a single config file, add a `sanitizer` key directly to your Playwright config:
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
// playwright.config.ts
|
|
303
|
+
import { defineConfig } from '@playwright/test';
|
|
304
|
+
import type { SanitizerConfig } from 'playwright-sanitizer';
|
|
305
|
+
|
|
306
|
+
export default defineConfig({
|
|
307
|
+
globalTeardown: require.resolve('playwright-sanitizer/teardown'),
|
|
308
|
+
|
|
309
|
+
sanitizer: {
|
|
310
|
+
redact: {
|
|
311
|
+
patterns: [
|
|
312
|
+
{ id: 'auth', key: 'authorization', severity: 'critical' },
|
|
313
|
+
],
|
|
314
|
+
},
|
|
315
|
+
output: { mode: 'in-place' },
|
|
316
|
+
} satisfies SanitizerConfig,
|
|
317
|
+
});
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
---
|
|
321
|
+
|
|
322
|
+
## Programmatic API
|
|
323
|
+
|
|
324
|
+
```ts
|
|
325
|
+
import { sanitize, redactReport, redactTrace } from 'playwright-sanitizer';
|
|
326
|
+
|
|
327
|
+
// Process all reports and traces using auto-discovered config
|
|
328
|
+
const results = await sanitize();
|
|
329
|
+
|
|
330
|
+
// Pass a config object directly (skips file discovery)
|
|
331
|
+
const results = await sanitize({
|
|
332
|
+
redact: { patterns: [{ id: 'token', key: 'x-token' }] },
|
|
333
|
+
output: { mode: 'copy', dir: './out' },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
// Process a single HTML report
|
|
337
|
+
const result = await redactReport('./playwright-report/index.html', config);
|
|
338
|
+
|
|
339
|
+
// Process a single trace zip
|
|
340
|
+
const result = await redactTrace('./test-results/my-test/trace.zip', config);
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Each call returns a `ProcessResult` (or an array of them for `sanitize()`):
|
|
344
|
+
|
|
345
|
+
```ts
|
|
346
|
+
interface ProcessResult {
|
|
347
|
+
file: string;
|
|
348
|
+
redactionsApplied: number;
|
|
349
|
+
stepsRemoved: number;
|
|
350
|
+
timestampRepairs: number;
|
|
351
|
+
redactionMatches: Array<{ keyPath: string; patternId: string }>;
|
|
352
|
+
removalMatches: Array<{ index: number; ruleLabel: string; event: TraceEvent }>;
|
|
353
|
+
}
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
---
|
|
357
|
+
|
|
358
|
+
## Output modes
|
|
359
|
+
|
|
360
|
+
| Mode | Behaviour |
|
|
361
|
+
|------|-----------|
|
|
362
|
+
| `copy` (default) | Writes sanitized files to `output.dir` (`./sanitized-report`). Originals are never touched. |
|
|
363
|
+
| `in-place` | Overwrites original files. A warning is printed. Use only with version control. |
|
|
364
|
+
| `side-by-side` | Creates `<name>.sanitized.<ext>` next to each original file. |
|
|
365
|
+
|
|
366
|
+
---
|
|
367
|
+
|
|
368
|
+
## How redaction works
|
|
369
|
+
|
|
370
|
+
Redaction walks every JSON structure inside the HTML report's embedded data and the trace file's event entries. For each field encountered, the declared patterns are checked:
|
|
371
|
+
|
|
372
|
+
1. If `key` is a **string** — matched case-insensitively as an exact field name.
|
|
373
|
+
2. If `key` is a **RegExp** — tested against the field name.
|
|
374
|
+
3. If `valuePattern` is provided — both `key` and `valuePattern` must match (AND logic).
|
|
375
|
+
4. The matched value is replaced with `placeholder`, or a partial redaction if `partialRedaction` is configured.
|
|
376
|
+
|
|
377
|
+
No built-in patterns are ever applied. Only what you declare runs.
|
|
378
|
+
|
|
379
|
+
---
|
|
380
|
+
|
|
381
|
+
## How step removal works
|
|
382
|
+
|
|
383
|
+
Step removal operates on the trace event tree inside each `.zip` file:
|
|
384
|
+
|
|
385
|
+
1. Each event is tested against your declared rules (`stepName`, `selector`, `url`, `actionType`).
|
|
386
|
+
2. Matching events are collected and removed from the event list.
|
|
387
|
+
3. Timestamps of surrounding events are adjusted according to `timestampStrategy` to keep the timeline coherent.
|
|
388
|
+
4. Child steps of removed parents are handled according to `orphanStrategy`.
|
|
389
|
+
|
|
390
|
+
The `minConsecutiveOccurrences` safety guard prevents accidentally removing a step that only appears occasionally — if the actual consecutive count is below the threshold, the step is **not** removed and a warning is logged instead.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
## CI integration example
|
|
395
|
+
|
|
396
|
+
```yaml
|
|
397
|
+
# .github/workflows/test.yml
|
|
398
|
+
- name: Run Playwright tests
|
|
399
|
+
run: npx playwright test
|
|
400
|
+
|
|
401
|
+
- name: Sanitize Playwright artifacts
|
|
402
|
+
run: npx playwright-sanitizer --summary-output sanitization-summary.json
|
|
403
|
+
|
|
404
|
+
- name: Upload sanitized report
|
|
405
|
+
uses: actions/upload-artifact@v4
|
|
406
|
+
with:
|
|
407
|
+
name: playwright-report
|
|
408
|
+
path: sanitized-report/
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
---
|
|
412
|
+
|
|
413
|
+
## License
|
|
414
|
+
|
|
415
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -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,113 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const loader_js_1 = require("./config/loader.js");
|
|
6
|
+
const index_js_1 = require("./index.js");
|
|
7
|
+
const program = new commander_1.Command();
|
|
8
|
+
program
|
|
9
|
+
.name('playwright-sanitizer')
|
|
10
|
+
.description('Post-process Playwright HTML reports and trace files to redact secrets and remove noisy steps')
|
|
11
|
+
.version('0.1.0')
|
|
12
|
+
.option('-c, --config <path>', 'Path to config file')
|
|
13
|
+
.option('-r, --report <path>', 'HTML report directory', './playwright-report')
|
|
14
|
+
.option('-t, --traces <path>', 'Trace directory', './test-results')
|
|
15
|
+
.option('-o, --output <path>', 'Output directory (for copy mode)')
|
|
16
|
+
.option('--in-place', 'Overwrite original files')
|
|
17
|
+
.option('--patterns <path...>', 'One or more pattern files (repeatable)')
|
|
18
|
+
.option('--placeholder <string>', 'Redaction placeholder', '[REDACTED]')
|
|
19
|
+
.option('--dry-run', 'Log changes without writing files')
|
|
20
|
+
.option('--no-traces', 'Skip trace file processing')
|
|
21
|
+
.option('--no-reports', 'Skip HTML report processing')
|
|
22
|
+
.option('--summary-output <path>', 'Write JSON summary to file')
|
|
23
|
+
.option('--log-level <level>', 'silent | normal | verbose', 'normal');
|
|
24
|
+
program.action(async (opts) => {
|
|
25
|
+
try {
|
|
26
|
+
// Load config
|
|
27
|
+
const config = await (0, loader_js_1.loadConfig)(opts['config']);
|
|
28
|
+
// Apply CLI overrides
|
|
29
|
+
applyCliOverrides(config, opts);
|
|
30
|
+
await (0, index_js_1.sanitize)(config);
|
|
31
|
+
process.exit(0);
|
|
32
|
+
}
|
|
33
|
+
catch (err) {
|
|
34
|
+
if (err instanceof Error) {
|
|
35
|
+
console.error(`[FATAL] ${err.message}`);
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.error(`[FATAL] ${String(err)}`);
|
|
39
|
+
}
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
/**
|
|
44
|
+
* Merges parsed CLI flag values into a {@link SanitizerConfig} object.
|
|
45
|
+
*
|
|
46
|
+
* CLI flags always take the highest priority — they overwrite any values
|
|
47
|
+
* that were loaded from a config file. Sections are created on-demand
|
|
48
|
+
* (e.g. `config.output` is initialised to `{}` if not already present).
|
|
49
|
+
*
|
|
50
|
+
* Flag → config field mapping:
|
|
51
|
+
* - `--report` → `output.reportDir`
|
|
52
|
+
* - `--no-traces` → `output.processTraces = false`
|
|
53
|
+
* - `--no-reports` → `output.processReports = false`
|
|
54
|
+
* - `--output` → `output.dir` + `output.mode = 'copy'`
|
|
55
|
+
* - `--in-place` → `output.mode = 'in-place'`
|
|
56
|
+
* - `--patterns` → `redact.patternFiles`
|
|
57
|
+
* - `--placeholder` → `redact.placeholder`
|
|
58
|
+
* - `--dry-run` → `remove.dryRun = true`
|
|
59
|
+
* - `--log-level` → `reporting.logLevel`
|
|
60
|
+
* - `--summary-output` → `reporting.summaryFile`
|
|
61
|
+
*
|
|
62
|
+
* @param config - The config object to mutate (loaded from file or empty).
|
|
63
|
+
* @param opts - Raw parsed options from Commander.js (`program.opts()`).
|
|
64
|
+
*/
|
|
65
|
+
function applyCliOverrides(config, opts) {
|
|
66
|
+
// Output overrides
|
|
67
|
+
if (!config.output)
|
|
68
|
+
config.output = {};
|
|
69
|
+
if (opts['report']) {
|
|
70
|
+
config.output.reportDir = opts['report'];
|
|
71
|
+
}
|
|
72
|
+
if (opts['traces'] === false) {
|
|
73
|
+
config.output.processTraces = false;
|
|
74
|
+
}
|
|
75
|
+
if (opts['reports'] === false) {
|
|
76
|
+
config.output.processReports = false;
|
|
77
|
+
}
|
|
78
|
+
if (opts['output']) {
|
|
79
|
+
config.output.dir = opts['output'];
|
|
80
|
+
config.output.mode = 'copy';
|
|
81
|
+
}
|
|
82
|
+
if (opts['inPlace']) {
|
|
83
|
+
config.output.mode = 'in-place';
|
|
84
|
+
}
|
|
85
|
+
// Redact overrides
|
|
86
|
+
if (opts['patterns']) {
|
|
87
|
+
if (!config.redact)
|
|
88
|
+
config.redact = {};
|
|
89
|
+
config.redact.patternFiles = opts['patterns'];
|
|
90
|
+
}
|
|
91
|
+
if (opts['placeholder']) {
|
|
92
|
+
if (!config.redact)
|
|
93
|
+
config.redact = {};
|
|
94
|
+
config.redact.placeholder = opts['placeholder'];
|
|
95
|
+
}
|
|
96
|
+
// Remove overrides
|
|
97
|
+
if (opts['dryRun']) {
|
|
98
|
+
if (!config.remove)
|
|
99
|
+
config.remove = {};
|
|
100
|
+
config.remove.dryRun = true;
|
|
101
|
+
}
|
|
102
|
+
// Reporting overrides
|
|
103
|
+
if (!config.reporting)
|
|
104
|
+
config.reporting = {};
|
|
105
|
+
if (opts['logLevel']) {
|
|
106
|
+
config.reporting.logLevel = opts['logLevel'];
|
|
107
|
+
}
|
|
108
|
+
if (opts['summaryOutput']) {
|
|
109
|
+
config.reporting.summaryFile = opts['summaryOutput'];
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
program.parse();
|
|
113
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { SanitizerConfig } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves and loads the sanitizer configuration.
|
|
4
|
+
*
|
|
5
|
+
* Config discovery priority (first match wins):
|
|
6
|
+
* 1. Explicit `configPath` (from `--config` CLI flag or programmatic call)
|
|
7
|
+
* 2. `playwright-sanitizer.config.ts` in `cwd`
|
|
8
|
+
* 3. `playwright-sanitizer.config.js` in `cwd`
|
|
9
|
+
* 4. `playwright-sanitizer.config.json` in `cwd`
|
|
10
|
+
* 5. `sanitizer` key inside `playwright.config.ts` / `playwright.config.js`
|
|
11
|
+
*
|
|
12
|
+
* If none of the above are found, the function calls `logger.fatal` which
|
|
13
|
+
* throws an `Error` with an actionable message.
|
|
14
|
+
*
|
|
15
|
+
* @param configPath - Optional explicit path to a config file.
|
|
16
|
+
* When provided, auto-discovery is skipped entirely.
|
|
17
|
+
* @returns The resolved {@link SanitizerConfig}.
|
|
18
|
+
* @throws Calls `logger.fatal` (which throws) when no config can be found or loaded.
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```ts
|
|
22
|
+
* // Auto-discover config in cwd
|
|
23
|
+
* const config = await loadConfig();
|
|
24
|
+
*
|
|
25
|
+
* // Load from an explicit path
|
|
26
|
+
* const config = await loadConfig('./configs/sanitizer.config.ts');
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadConfig(configPath?: string): Promise<SanitizerConfig>;
|
|
30
|
+
//# sourceMappingURL=loader.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.d.ts","sourceRoot":"","sources":["../../src/config/loader.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AA+FlD;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAsB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC,CA6B9E"}
|