@chaos-maker/playwright 0.3.0 → 0.5.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 +160 -0
- package/dist/{chunk-XVP3BFFM.js → chunk-IJDMHBUQ.js} +155 -2
- package/dist/fixture.cjs +103 -2
- package/dist/fixture.d.cts +5 -1
- package/dist/fixture.d.ts +5 -1
- package/dist/fixture.js +10 -2
- package/dist/index.cjs +167 -3
- package/dist/index.d.cts +83 -4
- package/dist/index.d.ts +83 -4
- package/dist/index.js +25 -3
- package/package.json +4 -4
package/README.md
CHANGED
|
@@ -59,6 +59,27 @@ test('handles slow network', async ({ page, chaos }) => {
|
|
|
59
59
|
|
|
60
60
|
### With Presets
|
|
61
61
|
|
|
62
|
+
Drop a built-in preset by name with the declarative `presets` field:
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
await injectChaos(page, { presets: ['slow-api'] });
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Register your own bundle inline via `customPresets`:
|
|
69
|
+
|
|
70
|
+
```ts
|
|
71
|
+
await injectChaos(page, {
|
|
72
|
+
customPresets: {
|
|
73
|
+
'team-flow': {
|
|
74
|
+
network: { failures: [{ urlPattern: '/checkout', statusCode: 503, probability: 1 }] },
|
|
75
|
+
},
|
|
76
|
+
},
|
|
77
|
+
presets: ['team-flow'],
|
|
78
|
+
});
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
The legacy spread style still works for migration:
|
|
82
|
+
|
|
62
83
|
```ts
|
|
63
84
|
import { test, expect } from '@playwright/test';
|
|
64
85
|
import { presets } from '@chaos-maker/core';
|
|
@@ -90,6 +111,71 @@ test('checkout handles combined chaos', async ({ page }) => {
|
|
|
90
111
|
});
|
|
91
112
|
```
|
|
92
113
|
|
|
114
|
+
### Rule Groups
|
|
115
|
+
|
|
116
|
+
Use Rule Groups to toggle a set of rules during a test without reinjecting chaos.
|
|
117
|
+
|
|
118
|
+
```ts
|
|
119
|
+
import { test, expect } from '@playwright/test';
|
|
120
|
+
import { ChaosConfigBuilder } from '@chaos-maker/core';
|
|
121
|
+
import {
|
|
122
|
+
injectChaos,
|
|
123
|
+
enableGroup,
|
|
124
|
+
disableGroup,
|
|
125
|
+
enableSWGroup,
|
|
126
|
+
disableSWGroup,
|
|
127
|
+
} from '@chaos-maker/playwright';
|
|
128
|
+
|
|
129
|
+
test('toggles checkout chaos', async ({ page }) => {
|
|
130
|
+
const config = new ChaosConfigBuilder()
|
|
131
|
+
.defineGroup('payments', { enabled: false })
|
|
132
|
+
.inGroup('payments')
|
|
133
|
+
.failRequests('/api/pay', 503, 1)
|
|
134
|
+
.build();
|
|
135
|
+
|
|
136
|
+
await injectChaos(page, config);
|
|
137
|
+
await page.goto('/checkout');
|
|
138
|
+
|
|
139
|
+
await enableGroup(page, 'payments');
|
|
140
|
+
await expect(page.getByText('Try again')).toBeVisible();
|
|
141
|
+
|
|
142
|
+
await disableGroup(page, 'payments');
|
|
143
|
+
});
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
With the fixture, the same helpers are available as `chaos.enableGroup(name)` and `chaos.disableGroup(name)`.
|
|
147
|
+
|
|
148
|
+
For Service Worker rules, use the SW helpers after `injectSWChaos`:
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
await enableSWGroup(page, 'payments');
|
|
152
|
+
await disableSWGroup(page, 'payments');
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
Browser-side `enableGroup` and `disableGroup` affect page rules from `injectChaos`. `enableSWGroup` and `disableSWGroup` affect Service Worker rules from `injectSWChaos`.
|
|
156
|
+
|
|
157
|
+
### SSE and GraphQL
|
|
158
|
+
|
|
159
|
+
```ts
|
|
160
|
+
await injectChaos(page, {
|
|
161
|
+
seed: 42,
|
|
162
|
+
sse: {
|
|
163
|
+
drops: [{ urlPattern: '/events', eventType: 'token', probability: 0.1 }],
|
|
164
|
+
},
|
|
165
|
+
network: {
|
|
166
|
+
failures: [{
|
|
167
|
+
urlPattern: '/graphql',
|
|
168
|
+
graphqlOperation: 'GetUser',
|
|
169
|
+
statusCode: 503,
|
|
170
|
+
probability: 1,
|
|
171
|
+
}],
|
|
172
|
+
},
|
|
173
|
+
});
|
|
174
|
+
await page.goto('/dashboard');
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
SSE chaos and GraphQL operation matching use the same pre-navigation `injectChaos()` timing as fetch, XHR, and WebSocket chaos.
|
|
178
|
+
|
|
93
179
|
## API
|
|
94
180
|
|
|
95
181
|
### `injectChaos(page, config, opts?)`
|
|
@@ -111,6 +197,14 @@ Stop chaos and restore original `fetch`/`XHR`/DOM behavior.
|
|
|
111
197
|
|
|
112
198
|
Retrieve the chaos event log from the page. Returns `ChaosEvent[]` — every chaos check emitted since injection, with `applied: true/false`.
|
|
113
199
|
|
|
200
|
+
### `enableGroup(page, name)` / `disableGroup(page, name)`
|
|
201
|
+
|
|
202
|
+
Toggle a browser-side Rule Group at runtime.
|
|
203
|
+
|
|
204
|
+
### `enableSWGroup(page, name, opts?)` / `disableSWGroup(page, name, opts?)`
|
|
205
|
+
|
|
206
|
+
Toggle a Service Worker Rule Group at runtime. Pass `opts.timeoutMs` to override the Service Worker acknowledgement timeout.
|
|
207
|
+
|
|
114
208
|
### Fixture: `chaos`
|
|
115
209
|
|
|
116
210
|
Available when importing `test` from `@chaos-maker/playwright/fixture`:
|
|
@@ -118,6 +212,29 @@ Available when importing `test` from `@chaos-maker/playwright/fixture`:
|
|
|
118
212
|
- `chaos.inject(config)` — same as `injectChaos(page, config)`
|
|
119
213
|
- `chaos.remove()` — same as `removeChaos(page)` (also called automatically after each test)
|
|
120
214
|
- `chaos.getLog()` — same as `getChaosLog(page)`
|
|
215
|
+
- `chaos.enableGroup(name)` — same as `enableGroup(page, name)`
|
|
216
|
+
- `chaos.disableGroup(name)` — same as `disableGroup(page, name)`
|
|
217
|
+
- `chaos.enableSWGroup(name, opts?)` — same as `enableSWGroup(page, name, opts)`
|
|
218
|
+
- `chaos.disableSWGroup(name, opts?)` — same as `disableSWGroup(page, name, opts)`
|
|
219
|
+
|
|
220
|
+
## Validation
|
|
221
|
+
|
|
222
|
+
`injectChaos` validates the config from Node BEFORE any page touch. A malformed config throws `ChaosConfigError` synchronously from the test runner — your test fails before navigation, not in the browser console. `ChaosConfigError.issues` is a structured `ValidationIssue[]` with `path`, `code`, `ruleType`, and optional `expected` / `received`. See the [Rule Validation concept page](https://chaos-maker-dev.github.io/chaos-maker/concepts/validation/).
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { injectChaos, ChaosConfigError } from '@chaos-maker/playwright';
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
await injectChaos(page, config, {
|
|
229
|
+
validation: { unknownFields: 'warn' },
|
|
230
|
+
});
|
|
231
|
+
} catch (e) {
|
|
232
|
+
if (e instanceof ChaosConfigError) {
|
|
233
|
+
for (const issue of e.issues) console.error(issue.path, issue.code, issue.message);
|
|
234
|
+
}
|
|
235
|
+
throw e;
|
|
236
|
+
}
|
|
237
|
+
```
|
|
121
238
|
|
|
122
239
|
## Debugging with trace
|
|
123
240
|
|
|
@@ -167,6 +284,49 @@ test('with direct API', async ({ page }, testInfo) => {
|
|
|
167
284
|
});
|
|
168
285
|
```
|
|
169
286
|
|
|
287
|
+
## Service Worker chaos
|
|
288
|
+
|
|
289
|
+
Intercept SW-originated fetches. Requires one line in your service-worker script.
|
|
290
|
+
|
|
291
|
+
```js
|
|
292
|
+
// user's sw.js (classic)
|
|
293
|
+
importScripts('/chaos-maker-sw.js');
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import {
|
|
298
|
+
injectSWChaos,
|
|
299
|
+
removeSWChaos,
|
|
300
|
+
getSWChaosLog,
|
|
301
|
+
enableSWGroup,
|
|
302
|
+
disableSWGroup,
|
|
303
|
+
} from '@chaos-maker/playwright';
|
|
304
|
+
|
|
305
|
+
test('SW-fetched /api returns 503', async ({ page }) => {
|
|
306
|
+
await page.goto('/app-with-sw/');
|
|
307
|
+
// wait for controller after your app's SW registration
|
|
308
|
+
await injectSWChaos(page, {
|
|
309
|
+
groups: [{ name: 'payments', enabled: false }],
|
|
310
|
+
network: {
|
|
311
|
+
failures: [{ urlPattern: '/api/data', statusCode: 503, probability: 1, group: 'payments' }],
|
|
312
|
+
},
|
|
313
|
+
seed: 1,
|
|
314
|
+
});
|
|
315
|
+
await enableSWGroup(page, 'payments');
|
|
316
|
+
await page.click('#trigger');
|
|
317
|
+
const log = await getSWChaosLog(page);
|
|
318
|
+
expect(log.some(e => e.type === 'network:failure' && e.applied)).toBe(true);
|
|
319
|
+
await disableSWGroup(page, 'payments');
|
|
320
|
+
await removeSWChaos(page);
|
|
321
|
+
});
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
Two artifacts ship in `@chaos-maker/core`:
|
|
325
|
+
- `dist/sw.js` — IIFE bundle for classic SWs (`importScripts('/chaos-maker-sw.js')`).
|
|
326
|
+
- `dist/sw.mjs` — ESM bundle for module SWs (`import { installChaosSW } from '/chaos-maker-sw.mjs'`).
|
|
327
|
+
|
|
328
|
+
Serve whichever your SW type uses at a URL reachable from the service-worker scope.
|
|
329
|
+
|
|
170
330
|
## License
|
|
171
331
|
|
|
172
332
|
[MIT](../../LICENSE)
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
import { serializeForTransport, validateChaosConfig as validateChaosConfig2 } from "@chaos-maker/core";
|
|
2
3
|
import { resolve, dirname } from "path";
|
|
3
4
|
import { createRequire } from "module";
|
|
4
5
|
import { fileURLToPath } from "url";
|
|
@@ -49,6 +50,7 @@ function truncate(s, max) {
|
|
|
49
50
|
return `\u2026${s.slice(-(max - 1))}`;
|
|
50
51
|
}
|
|
51
52
|
function shouldEmitStep(event, verbose) {
|
|
53
|
+
if (event.type === "debug") return false;
|
|
52
54
|
if (event.applied) return true;
|
|
53
55
|
return verbose;
|
|
54
56
|
}
|
|
@@ -107,6 +109,100 @@ async function createTraceReporter(page, testInfo, opts = {}) {
|
|
|
107
109
|
};
|
|
108
110
|
}
|
|
109
111
|
|
|
112
|
+
// src/index.ts
|
|
113
|
+
import { Logger } from "@chaos-maker/core";
|
|
114
|
+
import { validateChaosConfig as validateChaosConfig3, ChaosConfigError } from "@chaos-maker/core";
|
|
115
|
+
|
|
116
|
+
// src/sw.ts
|
|
117
|
+
import { validateChaosConfig, SW_BRIDGE_SOURCE } from "@chaos-maker/core";
|
|
118
|
+
var BRIDGE_INIT_KEY = /* @__PURE__ */ Symbol.for("chaos-maker.playwright.sw.bridgeInit");
|
|
119
|
+
var DEFAULT_SW_TOGGLE_TIMEOUT = 2e3;
|
|
120
|
+
async function ensurePageBridge(page) {
|
|
121
|
+
if (!page[BRIDGE_INIT_KEY]) {
|
|
122
|
+
await page.addInitScript({ content: SW_BRIDGE_SOURCE });
|
|
123
|
+
page[BRIDGE_INIT_KEY] = true;
|
|
124
|
+
}
|
|
125
|
+
await page.evaluate(SW_BRIDGE_SOURCE).catch(() => {
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
async function injectSWChaos(page, config, opts = {}) {
|
|
129
|
+
const validated = validateChaosConfig(config, opts.validation);
|
|
130
|
+
const timeoutMs = opts.timeoutMs ?? 1e4;
|
|
131
|
+
await ensurePageBridge(page);
|
|
132
|
+
const result = await page.evaluate(
|
|
133
|
+
async ({ cfg, timeoutMs: timeoutMs2 }) => {
|
|
134
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
135
|
+
if (!bridge) throw new Error("[chaos-maker] SW bridge missing from page \u2014 ensurePageBridge failed");
|
|
136
|
+
return await bridge.apply(cfg, timeoutMs2);
|
|
137
|
+
},
|
|
138
|
+
{ cfg: validated, timeoutMs }
|
|
139
|
+
);
|
|
140
|
+
return result;
|
|
141
|
+
}
|
|
142
|
+
async function removeSWChaos(page, opts = {}) {
|
|
143
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
144
|
+
await page.evaluate(
|
|
145
|
+
async ({ timeoutMs: timeoutMs2 }) => {
|
|
146
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
147
|
+
if (!bridge) return;
|
|
148
|
+
await bridge.stop(timeoutMs2);
|
|
149
|
+
bridge.clearLocalLog();
|
|
150
|
+
},
|
|
151
|
+
{ timeoutMs }
|
|
152
|
+
).catch(() => {
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
async function enableSWGroup(page, name, opts = {}) {
|
|
156
|
+
if (typeof name !== "string") {
|
|
157
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
158
|
+
}
|
|
159
|
+
const nameNorm = name.trim();
|
|
160
|
+
if (!nameNorm) {
|
|
161
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
162
|
+
}
|
|
163
|
+
await toggleSWGroup(page, nameNorm, true, opts);
|
|
164
|
+
}
|
|
165
|
+
async function disableSWGroup(page, name, opts = {}) {
|
|
166
|
+
if (typeof name !== "string") {
|
|
167
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
168
|
+
}
|
|
169
|
+
const nameNorm = name.trim();
|
|
170
|
+
if (!nameNorm) {
|
|
171
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
172
|
+
}
|
|
173
|
+
await toggleSWGroup(page, nameNorm, false, opts);
|
|
174
|
+
}
|
|
175
|
+
async function toggleSWGroup(page, name, enabled, opts) {
|
|
176
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SW_TOGGLE_TIMEOUT;
|
|
177
|
+
await ensurePageBridge(page);
|
|
178
|
+
await page.evaluate(
|
|
179
|
+
async ({ n, e, t }) => {
|
|
180
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
181
|
+
if (!bridge) throw new Error("[chaos-maker] SW bridge missing \u2014 ensurePageBridge failed");
|
|
182
|
+
await bridge.toggleGroup(n, e, t);
|
|
183
|
+
},
|
|
184
|
+
{ n: name, e: enabled, t: timeoutMs }
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
async function getSWChaosLog(page) {
|
|
188
|
+
return page.evaluate(() => {
|
|
189
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
190
|
+
if (!bridge) return [];
|
|
191
|
+
return bridge.getLocalLog();
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
async function getSWChaosLogFromSW(page, opts = {}) {
|
|
195
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
196
|
+
return page.evaluate(
|
|
197
|
+
async ({ timeoutMs: timeoutMs2 }) => {
|
|
198
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
199
|
+
if (!bridge) return [];
|
|
200
|
+
return bridge.getRemoteLog(timeoutMs2);
|
|
201
|
+
},
|
|
202
|
+
{ timeoutMs }
|
|
203
|
+
);
|
|
204
|
+
}
|
|
205
|
+
|
|
110
206
|
// src/index.ts
|
|
111
207
|
var cachedUmdPath = null;
|
|
112
208
|
function getCoreUmdPath() {
|
|
@@ -120,6 +216,7 @@ function getCoreUmdPath() {
|
|
|
120
216
|
}
|
|
121
217
|
var TRACE_HANDLE_KEY = /* @__PURE__ */ Symbol.for("chaos-maker.playwright.traceHandle");
|
|
122
218
|
async function injectChaos(page, config, opts = {}) {
|
|
219
|
+
const validated = validateChaosConfig2(config, opts.validation);
|
|
123
220
|
const umdPath = getCoreUmdPath();
|
|
124
221
|
const tracingEnabled = resolveTracing(opts);
|
|
125
222
|
if (tracingEnabled) {
|
|
@@ -134,10 +231,11 @@ async function injectChaos(page, config, opts = {}) {
|
|
|
134
231
|
page[TRACE_HANDLE_KEY] = handle;
|
|
135
232
|
}
|
|
136
233
|
}
|
|
234
|
+
const serialized = serializeForTransport(validated);
|
|
137
235
|
await page.addInitScript((cfg) => {
|
|
138
236
|
const win = globalThis;
|
|
139
237
|
win.__CHAOS_CONFIG__ = cfg;
|
|
140
|
-
},
|
|
238
|
+
}, serialized);
|
|
141
239
|
await page.addInitScript({ path: umdPath });
|
|
142
240
|
}
|
|
143
241
|
function resolveTracing(opts) {
|
|
@@ -175,6 +273,50 @@ async function getChaosLog(page) {
|
|
|
175
273
|
return [];
|
|
176
274
|
});
|
|
177
275
|
}
|
|
276
|
+
async function enableGroup(page, name) {
|
|
277
|
+
if (typeof name !== "string") {
|
|
278
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
279
|
+
}
|
|
280
|
+
const nameNorm = name.trim();
|
|
281
|
+
if (!nameNorm) {
|
|
282
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
283
|
+
}
|
|
284
|
+
await page.evaluate(({ n }) => {
|
|
285
|
+
const utils = globalThis.chaosUtils;
|
|
286
|
+
if (!utils || !utils.instance) {
|
|
287
|
+
throw new Error("[chaos-maker] no chaos instance on page \u2014 call injectChaos first");
|
|
288
|
+
}
|
|
289
|
+
if (typeof utils.enableGroup !== "function") {
|
|
290
|
+
throw new Error("[chaos-maker] enableGroup API unavailable");
|
|
291
|
+
}
|
|
292
|
+
const result = utils.enableGroup(n);
|
|
293
|
+
if (result && result.success === false) {
|
|
294
|
+
throw new Error(`[chaos-maker] enableGroup('${n}') failed: ${result.message}`);
|
|
295
|
+
}
|
|
296
|
+
}, { n: nameNorm });
|
|
297
|
+
}
|
|
298
|
+
async function disableGroup(page, name) {
|
|
299
|
+
if (typeof name !== "string") {
|
|
300
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
301
|
+
}
|
|
302
|
+
const nameNorm = name.trim();
|
|
303
|
+
if (!nameNorm) {
|
|
304
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
305
|
+
}
|
|
306
|
+
await page.evaluate(({ n }) => {
|
|
307
|
+
const utils = globalThis.chaosUtils;
|
|
308
|
+
if (!utils || !utils.instance) {
|
|
309
|
+
throw new Error("[chaos-maker] no chaos instance on page \u2014 call injectChaos first");
|
|
310
|
+
}
|
|
311
|
+
if (typeof utils.disableGroup !== "function") {
|
|
312
|
+
throw new Error("[chaos-maker] disableGroup API unavailable");
|
|
313
|
+
}
|
|
314
|
+
const result = utils.disableGroup(n);
|
|
315
|
+
if (result && result.success === false) {
|
|
316
|
+
throw new Error(`[chaos-maker] disableGroup('${n}') failed: ${result.message}`);
|
|
317
|
+
}
|
|
318
|
+
}, { n: nameNorm });
|
|
319
|
+
}
|
|
178
320
|
async function getChaosSeed(page) {
|
|
179
321
|
return page.evaluate(() => {
|
|
180
322
|
const win = globalThis;
|
|
@@ -186,8 +328,19 @@ async function getChaosSeed(page) {
|
|
|
186
328
|
}
|
|
187
329
|
|
|
188
330
|
export {
|
|
331
|
+
injectSWChaos,
|
|
332
|
+
removeSWChaos,
|
|
333
|
+
enableSWGroup,
|
|
334
|
+
disableSWGroup,
|
|
335
|
+
getSWChaosLog,
|
|
336
|
+
getSWChaosLogFromSW,
|
|
189
337
|
injectChaos,
|
|
190
338
|
removeChaos,
|
|
191
339
|
getChaosLog,
|
|
192
|
-
|
|
340
|
+
enableGroup,
|
|
341
|
+
disableGroup,
|
|
342
|
+
getChaosSeed,
|
|
343
|
+
Logger,
|
|
344
|
+
validateChaosConfig3 as validateChaosConfig,
|
|
345
|
+
ChaosConfigError
|
|
193
346
|
};
|
package/dist/fixture.cjs
CHANGED
|
@@ -27,6 +27,7 @@ module.exports = __toCommonJS(fixture_exports);
|
|
|
27
27
|
var import_test2 = require("@playwright/test");
|
|
28
28
|
|
|
29
29
|
// src/index.ts
|
|
30
|
+
var import_core2 = require("@chaos-maker/core");
|
|
30
31
|
var import_path = require("path");
|
|
31
32
|
var import_module = require("module");
|
|
32
33
|
var import_url = require("url");
|
|
@@ -77,6 +78,7 @@ function truncate(s, max) {
|
|
|
77
78
|
return `\u2026${s.slice(-(max - 1))}`;
|
|
78
79
|
}
|
|
79
80
|
function shouldEmitStep(event, verbose) {
|
|
81
|
+
if (event.type === "debug") return false;
|
|
80
82
|
if (event.applied) return true;
|
|
81
83
|
return verbose;
|
|
82
84
|
}
|
|
@@ -135,6 +137,55 @@ async function createTraceReporter(page, testInfo, opts = {}) {
|
|
|
135
137
|
};
|
|
136
138
|
}
|
|
137
139
|
|
|
140
|
+
// src/index.ts
|
|
141
|
+
var import_core3 = require("@chaos-maker/core");
|
|
142
|
+
var import_core4 = require("@chaos-maker/core");
|
|
143
|
+
|
|
144
|
+
// src/sw.ts
|
|
145
|
+
var import_core = require("@chaos-maker/core");
|
|
146
|
+
var BRIDGE_INIT_KEY = /* @__PURE__ */ Symbol.for("chaos-maker.playwright.sw.bridgeInit");
|
|
147
|
+
var DEFAULT_SW_TOGGLE_TIMEOUT = 2e3;
|
|
148
|
+
async function ensurePageBridge(page) {
|
|
149
|
+
if (!page[BRIDGE_INIT_KEY]) {
|
|
150
|
+
await page.addInitScript({ content: import_core.SW_BRIDGE_SOURCE });
|
|
151
|
+
page[BRIDGE_INIT_KEY] = true;
|
|
152
|
+
}
|
|
153
|
+
await page.evaluate(import_core.SW_BRIDGE_SOURCE).catch(() => {
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
async function enableSWGroup(page, name, opts = {}) {
|
|
157
|
+
if (typeof name !== "string") {
|
|
158
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
159
|
+
}
|
|
160
|
+
const nameNorm = name.trim();
|
|
161
|
+
if (!nameNorm) {
|
|
162
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
163
|
+
}
|
|
164
|
+
await toggleSWGroup(page, nameNorm, true, opts);
|
|
165
|
+
}
|
|
166
|
+
async function disableSWGroup(page, name, opts = {}) {
|
|
167
|
+
if (typeof name !== "string") {
|
|
168
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
169
|
+
}
|
|
170
|
+
const nameNorm = name.trim();
|
|
171
|
+
if (!nameNorm) {
|
|
172
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
173
|
+
}
|
|
174
|
+
await toggleSWGroup(page, nameNorm, false, opts);
|
|
175
|
+
}
|
|
176
|
+
async function toggleSWGroup(page, name, enabled, opts) {
|
|
177
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SW_TOGGLE_TIMEOUT;
|
|
178
|
+
await ensurePageBridge(page);
|
|
179
|
+
await page.evaluate(
|
|
180
|
+
async ({ n, e, t }) => {
|
|
181
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
182
|
+
if (!bridge) throw new Error("[chaos-maker] SW bridge missing \u2014 ensurePageBridge failed");
|
|
183
|
+
await bridge.toggleGroup(n, e, t);
|
|
184
|
+
},
|
|
185
|
+
{ n: name, e: enabled, t: timeoutMs }
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
|
|
138
189
|
// src/index.ts
|
|
139
190
|
var import_meta = {};
|
|
140
191
|
var cachedUmdPath = null;
|
|
@@ -149,6 +200,7 @@ function getCoreUmdPath() {
|
|
|
149
200
|
}
|
|
150
201
|
var TRACE_HANDLE_KEY = /* @__PURE__ */ Symbol.for("chaos-maker.playwright.traceHandle");
|
|
151
202
|
async function injectChaos(page, config, opts = {}) {
|
|
203
|
+
const validated = (0, import_core2.validateChaosConfig)(config, opts.validation);
|
|
152
204
|
const umdPath = getCoreUmdPath();
|
|
153
205
|
const tracingEnabled = resolveTracing(opts);
|
|
154
206
|
if (tracingEnabled) {
|
|
@@ -163,10 +215,11 @@ async function injectChaos(page, config, opts = {}) {
|
|
|
163
215
|
page[TRACE_HANDLE_KEY] = handle;
|
|
164
216
|
}
|
|
165
217
|
}
|
|
218
|
+
const serialized = (0, import_core2.serializeForTransport)(validated);
|
|
166
219
|
await page.addInitScript((cfg) => {
|
|
167
220
|
const win = globalThis;
|
|
168
221
|
win.__CHAOS_CONFIG__ = cfg;
|
|
169
|
-
},
|
|
222
|
+
}, serialized);
|
|
170
223
|
await page.addInitScript({ path: umdPath });
|
|
171
224
|
}
|
|
172
225
|
function resolveTracing(opts) {
|
|
@@ -204,6 +257,50 @@ async function getChaosLog(page) {
|
|
|
204
257
|
return [];
|
|
205
258
|
});
|
|
206
259
|
}
|
|
260
|
+
async function enableGroup(page, name) {
|
|
261
|
+
if (typeof name !== "string") {
|
|
262
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
263
|
+
}
|
|
264
|
+
const nameNorm = name.trim();
|
|
265
|
+
if (!nameNorm) {
|
|
266
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
267
|
+
}
|
|
268
|
+
await page.evaluate(({ n }) => {
|
|
269
|
+
const utils = globalThis.chaosUtils;
|
|
270
|
+
if (!utils || !utils.instance) {
|
|
271
|
+
throw new Error("[chaos-maker] no chaos instance on page \u2014 call injectChaos first");
|
|
272
|
+
}
|
|
273
|
+
if (typeof utils.enableGroup !== "function") {
|
|
274
|
+
throw new Error("[chaos-maker] enableGroup API unavailable");
|
|
275
|
+
}
|
|
276
|
+
const result = utils.enableGroup(n);
|
|
277
|
+
if (result && result.success === false) {
|
|
278
|
+
throw new Error(`[chaos-maker] enableGroup('${n}') failed: ${result.message}`);
|
|
279
|
+
}
|
|
280
|
+
}, { n: nameNorm });
|
|
281
|
+
}
|
|
282
|
+
async function disableGroup(page, name) {
|
|
283
|
+
if (typeof name !== "string") {
|
|
284
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
285
|
+
}
|
|
286
|
+
const nameNorm = name.trim();
|
|
287
|
+
if (!nameNorm) {
|
|
288
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
289
|
+
}
|
|
290
|
+
await page.evaluate(({ n }) => {
|
|
291
|
+
const utils = globalThis.chaosUtils;
|
|
292
|
+
if (!utils || !utils.instance) {
|
|
293
|
+
throw new Error("[chaos-maker] no chaos instance on page \u2014 call injectChaos first");
|
|
294
|
+
}
|
|
295
|
+
if (typeof utils.disableGroup !== "function") {
|
|
296
|
+
throw new Error("[chaos-maker] disableGroup API unavailable");
|
|
297
|
+
}
|
|
298
|
+
const result = utils.disableGroup(n);
|
|
299
|
+
if (result && result.success === false) {
|
|
300
|
+
throw new Error(`[chaos-maker] disableGroup('${n}') failed: ${result.message}`);
|
|
301
|
+
}
|
|
302
|
+
}, { n: nameNorm });
|
|
303
|
+
}
|
|
207
304
|
async function getChaosSeed(page) {
|
|
208
305
|
return page.evaluate(() => {
|
|
209
306
|
const win = globalThis;
|
|
@@ -242,7 +339,11 @@ var test2 = import_test2.test.extend({
|
|
|
242
339
|
},
|
|
243
340
|
remove: () => removeChaos(page),
|
|
244
341
|
getLog: () => getChaosLog(page),
|
|
245
|
-
getSeed: () => getChaosSeed(page)
|
|
342
|
+
getSeed: () => getChaosSeed(page),
|
|
343
|
+
enableGroup: (name) => enableGroup(page, name),
|
|
344
|
+
disableGroup: (name) => disableGroup(page, name),
|
|
345
|
+
enableSWGroup: (name, opts) => enableSWGroup(page, name, opts),
|
|
346
|
+
disableSWGroup: (name, opts) => disableSWGroup(page, name, opts)
|
|
246
347
|
};
|
|
247
348
|
await use(fixture);
|
|
248
349
|
await removeChaos(page);
|
package/dist/fixture.d.cts
CHANGED
|
@@ -2,13 +2,17 @@ import * as _playwright_test from '@playwright/test';
|
|
|
2
2
|
export { expect } from '@playwright/test';
|
|
3
3
|
import { ChaosConfig, ChaosEvent } from '@chaos-maker/core';
|
|
4
4
|
export { ChaosConfig, ChaosEvent } from '@chaos-maker/core';
|
|
5
|
-
import { InjectChaosOptions } from './index.cjs';
|
|
5
|
+
import { InjectChaosOptions, SWChaosOptions } from './index.cjs';
|
|
6
6
|
|
|
7
7
|
interface ChaosFixture {
|
|
8
8
|
inject: (config: ChaosConfig, opts?: InjectChaosOptions) => Promise<void>;
|
|
9
9
|
remove: () => Promise<void>;
|
|
10
10
|
getLog: () => Promise<ChaosEvent[]>;
|
|
11
11
|
getSeed: () => Promise<number | null>;
|
|
12
|
+
enableGroup: (name: string) => Promise<void>;
|
|
13
|
+
disableGroup: (name: string) => Promise<void>;
|
|
14
|
+
enableSWGroup: (name: string, opts?: SWChaosOptions) => Promise<void>;
|
|
15
|
+
disableSWGroup: (name: string, opts?: SWChaosOptions) => Promise<void>;
|
|
12
16
|
}
|
|
13
17
|
/**
|
|
14
18
|
* Extended Playwright test with a `chaos` fixture.
|
package/dist/fixture.d.ts
CHANGED
|
@@ -2,13 +2,17 @@ import * as _playwright_test from '@playwright/test';
|
|
|
2
2
|
export { expect } from '@playwright/test';
|
|
3
3
|
import { ChaosConfig, ChaosEvent } from '@chaos-maker/core';
|
|
4
4
|
export { ChaosConfig, ChaosEvent } from '@chaos-maker/core';
|
|
5
|
-
import { InjectChaosOptions } from './index.js';
|
|
5
|
+
import { InjectChaosOptions, SWChaosOptions } from './index.js';
|
|
6
6
|
|
|
7
7
|
interface ChaosFixture {
|
|
8
8
|
inject: (config: ChaosConfig, opts?: InjectChaosOptions) => Promise<void>;
|
|
9
9
|
remove: () => Promise<void>;
|
|
10
10
|
getLog: () => Promise<ChaosEvent[]>;
|
|
11
11
|
getSeed: () => Promise<number | null>;
|
|
12
|
+
enableGroup: (name: string) => Promise<void>;
|
|
13
|
+
disableGroup: (name: string) => Promise<void>;
|
|
14
|
+
enableSWGroup: (name: string, opts?: SWChaosOptions) => Promise<void>;
|
|
15
|
+
disableSWGroup: (name: string, opts?: SWChaosOptions) => Promise<void>;
|
|
12
16
|
}
|
|
13
17
|
/**
|
|
14
18
|
* Extended Playwright test with a `chaos` fixture.
|
package/dist/fixture.js
CHANGED
|
@@ -1,9 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
+
disableGroup,
|
|
3
|
+
disableSWGroup,
|
|
4
|
+
enableGroup,
|
|
5
|
+
enableSWGroup,
|
|
2
6
|
getChaosLog,
|
|
3
7
|
getChaosSeed,
|
|
4
8
|
injectChaos,
|
|
5
9
|
removeChaos
|
|
6
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-IJDMHBUQ.js";
|
|
7
11
|
|
|
8
12
|
// src/fixture.ts
|
|
9
13
|
import { test as base } from "@playwright/test";
|
|
@@ -34,7 +38,11 @@ var test = base.extend({
|
|
|
34
38
|
},
|
|
35
39
|
remove: () => removeChaos(page),
|
|
36
40
|
getLog: () => getChaosLog(page),
|
|
37
|
-
getSeed: () => getChaosSeed(page)
|
|
41
|
+
getSeed: () => getChaosSeed(page),
|
|
42
|
+
enableGroup: (name) => enableGroup(page, name),
|
|
43
|
+
disableGroup: (name) => disableGroup(page, name),
|
|
44
|
+
enableSWGroup: (name, opts) => enableSWGroup(page, name, opts),
|
|
45
|
+
disableSWGroup: (name, opts) => disableSWGroup(page, name, opts)
|
|
38
46
|
};
|
|
39
47
|
await use(fixture);
|
|
40
48
|
await removeChaos(page);
|
package/dist/index.cjs
CHANGED
|
@@ -20,12 +20,24 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
20
20
|
// src/index.ts
|
|
21
21
|
var index_exports = {};
|
|
22
22
|
__export(index_exports, {
|
|
23
|
+
ChaosConfigError: () => import_core4.ChaosConfigError,
|
|
24
|
+
Logger: () => import_core3.Logger,
|
|
25
|
+
disableGroup: () => disableGroup,
|
|
26
|
+
disableSWGroup: () => disableSWGroup,
|
|
27
|
+
enableGroup: () => enableGroup,
|
|
28
|
+
enableSWGroup: () => enableSWGroup,
|
|
23
29
|
getChaosLog: () => getChaosLog,
|
|
24
30
|
getChaosSeed: () => getChaosSeed,
|
|
31
|
+
getSWChaosLog: () => getSWChaosLog,
|
|
32
|
+
getSWChaosLogFromSW: () => getSWChaosLogFromSW,
|
|
25
33
|
injectChaos: () => injectChaos,
|
|
26
|
-
|
|
34
|
+
injectSWChaos: () => injectSWChaos,
|
|
35
|
+
removeChaos: () => removeChaos,
|
|
36
|
+
removeSWChaos: () => removeSWChaos,
|
|
37
|
+
validateChaosConfig: () => import_core4.validateChaosConfig
|
|
27
38
|
});
|
|
28
39
|
module.exports = __toCommonJS(index_exports);
|
|
40
|
+
var import_core2 = require("@chaos-maker/core");
|
|
29
41
|
var import_path = require("path");
|
|
30
42
|
var import_module = require("module");
|
|
31
43
|
var import_url = require("url");
|
|
@@ -76,6 +88,7 @@ function truncate(s, max) {
|
|
|
76
88
|
return `\u2026${s.slice(-(max - 1))}`;
|
|
77
89
|
}
|
|
78
90
|
function shouldEmitStep(event, verbose) {
|
|
91
|
+
if (event.type === "debug") return false;
|
|
79
92
|
if (event.applied) return true;
|
|
80
93
|
return verbose;
|
|
81
94
|
}
|
|
@@ -134,6 +147,100 @@ async function createTraceReporter(page, testInfo, opts = {}) {
|
|
|
134
147
|
};
|
|
135
148
|
}
|
|
136
149
|
|
|
150
|
+
// src/index.ts
|
|
151
|
+
var import_core3 = require("@chaos-maker/core");
|
|
152
|
+
var import_core4 = require("@chaos-maker/core");
|
|
153
|
+
|
|
154
|
+
// src/sw.ts
|
|
155
|
+
var import_core = require("@chaos-maker/core");
|
|
156
|
+
var BRIDGE_INIT_KEY = /* @__PURE__ */ Symbol.for("chaos-maker.playwright.sw.bridgeInit");
|
|
157
|
+
var DEFAULT_SW_TOGGLE_TIMEOUT = 2e3;
|
|
158
|
+
async function ensurePageBridge(page) {
|
|
159
|
+
if (!page[BRIDGE_INIT_KEY]) {
|
|
160
|
+
await page.addInitScript({ content: import_core.SW_BRIDGE_SOURCE });
|
|
161
|
+
page[BRIDGE_INIT_KEY] = true;
|
|
162
|
+
}
|
|
163
|
+
await page.evaluate(import_core.SW_BRIDGE_SOURCE).catch(() => {
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
async function injectSWChaos(page, config, opts = {}) {
|
|
167
|
+
const validated = (0, import_core.validateChaosConfig)(config, opts.validation);
|
|
168
|
+
const timeoutMs = opts.timeoutMs ?? 1e4;
|
|
169
|
+
await ensurePageBridge(page);
|
|
170
|
+
const result = await page.evaluate(
|
|
171
|
+
async ({ cfg, timeoutMs: timeoutMs2 }) => {
|
|
172
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
173
|
+
if (!bridge) throw new Error("[chaos-maker] SW bridge missing from page \u2014 ensurePageBridge failed");
|
|
174
|
+
return await bridge.apply(cfg, timeoutMs2);
|
|
175
|
+
},
|
|
176
|
+
{ cfg: validated, timeoutMs }
|
|
177
|
+
);
|
|
178
|
+
return result;
|
|
179
|
+
}
|
|
180
|
+
async function removeSWChaos(page, opts = {}) {
|
|
181
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
182
|
+
await page.evaluate(
|
|
183
|
+
async ({ timeoutMs: timeoutMs2 }) => {
|
|
184
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
185
|
+
if (!bridge) return;
|
|
186
|
+
await bridge.stop(timeoutMs2);
|
|
187
|
+
bridge.clearLocalLog();
|
|
188
|
+
},
|
|
189
|
+
{ timeoutMs }
|
|
190
|
+
).catch(() => {
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
async function enableSWGroup(page, name, opts = {}) {
|
|
194
|
+
if (typeof name !== "string") {
|
|
195
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
196
|
+
}
|
|
197
|
+
const nameNorm = name.trim();
|
|
198
|
+
if (!nameNorm) {
|
|
199
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
200
|
+
}
|
|
201
|
+
await toggleSWGroup(page, nameNorm, true, opts);
|
|
202
|
+
}
|
|
203
|
+
async function disableSWGroup(page, name, opts = {}) {
|
|
204
|
+
if (typeof name !== "string") {
|
|
205
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
206
|
+
}
|
|
207
|
+
const nameNorm = name.trim();
|
|
208
|
+
if (!nameNorm) {
|
|
209
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
210
|
+
}
|
|
211
|
+
await toggleSWGroup(page, nameNorm, false, opts);
|
|
212
|
+
}
|
|
213
|
+
async function toggleSWGroup(page, name, enabled, opts) {
|
|
214
|
+
const timeoutMs = opts.timeoutMs ?? DEFAULT_SW_TOGGLE_TIMEOUT;
|
|
215
|
+
await ensurePageBridge(page);
|
|
216
|
+
await page.evaluate(
|
|
217
|
+
async ({ n, e, t }) => {
|
|
218
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
219
|
+
if (!bridge) throw new Error("[chaos-maker] SW bridge missing \u2014 ensurePageBridge failed");
|
|
220
|
+
await bridge.toggleGroup(n, e, t);
|
|
221
|
+
},
|
|
222
|
+
{ n: name, e: enabled, t: timeoutMs }
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
async function getSWChaosLog(page) {
|
|
226
|
+
return page.evaluate(() => {
|
|
227
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
228
|
+
if (!bridge) return [];
|
|
229
|
+
return bridge.getLocalLog();
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
async function getSWChaosLogFromSW(page, opts = {}) {
|
|
233
|
+
const timeoutMs = opts.timeoutMs ?? 5e3;
|
|
234
|
+
return page.evaluate(
|
|
235
|
+
async ({ timeoutMs: timeoutMs2 }) => {
|
|
236
|
+
const bridge = globalThis.__chaosMakerSWBridge;
|
|
237
|
+
if (!bridge) return [];
|
|
238
|
+
return bridge.getRemoteLog(timeoutMs2);
|
|
239
|
+
},
|
|
240
|
+
{ timeoutMs }
|
|
241
|
+
);
|
|
242
|
+
}
|
|
243
|
+
|
|
137
244
|
// src/index.ts
|
|
138
245
|
var import_meta = {};
|
|
139
246
|
var cachedUmdPath = null;
|
|
@@ -148,6 +255,7 @@ function getCoreUmdPath() {
|
|
|
148
255
|
}
|
|
149
256
|
var TRACE_HANDLE_KEY = /* @__PURE__ */ Symbol.for("chaos-maker.playwright.traceHandle");
|
|
150
257
|
async function injectChaos(page, config, opts = {}) {
|
|
258
|
+
const validated = (0, import_core2.validateChaosConfig)(config, opts.validation);
|
|
151
259
|
const umdPath = getCoreUmdPath();
|
|
152
260
|
const tracingEnabled = resolveTracing(opts);
|
|
153
261
|
if (tracingEnabled) {
|
|
@@ -162,10 +270,11 @@ async function injectChaos(page, config, opts = {}) {
|
|
|
162
270
|
page[TRACE_HANDLE_KEY] = handle;
|
|
163
271
|
}
|
|
164
272
|
}
|
|
273
|
+
const serialized = (0, import_core2.serializeForTransport)(validated);
|
|
165
274
|
await page.addInitScript((cfg) => {
|
|
166
275
|
const win = globalThis;
|
|
167
276
|
win.__CHAOS_CONFIG__ = cfg;
|
|
168
|
-
},
|
|
277
|
+
}, serialized);
|
|
169
278
|
await page.addInitScript({ path: umdPath });
|
|
170
279
|
}
|
|
171
280
|
function resolveTracing(opts) {
|
|
@@ -203,6 +312,50 @@ async function getChaosLog(page) {
|
|
|
203
312
|
return [];
|
|
204
313
|
});
|
|
205
314
|
}
|
|
315
|
+
async function enableGroup(page, name) {
|
|
316
|
+
if (typeof name !== "string") {
|
|
317
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
318
|
+
}
|
|
319
|
+
const nameNorm = name.trim();
|
|
320
|
+
if (!nameNorm) {
|
|
321
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
322
|
+
}
|
|
323
|
+
await page.evaluate(({ n }) => {
|
|
324
|
+
const utils = globalThis.chaosUtils;
|
|
325
|
+
if (!utils || !utils.instance) {
|
|
326
|
+
throw new Error("[chaos-maker] no chaos instance on page \u2014 call injectChaos first");
|
|
327
|
+
}
|
|
328
|
+
if (typeof utils.enableGroup !== "function") {
|
|
329
|
+
throw new Error("[chaos-maker] enableGroup API unavailable");
|
|
330
|
+
}
|
|
331
|
+
const result = utils.enableGroup(n);
|
|
332
|
+
if (result && result.success === false) {
|
|
333
|
+
throw new Error(`[chaos-maker] enableGroup('${n}') failed: ${result.message}`);
|
|
334
|
+
}
|
|
335
|
+
}, { n: nameNorm });
|
|
336
|
+
}
|
|
337
|
+
async function disableGroup(page, name) {
|
|
338
|
+
if (typeof name !== "string") {
|
|
339
|
+
throw new Error("[chaos-maker] group name must be a string");
|
|
340
|
+
}
|
|
341
|
+
const nameNorm = name.trim();
|
|
342
|
+
if (!nameNorm) {
|
|
343
|
+
throw new Error("[chaos-maker] group name cannot be empty");
|
|
344
|
+
}
|
|
345
|
+
await page.evaluate(({ n }) => {
|
|
346
|
+
const utils = globalThis.chaosUtils;
|
|
347
|
+
if (!utils || !utils.instance) {
|
|
348
|
+
throw new Error("[chaos-maker] no chaos instance on page \u2014 call injectChaos first");
|
|
349
|
+
}
|
|
350
|
+
if (typeof utils.disableGroup !== "function") {
|
|
351
|
+
throw new Error("[chaos-maker] disableGroup API unavailable");
|
|
352
|
+
}
|
|
353
|
+
const result = utils.disableGroup(n);
|
|
354
|
+
if (result && result.success === false) {
|
|
355
|
+
throw new Error(`[chaos-maker] disableGroup('${n}') failed: ${result.message}`);
|
|
356
|
+
}
|
|
357
|
+
}, { n: nameNorm });
|
|
358
|
+
}
|
|
206
359
|
async function getChaosSeed(page) {
|
|
207
360
|
return page.evaluate(() => {
|
|
208
361
|
const win = globalThis;
|
|
@@ -214,8 +367,19 @@ async function getChaosSeed(page) {
|
|
|
214
367
|
}
|
|
215
368
|
// Annotate the CommonJS export names for ESM import in node:
|
|
216
369
|
0 && (module.exports = {
|
|
370
|
+
ChaosConfigError,
|
|
371
|
+
Logger,
|
|
372
|
+
disableGroup,
|
|
373
|
+
disableSWGroup,
|
|
374
|
+
enableGroup,
|
|
375
|
+
enableSWGroup,
|
|
217
376
|
getChaosLog,
|
|
218
377
|
getChaosSeed,
|
|
378
|
+
getSWChaosLog,
|
|
379
|
+
getSWChaosLogFromSW,
|
|
219
380
|
injectChaos,
|
|
220
|
-
|
|
381
|
+
injectSWChaos,
|
|
382
|
+
removeChaos,
|
|
383
|
+
removeSWChaos,
|
|
384
|
+
validateChaosConfig
|
|
221
385
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ChaosEvent, ChaosConfig } from '@chaos-maker/core';
|
|
3
|
-
export { ChaosConfig, ChaosEvent, WebSocketCloseConfig, WebSocketConfig, WebSocketCorruptConfig, WebSocketCorruptionStrategy, WebSocketDelayConfig, WebSocketDirection, WebSocketDropConfig } from '@chaos-maker/core';
|
|
1
|
+
import { Page, TestInfo } from '@playwright/test';
|
|
2
|
+
import { ChaosEvent, ValidateChaosConfigOptions, ChaosConfig } from '@chaos-maker/core';
|
|
3
|
+
export { ChaosConfig, ChaosConfigError, ChaosDebugStage, ChaosEvent, ChaosLifecyclePhase, CorruptionStrategy, CustomRuleValidator, CustomValidatorMap, DebugOptions, GraphQLOperationMatcher, Logger, NetworkAbortConfig, NetworkConfig, NetworkCorruptionConfig, NetworkCorsConfig, NetworkFailureConfig, NetworkLatencyConfig, NetworkRuleMatchers, RuleType, SSECloseConfig, SSEConfig, SSECorruptConfig, SSECorruptionStrategy, SSEDelayConfig, SSEDropConfig, SSEEventTypeMatcher, ValidateChaosConfigOptions, ValidationIssue, ValidationIssueCode, WebSocketCloseConfig, WebSocketConfig, WebSocketCorruptConfig, WebSocketCorruptionStrategy, WebSocketDelayConfig, WebSocketDirection, WebSocketDropConfig, validateChaosConfig } from '@chaos-maker/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Shape of the JSON attachment written to `testInfo.attachments` on teardown.
|
|
@@ -17,6 +17,70 @@ interface TraceReporterOptions {
|
|
|
17
17
|
attachmentName?: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Options accepted by {@link injectSWChaos} / {@link removeSWChaos} /
|
|
22
|
+
* {@link getSWChaosLog}.
|
|
23
|
+
*/
|
|
24
|
+
interface SWChaosOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Maximum milliseconds to wait for `navigator.serviceWorker.controller` and
|
|
27
|
+
* the SW's ack message. Defaults to `10000`. Raise for slow CI workers or
|
|
28
|
+
* SWs that do heavy work during `install`.
|
|
29
|
+
*/
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Forwarded to `validateChaosConfig` before the config is posted
|
|
33
|
+
* to the SW. Malformed configs throw a `ChaosConfigError` from Node.
|
|
34
|
+
*/
|
|
35
|
+
validation?: ValidateChaosConfigOptions;
|
|
36
|
+
}
|
|
37
|
+
interface InjectSWChaosResult {
|
|
38
|
+
/** Seed used by the PRNG inside the SW. `null` if the ack did not carry one. */
|
|
39
|
+
seed: number | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Configure Service-Worker chaos for a Playwright page. Call **after**
|
|
43
|
+
* `page.goto(...)` so there is a SW registration + controller to target.
|
|
44
|
+
*
|
|
45
|
+
* Requires the user's service worker to load the chaos SW bundle — typically
|
|
46
|
+
* via `importScripts('/path/to/chaos-maker-sw.js')` (classic SW) or
|
|
47
|
+
* `import { installChaosSW } from '@chaos-maker/core/sw'; installChaosSW();`
|
|
48
|
+
* (module SW).
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* await page.goto('/');
|
|
53
|
+
* await injectSWChaos(page, {
|
|
54
|
+
* network: { failures: [{ urlPattern: '/api', statusCode: 503, probability: 1 }] },
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function injectSWChaos(page: Page, config: ChaosConfig, opts?: SWChaosOptions): Promise<InjectSWChaosResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Stop Service-Worker chaos for a Playwright page. Posts `__chaosMakerStop` to
|
|
61
|
+
* the current controller and clears the page's in-memory log buffer.
|
|
62
|
+
*/
|
|
63
|
+
declare function removeSWChaos(page: Page, opts?: SWChaosOptions): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Enable a rule group inside the active SW chaos engine. Posts
|
|
66
|
+
* `__chaosMakerToggleGroup` over MessageChannel and resolves only after the SW
|
|
67
|
+
* acks. Engine state and request counters are preserved (no restart).
|
|
68
|
+
*/
|
|
69
|
+
declare function enableSWGroup(page: Page, name: string, opts?: SWChaosOptions): Promise<void>;
|
|
70
|
+
/** Disable a rule group inside the active SW chaos engine. */
|
|
71
|
+
declare function disableSWGroup(page: Page, name: string, opts?: SWChaosOptions): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Read the chaos event log buffered on the page side. Every event emitted by
|
|
74
|
+
* the SW is broadcast to all controlled clients and captured here.
|
|
75
|
+
*/
|
|
76
|
+
declare function getSWChaosLog(page: Page): Promise<ChaosEvent[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Ask the SW for its in-memory log. Useful when debugging a race where the
|
|
79
|
+
* page-side listener missed an early broadcast (e.g. first-paint navigation).
|
|
80
|
+
* Prefer {@link getSWChaosLog} in normal assertions.
|
|
81
|
+
*/
|
|
82
|
+
declare function getSWChaosLogFromSW(page: Page, opts?: SWChaosOptions): Promise<ChaosEvent[]>;
|
|
83
|
+
|
|
20
84
|
/**
|
|
21
85
|
* Options for `injectChaos`. Most callers can omit this entirely; defaults
|
|
22
86
|
* preserve backward compatibility with the v0.1.x signature.
|
|
@@ -39,6 +103,13 @@ interface InjectChaosOptions {
|
|
|
39
103
|
testInfo?: TestInfo;
|
|
40
104
|
/** Pass through to the trace reporter. */
|
|
41
105
|
traceOptions?: TraceReporterOptions;
|
|
106
|
+
/**
|
|
107
|
+
* Forwarded to `validateChaosConfig` before the config is
|
|
108
|
+
* serialized for the page. Use to relax unknown-field handling, hook
|
|
109
|
+
* deprecation events, or run custom per-`RuleType` validators. Malformed
|
|
110
|
+
* configs throw a `ChaosConfigError` synchronously from Node.
|
|
111
|
+
*/
|
|
112
|
+
validation?: ValidateChaosConfigOptions;
|
|
42
113
|
}
|
|
43
114
|
/**
|
|
44
115
|
* Inject chaos into a Playwright page. Call before `page.goto()` to ensure
|
|
@@ -68,10 +139,18 @@ declare function removeChaos(page: Page): Promise<void>;
|
|
|
68
139
|
* Returns all events emitted since chaos was injected.
|
|
69
140
|
*/
|
|
70
141
|
declare function getChaosLog(page: Page): Promise<ChaosEvent[]>;
|
|
142
|
+
/**
|
|
143
|
+
* Enable a rule group at runtime in the page-side chaos engine. Resolves once
|
|
144
|
+
* `page.evaluate` round-trips so the call is safe to await before triggering
|
|
145
|
+
* the assertion that depends on the group being live.
|
|
146
|
+
*/
|
|
147
|
+
declare function enableGroup(page: Page, name: string): Promise<void>;
|
|
148
|
+
/** Disable a rule group at runtime in the page-side chaos engine. */
|
|
149
|
+
declare function disableGroup(page: Page, name: string): Promise<void>;
|
|
71
150
|
/**
|
|
72
151
|
* Retrieve the PRNG seed from a Playwright page.
|
|
73
152
|
* Log this value on test failure to replay exact chaos decisions.
|
|
74
153
|
*/
|
|
75
154
|
declare function getChaosSeed(page: Page): Promise<number | null>;
|
|
76
155
|
|
|
77
|
-
export { type ChaosTraceAttachment, type InjectChaosOptions, type TraceReporterOptions, getChaosLog, getChaosSeed, injectChaos, removeChaos };
|
|
156
|
+
export { type ChaosTraceAttachment, type InjectChaosOptions, type InjectSWChaosResult, type SWChaosOptions, type TraceReporterOptions, disableGroup, disableSWGroup, enableGroup, enableSWGroup, getChaosLog, getChaosSeed, getSWChaosLog, getSWChaosLogFromSW, injectChaos, injectSWChaos, removeChaos, removeSWChaos };
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { ChaosEvent, ChaosConfig } from '@chaos-maker/core';
|
|
3
|
-
export { ChaosConfig, ChaosEvent, WebSocketCloseConfig, WebSocketConfig, WebSocketCorruptConfig, WebSocketCorruptionStrategy, WebSocketDelayConfig, WebSocketDirection, WebSocketDropConfig } from '@chaos-maker/core';
|
|
1
|
+
import { Page, TestInfo } from '@playwright/test';
|
|
2
|
+
import { ChaosEvent, ValidateChaosConfigOptions, ChaosConfig } from '@chaos-maker/core';
|
|
3
|
+
export { ChaosConfig, ChaosConfigError, ChaosDebugStage, ChaosEvent, ChaosLifecyclePhase, CorruptionStrategy, CustomRuleValidator, CustomValidatorMap, DebugOptions, GraphQLOperationMatcher, Logger, NetworkAbortConfig, NetworkConfig, NetworkCorruptionConfig, NetworkCorsConfig, NetworkFailureConfig, NetworkLatencyConfig, NetworkRuleMatchers, RuleType, SSECloseConfig, SSEConfig, SSECorruptConfig, SSECorruptionStrategy, SSEDelayConfig, SSEDropConfig, SSEEventTypeMatcher, ValidateChaosConfigOptions, ValidationIssue, ValidationIssueCode, WebSocketCloseConfig, WebSocketConfig, WebSocketCorruptConfig, WebSocketCorruptionStrategy, WebSocketDelayConfig, WebSocketDirection, WebSocketDropConfig, validateChaosConfig } from '@chaos-maker/core';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* Shape of the JSON attachment written to `testInfo.attachments` on teardown.
|
|
@@ -17,6 +17,70 @@ interface TraceReporterOptions {
|
|
|
17
17
|
attachmentName?: string;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
/**
|
|
21
|
+
* Options accepted by {@link injectSWChaos} / {@link removeSWChaos} /
|
|
22
|
+
* {@link getSWChaosLog}.
|
|
23
|
+
*/
|
|
24
|
+
interface SWChaosOptions {
|
|
25
|
+
/**
|
|
26
|
+
* Maximum milliseconds to wait for `navigator.serviceWorker.controller` and
|
|
27
|
+
* the SW's ack message. Defaults to `10000`. Raise for slow CI workers or
|
|
28
|
+
* SWs that do heavy work during `install`.
|
|
29
|
+
*/
|
|
30
|
+
timeoutMs?: number;
|
|
31
|
+
/**
|
|
32
|
+
* Forwarded to `validateChaosConfig` before the config is posted
|
|
33
|
+
* to the SW. Malformed configs throw a `ChaosConfigError` from Node.
|
|
34
|
+
*/
|
|
35
|
+
validation?: ValidateChaosConfigOptions;
|
|
36
|
+
}
|
|
37
|
+
interface InjectSWChaosResult {
|
|
38
|
+
/** Seed used by the PRNG inside the SW. `null` if the ack did not carry one. */
|
|
39
|
+
seed: number | null;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Configure Service-Worker chaos for a Playwright page. Call **after**
|
|
43
|
+
* `page.goto(...)` so there is a SW registration + controller to target.
|
|
44
|
+
*
|
|
45
|
+
* Requires the user's service worker to load the chaos SW bundle — typically
|
|
46
|
+
* via `importScripts('/path/to/chaos-maker-sw.js')` (classic SW) or
|
|
47
|
+
* `import { installChaosSW } from '@chaos-maker/core/sw'; installChaosSW();`
|
|
48
|
+
* (module SW).
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* await page.goto('/');
|
|
53
|
+
* await injectSWChaos(page, {
|
|
54
|
+
* network: { failures: [{ urlPattern: '/api', statusCode: 503, probability: 1 }] },
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare function injectSWChaos(page: Page, config: ChaosConfig, opts?: SWChaosOptions): Promise<InjectSWChaosResult>;
|
|
59
|
+
/**
|
|
60
|
+
* Stop Service-Worker chaos for a Playwright page. Posts `__chaosMakerStop` to
|
|
61
|
+
* the current controller and clears the page's in-memory log buffer.
|
|
62
|
+
*/
|
|
63
|
+
declare function removeSWChaos(page: Page, opts?: SWChaosOptions): Promise<void>;
|
|
64
|
+
/**
|
|
65
|
+
* Enable a rule group inside the active SW chaos engine. Posts
|
|
66
|
+
* `__chaosMakerToggleGroup` over MessageChannel and resolves only after the SW
|
|
67
|
+
* acks. Engine state and request counters are preserved (no restart).
|
|
68
|
+
*/
|
|
69
|
+
declare function enableSWGroup(page: Page, name: string, opts?: SWChaosOptions): Promise<void>;
|
|
70
|
+
/** Disable a rule group inside the active SW chaos engine. */
|
|
71
|
+
declare function disableSWGroup(page: Page, name: string, opts?: SWChaosOptions): Promise<void>;
|
|
72
|
+
/**
|
|
73
|
+
* Read the chaos event log buffered on the page side. Every event emitted by
|
|
74
|
+
* the SW is broadcast to all controlled clients and captured here.
|
|
75
|
+
*/
|
|
76
|
+
declare function getSWChaosLog(page: Page): Promise<ChaosEvent[]>;
|
|
77
|
+
/**
|
|
78
|
+
* Ask the SW for its in-memory log. Useful when debugging a race where the
|
|
79
|
+
* page-side listener missed an early broadcast (e.g. first-paint navigation).
|
|
80
|
+
* Prefer {@link getSWChaosLog} in normal assertions.
|
|
81
|
+
*/
|
|
82
|
+
declare function getSWChaosLogFromSW(page: Page, opts?: SWChaosOptions): Promise<ChaosEvent[]>;
|
|
83
|
+
|
|
20
84
|
/**
|
|
21
85
|
* Options for `injectChaos`. Most callers can omit this entirely; defaults
|
|
22
86
|
* preserve backward compatibility with the v0.1.x signature.
|
|
@@ -39,6 +103,13 @@ interface InjectChaosOptions {
|
|
|
39
103
|
testInfo?: TestInfo;
|
|
40
104
|
/** Pass through to the trace reporter. */
|
|
41
105
|
traceOptions?: TraceReporterOptions;
|
|
106
|
+
/**
|
|
107
|
+
* Forwarded to `validateChaosConfig` before the config is
|
|
108
|
+
* serialized for the page. Use to relax unknown-field handling, hook
|
|
109
|
+
* deprecation events, or run custom per-`RuleType` validators. Malformed
|
|
110
|
+
* configs throw a `ChaosConfigError` synchronously from Node.
|
|
111
|
+
*/
|
|
112
|
+
validation?: ValidateChaosConfigOptions;
|
|
42
113
|
}
|
|
43
114
|
/**
|
|
44
115
|
* Inject chaos into a Playwright page. Call before `page.goto()` to ensure
|
|
@@ -68,10 +139,18 @@ declare function removeChaos(page: Page): Promise<void>;
|
|
|
68
139
|
* Returns all events emitted since chaos was injected.
|
|
69
140
|
*/
|
|
70
141
|
declare function getChaosLog(page: Page): Promise<ChaosEvent[]>;
|
|
142
|
+
/**
|
|
143
|
+
* Enable a rule group at runtime in the page-side chaos engine. Resolves once
|
|
144
|
+
* `page.evaluate` round-trips so the call is safe to await before triggering
|
|
145
|
+
* the assertion that depends on the group being live.
|
|
146
|
+
*/
|
|
147
|
+
declare function enableGroup(page: Page, name: string): Promise<void>;
|
|
148
|
+
/** Disable a rule group at runtime in the page-side chaos engine. */
|
|
149
|
+
declare function disableGroup(page: Page, name: string): Promise<void>;
|
|
71
150
|
/**
|
|
72
151
|
* Retrieve the PRNG seed from a Playwright page.
|
|
73
152
|
* Log this value on test failure to replay exact chaos decisions.
|
|
74
153
|
*/
|
|
75
154
|
declare function getChaosSeed(page: Page): Promise<number | null>;
|
|
76
155
|
|
|
77
|
-
export { type ChaosTraceAttachment, type InjectChaosOptions, type TraceReporterOptions, getChaosLog, getChaosSeed, injectChaos, removeChaos };
|
|
156
|
+
export { type ChaosTraceAttachment, type InjectChaosOptions, type InjectSWChaosResult, type SWChaosOptions, type TraceReporterOptions, disableGroup, disableSWGroup, enableGroup, enableSWGroup, getChaosLog, getChaosSeed, getSWChaosLog, getSWChaosLogFromSW, injectChaos, injectSWChaos, removeChaos, removeSWChaos };
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,34 @@
|
|
|
1
1
|
import {
|
|
2
|
+
ChaosConfigError,
|
|
3
|
+
Logger,
|
|
4
|
+
disableGroup,
|
|
5
|
+
disableSWGroup,
|
|
6
|
+
enableGroup,
|
|
7
|
+
enableSWGroup,
|
|
2
8
|
getChaosLog,
|
|
3
9
|
getChaosSeed,
|
|
10
|
+
getSWChaosLog,
|
|
11
|
+
getSWChaosLogFromSW,
|
|
4
12
|
injectChaos,
|
|
5
|
-
|
|
6
|
-
|
|
13
|
+
injectSWChaos,
|
|
14
|
+
removeChaos,
|
|
15
|
+
removeSWChaos,
|
|
16
|
+
validateChaosConfig
|
|
17
|
+
} from "./chunk-IJDMHBUQ.js";
|
|
7
18
|
export {
|
|
19
|
+
ChaosConfigError,
|
|
20
|
+
Logger,
|
|
21
|
+
disableGroup,
|
|
22
|
+
disableSWGroup,
|
|
23
|
+
enableGroup,
|
|
24
|
+
enableSWGroup,
|
|
8
25
|
getChaosLog,
|
|
9
26
|
getChaosSeed,
|
|
27
|
+
getSWChaosLog,
|
|
28
|
+
getSWChaosLogFromSW,
|
|
10
29
|
injectChaos,
|
|
11
|
-
|
|
30
|
+
injectSWChaos,
|
|
31
|
+
removeChaos,
|
|
32
|
+
removeSWChaos,
|
|
33
|
+
validateChaosConfig
|
|
12
34
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@chaos-maker/playwright",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Playwright adapter for @chaos-maker/core — one-line chaos injection in E2E tests",
|
|
6
6
|
"keywords": [
|
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
"license": "MIT",
|
|
14
14
|
"repository": {
|
|
15
15
|
"type": "git",
|
|
16
|
-
"url": "https://github.com/
|
|
16
|
+
"url": "https://github.com/chaos-maker-dev/chaos-maker.git",
|
|
17
17
|
"directory": "packages/playwright"
|
|
18
18
|
},
|
|
19
|
-
"homepage": "https://github.com/
|
|
19
|
+
"homepage": "https://github.com/chaos-maker-dev/chaos-maker",
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=18"
|
|
22
22
|
},
|
|
@@ -49,7 +49,7 @@
|
|
|
49
49
|
"dist"
|
|
50
50
|
],
|
|
51
51
|
"dependencies": {
|
|
52
|
-
"@chaos-maker/core": "0.
|
|
52
|
+
"@chaos-maker/core": "0.5.0"
|
|
53
53
|
},
|
|
54
54
|
"peerDependencies": {
|
|
55
55
|
"@playwright/test": ">=1.40.0"
|