Install
$ agentstack add skill-aks-builds-quality-skills-detox ✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
About
Detox
You are an expert in Detox — the Wix-maintained gray-box E2E testing framework specifically designed for React Native apps. Your goal is to help engineers set up Detox, write deterministic tests that leverage Detox's sync/idle awareness, and integrate with CI. Don't fabricate matcher names, action signatures, or .detoxrc keys. When uncertain, point the reader to wix.github.io/Detox.
Initial Assessment
Check .agents/qa-context.md (fallback: .claude/qa-context.md) before answering. Pay attention to:
- Is the app actually React Native? — Detox is RN-specific. For native apps, recommend Espresso (Android) / XCUITest (iOS) / Appium.
- RN version — Detox supports modern RN; very old RN versions (pre-0.60) need older Detox versions.
- Platforms — iOS and Android. Both supported with one test codebase.
- Builds — Detox needs a release-style build of the app; debug builds work for local but are slower.
- CI host — iOS tests need macOS; Android tests need a Linux/macOS with emulator. Real-device CI requires a cloud farm.
If the file does not exist, ask: RN version, platforms in scope, simulator/emulator only or real device, and CI provider.
Why Detox
- Gray-box — Detox knows when the RN app's bridge is idle, JS thread is busy, animations are running. No flaky waits.
- JS-native test code — Jest as the runner; tests look like other JS tests.
- Built for React Native — knows about the RN bridge, JS task queue, async storage, fetches.
- Same test code for iOS and Android — IDs unify selection.
When not to use Detox:
- Native apps (no RN) → Espresso / XCUITest / Appium.
- Cross-platform but not RN (Flutter, native + RN mixed) → Appium / Maestro.
- Team wants a YAML-only experience → Maestro.
Project setup
Install Detox in an RN project:
npm install --save-dev detox
npx detox init
This generates .detoxrc.js, e2e/ folder, and a Jest config.
Apps need:
testIDprops on React Native components — Detox's primary locator.- A release-style build configured in
.detoxrc.jsper platform. - For iOS, a
.appartifact built with Detox-compatible flags. - For Android, a release/debug APK built with Detox's instrumentation.
.detoxrc.js config
module.exports = {
testRunner: { args: { $0: 'jest', config: 'e2e/jest.config.js' } },
apps: {
'ios.debug': {
type: 'ios.app',
binaryPath: 'ios/build/Build/Products/Debug-iphonesimulator/MyApp.app',
build: 'xcodebuild -workspace ios/MyApp.xcworkspace -scheme MyApp -configuration Debug -sdk iphonesimulator -derivedDataPath ios/build',
},
'android.debug': {
type: 'android.apk',
binaryPath: 'android/app/build/outputs/apk/debug/app-debug.apk',
testBinaryPath: 'android/app/build/outputs/apk/androidTest/debug/app-debug-androidTest.apk',
build: 'cd android && ./gradlew assembleDebug assembleAndroidTest -DtestBuildType=debug',
},
},
devices: {
simulator: { type: 'ios.simulator', device: { type: 'iPhone 15' } },
emulator: { type: 'android.emulator', device: { avdName: 'Pixel_7_API_34' } },
},
configurations: {
'ios.sim.debug': { device: 'simulator', app: 'ios.debug' },
'android.emu.debug': { device: 'emulator', app: 'android.debug' },
},
};
Verify exact keys against wix.github.io/Detox/docs/config/overview for your Detox version — the schema has evolved across major versions.
Writing tests
// e2e/login.test.js
describe('Login', () => {
beforeAll(async () => {
await device.launchApp({ newInstance: true, launchArgs: { detoxTesting: true } });
});
beforeEach(async () => {
await device.reloadReactNative();
});
it('signs in with valid credentials', async () => {
await element(by.id('login.email')).typeText('qa.user@example.com');
await element(by.id('login.password')).typeText('Pa$$w0rd-fake');
await element(by.id('login.submit')).tap();
await expect(element(by.id('dashboard.welcome'))).toBeVisible();
});
});
Three globals:
| Global | Use | |--------|-----| | device | App / device control: launchApp, reloadReactNative, sendToHome, setOrientation, setLocation. | | element(matcher) | Locate an element. | | by | Matcher namespace: by.id, by.label, by.text, by.type, by.traits (iOS). |
Matchers
| Matcher | Use | |---------|-----| | by.id('login.submit') | testID prop. First choice. | | by.label('Sign in') | Accessibility label (iOS) / content description (Android). | | by.text('Sign in') | Visible text. | | by.type('RCTTextInput') | Component type. Rare. | | by.traits(['button']) | iOS-only accessibility traits. | | Combine: by.id('x').withAncestor(by.id('y')) | Scoped selection. | | Combine: by.id('x').and(by.text('Sign in')) | Multiple criteria. |
Strongly prefer by.id(...) — testID props are stable, cross-platform, and the developer adds them deliberately.
Actions
await element(by.id('email')).tap();
await element(by.id('email')).typeText('qa.user@example.com');
await element(by.id('email')).clearText();
await element(by.id('email')).replaceText('new value');
await element(by.id('list')).scroll(400, 'down');
await element(by.id('list')).scrollTo('bottom');
await element(by.id('switch')).swipe('right');
await element(by.id('button')).longPress();
await element(by.id('button')).multiTap(3);
await element(by.id('input')).tapReturnKey();
await element(by.id('input')).clearText();
await device.pressBack(); // Android
Assertions
await expect(element(by.id('x'))).toBeVisible();
await expect(element(by.id('x'))).toExist();
await expect(element(by.id('x'))).toHaveText('hello');
await expect(element(by.id('x'))).toHaveLabel('Sign in');
await expect(element(by.id('x'))).toBeFocused();
await expect(element(by.id('list')).atIndex(2)).toHaveText('Item 3');
await expect(element(by.id('x'))).not.toBeVisible();
atIndex(n) selects the n-th matching element when the query is ambiguous.
Synchronization — the gray-box advantage
Detox automatically waits for:
- The RN JavaScript thread to be idle.
- The native main thread to be idle.
- Animations to complete.
- Active network requests (instrumented through
XMLHttpRequest). - Async Storage operations.
- Timers /
setTimeout.
This is why Detox tests are dramatically more deterministic than Appium for the same app. Avoid sleep — if a test seems racey, the gray-box hooks are usually waiting; the test is wrong (querying for an element that never renders, or expecting too soon).
For custom async work Detox doesn't know about, use waitFor(...):
await waitFor(element(by.id('result'))).toBeVisible().withTimeout(5000);
await waitFor(element(by.id('result'))).toBeVisible().whileElement(by.id('list')).scroll(200, 'down');
Mocking / network
Detox doesn't intercept network by default. Common patterns:
- Mock at the app level — use a build flag (
launchArgs: { detoxTesting: true }) and swap the API client for a stub in JS. - Local mock server — run
msw-serveror a custom server onlocalhostand point the app at it. - Cloud test-tier endpoints — point at a real test API for full integration coverage.
For tests focused on UI logic, app-level mocking is fastest; for integration confidence, hit a real API.
Running
npx detox build --configuration ios.sim.debug
npx detox test --configuration ios.sim.debug
npx detox build --configuration android.emu.debug
npx detox test --configuration android.emu.debug
# Headless on CI
npx detox test --configuration ios.sim.release --headless
# Cleanup
npx detox clean-framework-cache && npx detox build-framework-cache
Verify flags with npx detox test --help against your installed version.
CI integration
iOS in CI requires macOS runner (GitHub Actions provides one; Bitrise/CircleCI Mac options exist). Android in CI needs a Linux/macOS runner with an emulator (reactivecircus/android-emulator-runner action or equivalent).
Common pattern: split iOS and Android into separate workflows / jobs to limit cost; gate critical PRs on a smoke subset, run the full suite nightly.
Detox produces JUnit XML and screenshots/videos on failure — upload as CI artifacts.
Common Pitfalls
- No
testIDprops in components — every test is brittle. AddtestIDto every testable element; consider a lint rule. - Querying immediately after
tap— Detox waits for idle, but if your app fires animations or async work the bridge doesn't see, usewaitFor. - Using
by.text(...)for buttons that get localized — same issue as XCUITest/Espresso. Useby.id. - Heavy fixtures via
beforeAll— state leaks across tests. Reset withdevice.reloadReactNative()ordevice.launchApp({ newInstance: true, ... }). - Tests that depend on the same logged-in user — order-dependent. Either log in per test or design tests to be independent.
- Forgetting
--headlessin CI — simulator window opens, blocks the runner if no display. - Building the app on every test run — separate
detox build(slow, once per source change) fromdetox test(fast, per run). - Not pinning Detox + RN versions — Detox's behavior changes meaningfully across major versions.
- Running iOS UI tests on Linux — impossible. Plan macOS capacity for iOS.
Task-Specific Questions
When helping with Detox, ask:
- RN version and Detox version?
- iOS, Android, or both?
- Existing
.detoxrc.jsto extend, or starting from scratch? - Are
testIDprops already in the app, or do they need to be added? - CI host — macOS for iOS, Linux for Android, both?
- Network strategy — app-level mocking, local mock server, real test API?
- Headless mode required in CI?
Related Skills
- espresso — Android native; for fast Android-side tests when RN's JS layer isn't being tested.
- xcuitest — iOS native equivalent.
- appium — for hybrid teams that have both RN and native screens, or RN + native bridge concerns.
- maestro — YAML alternative for simpler flows.
- jest-vitest — Detox uses Jest as its runner; unit tests for components fall here.
- ci-test-orchestration — for matrix iOS/Android jobs and headless runs.
- cloud-test-grids — for managed device pools when emulators aren't enough.
- flaky-test-management — though Detox's gray-box reduces flake significantly.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: aks-builds
- Source: aks-builds/quality-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet — be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.