Prompt
Write a Detox end-to-end test that:
- Launches the app
- Taps a "Login" button
- Types email and password
- Asserts the user sees a welcome screen
Match elements by testID, not by text.
Solution
E2E sits at the top of the testing pyramid for a reason: it's the most expensive layer — real simulator or device, minutes instead of milliseconds, infrastructure flake — so it's reserved for the few flows where a regression costs money: login, checkout, the critical path. Component behavior belongs to the integration layer (React Native Testing Library) below it; a healthy suite is many unit tests, a solid integration layer, and few E2E flows.
What makes Detox specifically worth naming is that it's gray-box: it instruments the app and waits for it to become idle — pending animations, in-flight network requests, timers — before executing each step. That synchronization is why a correct Detox test contains no sleep() calls, and why each await reads as a clean user action.
Matching strategy is the other signal. by.id targets testID — a stable contract between the app and its tests, decoupled from copy. by.text('Login') breaks the day marketing rewords the button or the app ships its Spanish localization. Finally, toBeVisible() asserts actual on-screen visibility, not mere existence in the tree — a welcome screen hidden behind a modal fails, as it should.
Red flag: sprinkling sleeps or generous waitFor timeouts to fight flakiness. That's suppressing Detox's synchronization instead of fixing the unsettled timer or looping animation that's actually keeping the app busy.
Say it: "Detox is gray-box — it waits for the app to be idle, so no sleeps; I match by testID because text is a copy decision, and I keep E2E to the few money paths the pyramid says it should cover."