Skip to main content

Maestro

The @archibald/maestro package wraps Maestro for native end-to-end testing of Archibald's React Native / Expo builds. It is invoked through the archibald maestro CLI command.

Installation

From the root of an Archibald project:

archibald add maestro

This:

  • Adds @archibald/maestro as a dev dependency.
  • Scaffolds a maestro/ directory at the project root (config.yaml + flows/smoke.yaml).
  • Registers a maestro npm script that invokes archibald maestro.

Installing the Maestro CLI

Maestro itself is a JVM-based CLI distributed outside of npm. You need to install it once on your host machine:

  • macOS (Homebrew):
    brew tap mobile-dev-inc/tap
    brew install maestro
  • Linux / WSL:
    curl -fsSL "https://get.maestro.mobile.dev" | bash
    export PATH="$PATH":"$HOME/.maestro/bin"
  • Windows: use WSL with the Linux instructions, or follow the manual setup in the Maestro docs.

The archibald maestro command preflights maestro --version and prints platform-specific install instructions if the CLI is missing, so you do not need to memorize the above.

caution

Use Maestro >= 2.5. Maestro <= 2.1 does not reliably trigger Pressable components under React Native's New Architecture (Fabric) on iOS, which the shop template uses.

Usage

Overview

Maestro flows are declarative YAML files. Each flow declares a target appId, then a sequence of commands (launchApp, tapOn, assertVisible, inputText, …). Flows live in maestro/flows/ by default; the workspace-level maestro/config.yaml declares the default appId and the flow glob.

The scaffold added by archibald add maestro looks like:

maestro/
├── .gitignore
├── config.yaml
└── flows/
└── smoke.yaml

Building the app first

Maestro drives an installed binary on a simulator/emulator — it does not connect to a Metro dev server. Before running flows, build and install the app:

pnpm ios # builds + installs on iOS simulator
pnpm android # Android equivalent (requires Android SDK)

Executing flows

archibald maestro # whole suite (config.yaml's flow glob)
archibald maestro -f maestro/flows/smoke.yaml # single flow
archibald maestro -e MAESTRO_USERNAME=alice # inject env into flows
archibald maestro -a com.example.app.preview # override appId for a preview build

See the full flag list under archibald maestro.

When cli.e2e.framework is "maestro" (set automatically by archibald add maestro), the unified archibald e2e command dispatches to the Maestro runner — a peer of Cypress and Playwright. You can also select it ad-hoc with archibald e2e --runner=maestro. Either way it runs the default flow suite (maestro/); for -f, -e, or -a overrides, use archibald maestro directly.

App ID

The default appId in the scaffolded config.yaml matches what archibald build --platform=app --native=ios --run installs locally — the unsuffixed bundle identifier. Override per run for preview / development builds with --appId:

archibald maestro -a net.netconomy.archibald.template.shop.preview
archibald maestro -a net.netconomy.archibald.template.shop.dev

Environment variables

Pass per-flow environment with -e KEY=value. The flag can be repeated:

archibald maestro \
-e MAESTRO_USERNAME="$MAESTRO_USERNAME" \
-e MAESTRO_PASSWORD="$MAESTRO_PASSWORD"

Inside a flow, reference an injected value with ${KEY}.

Selector strategy

Maestro's iOS visibility heuristic uses layout bounds (not viewport clipping) and its text: selector matches the iOS text attribute, not accessibilityText. As a result:

  • Prefer id: selectors (testID / resource-id) for anything you want a flow to interact with reliably. Instrument Pressables, inputs, and tab labels with testID props in the production component.
  • Visible English copy from messages.json is generally not searchable on iOS; do not rely on it for taps or assertions on RN screens.
  • For elements rendered below a sticky element (e.g. a floating tab bar), assert on a stable element near the top of the screen rather than scrolling the off-screen target into view — Maestro's "visible" heuristic may report a partially-occluded element as visible without actually being tappable.

API

The package also exposes a Node API used by the archibald maestro command. You can call it directly from scripts or custom runners:

import { assertMaestroInstalled, runMaestro } from '@archibald/maestro';

assertMaestroInstalled(); // throws MaestroNotInstalledError if the CLI is missing

const { exitCode } = await runMaestro({
flows: 'maestro/flows',
flowEnv: { USERNAME: process.env.MAESTRO_USERNAME ?? '' }
});

Public exports: verifyMaestroInstall, assertMaestroInstalled, MaestroNotInstalledError, runMaestro, getInstallInstructions, formatInstallInstructions, detectHostPlatform.

Further documentation