@mizchi/playwright-faults 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/src/types.ts ADDED
@@ -0,0 +1,196 @@
1
+ /**
2
+ * Public types for @mizchi/playwright-faults. Three layers of fault
3
+ * injection share a few types (UrlMatcher, FaultStats shapes); each layer
4
+ * has its own discriminated-union `Action` type.
5
+ *
6
+ * 1. Network — `FaultRule` / `Fault` (Playwright `route()` interception)
7
+ * 2. Page lifecycle — `LifecycleFault` / `LifecycleAction`
8
+ * (Playwright `Page` / `BrowserContext` / CDP at named stages)
9
+ * 3. JS runtime — `RuntimeFault` / `RuntimeAction`
10
+ * (Playwright `addInitScript` per page nav)
11
+ */
12
+
13
+ /** Anything that can match a URL. String inputs are compiled with `new RegExp`. */
14
+ export type UrlMatcher = string | RegExp;
15
+
16
+ /**
17
+ * Minimal RNG contract used by the runtime-fault compiler. Caller passes
18
+ * any object with `next(): number` returning [0, 1). Caller-provided so
19
+ * playwright-faults stays seed-agnostic.
20
+ */
21
+ export interface Rng {
22
+ next(): number;
23
+ }
24
+
25
+ // =====================================================================
26
+ // 1. Network-level fault injection (Playwright route())
27
+ // =====================================================================
28
+
29
+ /** What to do when a FaultRule matches a request. */
30
+ export type Fault =
31
+ | { kind: "abort"; errorCode?: string }
32
+ | { kind: "status"; status: number; body?: string; contentType?: string }
33
+ | { kind: "delay"; ms: number };
34
+
35
+ export interface FaultRule {
36
+ /** Optional human-readable name used in stats. */
37
+ name?: string;
38
+ /** URL matcher — a regex literal or a regex string. */
39
+ urlPattern: UrlMatcher;
40
+ /** HTTP methods to match (case-insensitive). Empty = all methods. */
41
+ methods?: string[];
42
+ /** Action taken on a match. */
43
+ fault: Fault;
44
+ /** 0..1, default 1.0. Uses the caller-provided RNG. */
45
+ probability?: number;
46
+ }
47
+
48
+ /** Per-rule stats for fault injection, emitted on the final report. */
49
+ export interface FaultInjectionStats {
50
+ rule: string;
51
+ matched: number;
52
+ injected: number;
53
+ }
54
+
55
+ // =====================================================================
56
+ // 2. Page-lifecycle fault injection (Playwright Page / BrowserContext)
57
+ // =====================================================================
58
+
59
+ /**
60
+ * When during a page's lifecycle a `LifecycleFault` fires.
61
+ *
62
+ * - `beforeNavigation`: before `page.goto` — for CDP-level conditions that need to
63
+ * apply during the load itself (CPU throttle, virtual time).
64
+ * - `afterLoad`: right after navigation completes, before any chaos actions or
65
+ * `afterLoad` invariants run — for in-page mutations (storage clears, tamper).
66
+ * - `beforeActions`: after `afterLoad` invariants pass, before the first chaos
67
+ * action — for one-shot evictions that should not affect invariants but should
68
+ * precede user simulation (Service Worker cache eviction).
69
+ * - `betweenActions`: after every chaos action — for sustained pressure faults
70
+ * that need re-application across the action loop.
71
+ */
72
+ export type LifecycleStage =
73
+ | "beforeNavigation"
74
+ | "afterLoad"
75
+ | "beforeActions"
76
+ | "betweenActions";
77
+
78
+ /** Where a `clear-storage` / `tamper-storage` action targets. */
79
+ export type StorageScope = "localStorage" | "sessionStorage" | "cookies" | "indexedDB";
80
+
81
+ /**
82
+ * What a lifecycle fault does when it fires.
83
+ *
84
+ * Distinct from network-side `Fault` (which is request-scoped). These are
85
+ * page-scoped client-side perturbations applied via the Playwright Page /
86
+ * BrowserContext / CDP session.
87
+ */
88
+ export type LifecycleAction =
89
+ /**
90
+ * Apply CPU throttling via CDP `Emulation.setCPUThrottlingRate`.
91
+ * `rate` is a multiplier ≥ 1 (1 = no throttle, 4 = ~4× slower).
92
+ */
93
+ | { kind: "cpu-throttle"; rate: number }
94
+ /** Wipe one or more storage scopes. */
95
+ | { kind: "clear-storage"; scopes: StorageScope[] }
96
+ /**
97
+ * Drop entries from the Service Worker `caches` API. When `cacheNames` is
98
+ * omitted, every cache is dropped.
99
+ */
100
+ | { kind: "evict-cache"; cacheNames?: string[] }
101
+ /**
102
+ * Set a single key/value in `localStorage` or `sessionStorage`. Useful for
103
+ * forcing a logged-in app into "stale auth token" state and similar
104
+ * targeted-corruption scenarios.
105
+ */
106
+ | {
107
+ kind: "tamper-storage";
108
+ scope: "localStorage" | "sessionStorage";
109
+ key: string;
110
+ value: string;
111
+ };
112
+
113
+ /**
114
+ * Page-level fault injected at a specific lifecycle stage. Network-level faults
115
+ * stay on `FaultRule` (URL-matched, applied via Playwright `route()`).
116
+ */
117
+ export interface LifecycleFault {
118
+ /** Optional human-readable name used in stats. Auto-derived when omitted. */
119
+ name?: string;
120
+ /** When during the page lifecycle this fault fires. */
121
+ when: LifecycleStage;
122
+ /**
123
+ * Restrict to URLs matching this matcher. Omit to apply on every page. For
124
+ * `beforeNavigation` faults the about-to-be-navigated URL is matched.
125
+ */
126
+ urlPattern?: UrlMatcher;
127
+ /** 0..1, default 1.0. Uses the caller-provided RNG. */
128
+ probability?: number;
129
+ /** What to do when the fault fires. */
130
+ action: LifecycleAction;
131
+ }
132
+
133
+ /** Per-fault stats emitted on the final report. */
134
+ export interface LifecycleFaultStats {
135
+ /** `name` from the `LifecycleFault`, or an auto-derived label. */
136
+ name: string;
137
+ /** Pages whose URL matched (regardless of probability). */
138
+ matched: number;
139
+ /** Pages where the fault actually fired (after the probability roll). */
140
+ fired: number;
141
+ /** Pages where the fault threw while firing. */
142
+ errored: number;
143
+ }
144
+
145
+ // =====================================================================
146
+ // 3. JS-runtime fault injection (Playwright addInitScript)
147
+ // =====================================================================
148
+
149
+ /**
150
+ * What a runtime fault does when it fires. Each kind is a persistent
151
+ * monkey-patch installed in every page via `addInitScript`.
152
+ */
153
+ export type RuntimeAction =
154
+ /**
155
+ * Reject `window.fetch` calls before any network round-trip. Different
156
+ * from a network `Fault` of kind `"abort"`: `flaky-fetch` rejects the
157
+ * Promise client-side with a TypeError, simulating "Failed to fetch" /
158
+ * Service Worker reject / DNS failure.
159
+ */
160
+ | { kind: "flaky-fetch"; rejectionMessage?: string }
161
+ /**
162
+ * Skew `Date.now()` / `performance.now()` (and the no-arg `Date`
163
+ * constructor) forward by `skewMs`. Useful for forcing token-expiry,
164
+ * cache-bust, and "clock drift" code paths.
165
+ */
166
+ | { kind: "clock-skew"; skewMs: number };
167
+
168
+ /**
169
+ * Page-level JS-runtime fault. Installed via `addInitScript` on every page
170
+ * navigation. Distinct from `FaultRule` (request-scoped) and
171
+ * `LifecycleFault` (one-shot at named stages of a page visit).
172
+ */
173
+ export interface RuntimeFault {
174
+ /** Optional human-readable name used in stats. Auto-derived when omitted. */
175
+ name?: string;
176
+ /**
177
+ * Restrict to pages whose URL matches this matcher. Omitted = applies on
178
+ * every page. The check happens inside the page (against `location.href`),
179
+ * so the matcher must be JSON-serializable (string regex or RegExp literal).
180
+ */
181
+ urlPattern?: UrlMatcher;
182
+ /** 0..1, default 1.0. Rolled per call against an in-page seeded RNG. */
183
+ probability?: number;
184
+ /** What to do when the fault fires. */
185
+ action: RuntimeAction;
186
+ }
187
+
188
+ /** Per-fault stats for runtime fault injection, emitted on the final report. */
189
+ export interface RuntimeFaultStats {
190
+ /** `name` from the `RuntimeFault`, or an auto-derived label. */
191
+ rule: string;
192
+ /** Times the fault was tested (URL matched, probability about to roll). */
193
+ matched: number;
194
+ /** Times the fault actually fired. */
195
+ fired: number;
196
+ }