mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-18 10:01:29 -07:00
Use raw writes to stdin where possible in tests (#11837)
This commit is contained in:
committed by
GitHub
parent
5e70a7dd46
commit
aa6ae954ef
@@ -31,33 +31,29 @@ vi.mock('ink', async (importOriginal) => {
|
||||
};
|
||||
});
|
||||
|
||||
const PASTE_START = '\x1B[200~';
|
||||
const PASTE_END = '\x1B[201~';
|
||||
|
||||
class MockStdin extends EventEmitter {
|
||||
isTTY = true;
|
||||
setRawMode = vi.fn();
|
||||
override on = this.addListener;
|
||||
override removeListener = super.removeListener;
|
||||
write = vi.fn();
|
||||
resume = vi.fn();
|
||||
pause = vi.fn();
|
||||
|
||||
// Helper to simulate a keypress event
|
||||
write(text: string) {
|
||||
this.emit('data', Buffer.from(text));
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to directly simulate keyPress events. Certain keypress events might
|
||||
* be impossible to fire in certain versions of node. This allows us to
|
||||
* sidestep readline entirely and just emit the keypress we want.
|
||||
*/
|
||||
pressKey(key: Partial<Key>) {
|
||||
this.emit('keypress', null, key);
|
||||
}
|
||||
|
||||
// Helper to simulate a kitty protocol sequence
|
||||
sendKittySequence(sequence: string) {
|
||||
this.emit('data', Buffer.from(sequence));
|
||||
}
|
||||
|
||||
// Helper to simulate a paste event
|
||||
sendPaste(text: string) {
|
||||
const PASTE_MODE_PREFIX = `\x1b[200~`;
|
||||
const PASTE_MODE_SUFFIX = `\x1b[201~`;
|
||||
this.emit('data', Buffer.from(PASTE_MODE_PREFIX));
|
||||
this.emit('data', Buffer.from(text));
|
||||
this.emit('data', Buffer.from(PASTE_MODE_SUFFIX));
|
||||
}
|
||||
}
|
||||
|
||||
describe('KeypressContext - Kitty Protocol', () => {
|
||||
@@ -94,13 +90,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
wrapper({ children, kittyProtocolEnabled: true }),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send kitty protocol sequence for regular enter: ESC[13u
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[13u`);
|
||||
stdin.write(`\x1b[13u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -122,13 +116,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
wrapper({ children, kittyProtocolEnabled: true }),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send kitty protocol sequence for numpad enter: ESC[57414u
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[57414u`);
|
||||
stdin.write(`\x1b[57414u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -150,13 +142,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
wrapper({ children, kittyProtocolEnabled: true }),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send kitty protocol sequence for numpad enter with Shift (modifier 2): ESC[57414;2u
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[57414;2u`);
|
||||
stdin.write(`\x1b[57414;2u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -178,13 +168,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
wrapper({ children, kittyProtocolEnabled: true }),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send kitty protocol sequence for numpad enter with Ctrl (modifier 5): ESC[57414;5u
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[57414;5u`);
|
||||
stdin.write(`\x1b[57414;5u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -206,13 +194,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
wrapper({ children, kittyProtocolEnabled: true }),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send kitty protocol sequence for numpad enter with Alt (modifier 3): ESC[57414;3u
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[57414;3u`);
|
||||
stdin.write(`\x1b[57414;3u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -234,13 +220,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
wrapper({ children, kittyProtocolEnabled: false }),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send kitty protocol sequence for numpad enter
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[57414u`);
|
||||
stdin.write(`\x1b[57414u`);
|
||||
});
|
||||
|
||||
// When kitty protocol is disabled, the sequence should be passed through
|
||||
@@ -263,13 +247,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
wrapper({ children, kittyProtocolEnabled: true }),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send kitty protocol sequence for escape: ESC[27u
|
||||
act(() => {
|
||||
stdin.sendKittySequence('\x1b[27u');
|
||||
stdin.write('\x1b[27u');
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -288,7 +270,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[9u`);
|
||||
stdin.write(`\x1b[9u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -307,7 +289,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
|
||||
// Modifier 2 is Shift
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[9;2u`);
|
||||
stdin.write(`\x1b[9;2u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -325,7 +307,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[127u`);
|
||||
stdin.write(`\x1b[127u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -344,7 +326,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
|
||||
// Modifier 3 is Alt/Option
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[127;3u`);
|
||||
stdin.write(`\x1b[127;3u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -363,7 +345,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
|
||||
// Modifier 5 is Ctrl
|
||||
act(() => {
|
||||
stdin.sendKittySequence(`\x1b[127;5u`);
|
||||
stdin.write(`\x1b[127;5u`);
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -385,13 +367,13 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
wrapper,
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Simulate a bracketed paste event
|
||||
act(() => {
|
||||
stdin.sendPaste(pastedText);
|
||||
stdin.write(PASTE_START);
|
||||
stdin.write(pastedText);
|
||||
stdin.write(PASTE_END);
|
||||
});
|
||||
|
||||
await waitFor(() => {
|
||||
@@ -437,13 +419,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send a kitty sequence
|
||||
act(() => {
|
||||
stdin.sendKittySequence('\x1b[27u');
|
||||
stdin.write('\x1b[27u');
|
||||
});
|
||||
|
||||
expect(keyHandler).toHaveBeenCalled();
|
||||
@@ -466,14 +446,10 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send a complete kitty sequence for escape
|
||||
act(() => {
|
||||
stdin.sendKittySequence('\x1b[27u');
|
||||
});
|
||||
act(() => stdin.write('\x1b[27u'));
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
'[DEBUG] Kitty buffer accumulating:',
|
||||
@@ -502,15 +478,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send a long sequence starting with a valid kitty prefix to trigger overflow
|
||||
const longSequence = '\x1b[1;' + '1'.repeat(100);
|
||||
act(() => {
|
||||
stdin.sendKittySequence(longSequence);
|
||||
});
|
||||
act(() => stdin.write(longSequence));
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
'[DEBUG] Kitty buffer overflow, clearing:',
|
||||
@@ -532,31 +504,13 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send incomplete kitty sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
sequence: '\x1b[1',
|
||||
});
|
||||
});
|
||||
act(() => stdin.pressKey({ sequence: '\x1b[1' }));
|
||||
|
||||
// Send Ctrl+C
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: 'c',
|
||||
ctrl: true,
|
||||
meta: false,
|
||||
shift: false,
|
||||
sequence: '\x03',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('\x03'));
|
||||
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
'[DEBUG] Kitty buffer cleared on Ctrl+C:',
|
||||
@@ -586,21 +540,11 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send incomplete kitty sequence
|
||||
const sequence = '\x1b[12';
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
sequence,
|
||||
});
|
||||
});
|
||||
act(() => stdin.pressKey({ sequence }));
|
||||
|
||||
// Verify debug logging for accumulation
|
||||
expect(consoleLogSpy).toHaveBeenCalledWith(
|
||||
@@ -629,6 +573,9 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
{ sequence: `\x1b[1~`, expected: { name: 'home' } },
|
||||
{ sequence: `\x1b[4~`, expected: { name: 'end' } },
|
||||
{ sequence: `\x1b[2~`, expected: { name: 'insert' } },
|
||||
// Reverse tabs
|
||||
{ sequence: `\x1b[Z`, expected: { name: 'tab', shift: true } },
|
||||
{ sequence: `\x1b[1;2Z`, expected: { name: 'tab', shift: true } },
|
||||
// Legacy Arrows
|
||||
{
|
||||
sequence: `\x1b[A`,
|
||||
@@ -662,7 +609,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() => stdin.sendKittySequence(sequence));
|
||||
act(() => stdin.write(sequence));
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining(expected),
|
||||
@@ -671,33 +618,14 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('Shift+Tab forms', () => {
|
||||
it.each([
|
||||
{ sequence: `\x1b[Z`, description: 'legacy reverse Tab' },
|
||||
{ sequence: `\x1b[1;2Z`, description: 'parameterized reverse Tab' },
|
||||
])(
|
||||
'should recognize $description "$sequence" as Shift+Tab',
|
||||
({ sequence }) => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() => stdin.sendKittySequence(sequence));
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ name: 'tab', shift: true }),
|
||||
);
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
describe('Double-tap and batching', () => {
|
||||
it('should emit two delete events for double-tap CSI[3~', async () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() => stdin.sendKittySequence(`\x1b[3~`));
|
||||
act(() => stdin.sendKittySequence(`\x1b[3~`));
|
||||
act(() => stdin.write(`\x1b[3~`));
|
||||
act(() => stdin.write(`\x1b[3~`));
|
||||
|
||||
expect(keyHandler).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
@@ -714,7 +642,7 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() => stdin.sendKittySequence(`\x1b[3~\x1b[5~`));
|
||||
act(() => stdin.write(`\x1b[3~\x1b[5~`));
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ name: 'delete' }),
|
||||
@@ -732,15 +660,9 @@ describe('KeypressContext - Kitty Protocol', () => {
|
||||
// Incomplete ESC sequence then a complete Delete
|
||||
act(() => {
|
||||
// Provide an incomplete ESC sequence chunk with a real ESC character
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
sequence: '\x1b[1;',
|
||||
});
|
||||
stdin.write('\x1b[1;');
|
||||
});
|
||||
act(() => stdin.sendKittySequence(`\x1b[3~`));
|
||||
act(() => stdin.write(`\x1b[3~`));
|
||||
|
||||
expect(keyHandler).toHaveBeenCalledTimes(1);
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -786,20 +708,9 @@ describe('Drag and Drop Handling', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: SINGLE_QUOTE,
|
||||
});
|
||||
});
|
||||
act(() => stdin.write(SINGLE_QUOTE));
|
||||
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -809,20 +720,9 @@ describe('Drag and Drop Handling', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: DOUBLE_QUOTE,
|
||||
});
|
||||
});
|
||||
act(() => stdin.write(DOUBLE_QUOTE));
|
||||
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
});
|
||||
@@ -834,33 +734,13 @@ describe('Drag and Drop Handling', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Start by single quote
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: SINGLE_QUOTE,
|
||||
});
|
||||
});
|
||||
act(() => stdin.write(SINGLE_QUOTE));
|
||||
|
||||
// Send single character
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: 'a',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('a'));
|
||||
|
||||
// Character should not be immediately broadcast
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
@@ -885,66 +765,16 @@ describe('Drag and Drop Handling', () => {
|
||||
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Start by single quote
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: SINGLE_QUOTE,
|
||||
});
|
||||
});
|
||||
act(() => stdin.write(SINGLE_QUOTE));
|
||||
|
||||
// Send multiple characters
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: 'p',
|
||||
});
|
||||
});
|
||||
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: 'a',
|
||||
});
|
||||
});
|
||||
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: 't',
|
||||
});
|
||||
});
|
||||
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: 'h',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('p'));
|
||||
act(() => stdin.write('a'));
|
||||
act(() => stdin.write('t'));
|
||||
act(() => stdin.write('h'));
|
||||
|
||||
// Characters should not be immediately broadcast
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
@@ -1101,7 +931,7 @@ describe('Kitty Sequence Parsing', () => {
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
if (kittySequence) {
|
||||
act(() => stdin.sendKittySequence(kittySequence));
|
||||
act(() => stdin.write(kittySequence));
|
||||
} else if (input) {
|
||||
act(() => stdin.pressKey(input));
|
||||
}
|
||||
@@ -1126,16 +956,7 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
act(() =>
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\\',
|
||||
}),
|
||||
);
|
||||
act(() => stdin.write('\\'));
|
||||
|
||||
// Advance timers to trigger the backslash timeout
|
||||
act(() => {
|
||||
@@ -1155,29 +976,16 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send incomplete kitty sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x1b[1;',
|
||||
});
|
||||
});
|
||||
act(() => stdin.pressKey({ sequence: '\x1b[1;' }));
|
||||
|
||||
// Should not broadcast immediately
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
|
||||
// Advance time just before timeout
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS - 5);
|
||||
});
|
||||
act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS - 5));
|
||||
|
||||
// Still shouldn't broadcast
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
@@ -1201,22 +1009,11 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send a CSI sequence that doesn't match kitty patterns
|
||||
// ESC[m is SGR reset, not a kitty sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x1b[m',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('\x1b[m'));
|
||||
|
||||
// Should broadcast immediately as it's not a valid kitty pattern
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -1232,21 +1029,10 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send complete kitty sequence for Ctrl+A
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x1b[97;5u',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('\x1b[97;5u'));
|
||||
|
||||
// Should parse and broadcast immediately
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -1262,21 +1048,10 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send multiple kitty sequences at once
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x1b[97;5u\x1b[98;5u', // Ctrl+a followed by Ctrl+b
|
||||
});
|
||||
});
|
||||
// Send Ctrl+a followed by Ctrl+b
|
||||
act(() => stdin.write('\x1b[97;5u\x1b[98;5u'));
|
||||
|
||||
// Should parse both sequences
|
||||
expect(keyHandler).toHaveBeenCalledTimes(2);
|
||||
@@ -1302,38 +1077,16 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send incomplete kitty sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x1b[1;',
|
||||
});
|
||||
});
|
||||
act(() => stdin.pressKey({ sequence: '\x1b[1;' }));
|
||||
|
||||
// Press Ctrl+C
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: 'c',
|
||||
ctrl: true,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x03',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('\x03'));
|
||||
|
||||
// Advance past timeout
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10);
|
||||
});
|
||||
act(() => vi.advanceTimersByTime(KITTY_SEQUENCE_TIMEOUT_MS + 10));
|
||||
|
||||
// Should only have received Ctrl+C, not the incomplete sequence
|
||||
expect(keyHandler).toHaveBeenCalledTimes(1);
|
||||
@@ -1349,21 +1102,11 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send valid kitty sequence followed by invalid CSI
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x1b[13u\x1b[!', // Valid enter, then invalid sequence
|
||||
});
|
||||
});
|
||||
// Valid enter, then invalid sequence
|
||||
act(() => stdin.write('\x1b[13u\x1b[!'));
|
||||
|
||||
// Should parse valid sequence and flush invalid immediately
|
||||
expect(keyHandler).toHaveBeenCalledTimes(2);
|
||||
@@ -1390,21 +1133,10 @@ describe('Kitty Sequence Parsing', () => {
|
||||
wrapper({ children, kittyProtocolEnabled: false }),
|
||||
});
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send what would be a kitty sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x1b[13u',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('\x1b[13u'));
|
||||
|
||||
// Should pass through without parsing
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -1454,58 +1186,25 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Start incomplete sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '\x1b[1',
|
||||
});
|
||||
});
|
||||
act(() => stdin.pressKey({ sequence: '\x1b[1' }));
|
||||
|
||||
// Advance time partway
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(30);
|
||||
});
|
||||
act(() => vi.advanceTimersByTime(30));
|
||||
|
||||
// Add more to sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: '3',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('3'));
|
||||
|
||||
// Advance time from the first timeout point
|
||||
act(() => {
|
||||
vi.advanceTimersByTime(25);
|
||||
});
|
||||
act(() => vi.advanceTimersByTime(25));
|
||||
|
||||
// Should not have timed out yet (timeout restarted)
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
|
||||
// Complete the sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
name: undefined,
|
||||
ctrl: false,
|
||||
meta: false,
|
||||
shift: false,
|
||||
paste: false,
|
||||
sequence: 'u',
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('u'));
|
||||
|
||||
// Should now parse as complete enter key
|
||||
expect(keyHandler).toHaveBeenCalledWith(
|
||||
@@ -1520,27 +1219,16 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send incomplete kitty sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
sequence: '\x1b[1;',
|
||||
});
|
||||
});
|
||||
act(() => stdin.pressKey({ sequence: '\x1b[1;' }));
|
||||
|
||||
// Incomplete sequence should be buffered, not broadcast
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
|
||||
// Send FOCUS_IN event
|
||||
const FOCUS_IN = '\x1b[I';
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
sequence: FOCUS_IN,
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('\x1b[I'));
|
||||
|
||||
// The buffered sequence should be flushed
|
||||
expect(keyHandler).toHaveBeenCalledTimes(1);
|
||||
@@ -1557,27 +1245,16 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send incomplete kitty sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
sequence: '\x1b[1;',
|
||||
});
|
||||
});
|
||||
act(() => stdin.pressKey({ sequence: '\x1b[1;' }));
|
||||
|
||||
// Incomplete sequence should be buffered, not broadcast
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
|
||||
// Send FOCUS_OUT event
|
||||
const FOCUS_OUT = '\x1b[O';
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
sequence: FOCUS_OUT,
|
||||
});
|
||||
});
|
||||
act(() => stdin.write('\x1b[O'));
|
||||
|
||||
// The buffered sequence should be flushed
|
||||
expect(keyHandler).toHaveBeenCalledTimes(1);
|
||||
@@ -1595,25 +1272,16 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const keyHandler = vi.fn();
|
||||
const { result } = renderHook(() => useKeypressContext(), { wrapper });
|
||||
|
||||
act(() => {
|
||||
result.current.subscribe(keyHandler);
|
||||
});
|
||||
act(() => result.current.subscribe(keyHandler));
|
||||
|
||||
// Send incomplete kitty sequence
|
||||
act(() => {
|
||||
stdin.pressKey({
|
||||
sequence: '\x1b[1;',
|
||||
});
|
||||
});
|
||||
act(() => stdin.pressKey({ sequence: '\x1b[1;' }));
|
||||
|
||||
// Incomplete sequence should be buffered, not broadcast
|
||||
expect(keyHandler).not.toHaveBeenCalled();
|
||||
|
||||
// Send paste start sequence
|
||||
const PASTE_MODE_PREFIX = `\x1b[200~`;
|
||||
act(() => {
|
||||
stdin.emit('data', Buffer.from(PASTE_MODE_PREFIX));
|
||||
});
|
||||
act(() => stdin.write(`\x1b[200~`));
|
||||
|
||||
// The buffered sequence should be flushed
|
||||
expect(keyHandler).toHaveBeenCalledTimes(1);
|
||||
@@ -1629,13 +1297,11 @@ describe('Kitty Sequence Parsing', () => {
|
||||
const pastedText = 'hello';
|
||||
const PASTE_MODE_SUFFIX = `\x1b[201~`;
|
||||
act(() => {
|
||||
stdin.emit('data', Buffer.from(pastedText));
|
||||
stdin.emit('data', Buffer.from(PASTE_MODE_SUFFIX));
|
||||
stdin.write(pastedText);
|
||||
stdin.write(PASTE_MODE_SUFFIX);
|
||||
});
|
||||
|
||||
act(() => {
|
||||
vi.runAllTimers();
|
||||
});
|
||||
act(() => vi.runAllTimers());
|
||||
|
||||
// The paste event should be broadcast
|
||||
expect(keyHandler).toHaveBeenCalledTimes(2);
|
||||
|
||||
Reference in New Issue
Block a user