Network
Test the app offline, on 3G, on a flapping link or with one host failing, from a spec or a suite.
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 to the suite's baseline when the test 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 (edge, 3g, 4g, lte, wifi, slow, lossy: values) or { kind: "custom", latencyMs?, jitterMs?, downKbps?, upKbps?, lossPct? }.
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 |
On iOS a condition reaches only an app launched with warden's network dylib: declare conditions.network for the suite, or pass { relaunch: true }. A running app without it fails E_NETWORK_RELAUNCH (how it reaches the app).
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.
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. On iOS, declaring it launches every app with the dylib.
Practice
- 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.
- Set
offlinebeforeapp.open()to test a cold start offline.
Platform details, limitations and warden ui network: Network on a simulator.
Device conditions
Test the app offline, on 3G, with one host failing, at a moving GPS fix, two weeks in the future, with a failed Face ID, a denied permission, a QR code in the camera, another locale or the largest text size, from a spec or a suite.
Location
Test geofences, run trackers and location-gated screens with a GPS point, a route or a GPX file played at a speed, from a spec or a suite.