feat(cli): implement logical component tracking for visual journey testing

- Expose Ink rootNode in AppRig and render utilities for tree traversal
- Add toVisuallyContain matcher to verify component presence in the Ink DOM
- Inject component metadata as comments into generated SVGs for auditing
- Add waitForComponent to AppRig for deterministic UI state synchronization
- Implement visual journey test for SuggestionsDisplay
This commit is contained in:
Taylor Mullen
2026-03-17 17:01:04 -07:00
parent c8d18eb2ac
commit 800aea6cfb
7 changed files with 275 additions and 3 deletions
@@ -0,0 +1,56 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import { AppRig } from '../../test-utils/AppRig.js';
import { SuggestionsDisplay } from './SuggestionsDisplay.js';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
describe('SuggestionsDisplay UX Journey', () => {
let rig: AppRig;
beforeEach(async () => {
const fakeResponsesPath = path.join(
__dirname,
'..',
'..',
'test-utils',
'fixtures',
'simple.responses',
);
rig = new AppRig({ fakeResponsesPath });
await rig.initialize();
rig.render();
await rig.waitForIdle();
});
afterEach(async () => {
await rig.unmount();
});
it('should visually show the suggestions display when / is typed', async () => {
// Initially should not have suggestions
expect(rig).not.toVisuallyContain(SuggestionsDisplay.name);
// Type '/' to trigger suggestions
await rig.type('/');
// Wait for SuggestionsDisplay to appear (Automatic lookup!)
await rig.waitForComponent(SuggestionsDisplay.name);
// Assert that the component is now present in the tree
expect(rig).toVisuallyContain(SuggestionsDisplay.name);
// Also verify text for sanity
expect(rig.lastFrame).toContain('about');
// Capture the state for manual inspection if needed
await expect(rig).toMatchSvgSnapshot({ name: 'suggestions-opened' });
});
});
@@ -0,0 +1,44 @@
<svg xmlns="http://www.w3.org/2000/svg" width="1100" height="581" viewBox="0 0 1100 581">
<style>
text { font-family: Consolas, "Courier New", monospace; font-size: 14px; dominant-baseline: text-before-edge; white-space: pre; }
</style>
<rect width="1100" height="581" fill="#000000" />
<g transform="translate(10, 10)">
<text x="18" y="19" fill="#4796e4" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="27" y="19" fill="#6688d9" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="36" y="19" fill="#847ace" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="63" y="19" fill="#ffffff" textLength="1017" lengthAdjust="spacingAndGlyphs"> Gemini CLI vtest-version </text>
<text x="36" y="36" fill="#847ace" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="45" y="36" fill="#a471a7" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="54" y="36" fill="#c3677f" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="27" y="53" fill="#6688d9" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="36" y="53" fill="#847ace" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="45" y="53" fill="#a471a7" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="63" y="53" fill="#ffffff" textLength="1017" lengthAdjust="spacingAndGlyphs"> Authenticated with gemini-api-key /auth </text>
<text x="18" y="70" fill="#4796e4" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="27" y="70" fill="#6688d9" textLength="9" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="121" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs">Tips for getting started: </text>
<text x="0" y="138" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs">1. Create GEMINI.md files to customize your interactions </text>
<text x="0" y="155" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs">2. /help for more information </text>
<text x="0" y="172" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs">3. Ask coding questions, edit code or run commands </text>
<text x="0" y="189" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs">4. Be specific for the best results </text>
<text x="0" y="240" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs">──────────────────────────────────────────────────────────── </text>
<text x="0" y="257" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> Shift+Tab to accept edits </text>
<text x="0" y="308" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs">▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ </text>
<text x="0" y="325" fill="#ffffff" textLength="36" lengthAdjust="spacingAndGlyphs"> &gt; /</text>
<rect x="36" y="323" width="9" height="17" fill="#ffffff" />
<text x="0" y="342" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs">▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄ </text>
<text x="0" y="359" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> about Show version info </text>
<text x="0" y="376" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> agents Manage agents </text>
<text x="0" y="393" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> auth Manage authentication </text>
<text x="0" y="410" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> bug Submit a bug report </text>
<text x="0" y="427" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> chat Browse auto-saved conversations and ma… </text>
<text x="0" y="444" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> clear Clear the screen and conversation hist… </text>
<text x="0" y="461" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> commands Manage custom slash commands. Usage: /… </text>
<text x="0" y="478" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> compress Compresses the context by replacing it… </text>
<text x="0" y="495" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"></text>
<text x="0" y="512" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> (1/38) </text>
<text x="0" y="529" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> workspace (/directory) sandbox /model </text>
<text x="0" y="546" fill="#ffffff" textLength="1080" lengthAdjust="spacingAndGlyphs"> ~ no sandbox test-model </text>
</g>
</svg>

After

Width:  |  Height:  |  Size: 6.8 KiB

@@ -0,0 +1,38 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports[`SuggestionsDisplay UX Journey > should visually show the suggestions display when / is typed 1`] = `
"
▝▜▄ Gemini CLI vtest-version
▝▜▄
▗▟▀ Authenticated with gemini-api-key /auth
▝▀
Tips for getting started:
1. Create GEMINI.md files to customize your interactions
2. /help for more information
3. Ask coding questions, edit code or run commands
4. Be specific for the best results
────────────────────────────────────────────────────────────
Shift+Tab to accept edits
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
> /
▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
about Show version info
agents Manage agents
auth Manage authentication
bug Submit a bug report
chat Browse auto-saved conversations and ma…
clear Clear the screen and conversation hist…
commands Manage custom slash commands. Usage: /…
compress Compresses the context by replacing it…
(1/38)
workspace (/directory) sandbox /model
~ no sandbox test-model
"
`;