| name | simulator-audio-e2e |
| description | Build and run repeatable react-native-nitro-sound recorder/player regression tests on an iOS Simulator or Android emulator, with explicit virtual-device selection, microphone permission, Maestro evidence, native log inspection, and cleanup. Use after dependency, Nitro, TypeScript, Swift, Kotlin, generated-binding, or audio state-machine changes when a physical device is not required. |
Simulator Audio E2E
Verify real native recording and playback on virtual devices without touching a
connected physical phone. Treat builds, UI automation, callback state, and log
inspection as separate evidence.
Safety Boundary
- Require explicit permission in the current request before recording. State
that
sound.example records a short non-sensitive clip through the host or
emulator microphone. A request to run audio E2E is sufficient permission.
- Select a concrete iOS Simulator UDID or Android serial beginning with
emulator-. Never allow an implicit destination when physical devices are
connected.
- Do not upload, transcribe, or retain the recording. Keep only screenshots,
JUnit output, and redacted logs needed to diagnose the run.
- Do not claim Bluetooth, interruptions, backgrounding, audio focus, route
changes, or audible fidelity from this lane; those require targeted tests and
can require a physical device.
Preflight
- Read
AGENTS.md, .claude/commands/e2e-tests.md, the changed source, and
references/flow-contract.md.
- Preserve
git status --short --branch; never clean unrelated work.
- Run
scripts/preflight.sh and select one available virtual destination.
- Run
yarn install --immutable, yarn prepare, yarn typecheck,
yarn lint, and yarn test --runInBand.
- Confirm
sound.example declares microphone permission and no Metro process
conflict exists on port 8081.
Build And Install
For iOS, boot the selected Simulator and build the Debug app for that exact
UDID. Install the produced .app with xcrun simctl install. For Android,
start the selected AVD, wait for boot completion, and build/install Debug with
ANDROID_SERIAL set to the selected emulator serial.
Start Metro from the repository root with yarn start --reset-cache. Keep its
session and save errors separately from native device logs. Grant microphone
permission to sound.example on the selected virtual device before the flow.
Run The Runtime Contract
Run the repository flow through the guarded helper:
.codex/skills/simulator-audio-e2e/scripts/run-maestro.sh ios <simulator-udid>
.codex/skills/simulator-audio-e2e/scripts/run-maestro.sh android <emulator-serial>
The flow must prove both direct and hook APIs can:
- Start recording and emit increasing record time.
- Pause and resume the recorder without a crash or duplicate instance.
- Stop and expose a playable path.
- Start, pause, resume, and stop playback with matching UI state.
- Complete with no visible error alert.
Do not weaken selectors or remove state assertions to make a failed flow pass.
Fix the product or explain a platform limitation.
Inspect Native Evidence
After Maestro, inspect simulator logs for the current app process only:
- iOS:
xcrun simctl spawn <udid> log show with a short time bound and a
sound.example predicate.
- Android:
adb -s <serial> logcat --pid=$(adb -s <serial> shell pidof sound.example) captured around the run.
Fail the runtime lane for uncaught exceptions, native crashes, rejected
promises, MediaRecorder/AVAudio errors, or leaked-resource warnings attributable
to the tested flow. Ignore unrelated host and OS noise with an explanation.
Report And Cleanup
Report BUILD PASS/FAIL and RUNTIME PASS/FAIL/BLOCKED per platform. Include
the exact UDID/serial, OS/API level, RN and Nitro versions, flow path, callback
states observed, JUnit result, and relevant redacted log findings. List every
untested device-only row.
Terminate only Metro/emulators started by this run. Leave user-owned devices
and processes intact. Use the guarded runner's mandatory cleanup and verify
that no recording remains in the selected app container or e2e/artifacts/.
Report RUNTIME BLOCKED if cleanup fails or cannot be confirmed. JUnit,
screenshots, debug output, and redacted logs under e2e/artifacts/ remain
disposable and ignored by Git.