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.

e2e/network-offline.e2e.ts
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.

PresetLatencyJitterDownUpLoss
edge400 ms25 ms240 kbps200 kbps0
3g100 ms10 ms780 kbps330 kbps0
4g170 ms15 ms9 Mbps9 Mbps0
lte50 ms5 ms50 Mbps10 Mbps0
wifi2 ms040 Mbps33 Mbps0
slow2000 ms100 ms400 kbps400 kbps0
lossy500 ms50 ms1 Mbps1 Mbps10%

{ 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)
MemberDoes
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 simulatorAndroid emulator
Speed, delay, offlineper-app proxy (a dylib routes the app's NSURLSession traffic through the lease's shaping proxy) and a reachability signalnative: adb emu network, airplane mode
Loss, faultsthe proxythe 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

warden.config.ts
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 reset

warden 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 NSURLSession traffic only. Raw sockets, NWConnection and CFStream bypass 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 status fault 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.

On this page