Skip to content

test: screenshot harness for the interactive UI - #122

Merged
hbrooks merged 1 commit into
mainfrom
screenshot-harness
Aug 20, 2026
Merged

test: screenshot harness for the interactive UI#122
hbrooks merged 1 commit into
mainfrom
screenshot-harness

Conversation

@hbrooks

@hbrooks hbrooks commented Aug 20, 2026

Copy link
Copy Markdown
Member

Important

Adds a screenshot harness for end-to-end testing of the interactive terminal UI.

  • New harness: test/screenshot.ts sets up a fake terminal emulator using @xterm/headless, feeds rendered ink output through it, and exposes page.text() (plain text for CI snapshots) and page.png(name) (PNG for visual review). Network and timers are stubbed. Window resize and key presses (named keys and literals) are supported.
  • First test suite: test/screenshot.test.ts covers three flows — new-session composer startup, session picker navigation with keyboard, and opening a session chat. Tests save both text snapshots (for assertion) and PNG images to test/__screenshots__/.
  • PNG rendering: Terminal grid is drawn cell-by-cell into an SVG with @resvg/resvg-js, capturing text content, colors, and style (bold, italic, underline) faithfully.
  • Dependencies: Adds @resvg/resvg-js@^2.6.2 and @xterm/headless@^6.0.0.
  • Artifacts: Screenshot artifacts are gitignored and regenerated on every test run.

This description was created by Ellipsis for 7171890. It will automatically update as commits are pushed.

@hbrooks
hbrooks merged commit b6f3283 into main Aug 20, 2026
1 check passed

@ellipsis-dev ellipsis-dev Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Changes requested ❌ — 1 issue

Reviewed 7171890 in 11 minutes, 19 seconds.
  • Reviewed 1 commit with 545 lines of code in 6 files
  • Ran 1 review agent producing 1 comment where 1 was posted
  • This pipeline runs no gatekeeper, so findings are posted as written.
  • View full details on ellipsis.dev

This review was created by Ellipsis. You can tag @ellipsis in this pull request.

Comment thread test/screenshot.ts
Comment on lines +91 to +94
// interactive pinned on: ink treats CI as non-interactive and would buffer
// one final frame — no repaints, no alt-screen hop, nothing this harness is
// for (same reason connect-render.test.ts pins it).
const app = render(element, { stdout, stdin, patchConsole: false, interactive: true })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The harness renders with every style stripped, so the PNGs are monochrome and the whole colour path is dead code: chalk sizes itself from the real process.stdout, which is never a TTY inside a vitest worker, so ink emits no SGR bytes at all. Pin the level in the harness.

Measured in this repo: under npx vitest run (piped and under a pty) chalk.level === 0, and ink's output for <Text color="#ff8800" bold> is literally HELLO with no escape. So renderPng's fg/bg, isInverse, bold/dim/italic/underline branches and the entire 256-colour palette never fire — the four committed screenshots are default-fg-on-default-bg, and a human reviewing one cannot see a colour, emphasis or surface regression. With chalk.level = 3 the same run renders the brand cursor, the bold header and the composer's inputSurface fill; note it also changes 2 of the 3 text snapshots (painted cells stop being trimmed as trailing whitespace), so those need regenerating with the fix.

Suggested change
// interactive pinned on: ink treats CI as non-interactive and would buffer
// one final frame — no repaints, no alt-screen hop, nothing this harness is
// for (same reason connect-render.test.ts pins it).
const app = render(element, { stdout, stdin, patchConsole: false, interactive: true })
// interactive pinned on: ink treats CI as non-interactive and would buffer
// one final frame — no repaints, no alt-screen hop, nothing this harness is
// for (same reason connect-render.test.ts pins it).
// Colour pinned on for the same class of reason: chalk sizes itself from the
// REAL process.stdout, which is never a TTY under vitest, so without this ink
// emits no SGR bytes and every screenshot comes out monochrome.
;(await import('chalk')).default.level = 3
const app = render(element, { stdout, stdin, patchConsole: false, interactive: true })

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant