fix: try to fix test case failures on Windows

This commit is contained in:
tanzhenxin
2025-09-16 14:59:03 +08:00
parent c093bed38c
commit 35efa9f04a
4 changed files with 78 additions and 51 deletions

View File

@@ -127,9 +127,11 @@ interface AppProps {
export const AppWrapper = (props: AppProps) => {
const kittyProtocolStatus = useKittyKeyboardProtocol();
const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10);
return (
<KeypressProvider
kittyProtocolEnabled={kittyProtocolStatus.enabled}
pasteWorkaround={process.platform === 'win32' || nodeMajorVersion < 20}
config={props.config}
>
<SessionStatsProvider>

View File

@@ -66,11 +66,16 @@ describe('KeypressContext - Kitty Protocol', () => {
const wrapper = ({
children,
kittyProtocolEnabled = true,
pasteWorkaround = false,
}: {
children: React.ReactNode;
kittyProtocolEnabled?: boolean;
pasteWorkaround?: boolean;
}) => (
<KeypressProvider kittyProtocolEnabled={kittyProtocolEnabled}>
<KeypressProvider
kittyProtocolEnabled={kittyProtocolEnabled}
pasteWorkaround={pasteWorkaround}
>
{children}
</KeypressProvider>
);
@@ -389,17 +394,15 @@ describe('KeypressContext - Kitty Protocol', () => {
});
describe('paste mode markers', () => {
beforeEach(() => {
// Force passthrough mode for raw keypress testing
vi.stubEnv('PASTE_WORKAROUND', '1');
});
// These tests use pasteWorkaround=true to force passthrough mode for raw keypress testing
it('should handle complete paste sequence with markers', async () => {
const keyHandler = vi.fn();
const pastedText = 'pasted content';
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) =>
wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -429,7 +432,8 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) =>
wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -459,7 +463,8 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) =>
wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -515,7 +520,8 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) =>
wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -554,7 +560,8 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) =>
wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -658,7 +665,8 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) =>
wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -689,7 +697,8 @@ describe('KeypressContext - Kitty Protocol', () => {
const multilineContent = 'line1\nline2\nline3';
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) =>
wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -721,7 +730,8 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) =>
wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -754,7 +764,7 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) => wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -789,17 +799,14 @@ describe('KeypressContext - Kitty Protocol', () => {
});
describe('Raw keypress pipeline', () => {
beforeEach(() => {
// Force passthrough mode for raw keypress testing
vi.stubEnv('PASTE_WORKAROUND', '1');
});
// These tests use pasteWorkaround=true to force passthrough mode for raw keypress testing
it('should buffer input data and wait for timeout', () => {
vi.useFakeTimers();
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) => wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -829,7 +836,7 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) => wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -892,7 +899,7 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) => wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -934,7 +941,7 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) => wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -987,7 +994,7 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) => wrapper({ children, pasteWorkaround: true }),
});
act(() => {
@@ -1036,7 +1043,7 @@ describe('KeypressContext - Kitty Protocol', () => {
const keyHandler = vi.fn();
const { result } = renderHook(() => useKeypressContext(), {
wrapper,
wrapper: ({ children }) => wrapper({ children, pasteWorkaround: true }),
});
act(() => {

View File

@@ -69,10 +69,12 @@ export function useKeypressContext() {
export function KeypressProvider({
children,
kittyProtocolEnabled,
pasteWorkaround = false,
config,
}: {
children: React.ReactNode;
children?: React.ReactNode;
kittyProtocolEnabled: boolean;
pasteWorkaround?: boolean;
config?: Config;
}) {
const { stdin, setRawMode } = useStdin();
@@ -97,18 +99,8 @@ export function KeypressProvider({
const keypressStream = new PassThrough();
let usePassthrough = false;
const nodeMajorVersion = parseInt(process.versions.node.split('.')[0], 10);
const isWindows = process.platform === 'win32';
// On Windows, Node's readline keypress stream often loses bracketed paste
// boundaries, causing multi-line pastes to be delivered as plain Return
// key events. This leads to accidental submits on Enter within pasted text.
// Force passthrough on Windows to parse raw bytes and detect ESC[200~...201~.
if (
nodeMajorVersion < 20 ||
isWindows ||
process.env['PASTE_WORKAROUND'] === '1' ||
process.env['PASTE_WORKAROUND'] === 'true'
) {
// Use passthrough mode when pasteWorkaround is enabled,
if (pasteWorkaround) {
usePassthrough = true;
}
@@ -500,7 +492,14 @@ export function KeypressProvider({
pasteBuffer = Buffer.alloc(0);
}
};
}, [stdin, setRawMode, kittyProtocolEnabled, config, subscribers]);
}, [
stdin,
setRawMode,
kittyProtocolEnabled,
pasteWorkaround,
config,
subscribers,
]);
return (
<KeypressContext.Provider value={{ subscribe, unsubscribe }}>

View File

@@ -105,7 +105,28 @@ describe('useKeypress', () => {
let originalNodeVersion: string;
const wrapper = ({ children }: { children: React.ReactNode }) =>
React.createElement(KeypressProvider, null, children);
React.createElement(
KeypressProvider,
{
kittyProtocolEnabled: false,
pasteWoraround: false,
},
children,
);
const wrapperWithWindowsWorkaround = ({
children,
}: {
children: React.ReactNode;
}) =>
React.createElement(
KeypressProvider,
{
kittyProtocolEnabled: false,
pasteWoraround: true,
},
children,
);
beforeEach(() => {
vi.clearAllMocks();
@@ -187,21 +208,17 @@ describe('useKeypress', () => {
description: 'Modern Node (>= v20)',
setup: () => setNodeVersion('20.0.0'),
isLegacy: false,
pasteWoraround: false,
},
{
description: 'Legacy Node (< v20)',
setup: () => setNodeVersion('18.0.0'),
isLegacy: true,
},
{
description: 'Workaround Env Var',
description: 'PasteWorkaround Environment Variable',
setup: () => {
setNodeVersion('20.0.0');
vi.stubEnv('PASTE_WORKAROUND', 'true');
},
isLegacy: true,
isLegacy: false,
pasteWoraround: true,
},
])('in $description', ({ setup, isLegacy }) => {
])('in $description', ({ setup, isLegacy, pasteWoraround }) => {
beforeEach(() => {
setup();
stdin.setLegacy(isLegacy);
@@ -209,7 +226,7 @@ describe('useKeypress', () => {
it('should process a paste as a single event', async () => {
renderHook(() => useKeypress(onKeypress, { isActive: true }), {
wrapper,
wrapper: pasteWoraround ? wrapperWithWindowsWorkaround : wrapper,
});
const pasteText = 'hello world';
act(() => stdin.paste(pasteText));
@@ -230,7 +247,7 @@ describe('useKeypress', () => {
it('should handle keypress interspersed with pastes', async () => {
renderHook(() => useKeypress(onKeypress, { isActive: true }), {
wrapper,
wrapper: pasteWoraround ? wrapperWithWindowsWorkaround : wrapper,
});
const keyA = { name: 'a', sequence: 'a' };
@@ -266,7 +283,9 @@ describe('useKeypress', () => {
it('should emit partial paste content if unmounted mid-paste', async () => {
const { unmount } = renderHook(
() => useKeypress(onKeypress, { isActive: true }),
{ wrapper },
{
wrapper: pasteWoraround ? wrapperWithWindowsWorkaround : wrapper,
},
);
const pasteText = 'incomplete paste';