[ Documentation ]
Detox Driver

Detox Driver

Drop-in replacement for import { by, element, expect, waitFor, device } from 'detox'. Your existing Detox specs run against rnx’s canvas with no native build.

Running your suite

You don’t wire this up by hand. rnx detox runs your own Jest, forcing a preset that remaps ^detox$ to the rnx driver, so import ... from 'detox' resolves to rnx without touching your test files:

terminal
rnx detox # discover e2e/ (or test/e2e, detox/) and run
rnx detox e2e/login.test.ts # run specific files
rnx detox init # scaffold rnx-detox.config.cjs + e2e/example.test.ts
rnx detox --headed # watch it run in a visible browser
rnx detox -t "login" # jest --testNamePattern (also --grep)

It launches a simulator for the run (pass --no-launch to attach to one you already have) and picks up config from --config <path>, else rnx-detox.config.cjs / jest.config.cjs / jest.config.js, else a generated temp config. Your Jest version, transforms, and setup files are preserved.

Matchers

by.id(testID) // match by testID
by.text(text) // match by text content
by.label(label) // match by accessibility label
by.role(role) // match by accessibility role
by.type(componentType) // match by component type

Element actions

element(matcher).tap(point?) // optional {x,y} offset
element(matcher).multiTap(times)
element(matcher).longPress(duration?) // default 1000ms
element(matcher).longPressAndDrag(duration, startX, startY, target, endX, endY)
element(matcher).typeText(text)
element(matcher).replaceText(text)
element(matcher).clearText()
element(matcher).scroll(pixels, direction) // 'up' | 'down' | 'left' | 'right'
element(matcher).scrollTo(edge) // 'top' | 'bottom' | 'left' | 'right'
element(matcher).swipe(direction, speed?, percentage?)
element(matcher).pinch(scale, speed?, angle?) // angle in radians
element(matcher).rotate(radians, speed?)
element(matcher).takeScreenshot(name)
element(matcher).getAttributes()
element(matcher).atIndex(i) // rnx resolves one node per testID

Assertions

expect(element(matcher)).toExist()
expect(element(matcher)).toBeVisible() // 75% visible-area threshold
expect(element(matcher)).toBeNotVisible()
expect(element(matcher)).toHaveText(text) // exact or substring
expect(element(matcher)).toHaveId(id)
expect(element(matcher)).toHaveLabel(label)
expect(element(matcher)).toHaveAccessibilityRole(role)
expect(element(matcher)).toBeEnabled()
expect(element(matcher)).toHaveValue(value)
// all support the .not prefix

toBeFocused, toHaveSliderPosition, and toHaveToggleValue exist for API compatibility but currently only assert existence.

waitFor

waitFor(element(matcher)).toExist().withTimeout(ms)
waitFor(element(matcher)).toBeVisible().withTimeout(ms)
waitFor(element(matcher)).toBeNotVisible().withTimeout(ms)
waitFor(element(matcher)).toHaveText(text).withTimeout(ms)
waitFor(element(matcher)).toHaveValue(value).withTimeout(ms)
waitFor(element(matcher)).whileElement(by.id('list')).scroll(200, 'down')

The waitFor chain intentionally exposes a narrower set of conditions than expect.

Device

device.launchApp(opts?)
device.reloadReactNative()
device.terminateApp()
device.openURL({ url })
device.takeScreenshot(name)
device.takeScreenshotFast(name) // no stability wait
device.startBridgeRecording(opts?)
device.stopAndSaveBridgeRecording(outPath)
device.getPlatform() // 'ios' | 'android'
device.pressBack() // android only

No-ops kept for source compatibility (there is no native process in the browser): installApp, uninstallApp, shake, setLocation, setURLBlacklist, sendToHome.