Test Results
Show an agent’s test run as it happens. Tests stream into their files, then fold into “118 passed, 2 failed in 4.2s” with every failure still open. Word-level Expected and Received diffs, timeouts and snapshots, stacks from Stack Trace, filters, Rerun failed and Ask to fix, plus adapters for Vitest, Jest, Playwright and pytest.
When an agent runs your tests, the chat should show what a developer looks for: how far along it is, what failed and why. While tests run, Test Results shows a live “Running tests · 42 of 120” line with counts that tick digit by digit and a bar split into passed, failed and skipped. Tests stream into their files as they finish, and the file that’s running stays open.
When the run ends, the line turns into a past-tense summary such as “118 passed, 2 failed in 4.2s”. A clean run folds away. A failing one stays open with passing files folded and failing files open, unless you opened or closed them yourself. Each failure shows its message, a word-level Expected and Received diff and its stack through Stack Trace, with your frames first and framework frames folded. Timeouts and snapshot mismatches get their own wording.
Filter by All, Failed, Skipped or Slow, copy a failure as text, open frames in your editor, and send Ask to fix, Rerun failed, Rerun and Update snapshot back to the agent. fromVitestJson, fromJestJson, fromPlaywrightJson and fromJUnitXml turn reports into a run, and applyTestEvent folds live events into one.
Get HextaUI Pro
One payment unlocks every block, including future ones. See pricing.
Add the Pro registry to components.json
components.json Add your token
Create a token on your account page and put it in
.env.localasHEXTAUI_PRO_TOKEN.Add the block
pnpm dlx shadcn@latest add @hextaui-pro/test-results
With the AI SDK
Make runTests a tool whose execute yields the run after every reporter event, so each update arrives as a preliminary output. Render it inside a ToolCall that stays open when tests fail, and send Ask to fix and Rerun failed back to the agent as messages.
From a saved report
fromVitestJson and fromJestJson read the JSON reporter’s output. Pass a root to trim absolute paths, and getEditorHref to open frames in Cursor or VS Code.
pytest and JUnit XML
fromJUnitXml reads pytest --junitxml and other JUnit reports. Test classes become describe blocks, the failing source shows as code, and Rerun failed hands you the node ids.
From your own events
Send run-start, test-start, test-end, file-end and run-end events from any runner and fold them with applyTestEvent. It’s pure, so it works in a reducer, a store or on the server.
Anatomy
The parts you compose, from the outside in.
| Part | Description |
|---|---|
TestResults | The run: live header, progress, filter, actions and files. |
ValueDiff | Expected and received, with the changed words marked. |
applyTestEvent, createTestRun | Pure helpers that build a run from streamed events. |
fromVitestJson, fromJestJson, fromPlaywrightJson, fromJUnitXml | Adapters from reporter output to a run. |
summarizeRun, formatFailures | The summary line, and failures as plain text for a prompt or the clipboard. |
TestResults
Also accepts every section prop except children.
| Prop | Type | Default |
|---|---|---|
runThe run, from an adapter, applyTestEvent or your own data. | TestRun | – |
titleThe live label while tests run. | string | "Running tests" |
labelAccessible name of the region. | string | "Test results" |
slowThresholdTests that take at least this many milliseconds count as slow. | number | 300 |
rootProject root, stripped from paths in stacks. | string | – |
defaultOpenOverride whether the run is open. By default it stays open while running and when something failed. | boolean | – |
defaultFilterThe filter it starts with. | "all" | "failed" | "skipped" | "slow" | "all" |
onAskToFixShows Ask to fix on the run and on each failure. | (failed: FailedTest[]) => void | – |
onRerunFailedShows Rerun failed once the run ends with failures. | (failed: FailedTest[]) => void | – |
onRerunTestShows Rerun on each failure. | (failed: FailedTest) => void | – |
onUpdateSnapshotShows Update snapshot on snapshot mismatches. | (failed: FailedTest) => void | – |
getFrameHrefTurns stack frames into links, for example with getEditorHref. | (frame: StackFrame) => string | undefined | – |
onFrameClickCalled when a frame is clicked. | (frame: StackFrame, event: MouseEvent) => void | – |
| Prop | Type | Default |
|---|---|---|
statusWhether tests are still running. | "running" | "done" | "cancelled" | – |
filesFiles in the order they started, each with its tests, duration and an error if it failed to load. | TestFile[] | – |
totalHow many tests to expect, for “42 of 120”. Defaults to the counts collected per file. | number | – |
durationMilliseconds, for the summary. Measured while running when left out. | number | – |
startedAtWhen the run started, as a timestamp, for the live timer. | number | – |
| Prop | Type | Default |
|---|---|---|
nameThe test’s own name. | string | – |
suiteEnclosing describe blocks, outermost first. | string[] | – |
statusThe test’s state. | "queued" | "running" | "passed" | "failed" | "skipped" | "todo" | – |
durationMilliseconds. | number | – |
retriesHow many times it was retried. | number | – |
failureWhy it failed. | TestFailure | – |
| Prop | Type | Default |
|---|---|---|
kindPicks the wording, such as “Timed out after 5s” or “Snapshot mismatch”. | "assertion" | "snapshot" | "timeout" | "error" | – |
messageThe assertion message, without the Expected and Received lines. | string | – |
expected, receivedBoth sides of the comparison, read from the error, a diff or the message. | string | – |
stackText for parseStackTrace. Left out when there are no frames. | string | – |
detailsExtra output, such as pytest’s failing source. | string | – |
rawThe original text, used by Copy. | string | – |
applyTestEvent(run, event)
Returns a new run. createTestRun() gives an empty one to start from.
| Prop | Type | Default |
|---|---|---|
eventOne of run-start, file-start, test-start, test-end, file-end and run-end. Tests match by id, or by file, suite and name. | TestEvent | – |
event.errorA string, or an object with message, stack, expected, actual and diff, like Vitest’s TestError. | TestErrorInput | – |
fromVitestJson(report, options)
fromJestJson takes the same report. fromPlaywrightJson reads Playwright’s JSON reporter, and fromJUnitXml(xml, options) reads JUnit XML.
| Prop | Type | Default |
|---|---|---|
options.rootProject root to strip from file paths. Without it, paths start at the first folder such as src or tests. | string | – |
| Key | Action |
|---|---|
| Tab | Moves through the run header, the filter, the actions, each file and each failed test. |
| EnterSpace | Opens or closes the run, a file or a failure. |
| ←→ | Moves between filters. |
- The run is a labelled region with aria-busy while tests run. A polite live region announces the final summary once, so streaming tests and ticking counts stay quiet.
- The run header, each file and each failed test are disclosure buttons with aria-expanded. Their names read the summary, the file’s counts, or the test’s status and name.
- The filter is a toggle group. Arrow keys move between options, and filters with nothing in them are disabled.
- Status is never color alone: each state has its own mark, screen readers hear “Passed” or “Failed” before each test, Expected and Received are labelled, and changed words are underlined in forced colors. With reduced motion, rows and marks appear without sliding.
Built with
The free HextaUI components Test Results is made from. Each one installs on its own.
Code
7 files, added to components/blocks/test-results.