Presets
Presets are the recommended quick-start path. They wrap realistic scenarios - flaky backend, mobile-3g, degraded checkout - into named bundles you drop into a config:
import { injectChaos } from '@chaos-maker/playwright';
await injectChaos(page, { presets: ['mobile-3g', 'checkout-degraded'], seed: 1234,});Layer presets for compound scenarios (mobile network + checkout instability), or mix a preset with explicit rules when a single endpoint needs tighter control:
await injectChaos(page, { presets: ['mobile-3g'], network: { failures: [{ urlPattern: '/api/checkout', statusCode: 500, probability: 1 }], },});When to use presets
Section titled “When to use presets”Lead with presets for onboarding, smoke coverage, and exploratory resilience tests. Drop down to explicit configs for product-critical flows where the exact status code, selector, message direction, and probability matter.
Built-in catalog
Section titled “Built-in catalog”Pick presets the way engineers describe scenarios - flaky backend, bad mobile network, checkout degradation, lossy realtime. Layer multiple presets when a scenario spans transports.
| camelCase name | Kebab aliases | Transports | Intent |
|---|---|---|---|
slowNetwork | slow-api | network | Every request is delayed by 2000ms. Smoke-test loading states. |
flakyConnection | flaky-api, api-flaky | network | Rare aborts (5%) and latency (10% @ 3000ms) on every request. |
offlineMode | offline-mode | network | Block all requests like an offline browser. |
unstableApi | high-latency | network | 10% 500s and 20% latency (1000ms) scoped to /api/. |
mobileThreeG | mobile-3g | network | All requests get 1500ms latency plus 2% aborts. Approximates 3G round-trip; does not throttle bytes. |
checkoutDegraded | checkout-degraded | network | Payment instability scoped to /checkout and /api/payments - 30% latency at 800ms, occasional 503/500. |
degradedUi | ui | Disable buttons (20%) and hide links (10%). | |
unreliableWebSocket | websocket-instability | websocket | 10% drops, 500ms inbound delays, 5% truncations. |
unreliableEventStream | realtime-lag | sse | 5% drops, 200ms delays, 2% close-after-2s on every stream. |
aiSlowFirstChunk | ai-slow-first-chunk | fetch-stream, sse, websocket | First streamed chunk held for 3000ms. Smoke-test typing indicators and skeleton states. |
aiStreamPaused | ai-stream-paused | fetch-stream, sse, websocket | Stream stalls for 5000ms after chunk 10, then resumes. |
aiStreamTruncated | ai-stream-truncated | fetch-stream, sse, websocket | Stream cut after chunk 20. Assert partial-response handling. |
aiToolCallFails | ai-tool-call-fails | fetch-stream | Chunks carrying tool/function-call wire markers (tool_calls, tool_use, function_call) arrive as malformed JSON, tagged ai:tool-call-failed. |
aiRetryLoop | ai-retry-loop | network | 429 on the first two matching requests, success from the third. Exercises backoff and retry UI. |
aiReconnectAfterDrop | ai-reconnect-after-drop | fetch-stream, sse | Stream closes early (after chunk 5, or 3s for SSE). Assert the app reconnects or resumes. |
aiMobileInterrupt | ai-mobile-interrupt | fetch-stream, sse, ui | User backgrounds the app mid-generation: tab reports hidden for 3s (from 1s) while the stream drops (after chunk 10, or 4s for SSE). Assert visibility pause logic plus stream recovery. |
AI streaming presets
Section titled “AI streaming presets”The ai* presets reproduce the failure modes chat and assistant interfaces hit in production. The delay, pause, and truncate presets are generated from the same compiler the ai shorthand uses, so presets: ['ai-slow-first-chunk'] and ai: { firstChunkDelayMs: 3000 } produce identical rules; the preset form composes with other presets and needs no extra config. All of them apply to every URL, so scope your test to the chat call or pair the preset with app-side assertions that tolerate chaos on secondary requests.
await injectChaos(page, { presets: ['ai-slow-first-chunk'], seed: 42,});aiToolCallFailstargets chunk CONTENT: only chunks whose decoded text matches the tool-call marker pattern are corrupted, so prose renders normally while the structured payload breaks mid-stream. The corruption event carriesphase: 'ai:tool-call-failed'for report tooling.aiRetryLoopusesfirstN: 2counting, so exactly the first two matching requests fail regardless of how many retries the app makes.aiMobileInterruptis the first preset to combine transport rules with a human-interaction trigger: thetabHiddentrigger flipsdocument.visibilityStatewhile close rules drop the stream. Trigger merging is per trigger, so setting your ownuserInteraction.tabHiddenalongside the preset overrides the preset’s timing.- For markdown/code-fence breakage, use a stream replay fixture with a split mutation instead of a preset; splitting needs a captured fixture.
Suggested assertions
Section titled “Suggested assertions”The presets fire deterministically given a fixed seed. Existence-check, not rate, keeps tests stable across browsers:
mobile-3g- observed elapsed time on a fetch should exceed 1000ms (well below the 1500ms target).checkout-degraded- every rule on/checkoutand/api/paymentsis probabilistic (30% latency, 5% / 10% failure), so a single request is not enough to assert against. Issue a small fixed batch (e.g. 20 sequential requests across/checkoutand/api/payments) under a fixedseed, then callgetChaosLog()and assert at least one event whosedetail.urlmatches/checkoutor/api/payments(any ofnetwork:latency,network:failure).unreliableWebSocket- chaos log should contain at least onewebsocket:delayevent withapplied: true.unreliableEventStream- chaos log should contain at least onesse:delayorsse:closeevent.aiSlowFirstChunk- first-chunk latency measured from before the fetch call should exceed 3000ms, and the log should contain an appliedfetch-stream:chunk-delayedevent.aiToolCallFails- the log should contain an appliedfetch-stream:chunk-corruptedevent withdetail.phase === 'ai:tool-call-failed'.aiRetryLoop- exactly two appliednetwork:failureevents withstatusCode: 429, then a clean response.aiMobileInterrupt- the log should contain an appliedui:visibilityevent withdetail.phase === 'user:tab-hidden'plus an appliedfetch-stream:truncated(orsse:close) event.
Kebab-case aliases (slow-api, flaky-api, api-flaky, offline-mode, high-latency, mobile-3g, checkout-degraded, websocket-instability, realtime-lag, and the ai-* names) are registry-only. They resolve via presets: ['mobile-3g'] and new PresetRegistry().get('mobile-3g'). They are NOT keys on the legacy presets record export - presets['mobile-3g'] is undefined by design. Use the camelCase key (presets.mobileThreeG) when reading from the record.
Custom presets
Section titled “Custom presets”Register your own bundle inline via customPresets. Names collide fail-fast against built-ins and against each other.
await injectChaos(page, { customPresets: { 'team-flow': { network: { failures: [{ urlPattern: '/checkout', statusCode: 503, probability: 1 }], }, }, }, presets: ['team-flow'],});Custom preset values may carry only rule arrays plus the optional groups field. presets, customPresets, seed, and debug are rejected at validation. Dependency chains are out of scope.
Builder helper
Section titled “Builder helper”import { ChaosConfigBuilder } from '@chaos-maker/core';
const config = new ChaosConfigBuilder() .usePreset('slow-api') .failRequests('/api/checkout', 500, 1) .build();.usePreset() silently dedupes within the builder while preserving insertion order. Empty / whitespace-only names throw, matching the schema and registry rules.
Validation errors
Section titled “Validation errors”Every failure mode surfaces as a ChaosConfigError at construction time, never at runtime:
- Unknown preset name in
presets[]- message names the offending name and lists the registered ones. - Custom preset value carrying
presets,customPresets,seed, ordebug- strict-key rejection. - Empty or whitespace-only preset name (in either
presets[]orcustomPresetskeys). - Custom preset whose name collides with a built-in (
slowNetwork,slow-api, etc.) or with another custom preset. - Group name collision between a preset’s
groupsand the user’sgroups. Workaround: copy the preset locally viacustomPresetsand rename the colliding group.
Mutability
Section titled “Mutability”Built-in preset configs are deep-frozen. Attempts to mutate them via presets.slowNetwork or registry.get(...) throw in strict mode (and are no-ops in sloppy mode). Custom presets passed via customPresets are NOT frozen on input - your own preset literals stay mutable in your code. The engine takes a deep clone at expansion time, so any post-construction tweaks you make are not observed.
Profiles vs presets
Section titled “Profiles vs presets”Presets are the primitive resilience bundles. Scenario profiles are the layer above - one named entry that composes presets, seed, debug, and rule slices into a single identifier you reach for from many tests. See the scenario profiles concept for the full surface, including the runtime override block applied at the inject site.
Legacy spread
Section titled “Legacy spread”import { presets } from '@chaos-maker/core';
await injectChaos(page, { ...presets.unstableApi, seed: 42,});Still supported for migration. The seed belongs at the root config level, so it can be added to any preset before injection. Prefer the declarative presets: field for new code.
Sharing targeting across preset rules
Section titled “Sharing targeting across preset rules”When a preset’s rules all hit the same API surface, register the shared targeting on the top-level matchers registry and reference it from each rule with matcher: 'name'. Named matcher resolution runs after preset expansion, so rules brought in by a preset can reference top-level matchers without duplicating matcher fields.