test(integration): Add "Ctrl + C" to exit integration test (#9272)

This commit is contained in:
David East
2025-09-23 18:40:20 -04:00
committed by GitHub
parent 03bd6876d2
commit 38e053b7bb
2 changed files with 90 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect } from 'vitest';
import { TestRig } from './test-helper.js';
describe('Ctrl+C exit', () => {
it('should exit gracefully on second Ctrl+C', async () => {
const rig = new TestRig();
await rig.setup('should exit gracefully on second Ctrl+C');
const { ptyProcess, promise } = rig.runInteractive();
let output = '';
ptyProcess.onData((data) => {
output += data;
});
// Wait for the app to be ready by looking for the initial prompt indicator
await rig.poll(() => output.includes('▶'), 5000, 100);
// Send first Ctrl+C
ptyProcess.write('\x03');
// Wait for the exit prompt
await rig.poll(
() => output.includes('Press Ctrl+C again to exit'),
1500,
50,
);
// Send second Ctrl+C
ptyProcess.write('\x03');
const result = await promise;
// Expect a graceful exit (code 0)
expect(
result.exitCode,
`Process exited with code ${result.exitCode}. Output: ${result.output}`,
).toBe(0);
// Check that the quitting message is displayed
const quittingMessage = 'Agent powering down. Goodbye!';
// The regex below is intentionally matching the ESC control character (\x1b)
// to strip ANSI color codes from the terminal output.
// eslint-disable-next-line no-control-regex
const cleanOutput = output.replace(/\x1b\[[0-9;]*m/g, '');
expect(cleanOutput).toContain(quittingMessage);
});
});

View File

@@ -10,6 +10,7 @@ import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { env } from 'node:process';
import fs from 'node:fs';
import * as pty from '@lydell/node-pty';
const __dirname = dirname(fileURLToPath(import.meta.url));
@@ -719,4 +720,39 @@ export class TestRig {
}
return lastApiRequest;
}
runInteractive(...args: string[]): {
ptyProcess: pty.IPty;
promise: Promise<{ exitCode: number; signal?: number; output: string }>;
} {
const commandArgs = [this.bundlePath, '--yolo', ...args];
const ptyProcess = pty.spawn('node', commandArgs, {
name: 'xterm-color',
cols: 80,
rows: 30,
cwd: this.testDir!,
env: process.env as { [key: string]: string },
});
let output = '';
ptyProcess.onData((data) => {
output += data;
if (env.KEEP_OUTPUT === 'true' || env.VERBOSE === 'true') {
process.stdout.write(data);
}
});
const promise = new Promise<{
exitCode: number;
signal?: number;
output: string;
}>((resolve) => {
ptyProcess.onExit(({ exitCode, signal }) => {
resolve({ exitCode, signal, output });
});
});
return { ptyProcess, promise };
}
}