E2E Test Commands
Norrix runs E2E tests against completed virtual-device builds on Norrix cloud runners, with either runner:
- Maestro : YAML flows.
- TesterArmy e2e : TypeScript tests that mix plain-English agent steps (
agent.act,agent.assert) with exact locator checks, driven on the simulator or emulator through@e2e-dev/mobile.
E2E tests are available to Enterprise organizations.
norrix test
Run E2E tests against a completed build.
Synopsis
norrix test [platform] [options]Prerequisites
E2E tests need an artifact that can install on a virtual device:
- iOS: build with
norrix build ios --simulator - Android: build with
norrix build android --emulatoror produce an APK
Maestro flows live in a local directory, usually .maestro/ or a project-specific folder such as apps/mobile-e2e/src/flows. A TesterArmy e2e suite lives in its own directory, usually e2e/; see TesterArmy e2e suites.
Choosing a Runner
--runner maestro or --runner e2e picks the runner. Without it, the CLI uses the runner your options belong to (--flows-dir and --flows are Maestro; --suite-dir, --target, --tests, --grep, and --tag are e2e), then a suite it finds (e2e.config.ts in the current directory or ./e2e). It asks when the project has both .maestro/ and a suite, and otherwise runs Maestro.
Arguments
| Argument | Description |
|---|---|
platform | Target platform: ios or android |
If platform is omitted, the CLI can prompt you to select from available testable builds.
Options
| Flag | Description |
|---|---|
-b, --build <buildId> | Build ID to test. Required with --non-interactive |
--runner <runner> | maestro or e2e. Detected when omitted, see Choosing a Runner |
--flows-dir <path> | Maestro: path to the flows directory (default: .maestro) |
--flows <flows> | Maestro: comma-delimited flow files, relative to the flows directory (default: all flows) |
--suite-dir <path> | e2e: directory holding e2e.config.ts and its package.json (default: . or ./e2e) |
--target <name> | e2e: target from e2e.config.ts (default: every target on the build’s platform) |
--tests <files> | e2e: comma-delimited test files (default: all tests) |
--grep <pattern> | e2e: only tests whose title matches the pattern |
--tag <tags> | e2e: comma-delimited tags; tests carrying any of them run |
--device-model <model> | Preferred iOS simulator model for the E2E runner |
--device-os <os> | Preferred iOS simulator runtime for the E2E runner |
--wait | Wait for the E2E run to finish |
--timeout-minutes <minutes> | Maximum time to wait for completion (default: 60) |
--poll-interval <seconds> | Polling interval while waiting for completion (default: 20) |
--junit-output <dir> | Download JUnit XML result files into this directory. Implies --wait |
--org <orgId> | Target organization ID |
-n, --non-interactive | Run without prompts. Requires --build |
Examples
Interactive Run
norrix testThe CLI lists successful testable builds and runs all flow files in .maestro/.
CI-Gated iOS Run
# 1. Build a simulator artifact.
norrix build ios debug --simulator \
--project my-app \
--config stg \
-V 1.2.3 \
-B "$BUILD_NUMBER" \
--org "$NORRIX_ORG_ID" \
-n
# 2. Run selected Maestro flows and publish JUnit from the output directory.
norrix test ios \
--build "$NORRIX_BUILD_ID" \
--flows-dir apps/my-app-e2e/src/flows \
--flows 01-launch.yaml,02-login.yaml \
--device-model iPhone-16-Pro \
--device-os iOS-26-2 \
--wait \
--timeout-minutes 50 \
--poll-interval 20 \
--junit-output ./norrix-e2e-junit \
--org "$NORRIX_ORG_ID" \
-nAndroid Run
norrix build android debug --emulator --project my-app --config stg -n
norrix test android \
--build "$NORRIX_BUILD_ID" \
--flows-dir e2e/maestro \
--wait \
--junit-output ./norrix-e2e-junit \
-nTesterArmy e2e Run
norrix build ios debug --simulator --project my-app --config stg -n
norrix test ios \
--build "$NORRIX_BUILD_ID" \
--suite-dir e2e \
--tag smoke \
--wait \
--junit-output ./norrix-e2e-junit \
-nDevice Pinning
--device-model and --device-os are iOS runner hints. If either option is provided and the runner cannot find a matching available simulator, the run fails instead of silently using a different device.
These options are useful when flows depend on stable screen geometry, for example coordinate-based login steps.
Test-Time Environment Variables
The E2E workflow injects Norrix environment variables before the tests run. For TesterArmy e2e suites, see Model credentials and secrets.
For Maestro, Any injected variable whose name starts with MAESTRO_ is passed to maestro test with -e, so flows can reference values such as ${MAESTRO_USERNAME}.
Set org-global values when every project can share the same test account:
norrix env set MAESTRO_USERNAME "$MAESTRO_USERNAME"
norrix env set MAESTRO_PASSWORD "$MAESTRO_PASSWORD"Set project-scoped values when one app needs different credentials:
norrix env set MAESTRO_USERNAME "$MAESTRO_USERNAME" --project my-app
norrix env set MAESTRO_PASSWORD "$MAESTRO_PASSWORD" --project my-appProject-scoped values override org-global values with the same name.
Exit Behavior
- Without
--wait,norrix testexits after the remote run is scheduled. - With
--wait, the CLI exits non-zero when the run fails, is cancelled, or times out. - With
--junit-output, the CLI downloads JUnit XML from Norrix when reports are available. Your CI system can publish*.xmlfrom that directory. - For e2e runs,
--waitalso prints the run’s results: passed, failed, and flaky tests, model tokens, estimated cost, and replay cache hits.
TesterArmy e2e Suites
TesterArmy e2e is an open source agentic test runner. Norrix runs your suite as-is: the runner installs its dependencies, installs your build on a booted simulator or emulator, and runs e2e run for the targets on the build’s platform.
For a step-by-step setup with an example test, a local run, and a CI workflow, see the E2E Testing guide.
Suite Layout
Keep the suite in its own directory with its own package.json, so Norrix can install it without your app’s dependencies:
e2e/
├── package.json # e2e, @e2e-dev/mobile, and your model provider (e.g. @ai-sdk/anthropic)
├── package-lock.json # or pnpm-lock.yaml, yarn.lock, bun.lock
├── e2e.config.ts
└── tests/
└── login.e2e.tsnorrix test uploads that directory without node_modules, .env files, platforms/, build artifacts, or run output under .e2e/ (the replay cache in .e2e/cache/ is kept).
import type { E2EConfig } from 'e2e';
import { mobile } from '@e2e-dev/mobile';
import { anthropic } from '@ai-sdk/anthropic';
export default {
agents: {
default: { model: anthropic('claude-sonnet-5-5') },
},
targets: [
{
name: 'ios',
engine: mobile({ platform: 'ios' }),
app: {
bundleId: 'org.example.myapp',
appPath: '../platforms/ios/build/Debug-iphonesimulator/myapp.app',
},
},
{
name: 'android',
engine: mobile({ platform: 'android' }),
app: { bundleId: 'org.example.myapp' },
},
],
workers: 1,
} satisfies E2EConfig;On Norrix, every target on the build’s platform runs unless you pass --target. Norrix installs the build before the run and points those targets’ app.appPath at it, so device.installApp() in your fixtures installs the Norrix build. Leave device out of mobile() to use the simulator Norrix booted (pick it with --device-model/--device-os); a pinned device must exist on the runner.
Model Credentials and Secrets
Agent steps call the model your config names, with your own key. Store the key as a Norrix environment variable; tests without agent steps need none:
norrix env set ANTHROPIC_API_KEY "$ANTHROPIC_API_KEY"Every Norrix environment variable reaches the suite’s processes, so E2E_SECRET_<NAME> and E2E_USER_<NAME>_USERNAME/_PASSWORD fill the config’s secrets and credentials, which the runner keeps out of model input. Norrix’s own runner credentials are never passed to the suite.
Replay Cache
Passing agent.act steps are recorded and replay later without a model call. Norrix keeps a replay cache per organization, project, and platform between cloud runs, so repeat runs get cheaper. Entries committed in your suite’s .e2e/cache/ fill gaps in it. agent.assert, agent.extract, and agent.waitFor always call the model.
Results
Each run uploads report.json, junit.xml, the markdown report, screenshots, and a video of every failed attempt. In the dashboard, View Report shows the report and failure videos. The run’s message names failed tests, and a run that never reaches its tests says why (for example a missing model key or an unknown target).
norrix test-status
Check the status of an E2E test run.
Synopsis
norrix test-status <e2eTestId> [options]Arguments
| Argument | Description |
|---|---|
e2eTestId | E2E test ID, such as e2e-test-1751234567890 |
Options
| Flag | Description |
|---|---|
--org <orgId> | Target organization ID |
Example
norrix test-status e2e-test-1751234567890Status Values
| Status | Description |
|---|---|
scheduled | E2E test record was created |
testing | Workflow has been dispatched/running |
success | All selected flows or tests passed (flaky tests count as passed) |
failed | One or more flows or tests failed, or setup failed |
cancelled | Run was cancelled |
Dashboard
Enterprise organizations can also start and monitor E2E tests from the Norrix dashboard at /test. The dashboard accepts uploaded Maestro flow zips or e2e suite zips, shows recent runs with their results, supports cancellation, and links to streamed logs and, for e2e runs, the report and failure videos.