diff --git a/packages/cli/src/test-utils/render.tsx b/packages/cli/src/test-utils/render.tsx index ac6862e893..2375a0fba1 100644 --- a/packages/cli/src/test-utils/render.tsx +++ b/packages/cli/src/test-utils/render.tsx @@ -76,7 +76,6 @@ class XtermStdout extends EventEmitter { isTTY = true; private lastRenderOutput: string | undefined = undefined; - private lastRenderStaticContent: string | undefined = undefined; constructor(state: TerminalState, queue: { promise: Promise }) { super(); @@ -109,7 +108,6 @@ class XtermStdout extends EventEmitter { clear = () => { this.state.terminal.reset(); this.lastRenderOutput = undefined; - this.lastRenderStaticContent = undefined; }; dispose = () => { @@ -118,33 +116,23 @@ class XtermStdout extends EventEmitter { onRender = (staticContent: string, output: string) => { this.renderCount++; - this.lastRenderStaticContent = staticContent; this.lastRenderOutput = output; this.emit('render'); }; lastFrame = (options: { allowEmpty?: boolean } = {}) => { - let result: string; - // On Windows, xterm.js headless can sometimes have timing or rendering issues - // that lead to duplicated content or incorrect buffer state in tests. - // As a fallback, we can trust the raw output Ink provided during onRender. - if (os.platform() === 'win32') { - result = - (this.lastRenderStaticContent ?? '') + (this.lastRenderOutput ?? ''); - } else { - const buffer = this.state.terminal.buffer.active; - const allLines: string[] = []; - for (let i = 0; i < buffer.length; i++) { - allLines.push(buffer.getLine(i)?.translateToString(true) ?? ''); - } - - const trimmed = [...allLines]; - while (trimmed.length > 0 && trimmed[trimmed.length - 1] === '') { - trimmed.pop(); - } - result = trimmed.join('\n'); + const buffer = this.state.terminal.buffer.active; + const allLines: string[] = []; + for (let i = 0; i < buffer.length; i++) { + allLines.push(buffer.getLine(i)?.translateToString(true) ?? ''); } + const trimmed = [...allLines]; + while (trimmed.length > 0 && trimmed[trimmed.length - 1] === '') { + trimmed.pop(); + } + const result = trimmed.join('\n'); + // Normalize for cross-platform snapshot stability: // Normalize any \r\n to \n const normalized = result.replace(/\r\n/g, '\n'); @@ -195,9 +183,7 @@ class XtermStdout extends EventEmitter { const currentFrame = stripAnsi( this.lastFrame({ allowEmpty: true }), ).trim(); - const expectedFrame = stripAnsi( - (this.lastRenderStaticContent ?? '') + (this.lastRenderOutput ?? ''), - ) + const expectedFrame = stripAnsi(this.lastRenderOutput ?? '') .trim() .replace(/\r\n/g, '\n'); @@ -336,7 +322,11 @@ export const render = ( terminalWidth?: number, ): RenderInstance => { const cols = terminalWidth ?? 100; - const rows = 40; + // We use 1000 rows to avoid windows with incorrect snapshots if a correct + // value was used (e.g. 40 rows). The alternatives to make things worse are + // windows unfortunately with odd duplicate content in the backbuffer + // which does not match actual behavior in xterm.js on windows. + const rows = 1000; const terminal = new Terminal({ cols, rows, diff --git a/packages/cli/src/ui/components/__snapshots__/ConfigInitDisplay.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/ConfigInitDisplay.test.tsx.snap index 8d03baaa49..28929deee5 100644 --- a/packages/cli/src/ui/components/__snapshots__/ConfigInitDisplay.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/ConfigInitDisplay.test.tsx.snap @@ -18,8 +18,20 @@ Spinner Connecting to MCP servers... (0/5) - Waiting for: s1, s2, s3, +2 more " `; +exports[`ConfigInitDisplay > truncates list of waiting servers if too many 2`] = ` +" +Spinner Connecting to MCP servers... (0/5) - Waiting for: s1, s2, s3, +2 more +" +`; + exports[`ConfigInitDisplay > updates message on McpClientUpdate event 1`] = ` " Spinner Connecting to MCP servers... (1/2) - Waiting for: server2 " `; + +exports[`ConfigInitDisplay > updates message on McpClientUpdate event 2`] = ` +" +Spinner Connecting to MCP servers... (1/2) - Waiting for: server2 +" +`; diff --git a/packages/cli/src/ui/components/__snapshots__/ExitPlanModeDialog.test.tsx.snap b/packages/cli/src/ui/components/__snapshots__/ExitPlanModeDialog.test.tsx.snap index 587ded8f29..faa759a050 100644 --- a/packages/cli/src/ui/components/__snapshots__/ExitPlanModeDialog.test.tsx.snap +++ b/packages/cli/src/ui/components/__snapshots__/ExitPlanModeDialog.test.tsx.snap @@ -27,6 +27,33 @@ Enter to select · ↑/↓ to navigate · Esc to cancel " `; +exports[`ExitPlanModeDialog > useAlternateBuffer: false > bubbles up Ctrl+C when feedback is empty while editing 2`] = ` +"Overview + +Add user authentication to the CLI application. + +Implementation Steps + + 1. Create src/auth/AuthService.ts with login/logout methods + 2. Add session storage in src/storage/SessionStore.ts + 3. Update src/commands/index.ts to check auth status + 4. Add tests in src/auth/__tests__/ + +Files to Modify + + - src/index.ts - Add auth middleware + - src/config.ts - Add auth configuration options + + 1. Yes, automatically accept edits + Approves plan and allows tools to run automatically + 2. Yes, manually accept edits + Approves plan but requires confirmation for each tool +● 3. Type your feedback... + +Enter to submit · Esc to cancel +" +`; + exports[`ExitPlanModeDialog > useAlternateBuffer: false > calls onFeedback when feedback is typed and submitted 1`] = ` "Overview @@ -54,6 +81,33 @@ Enter to select · ↑/↓ to navigate · Esc to cancel " `; +exports[`ExitPlanModeDialog > useAlternateBuffer: false > calls onFeedback when feedback is typed and submitted 2`] = ` +"Overview + +Add user authentication to the CLI application. + +Implementation Steps + + 1. Create src/auth/AuthService.ts with login/logout methods + 2. Add session storage in src/storage/SessionStore.ts + 3. Update src/commands/index.ts to check auth status + 4. Add tests in src/auth/__tests__/ + +Files to Modify + + - src/index.ts - Add auth middleware + - src/config.ts - Add auth configuration options + + 1. Yes, automatically accept edits + Approves plan and allows tools to run automatically + 2. Yes, manually accept edits + Approves plan but requires confirmation for each tool +● 3. Add tests + +Enter to submit · Esc to cancel +" +`; + exports[`ExitPlanModeDialog > useAlternateBuffer: false > displays error state when file read fails 1`] = ` " Error reading plan: File not found " @@ -140,6 +194,33 @@ Enter to select · ↑/↓ to navigate · Esc to cancel " `; +exports[`ExitPlanModeDialog > useAlternateBuffer: true > bubbles up Ctrl+C when feedback is empty while editing 2`] = ` +"Overview + +Add user authentication to the CLI application. + +Implementation Steps + + 1. Create src/auth/AuthService.ts with login/logout methods + 2. Add session storage in src/storage/SessionStore.ts + 3. Update src/commands/index.ts to check auth status + 4. Add tests in src/auth/__tests__/ + +Files to Modify + + - src/index.ts - Add auth middleware + - src/config.ts - Add auth configuration options + + 1. Yes, automatically accept edits + Approves plan and allows tools to run automatically + 2. Yes, manually accept edits + Approves plan but requires confirmation for each tool +● 3. Type your feedback... + +Enter to submit · Esc to cancel +" +`; + exports[`ExitPlanModeDialog > useAlternateBuffer: true > calls onFeedback when feedback is typed and submitted 1`] = ` "Overview @@ -167,6 +248,33 @@ Enter to select · ↑/↓ to navigate · Esc to cancel " `; +exports[`ExitPlanModeDialog > useAlternateBuffer: true > calls onFeedback when feedback is typed and submitted 2`] = ` +"Overview + +Add user authentication to the CLI application. + +Implementation Steps + + 1. Create src/auth/AuthService.ts with login/logout methods + 2. Add session storage in src/storage/SessionStore.ts + 3. Update src/commands/index.ts to check auth status + 4. Add tests in src/auth/__tests__/ + +Files to Modify + + - src/index.ts - Add auth middleware + - src/config.ts - Add auth configuration options + + 1. Yes, automatically accept edits + Approves plan and allows tools to run automatically + 2. Yes, manually accept edits + Approves plan but requires confirmation for each tool +● 3. Add tests + +Enter to submit · Esc to cancel +" +`; + exports[`ExitPlanModeDialog > useAlternateBuffer: true > displays error state when file read fails 1`] = ` " Error reading plan: File not found "