Network
Take the device offline, slow it to 3G, flap the connection or fail one host, from a spec, a suite or warden ui.
warden ui network set offline, edge, online: the app's own requests are shaped, nothing else on the Mac slows down.device.network shapes what the app's own requests see: offline, a preset speed, a flapping link or one host that fails. The rest of the Mac's traffic is untouched, and the network is back online when the lease ends.
import { expect, test } from "@delacour/warden-e2e";
test("cold start offline shows the banner, going online recovers the fetch", async ({ app, device, screen }) => {
await device.network.set("offline"); // before the app opens: a cold start offline
await app.open();
await expect(screen.getByTestId("conditions-offline-banner")).toBeVisible({ timeout: 15_000 });
await device.network.set("online");
await expect(screen.getByTestId("conditions-offline-banner")).toBeHidden({ timeout: 15_000 });
});Set a condition
device.network.set(condition) takes "online", "offline", a preset or a custom shape.
| Preset | Latency | Jitter | Down | Up | Loss |
|---|---|---|---|---|---|
edge | 400 ms | 25 ms | 240 kbps | 200 kbps | 0 |
3g | 100 ms | 10 ms | 780 kbps | 330 kbps | 0 |
4g | 170 ms | 15 ms | 9 Mbps | 9 Mbps | 0 |
lte | 50 ms | 5 ms | 50 Mbps | 10 Mbps | 0 |
wifi | 2 ms | 0 | 40 Mbps | 33 Mbps | 0 |
slow | 2000 ms | 100 ms | 400 kbps | 400 kbps | 0 |
lossy | 500 ms | 50 ms | 1 Mbps | 1 Mbps | 10% |
{ kind: "custom", latencyMs?, jitterMs?, downKbps?, upKbps?, lossPct? } sets your own.
await device.network.set("3g");
await device.network.during("offline", async () => {
// the previous condition is back afterwards, also when this throws
});
await device.network.reset(); // back to the suite's conditions.network (online without one)| Member | Does |
|---|---|
set(c, { relaunch? }) | change the network. A running flap stops first |
during(c, fn) | run fn under c, then restore |
flap({ up, down, jitterPct?, seed?, base? }) | alternate base (default online) and offline until stop() or the end of the test; returns { stop(), done, status() } |
fault(rule | rules) | per-host faults; [] clears |
status() | { condition, faults, flapping } |
reset() | stop the flap, clear faults, back to the baseline |
Flapping
const flap = await device.network.flap({ up: "1s", down: "1s" });
// ... exercise the retry path ...
await flap.stop();A fixed seed replays the same sequence. A flap and faults do not combine: reset one before setting the other.
Faults
A fault applies to every request whose host matches a glob.
await device.network.fault({ match: "api.example.com", action: { kind: "reset" } });Actions: { kind: "status", code }, { kind: "reset" }, { kind: "delay", ms }, { kind: "blackhole" }.
HTTPS requests are tunnelled without being opened, so a status fault cannot answer one: an https:// request matching it is reset, and fault() returns a note saying so. Use reset (or delay / blackhole) for HTTPS hosts and status for plain HTTP.
How it reaches the app
| iOS simulator | Android emulator | |
|---|---|---|
| Speed, delay, offline | per-app proxy (a dylib routes the app's NSURLSession traffic through the lease's shaping proxy) and a reachability signal | native: adb emu network, airplane mode |
| Loss, faults | the proxy | the proxy, through the system http_proxy |
A simulator shares the Mac's network, so on iOS a condition reaches only an app that was launched with warden's network dylib. A running app without it fails E_NETWORK_RELAUNCH. Either declare conditions.network for the suite (suite baseline), so every launch carries the dylib, or pass { relaunch: true }.
The shaping proxy binds loopback, and only while it shapes traffic.
In a suite
conditions: { network: "online" },network takes "online", "offline", a preset or { latencyMs, jitterMs, downKbps, upKbps, lossPct }. Every spec starts from it, and reset() returns to it.
By hand
warden ui network set 3g
warden ui network flap --up 10s --down 3s
warden ui network fault api.example.com reset
warden ui network status
warden ui network resetwarden ui network log is the request log of the dev client. See warden ui for every flag, including --relaunch.
Limitations
- iOS shapes the app's
NSURLSessiontraffic only. Raw sockets,NWConnectionandCFStreambypass the proxy. - Android loss and faults go through the system
http_proxy: an app that ignores it (a raw socket,Proxy.NO_PROXY) is not covered. Speed, delay and offline are native and reach everything. - A
statusfault cannot answer an HTTPS request (it is reset). - Shaping applies to new traffic through the proxy, and an open connection keeps its cost: after warming a connection, a 3g request pays one round trip (the preset's latency), not the three or four of a cold connection. Assert an ordering with a margin, not an absolute number.
Device conditions
Change the network, GPS location, clock, biometrics, permissions, camera feed, locale, accessibility settings and battery of a simulator or emulator from a spec, a suite or warden ui, and have it undone on release.
Location
Put the device at a GPS point, play a route or a GPX file at a speed, or run a bundled scenario, from a spec, a suite or warden ui.