taqwright

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 file under the test's output directory, attached to the Playwright HTML report as taqwright-trace.

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:

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.

How it works 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 (click, fill, etc.) records an entry. So mobile.getByType('Cell').nth(2).click() produces one row in the trace, not three.

For interactive debugging during development (instead of post-mortem), drop await mobile.pause() in a test — it hands the in-flight WebDriver session off to the inspector in your browser. See Generating tests → Debug a live test.

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.

It's a plain video. In the Playwright report, click the taqwright-video attachment to play the .mp4. It is not a Playwright Trace Viewer trace, and it's deliberately named taqwright-video (not video) so the report doesn't confuse it with its own browser recordings.
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 in that project fails in fixture setup with WebDriverError: 'ffmpeg' binary is not found in PATH — this is by design (the fixture surfaces the error loudly rather than producing an empty .mp4). Install it once: brew install ffmpeg (macOS) / sudo apt-get install ffmpeg (Linux), then verify with ffmpeg -version. Android emulators/devices and real iOS devices record on-device and need no host ffmpeg. Note this affects every non-'off' mode: 'on-failure' still starts the recorder at session start, so it fails the same way without ffmpeg — set video: 'off' for the iOS project if you can't install it.
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

In the report, open the test and expand Attachments — click taqwright-video to play the recording inline (alongside taqwright-trace if tracing is also on). 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 {} \;

Expectations: with 'on' you get an .mp4 every run (pass or fail); with 'on-failure' only failing tests keep one. The file lives under outputDir (gitignored by the taqwright init scaffold), so it isn't committed. If you don't see the attachment, confirm the run actually reached teardown and that the project's use.video isn't 'off'.

Cost & caveats: recording happens on the device for the whole run, so unlike tracing there is 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 ('on-failure'/'retain-on-failure'). Expect a few MB per minute. Long tests are capped at 30 min (the Appium maximum) so recordings aren't silently truncated. On-device recording support varies by driver — real devices and Android emulators record on-device and work out of the box; the iOS simulator needs host ffmpeg on PATH (see the callout above) and the fixture surfaces a clear error at session start rather than producing an empty file.

How it works under the hood: the mobile fixture calls Appium's startRecordingScreen before the test body and always stopRecordingScreen at teardown (so the device recorder can't leak between tests). The base64 payload is written to screen.mp4 and attached only when the run's outcome matches the selected mode. video and trace are independent — enable both and you get a taqwright-video MP4 plus a taqwright-trace HTML timeline for the same run.

Output directory & artifacts

outputDir is the root directory for everything a test run produces on disk — the trace.html from tracing, any testInfo.attach(...) files, and anything a test writes 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: { /* ... */ },
    },
  ],
});
Git: add outputDir (e.g. test-results) to .gitignore — it's regenerated every run, not source. The taqwright init scaffold already ignores test-results/ for you.

Reports

taqwright forwards the reporter option straight to Playwright. Reporters are run-level (there is no per-project reporter), and you stack several at once 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' }],
  ],
});
Don't nest the HTML report inside outputDir. The html reporter wipes its outputFolder before regenerating. If that folder sits inside the test outputDir (e.g. outputFolder: 'test-results/html' with outputDir: 'test-results'), Playwright errors — "HTML reporter output folder clashes with the tests output folder" — and per-test artifacts are destroyed. Keep reports a sibling of outputDir (e.g. reports/ next to test-results/).

Viewing the HTML report

After a run, serve it with:

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

taqwright prints this exact (taqwright-branded) 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 is just TypeScript, so it can read process.env:

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",
    "report:ios":     "taqwright show-report reports/ios/html-report"
  }
}

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