Most Maestro guides assume you're testing against a release build. But during active development, compiling a release build for every iteration is slow. You naturally want to test against the debug build that's already running — Metro is live, hot reload works, and JS errors show up inline.
The catch: every time the debug app launches, the Metro bundler dev menu may appear as an overlay showing the bundler address (e.g. http://192.168.1.10:8081). If Maestro starts running commands while this overlay is visible, your taps land on the overlay instead of your actual UI — and the test fails in a confusing way.
The Fix: A Conditional Dismiss Flow
Create a reusable file called dismissDevMenu.yaml:
# Dismiss Metro bundler dev menu when running a debug build. - runFlow: when: visible: text: ".*http://.*" commands: - tapOn: text: ".*http://.*" - tapOn: text: "Close" optional: true label: "Close dev menu if visible"
How it works
when: visible: text: ".*http://.*"
A conditional guard — the block only runs if a visible element matches the regex. The Metro dev menu always shows the bundler URL, so this reliably detects it. On a release build, nothing matches and the block is skipped entirely. No side-effects.
First tapOn — the bundler URL
Taps the URL text (matched via .*http://.*) to interact with the overlay. The regex handles any host or port — localhost, LAN IP, custom Metro host — without hardcoding anything.
Second tapOn — "Close"
Taps the "Close" button that typically appears on the RN developer menu. optional: true means the test won't fail if the button isn't found — it's a best-effort cleanup. The label is just for readable test logs.
Using It in Your Test Flows
Reference dismissDevMenu.yaml at the top of every test flow:
appId: com.yourcompany.yourapp --- # Step 1: always dismiss Metro dev menu if present. - runFlow: dismissDevMenu.yaml # Step 2: proceed with your actual test. - tapOn: "Login" - inputText: "user@example.com" - tapOn: "Continue" - assertVisible: "Welcome"
Tip: If you have a global setup flow in
.maestro/config.yaml, add the dismiss call there once and every test in the suite inherits it automatically.
Running the Test
# Terminal 1 — start Metro npx react-native start # Terminal 2 — build and test npx react-native run-android maestro test loginFlow.yaml
Debug vs Release — When to Use Which
| Aspect | Release Build | Debug Build |
|---|---|---|
| Build time | Slow | Fast |
| Metro dev menu | ❌ Not present | ⚠ May appear — needs dismissal |
| Hot reload | ❌ No | ✅ Yes |
| JS error overlay | ❌ No | ✅ Red screen errors visible |
| Good for CI | ✅ Best — production parity | ⚠ Works with dismiss flow |
| Good for local dev | ❌ Slow iteration | ✅ Yes |
A practical workflow: write and iterate tests locally against a debug build, then run the same flow files against a release build in CI. Because the dismiss guard is conditional, it has zero effect on release builds — one set of test files, two environments, no changes needed.
Summary
- Use
runFlowwithwhen: visibleto conditionally detect the Metro overlay - Match the bundler URL with regex
.*http://.*— works for any host or port - Tap through to close it, with
optional: trueon the "Close" step - Include the helper at the top of every test flow, or once in a global setup config