dánwòdánwò

Running

Recordings

Every test's flight record in the dashboard, its video, steps, console, network and every provider call, and a prompt to fix it.

Every test keeps a flight record: a video, each step, the page's console and network, and every call and webhook behind it. In the dashboard it opens over the page you came from.

Open one

Click a test, a run's row or a matrix cell. The recording opens on top, and Esc closes it.

A recording: the steps on the left, the video and its tabs on the right

On the left are the steps. Each has Go to, which moves the video to that step. Under them are the error, where the test stopped, the faults and any lint findings.

Follow a popup

When the app opens a popup or a new tab, such as a provider's "Connect your account" page, danwo films it as a tab of its own (video/tab-2.webm) and records which tab each step ran in. While the video plays, the player switches to the tab the step is running in and keeps playing there.

  • The scrubber and the steps mark Popup opened: accounts.example.com where it opened, and Back to the app where it closed.
  • Steps that ran in the popup have a popup badge.
  • Go to shows that step's tab.
  • The clock reads from the first video, so a step's time is the same in every tab.
  • The Tab buttons still switch tabs by hand.

See what each step did

In the video, a cursor moves to each element before the test acts on it. A box outlines the element, a ripple shows a click, and a typing badge shows a fill. This includes the agent's actions.

The cursor never shows in a compared screenshot or in one the model reads. It is drawn only while video is on, and adds about 0.2 s to each action. To turn it off:

danwo.config.ts · no cursor
video: { mode: "on", cursor: false },

Read the checks

After the steps comes Checks: what ran once the browser was done. First, the wait for background work, such as webhooks and Inngest runs, with how long it took and what it waited for. Then each check your invariant made, with a ✓ or ✗.

tests/checkout.danwo.ts (fragment)
}).invariant(async ({ app, stripe, expect }) => {
  expect(await grantedCredits(app.baseUrl, "u1"), "credits granted").toBe(500)
})

This check shows as expect(credits granted).toBe(500) with the value it got. When it fails, it shows what it wanted too. Give a check a message, as above, so its row reads well.

Skip the idle tail

The browser sits still while danwo waits and checks. The scrubber shades these stretches as "settling" and "checks", each with a Go to button. The video plays through them at 4× by default; turn off Idle at 4× to watch them at normal speed.

The tabs

TabWhat it shows
Replaythe video, with steps, faults, settling and checks marked on the scrubber
Consolethe page's console, filtered by level
Networkthe page's own requests
Providersa waterfall of steps, provider calls, webhooks and faults; then the calls as a table
Emulator statewhat each emulator held when the test ended
Effectswhat each step was expected to change, and what it did
Matrixon a trial: the fault it ran under, and its verdict

Read the calls behind a step

The Providers tab lines up every call with the step that caused it:

The Providers tab: a waterfall of the test's steps and calls, with Stripe answering 500

Click a call to see its request and response. Secrets are redacted before the record is written, so they never reach the page.

Copy a prompt to fix it

Copy prompt to fix copies the failure as a prompt for a coding agent: what failed, where, the calls and faults around it. The menu next to it copies the same page as Markdown.

Address partOpens
?open=<record>that recording
&pane=providersthat tab
&t=12.5the video 12.5 seconds in
#step-3the video where step 3 starts

What's kept

SettingKept
record: "always" (default)every run's flight record
record: "on-failure"only failed runs'
record: { keepRuns: 50 }how many of the newest runs keep recordings
video: "on" (default)a video with every record
video: "retain-on-failure"videos of runs that didn't pass
video: "off"no video
video: { cursor: false }video without the cursor and click marks

Older runs' recordings are deleted as each run finishes. danwo test --keep-runs 200 keeps more for one run. The run summaries the dashboard lists stay longer (200 runs), so an old run's row reads recording deleted and its Watch button is turned off.

When a video or screenshot is missing

A record that lacks a video or a screenshot says why, in its missing list. The Replay pane and the HTML report show it as Video not saved: …:

CauseWhat happened
failedthe browser or the disk failed to save it (ENOSPC, a browser crash)
secreta secret danwo knows showed on the page as text, so it wasn't kept
closedthe tab had closed before its step's screenshot
settingvideo: "off" or "retain-on-failure" left it out

When a run loses a video or screenshot it asked for (failed, or a video withheld for a secret), the summary ends with one warning line:

danwo: 1 video was not saved (the run's result stands): tests/pay.danwo.ts › pays, the video of tab 1: saving it failed: ENOSPC: no space left on device, write. Each record's `missing` says why.

The run still passes or fails on what the app did. A full disk shouldn't turn a green run red.

How much disk it takes

A browser test's video is usually 50 to 250 KB. A run's folder, with its videos, screenshots and records, is typically under 1 MB for a small suite and grows with the number of tests. Fifty runs of a 20-test suite come to tens of megabytes. Mobile screen recordings are larger; turn video to "retain-on-failure" to keep only the ones you'll watch.

danwo.config.ts · recordings
record: { mode: "always", keepRuns: 100 },
video: "retain-on-failure",

Video shows the screen as it was. Fields holding a secret show as dots, but a secret your app prints as text is on screen. Use video: "off" for suites that show one.

Next

On this page

Run it on every pull request

A GitHub check and one comment per pull request, with the flight records attached.

Set up CI