|
1 | 1 | import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; |
2 | 2 |
|
3 | | -import { KimiTUI, type KimiTUIStartupInput } from '#/tui/kimi-tui'; |
| 3 | +import { KimiTUI, type KimiTUIStartupInput, type TUIState } from '#/tui/kimi-tui'; |
4 | 4 |
|
5 | 5 | interface SignalDriver { |
| 6 | + state: TUIState; |
6 | 7 | registerSignalHandlers(): void; |
7 | 8 | unregisterSignalHandlers(): void; |
8 | 9 | emergencyTerminalExit(): never; |
@@ -298,6 +299,27 @@ describe('KimiTUI signal handlers', () => { |
298 | 299 | expect(process.listenerCount('SIGTERM')).toBe(beforeSigterm); |
299 | 300 | }); |
300 | 301 |
|
| 302 | + it('stop() drains terminal input before stopping the UI and exiting', async () => { |
| 303 | + const { driver, tui } = makeDriver(); |
| 304 | + const events: string[] = []; |
| 305 | + const drainInput = vi.spyOn(driver.state.terminal, 'drainInput').mockImplementation(async () => { |
| 306 | + events.push('drain'); |
| 307 | + }); |
| 308 | + const uiStop = vi.spyOn(driver.state.ui, 'stop').mockImplementation(() => { |
| 309 | + events.push('ui.stop'); |
| 310 | + }); |
| 311 | + tui.onExit = vi.fn(async () => { |
| 312 | + events.push('exit'); |
| 313 | + }); |
| 314 | + |
| 315 | + await tui.stop(); |
| 316 | + |
| 317 | + expect(drainInput).toHaveBeenCalledOnce(); |
| 318 | + expect(uiStop).toHaveBeenCalledOnce(); |
| 319 | + expect(tui.onExit).toHaveBeenCalledOnce(); |
| 320 | + expect(events).toEqual(['drain', 'ui.stop', 'exit']); |
| 321 | + }); |
| 322 | + |
301 | 323 | it('start() unregisters signal handlers when initialization throws', async () => { |
302 | 324 | const { tui } = makeDriver(); |
303 | 325 | // Force the very first awaited call inside start() to reject. We don't |
|
0 commit comments