Troupeby AliveDocsSign in

Start

Quick start

This page takes you from an empty directory to a passing test on an iOS Simulator, its HTML report, and a live view of the device. It uses five commands: troupe init, troupe install, troupe test, troupe show-report and troupe studio.

Before you start

You need a Mac with Apple silicon, Xcode with an iOS simulator runtime, and Bun 1.4.3 or newer. You also need a simulator build of your app: a .app built for the iOS Simulator. Install has the full list, including what Android needs.

1. Add Troupe to your project

In your app's directory, or a new one:

Shell
bun add -d --exact @alivelabs/troupe

Run this before any bunx troupe command. bunx runs the troupe command from the project's node_modules; where there is none it fetches whatever npm calls troupe, which is not this.

2. Create the test project

Name your simulator build with --app:

Shell
bunx troupe init --app ./build/MyApp.app

In a new directory, after step 1, it prints:

Output
troupe init in /Users/you/shop:
  wrote    troupe.config.ts (ios: iPhone 18 Pro, 27.0, ./build/MyApp.app; android: commented out)
  wrote    e2e/example.e2e.ts
  added    package.json: scripts.e2e
  kept     package.json: @alivelabs/troupe
  wrote    .gitignore: /test-results/, /troupe-report/, /blob-report/

After those lines it lists the commands to run next, which are the steps below. The device type and iOS version are the defaults installed on your Mac. troupe init never replaces a file: what exists is skipped, and it says so. The config it wrote, with its comments left out:

TypeScript
import { defineConfig } from '@alivelabs/troupe'

export default defineConfig({
  testDir: 'e2e',
  reporter: [['list'], ['html']],
  use: {
    trace: 'retain-on-failure',
  },
  projects: [
    {
      name: 'ios',
      use: {
        platform: 'ios',
        deviceType: 'iPhone 18 Pro',
        os: '27.0',
        app: './build/MyApp.app',
      },
    },
  ],
})

An Android project is written too, commented out until you name an .apk. --app takes one iOS build and one Android build: bunx troupe init --app ./build/MyApp.app --app ./build/my-app.apk.

3. Prepare the Mac

Shell
bunx troupe install ios

This runs once. It records your Xcode in ~/.troupe/host.json, runs the environment checks (each failure prints its fix), builds the XCUITest runner and a simulator template, and proves the device path with one simulator. The first run can take minutes. It ends with the line ios-simulator: a device was ready in <n> s., where <n> is the time it took.

4. Run the test

Shell
bunx troupe test

The first run starts the runtime daemon in the background, boots a simulator named troupe-… and installs your app. A released device stays warm for five minutes, so a run soon after reuses it. With the durations left out, the list reporter prints:

Output
Running 1 test using 1 worker

  ✓  1 [ios] › e2e/example.e2e.ts:11:1 › the app launches and shows its first screen (…)

  1 passed (…)

The example test passes on any app. It waits for the app's first screen and attaches a screenshot of it:

TypeScript
import { expect, test } from '@alivelabs/troupe'

test('the app launches and shows its first screen', async ({ screen }, testInfo) => {
  // The screen's accessibility tree, as the device reports it; an app that shows anything has one.
  await expect.poll(() => screen.ariaSnapshot()).not.toBe('')
  await testInfo.attach('first screen', {
    body: await screen.screenshot(),
    contentType: 'image/png',
  })
})

5. Open the report

Shell
bunx troupe show-report

It serves troupe-report/ on a loopback port, prints the address and runs until you press Ctrl+C. It does not open a browser: open the address yourself. The report lists every test with its steps and attachments, and links each kept trace.

A failing test keeps its trace by default. To keep every test's trace and open one in the trace viewer:

Shell
bunx troupe test --trace on
bunx troupe show-trace test-results/<test>/trace.zip

6. Write your own test

Add a file in e2e/ whose name ends in .e2e.ts. Locators find elements on the device and wait for them; assertions retry until the screen matches. For an app with a sign-in form:

TypeScript
// e2e/sign-in.e2e.ts
import { expect, test } from '@alivelabs/troupe'

test('signs in', async ({ screen }) => {
  await screen.getByLabel('Email').fill('a@b.co')
  await screen.getByRole('button', { name: 'Sign in' }).tap()
  await expect(screen.getByRole('heading', { name: 'Welcome back' })).toBeVisible()
})

7. Watch the device

Shell
bunx troupe studio

It prints an address of the form http://127.0.0.1:<port>/#otc=<code>. Open it in a browser on the same Mac within a minute: the code in the address pairs the page with the runtime and works once. In the studio you create a session, watch and drive the device, inspect the accessibility tree and read the logs. The tree is where you find the roles, labels and test ids to use in locators.

Next

esc

    Type to search every page, the protocol reference and the cloud API.