fix(input) Resolve cases where escape was broken (#6304)

This commit is contained in:
Jacob Richman
2025-08-15 10:54:00 -07:00
committed by GitHub
parent 1a2906a8ad
commit bd5e49c5ff
14 changed files with 697 additions and 582 deletions

View File

@@ -4,8 +4,10 @@
* SPDX-License-Identifier: Apache-2.0
*/
import React from 'react';
import { renderHook, act } from '@testing-library/react';
import { useKeypress, Key } from './useKeypress.js';
import { KeypressProvider } from '../contexts/KeypressContext.js';
import { useStdin } from 'ink';
import { EventEmitter } from 'events';
import { PassThrough } from 'stream';
@@ -102,6 +104,9 @@ describe('useKeypress', () => {
const onKeypress = vi.fn();
let originalNodeVersion: string;
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(KeypressProvider, null, children);
beforeEach(() => {
vi.clearAllMocks();
stdin = new MockStdin();
@@ -129,7 +134,9 @@ describe('useKeypress', () => {
};
it('should not listen if isActive is false', () => {
renderHook(() => useKeypress(onKeypress, { isActive: false }));
renderHook(() => useKeypress(onKeypress, { isActive: false }), {
wrapper,
});
act(() => stdin.pressKey({ name: 'a' }));
expect(onKeypress).not.toHaveBeenCalled();
});
@@ -141,14 +148,15 @@ describe('useKeypress', () => {
{ key: { name: 'up', sequence: '\x1b[A' } },
{ key: { name: 'down', sequence: '\x1b[B' } },
])('should listen for keypress when active for key $key.name', ({ key }) => {
renderHook(() => useKeypress(onKeypress, { isActive: true }));
renderHook(() => useKeypress(onKeypress, { isActive: true }), { wrapper });
act(() => stdin.pressKey(key));
expect(onKeypress).toHaveBeenCalledWith(expect.objectContaining(key));
});
it('should set and release raw mode', () => {
const { unmount } = renderHook(() =>
useKeypress(onKeypress, { isActive: true }),
const { unmount } = renderHook(
() => useKeypress(onKeypress, { isActive: true }),
{ wrapper },
);
expect(mockSetRawMode).toHaveBeenCalledWith(true);
unmount();
@@ -156,8 +164,9 @@ describe('useKeypress', () => {
});
it('should stop listening after being unmounted', () => {
const { unmount } = renderHook(() =>
useKeypress(onKeypress, { isActive: true }),
const { unmount } = renderHook(
() => useKeypress(onKeypress, { isActive: true }),
{ wrapper },
);
unmount();
act(() => stdin.pressKey({ name: 'a' }));
@@ -165,7 +174,7 @@ describe('useKeypress', () => {
});
it('should correctly identify alt+enter (meta key)', () => {
renderHook(() => useKeypress(onKeypress, { isActive: true }));
renderHook(() => useKeypress(onKeypress, { isActive: true }), { wrapper });
const key = { name: 'return', sequence: '\x1B\r' };
act(() => stdin.pressKey(key));
expect(onKeypress).toHaveBeenCalledWith(
@@ -199,7 +208,9 @@ describe('useKeypress', () => {
});
it('should process a paste as a single event', () => {
renderHook(() => useKeypress(onKeypress, { isActive: true }));
renderHook(() => useKeypress(onKeypress, { isActive: true }), {
wrapper,
});
const pasteText = 'hello world';
act(() => stdin.paste(pasteText));
@@ -215,7 +226,9 @@ describe('useKeypress', () => {
});
it('should handle keypress interspersed with pastes', () => {
renderHook(() => useKeypress(onKeypress, { isActive: true }));
renderHook(() => useKeypress(onKeypress, { isActive: true }), {
wrapper,
});
const keyA = { name: 'a', sequence: 'a' };
act(() => stdin.pressKey(keyA));
@@ -239,8 +252,9 @@ describe('useKeypress', () => {
});
it('should emit partial paste content if unmounted mid-paste', () => {
const { unmount } = renderHook(() =>
useKeypress(onKeypress, { isActive: true }),
const { unmount } = renderHook(
() => useKeypress(onKeypress, { isActive: true }),
{ wrapper },
);
const pasteText = 'incomplete paste';