Skip to main content

Running & debugging tests

When a test fails on a device you can't see, you need a record of what happened. Taqwright captures two kinds, a per-action trace and a full-run video, writes them under the output directory, and hands everything to Playwright's reporters.

Tracing: post-mortem debugging

Set trace in a project's use block to capture a per-action screenshot + page-source timeline of every test run. The result is a self-contained trace.html under the test's output directory, attached to the Playwright HTML report as taqwright-trace.

:::note Open it directly It's a standalone HTML page, in the Playwright report, click the taqwright-trace attachment to open it in a new tab. It is not a Playwright .zip trace, so don't load it in the Playwright Trace Viewer (that viewer reports "Could not load trace" for it). :::

ValueBehaviour
'off' (default)No overhead, Tracer never instantiated.
'on'Trace every test; HTML always written.
'on-failure'Trace every test; HTML written only when the test fails. Recommended for CI.
'retain-on-failure'Alias of 'on-failure' on mobile.
taqwright.config.ts
{
name: 'android',
use: {
// ... rest of your use options
trace: 'on-failure',
},
}

What ends up in trace.html:

  • Per-action timeline: each click / fill / assertVisible / etc. gets a row, in execution order.
  • Post-state screenshot for every step (click thumbnail to zoom).
  • Page-source XML snapshot at that moment.
  • Per-step timing + error message highlighted on the failing step.

:::caution Cost Each traced action adds one takeScreenshot + one getPageSource round-trip (~100–300 ms on a local emulator, more over USB / cloud). A 30-action test on trace: 'on' typically goes from ~5 s to ~15 s. That's why 'on-failure' is the sweet spot for CI. :::

Under the hood, the mobile fixture wraps the Mobile + Locator instances in a Proxy when trace !== 'off'. Chain methods (filter / first / nth / locator / and / or) pass through untraced, only the terminal action records an entry. So mobile.getByType('Cell').nth(2).click() produces one row in the trace, not three.

Screen recording: full-run video

Set video in a project's use block to capture an Appium on-device screen recording of every test run. The result is a screen.mp4 under the test's output directory, attached to the Playwright HTML report as taqwright-video.

:::caution iOS simulators require ffmpeg on PATH XCUITest records the iOS simulator by piping frames through host ffmpeg. If it isn't installed, Appium's startRecordingScreen throws at session start and every iOS test fails in fixture setup with WebDriverError: 'ffmpeg' binary is not found in PATH. Install it once (brew install ffmpeg / sudo apt-get install ffmpeg). This affects every non-'off' mode. Android emulators/devices and real iOS devices record on-device and need no host ffmpeg. :::

ValueBehaviour
'off' (default)No recording, recorder never started.
'on'Record every test; .mp4 always kept.
'on-failure'Record every test; .mp4 kept only when the test fails. Recommended for CI.
'retain-on-failure'Alias of 'on-failure' on mobile.
taqwright.config.ts
{
name: 'android',
use: {
// ... rest of your use options
video: 'on-failure',
},
}

Run & verify

With video set, just run the project, recording is automatic. The .mp4 is written when the test finishes (teardown), so let the run complete.

# run the project (emulator/simulator up; Appium auto-starts if configured)
npx taqwright test --project=android

# open the HTML report, then: pick the test → Attachments → taqwright-video
npx taqwright show-report

To skip the report and grab the file directly, it's screen.mp4 in the test's output folder:

find test-results -name screen.mp4 -exec open {} \;

Recording happens on the device for the whole run, so unlike tracing there's no per-action penalty, but every run pays the device recorder plus a base64 transfer of the .mp4 at teardown, even when the buffer is discarded on a pass. Expect a few MB per minute. Long tests are capped at 30 min (the Appium maximum). video and trace are independent, enable both for the same run.

Output directory & artifacts

outputDir is the root directory for everything a test run produces on disk, the trace.html, any testInfo.attach(...) files, and anything written via testInfo.outputPath(...). It's a top-level defineConfig option, overridable per project.

taqwright.config.ts
export default defineConfig({
testDir: './tests',
outputDir: './test-results', // top-level default for every project

projects: [
{
name: 'android',
outputDir: './test-results/android', // optional per-project override
use: { /* ... */ },
},
],
});
  • Per-test subfolder: each test gets its own directory under outputDir. Two tests never clobber each other's artifacts.
  • Path resolution: relative paths resolve against the taqwright.config.ts location, not the shell's cwd.
  • Default: if you omit outputDir, it falls back to Playwright's default test-results/.
  • Reading it from a test: testInfo is the second argument: test('name', async ({ mobile }, testInfo) => { ... }).

:::note Git Add outputDir (e.g. test-results) to .gitignore, it's regenerated every run, not source. The taqwright init scaffold already ignores test-results/. :::

Reports

Taqwright forwards the reporter option straight to Playwright. Reporters are run-level (no per-project reporter), and you stack several with the array form. Need a bespoke format? See Custom reporters.

ReporterWrites toUse for
'list' (default)console, one line per testlocal / interactive
'line'console, compact, updating linelarger suites
'dot'console, one char per testhuge suites / CI logs
'html'outputFolder/, browsable sitedebugging, sharing
'json'outputFilecustom tooling
'junit'outputFileCI (Jenkins/GitLab/Azure)
'blob'outputDir/, shardablemerge-reports across shards
'github'GitHub Actions annotationsCI only
taqwright.config.ts
export default defineConfig({
reporter: [
['list'], // ONE console reporter only
['html', { open: 'never', outputFolder: 'reports/html' }],
['json', { outputFile: 'reports/results.json' }],
['junit', { outputFile: 'reports/results.xml' }],
...(process.env.CI ? [['github'] as [string]] : []), // CI-only annotations
['blob', { outputDir: 'reports/blob' }],
],
});
  • One console reporter. list / line / dot all write to stdout, stacking two interleaves the output into noise. The file reporters coexist cleanly.
  • Give json / junit an outputFile: without it they print to stdout and collide with the console reporter.
  • github is CI-only. Gate it on process.env.CI as shown.

:::caution Don't nest the HTML report inside outputDir The html reporter wipes its outputFolder before regenerating. If that folder sits inside the test outputDir, Playwright errors, "HTML reporter output folder clashes with the tests output folder", and per-test artifacts are destroyed. Keep reports/ a sibling of test-results/. :::

Viewing the HTML report

npx taqwright show-report reports/html
# --host (default localhost) · --port (default 9323)

Taqwright prints this exact command at the end of an interactive run. show-report also serves a blob .zip directly.

Per-project report directories

Because reporters are run-level, one run merges every project into a single report. For an isolated report per project, run once per project and let the config derive reporter paths from an env var:

taqwright.config.ts
const proj = process.env.TW_REPORT_PROJECT;
const base = proj ? `reports/${proj}` : 'reports/_all';

export default defineConfig({
outputDir: './test-results', // sibling of reports/, no clash
reporter: [
['list'],
['html', { open: 'never', outputFolder: `${base}/html-report` }],
['json', { outputFile: `${base}/results.json` }],
['junit', { outputFile: `${base}/results.xml` }],
['blob', { outputDir: `${base}/blob-report` }],
],
});
package.json
{
"scripts": {
"test:android": "TW_REPORT_PROJECT=android taqwright test --project=android",
"test:ios": "TW_REPORT_PROJECT=ios taqwright test --project=ios",
"report:android": "taqwright show-report reports/android/html-report"
}
}

Each invocation writes only under its own reports/<project>/, runs never clobber each other. A plain taqwright test (env unset) falls back to reports/_all/.