Platforms
iOS tests
Bind an iOS Simulator to the run, then run the same tests, agent steps, invariants and matrix as on Android.
An iOS test is the same file as an Android test. screen finds elements on the simulator's
screen, expect waits for checks, and an invariant compares your backend with Stripe. danwo boots
the simulator, routes its traffic through the proxy, and installs your build.
A test on a simulator
import type { DeviceFixtures } from "@danwo/device"
import { type StripeFixtures, test as base } from "@danwo/sdk"
const test = base.extend<StripeFixtures & DeviceFixtures>()
test("buys 500 credits", async ({ screen, expect, device }) => {
await screen.getByTestId("buy").tap()
/* Checkout opens in a Safari view; the same locators read the web page. */
expect((await device.foreground()).browser).toBe(true)
await screen.getByLabel("Email").fill("[email protected]")
await screen.getByRole("button", { name: "Pay $5.00" }).tap()
await expect(screen.getByTestId("credits")).toHaveText("Credits: 500")
})getByTestId matches React Native's testID, which iOS reports as the accessibility identifier.
A tap the keyboard covers, such as Pay under a field just filled, puts the keyboard away first.
As on Android, a tap waits for its element to be enabled and finds it again on the latest screen.
Bind the simulator in the config
withIosDevice wraps your environment and binds a simulator to it:
return withIosDevice(environment, {
worker: workerIndex,
app: { bundleId: "com.danwo.shopmobile", app: build.app, scheme: "danwoshop" },
})The full file, which runs on Android or iOS by DANWO_PLATFORM, is in
examples/shop-mobile. For each worker, danwo:
- finds its simulator by name, or creates it, and boots it headless;
- trusts the run's certificate in the simulator's keychain;
- sets the simulator's own proxy to danwo's;
- installs the
.app.
| Option | What it does |
|---|---|
name | the simulator's name; default danwo iPhone |
runtime | the iOS to create it on: iOS 26.5, 26.5 or an identifier |
deviceType | the device to create: iPhone 17 Pro; default the newest iPhone Pro |
udid | uses a simulator that already exists, as it is |
worker | gives each worker its own simulator: danwo iPhone 2, and so on |
remove | deletes a simulator this run created when it closes |
app.launchArgs | arguments the app is launched with |
app.env | environment the app is launched with |
app.permissions | simctl privacy services to grant before each launch |
Pass blocked: IOS_BACKGROUND_HOSTS from @danwo/device to your services. It blocks the
simulator's own background traffic (Safari's safe browsing, Apple's services), so the matrix never
faults it.
Run it
DANWO_PLATFORM=ios npx @danwo/cli test ✓ tests/agent-buy.danwo.ts › buys 500 credits when asked in plain words 35842 ms, worker 0
✓ tests/buy-credits.danwo.ts › buys 500 credits through Stripe Checkout in the in-app browser 11239 ms, worker 0
2 passed
cache: 3 replayed · 0 relocated · 0 handed off · 0 missedEvery attempt starts the app afresh: it is stopped, its data container emptied, and launched again. Agent steps record per platform, so steps recorded on Android don't replay on iOS: record them once on each.
What a test can do
| Fixture | On iOS |
|---|---|
screen | getByTestId, getByLabel, getByText, getByRole (no headings); tap, fill |
expect | the same checks as the web, including toHaveScreenshot |
app.open | opens a deep link; with no path, restarts the app |
device | foreground(), back(), openLink(url), logs(), swipe(), dismissKeyboard() |
agent | act, assert, extract, waitFor, with device tools |
back() taps the screen's own back or close control, since an iPhone has no Back button.
swipe() takes the same options as on Android.
Run the matrix on several simulators
iosWorkers picks a worker count from the Mac: a simulator takes about 2 GB and two cores.
DANWO_PLATFORM=ios npx @danwo/cli matrix tests/buy-credits.danwo.ts --workers 2The example shop's matrix, on a 12-core, 32 GB Apple silicon Mac:
| Matrix | Simulators | Wall time | Median trial |
|---|---|---|---|
buy-credits, 23 runs | 1 | 370 s | 13.4 s |
agent-buy, 23 runs, replayed | 2 | 298 s | 13.4 s |