refactor(test): simplify keyboard input handling and extract type definitions

Remove reliance on key.name and key.sequence in favor of using input directly.
Extract TTY type definitions for clarity and add scoping constraints to prevent
variable shadowing. Update AGENTS.md documentation for accuracy.
This commit is contained in:
weishu
2025-12-24 13:34:55 +08:00
parent 1a23bfa430
commit c09b870fe7
3 changed files with 27 additions and 32 deletions
+21 -22
View File
@@ -6,8 +6,6 @@ import { useSwitchControls, type ConfirmationMode, type ActionInProgress } from
type Key = {
ctrl?: boolean;
name?: string;
sequence?: string;
};
type SwitchState = {
@@ -27,24 +25,24 @@ vi.mock('ink', async () => {
};
});
type TtyWriteStream = NodeJS.WriteStream & {
isTTY?: boolean;
columns?: number;
rows?: number;
};
type TtyReadStream = NodeJS.ReadStream & {
isTTY?: boolean;
};
const createInkStreams = (): {
stdout: NodeJS.WriteStream;
stderr: NodeJS.WriteStream;
stdin: NodeJS.ReadStream;
} => {
const stdout = new PassThrough() as NodeJS.WriteStream & {
isTTY?: boolean;
columns?: number;
rows?: number;
};
const stderr = new PassThrough() as NodeJS.WriteStream & {
isTTY?: boolean;
columns?: number;
rows?: number;
};
const stdin = new PassThrough() as NodeJS.ReadStream & {
isTTY?: boolean;
};
const stdout = new PassThrough() as unknown as TtyWriteStream;
const stderr = new PassThrough() as unknown as TtyWriteStream;
const stdin = new PassThrough() as unknown as TtyReadStream;
Object.assign(stdout, { isTTY: true, columns: 80, rows: 24 });
Object.assign(stderr, { isTTY: true, columns: 80, rows: 24 });
@@ -143,10 +141,11 @@ describe('useSwitchControls', () => {
vi.runOnlyPendingTimers();
});
if (renderer) {
const activeRenderer = renderer;
await act(async () => {
renderer.unmount();
activeRenderer.unmount();
});
renderer.cleanup();
activeRenderer.cleanup();
renderer = null;
}
vi.useRealTimers();
@@ -158,7 +157,7 @@ describe('useSwitchControls', () => {
const killSpy = vi.spyOn(process, 'kill').mockImplementation(() => true);
await mount({ onSwitch });
await triggerInput(' ', { name: 'space' });
await triggerInput(' ', {});
expect(latestState?.confirmationMode).toBe('switch');
expect(latestState?.actionInProgress).toBe(null);
expect(onSwitch).not.toHaveBeenCalled();
@@ -187,10 +186,10 @@ describe('useSwitchControls', () => {
const onSwitch = vi.fn();
await mount({ onSwitch });
await triggerInput(' ', { name: 'space' });
await triggerInput(' ', {});
expect(latestState?.confirmationMode).toBe('switch');
await triggerInput('', { sequence: '\u001b[1:3u' });
await triggerInput('\u001b[1:3u', {});
expect(latestState?.confirmationMode).toBe('switch');
expect(latestState?.actionInProgress).toBe(null);
});
@@ -199,7 +198,7 @@ describe('useSwitchControls', () => {
const onSwitch = vi.fn();
await mount({ onSwitch });
await triggerInput('', { sequence: '\u001b[3:3u', name: 'space' });
await triggerInput('\u001b[3:3u', {});
expect(onSwitch).not.toHaveBeenCalled();
expect(latestState?.confirmationMode).toBe(null);
});
@@ -208,7 +207,7 @@ describe('useSwitchControls', () => {
const onSwitch = vi.fn();
await mount({ onSwitch });
await triggerInput(' ', { name: 'space' });
await triggerInput(' ', {});
expect(latestState?.confirmationMode).toBe('switch');
await advanceTimers(5000);
+2 -3
View File
@@ -72,9 +72,8 @@ export function useSwitchControls(opts: {
return;
}
const sequence = typeof key.sequence === 'string' ? key.sequence : input;
const isKeyRelease = typeof sequence === 'string' && /^\u001b\[[0-9;]*:3u$/.test(sequence);
const isSpace = Boolean(onSwitch) && !isKeyRelease && (input === ' ' || key.name === 'space');
const isKeyRelease = /^\u001b\[[0-9;]*:3u$/.test(input);
const isSpace = Boolean(onSwitch) && !isKeyRelease && input === ' ';
const hasPrintableInput = typeof input === 'string' && input.length > 0;
if (isSpace) {