test(tui): screenshot snapshot tests for OutroScreen / RegionSelect / Logout / SettingsOverride#262
Conversation
🧙 Wizard CIRun the Wizard CI and test your changes against wizard-workbench example apps by replying with a GitHub comment using one of the following commands: Test all apps:
Test all apps in a directory:
Test an individual app:
Show more apps
Results will be posted here when complete. |
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Autofix Details
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: Exported
SnapshotOptionsinterface is unused dead code- Removed the unused
SnapshotOptionsinterface and its sole consumer, theFlowtype import, since neither was referenced anywhere in the codebase.
- Removed the unused
Or push these changes by commenting:
@cursor push c3d530324c
Preview (c3d530324c)
diff --git a/src/ui/tui/__tests__/snapshot-utils.tsx b/src/ui/tui/__tests__/snapshot-utils.tsx
--- a/src/ui/tui/__tests__/snapshot-utils.tsx
+++ b/src/ui/tui/__tests__/snapshot-utils.tsx
@@ -40,7 +40,6 @@
import type { ReactElement } from 'react';
import { WizardStore } from '../store.js';
import type { WizardSession } from '../store.js';
-import type { Flow } from '../router.js';
/**
* Strip ANSI color/style escapes so snapshots are readable in PR reviews.
@@ -62,20 +61,6 @@
.map((line) => line.replace(/[ \t]+$/, ''))
.join('\n');
-export interface SnapshotOptions {
- /**
- * Optional session patch applied before render. Use this to control
- * which branch of the screen is exercised — e.g. `{ outroData: { ... } }`
- * for OutroScreen success vs error vs cancel.
- */
- sessionPatch?: Partial<WizardSession>;
- /**
- * Flow to construct the WizardStore with. Defaults to `Wizard`. Override
- * for screens that only render under specific flows (e.g. SUSI).
- */
- flow?: Flow;
-}
-
export interface RenderedSnapshot {
/** Sanitized terminal output, ready to feed `toMatchSnapshot()`. */
frame: string;You can send follow-ups to the cloud agent here.
… Logout / SettingsOverride
The TUI is a chain of Ink screens (`src/ui/tui/screens/`). Today there are
no rendered-output tests — only assertions on a few pure helpers. A
change to layout, copy, padding, or color silently slips through unless
someone happens to run `pnpm try` and notice. Snapshot tests catch
unintended visual regressions: every PR that changes a rendered frame
fails until reviewed and updated with `pnpm test -u`.
What's in:
src/ui/tui/__tests__/snapshot-utils.tsx
- renderSnapshot(element, store) — renders via ink-testing-library at
a deterministic fake-stdout, strips ANSI color escapes, trims
trailing whitespace per line. Output is identical on macOS / Linux /
CI so snapshots don't drift.
- makeStoreForSnapshot(patch) — builds a fresh WizardStore with an
optional WizardSession patch so tests can target specific branches
(success / error / cancel, region forced, settings conflict, ...)
without exercising the full wizard flow.
src/ui/tui/screens/__tests__/OutroScreen.snap.test.tsx (3 snaps)
src/ui/tui/screens/__tests__/RegionSelectScreen.snap.test.tsx (2 snaps)
src/ui/tui/screens/__tests__/LogoutScreen.snap.test.tsx (1 snap)
src/ui/tui/screens/__tests__/SettingsOverrideScreen.snap.test.tsx (2 snaps)
vitest.config.ts — include *.test.tsx so JSX tests are picked up.
package.json — adds ink-testing-library as devDep.
The 8 snapshots cover the highest-churn TUI surfaces today: the success
/ error / cancel outros, the first-time and /region-forced region
pickers, the logout confirmation, and the settings-override blocking
modal. Helper makes it ~5 minutes per additional screen to add coverage.
Updating a snapshot intentionally:
pnpm test -u # vitest --update; review all diffs first
# or:
pnpm vitest run src/ui/tui/screens/__tests__/OutroScreen.snap.test.tsx -u
Stable across runs:
- Width pinned by ink-testing-library's fake stdout
- ANSI escapes stripped (chalk colors)
- Trailing whitespace trimmed (Ink right-pads to terminal width)
- Spinners/animation NOT exercised — caller pre-sets stable session state
Tests: +12 new (1245 total). Suite green, lint clean.
Independent of the sub-agent stack and the wizard-cleanup PR — lands
straight onto main.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
59a3f03 to
647fa0b
Compare
Bugbot caught: SnapshotOptions was exported but never imported by any consumer. The Flow type import existed solely to type its `flow` field. makeStoreForSnapshot accepts Partial<WizardSession> directly, so the interface is dead code.
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
Bugbot Autofix prepared a fix for the issue found in the latest run.
- ✅ Fixed: ANSI regex misses OSC hyperlink sequences from TerminalLink
- Expanded ANSI_REGEX to match OSC sequences (ESC ] ... BEL/ST) in addition to CSI sequences, ensuring terminal-link's OSC 8 hyperlink escapes are stripped for deterministic snapshots.
Or push these changes by commenting:
@cursor push 6d9509edd4
Preview (6d9509edd4)
diff --git a/src/ui/tui/__tests__/snapshot-utils.tsx b/src/ui/tui/__tests__/snapshot-utils.tsx
--- a/src/ui/tui/__tests__/snapshot-utils.tsx
+++ b/src/ui/tui/__tests__/snapshot-utils.tsx
@@ -42,11 +42,12 @@
import type { WizardSession } from '../store.js';
/**
- * Strip ANSI color/style escapes so snapshots are readable in PR reviews.
- * Pattern matches CSI sequences: ESC [ <params> <command-byte>.
+ * Strip ANSI escape sequences so snapshots are readable and deterministic.
+ * Matches CSI sequences (ESC [ ... letter) and OSC sequences
+ * (ESC ] ... BEL/ST) such as the OSC 8 hyperlinks emitted by terminal-link.
*/
// eslint-disable-next-line no-control-regex
-const ANSI_REGEX = /\x1b\[[0-9;]*[A-Za-z]/g;
+const ANSI_REGEX = /\x1b(?:\[[0-9;]*[A-Za-z]|\].*?(?:\x07|\x1b\\))/g;
const stripAnsi = (s: string): string => s.replace(ANSI_REGEX, '');
/**You can send follow-ups to the cloud agent here.
Reviewed by Cursor Bugbot for commit 1c754a1. Configure here.
Bugbot caught: ANSI_REGEX only stripped CSI sequences (ESC [ ...). OutroScreen's error and cancel views render TerminalLink, which emits OSC 8 hyperlink escapes (\x1b]8;;URL\x07text\x1b]8;;\x07) when supports-hyperlinks detects a capable terminal. Updating snapshots on iTerm2/VS Code (TTY=true) would have embedded unstripped OSC sequences; CI (TTY=false) then diffs and fails with confusing output. Fix: add a second regex pass for OSC sequences (ESC ] ... BEL) so snapshots are deterministic regardless of whether the test was generated on a TTY-capable terminal.


Summary
The TUI is a chain of Ink screens (
src/ui/tui/screens/). Today there are no rendered-output tests — only assertions on a few pure helpers. A change to layout, copy, padding, or color silently slips through unless someone happens to runpnpm tryand notice. Snapshot tests catch unintended visual regressions: every PR that changes a rendered frame fails until reviewed and updated withpnpm test -u.This PR is independent of the sub-agent stack and the wizard-cleanup PR — lands straight onto
main, can review and merge in parallel with everything else in the queue.What's in
src/ui/tui/__tests__/snapshot-utils.tsx(new) — two small helpers:renderSnapshot(element, store)— renders viaink-testing-library, strips ANSI color escapes, trims trailing whitespace per line. Output is identical on macOS / Linux / CI so snapshots don't drift.makeStoreForSnapshot(patch)— freshWizardStorewith optionalWizardSessionoverrides so tests can target specific branches (success/error/cancel, region forced, settings conflict, …) without running the wizard flow.OutroScreen— success (with changes + report link), error (with docs fallback), cancelRegionSelectScreen— first-time picker,/region-forced switchLogoutScreen— initial confirm promptSettingsOverrideScreen— empty (no overrides) and conflict modalvitest.config.ts— include*.test.tsxso JSX tests are picked upink-testing-libraryadded as devDep (5 KB, stable)Why these 4 screens first
These are the highest-blast-radius surfaces today. Every PR that touches signup, region resolution, settings handling, or auth has touched one of them in the last month. Snapshot coverage here would have caught accidental copy regressions in #156, #234, #237, and the env-selection prompt rewording in #257.
Sample snapshot — what reviewers see in PRs
Reviewer can read it, spot the change, and decide whether to approve or request rework — no need to checkout the branch and
pnpm try.Updating a snapshot intentionally
Determinism guarantees
ink-testing-library's fake stdout (no real TTY)sessionPatch. Adding a screen with animation? Passframe={0}toBrailleSpinner.Test plan
pnpm test— 1245 passed, 17 skipped (12 new tests across 4 new files)pnpm tsc --noEmitcleanpnpm lintcleanpnpm test -uregenerates all 8 snapshots cleanlyOut of scope (follow-up — ~5 min per screen)
IntroScreen,AuthScreen,RunScreen,McpScreen,SetupScreen,CreateProjectScreen,DataSetupScreen,DataIngestionCheckScreen,LoginScreen,SlackScreen,OutageScreen,FeatureOptInScreen,ActivationOptionsScreen. Each takes ~1 sessionPatch + 1-3it()blocks. Land them as the screens are touched.AmplitudeTextLogocollapses). The helper supports it via additionalrenderoptions; not exercised here.cc @amplitude/growth
🤖 Generated with Claude Code
Note
Low Risk
Low risk: changes are limited to dev dependencies and Vitest configuration, plus new snapshot tests/utilities that don’t affect production runtime behavior.
Overview
Adds deterministic snapshot testing for the Ink-based TUI by introducing a
snapshot-utilshelper that renders screens viaink-testing-libraryand normalizes output (strips ANSI escapes and trims trailing whitespace).Introduces snapshot tests (and committed snapshots) for
OutroScreen,RegionSelectScreen,LogoutScreen, andSettingsOverrideScreenacross several key states, and updatesvitest.config.tsto include*.test.tsx/*.spec.tsxso the new JSX tests run.Reviewed by Cursor Bugbot for commit 45f7c6f. Bugbot is set up for automated code reviews on this repo. Configure here.