Testing
Traces
A trace is a recording of one test attempt: every step with its timing, the screen after each action, the accessibility tree at the failure, and the device's logs. This page covers what a trace holds, the trace modes, and the trace viewer.
What a trace holds
A trace is one zip file, trace.zip, written into the test's directory under test-results/ and attached to
the test's result under the name trace.
| In the zip | Written by | Holds |
|---|---|---|
test.trace | the test runner | Every step of the test (test.step, assertions, fixtures, hooks, runtime calls) with its start, end and call site, plus errors and the test's output |
device.trace | the runtime | What happened on the device for each call: the point touched, the waiting log of each action, screenshots, accessibility snapshots, device logs and app events such as a crash |
resources/ | the runtime | The screenshots and snapshots themselves, named by their SHA-256 |
src@<hash>.txt | the test runner | The test's source file, for showing the line of each step |
The two event files share a call id per action, which is how the viewer lines up a step in your test with what the device did.
By default the runtime takes a screenshot after each action, takes the accessibility tree when the test fails, and records the device logs. The next action waits for that screenshot, so the picture is the screen the action left.
Trace modes
The trace option decides which attempts are recorded and which recordings are kept.
| Mode | Records | Keeps |
|---|---|---|
'retain-on-failure' | every attempt | the attempts that failed |
'on' | every attempt | all of them, with an accessibility snapshot for every action |
'off' | nothing | nothing |
'on-first-retry' | the first retry only | it |
'on-all-retries' | every retry | all of them |
'retain-on-first-failure' | the first attempt only | it, when it failed |
The default is 'retain-on-failure' on iOS Simulator and Android Emulator. A passing test's recording is
discarded, and no trace.zip is written for it. On Mac Catalyst the default is 'on-first-retry', because the
screenshot after each action is expensive there; with retries: 0 that records nothing, so set
trace: 'retain-on-failure' in a Catalyst project to keep its failures' traces.
Set the mode in the config, for some tests, or for one run:
// troupe.config.ts
export default defineConfig({ use: { trace: 'retain-on-failure' } })// in a spec file
test.use({ trace: 'on' })bunx troupe test --trace onTo choose what is captured, give an object instead of a mode:
export default defineConfig({
use: {
trace: { mode: 'retain-on-failure', screenshots: 'beforeAfter', a11y: 'always', logs: true },
},
})| Field | Values | Default |
|---|---|---|
mode | One of the modes above | required |
screenshots | 'off', 'after' (one after each action) or 'beforeAfter' | 'after' |
a11y | 'off', 'onFailure' or 'always' (a snapshot for every action) | 'onFailure', or 'always' when the mode is 'on' |
logs | Whether device logs are recorded | true |
Viewing a trace
A failed test prints where its trace is:
attachment #1: trace (application/zip)
test-results/<test>/trace.zip
Usage:
troupe show-trace test-results/<test>/trace.zip<test> stands for the test's own directory, named after its file, its titles, its project and a short hash.
troupe show-trace serves the trace viewer and that one trace on a loopback port, prints the address, and
runs until you press Ctrl+C. It does not open a browser.
bunx troupe show-trace test-results/<test>/trace.zipThe HTML report holds a copy of every kept trace. Each test's page links to
its trace, and each step links to its action inside the trace. You can also drop a trace.zip onto the
viewer's trace page.
A retry's trace is in its own directory, with -retry1, -retry2 and so on after the test's name.
The trace viewer
The viewer has a timeline, the list of actions, the screen, and a row of tabs for the selected action.
| Part | Shows |
|---|---|
| Timeline | A film strip of the screen over time, with the actions below it |
| Actions | The steps as a tree. Select one to see everything else for that moment |
| Screen | The screen before and after the selected action, with a marker where the action touched it |
| Call tab | The step's title, method and timing, the locator, and the device calls with the point touched |
| Aria tab | The accessibility snapshot for the action, foldable and filterable. For the failed action it is the tree at the failure |
| Log tab | The runtime's waiting log: which check the element had not passed yet |
| Console tab | Device logs, app events and the test's output while the action ran |
| Errors tab | The errors of the attempt |
| Source tab | The test's source at the step's call site |
| Metadata tab | The platform, device type and OS the trace ran on |
Steps of internal fixtures are hidden until you press "Show internal".
| Key | Does |
|---|---|
↑ ↓, or j k | Move through the actions |
← → | Switch tab |
Esc | Go up a level: trace, test, report |
Reading a failure
Start from the failed action in the list of actions.
- Look at the screen before the action. If the element is not there, the test was on another screen than you thought, and the earlier actions show how it got there.
- Read the Log tab. A line such as "waiting for … to be enabled" says the element was found but was not ready.
- Open the Aria tab and filter for the text you expected. It shows the role and name the element really has, which a locator that found nothing needs to match.
- Check the Console tab for an error from your app while the action ran.