summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/contexts/KeypressContext.test.tsx
diff options
context:
space:
mode:
authorJacob Richman <[email protected]>2025-08-19 13:41:08 -0700
committerGitHub <[email protected]>2025-08-19 20:41:08 +0000
commit2143731f6efdf1aafff38ec249caf01a8bcd163e (patch)
treeb9f3a81148192632f65c0bcc21250da11fe12100 /packages/cli/src/ui/contexts/KeypressContext.test.tsx
parented1fc4ddb39a25fad2c9dd12a250959c050a6343 (diff)
fix(paste) incorrect handling of \\\n in pastes (#6532)
Diffstat (limited to 'packages/cli/src/ui/contexts/KeypressContext.test.tsx')
-rw-r--r--packages/cli/src/ui/contexts/KeypressContext.test.tsx82
1 files changed, 44 insertions, 38 deletions
diff --git a/packages/cli/src/ui/contexts/KeypressContext.test.tsx b/packages/cli/src/ui/contexts/KeypressContext.test.tsx
index 01630229..caed50be 100644
--- a/packages/cli/src/ui/contexts/KeypressContext.test.tsx
+++ b/packages/cli/src/ui/contexts/KeypressContext.test.tsx
@@ -5,7 +5,7 @@
*/
import React from 'react';
-import { renderHook, act } from '@testing-library/react';
+import { renderHook, act, waitFor } from '@testing-library/react';
import { vi, Mock } from 'vitest';
import {
KeypressProvider,
@@ -28,18 +28,6 @@ vi.mock('ink', async (importOriginal) => {
};
});
-// Mock the 'readline' module
-vi.mock('readline', () => {
- const mockedReadline = {
- createInterface: vi.fn().mockReturnValue({ close: vi.fn() }),
- emitKeypressEvents: vi.fn(),
- };
- return {
- ...mockedReadline,
- default: mockedReadline,
- };
-});
-
class MockStdin extends EventEmitter {
isTTY = true;
setRawMode = vi.fn();
@@ -47,6 +35,7 @@ class MockStdin extends EventEmitter {
override removeListener = super.removeListener;
write = vi.fn();
resume = vi.fn();
+ pause = vi.fn();
// Helper to simulate a keypress event
pressKey(key: Partial<Key>) {
@@ -55,32 +44,16 @@ class MockStdin extends EventEmitter {
// Helper to simulate a kitty protocol sequence
sendKittySequence(sequence: string) {
- // Kitty sequences come in multiple parts
- // For example, ESC[13u comes as: ESC[ then 1 then 3 then u
- // ESC[57414;2u comes as: ESC[ then 5 then 7 then 4 then 1 then 4 then ; then 2 then u
- const escIndex = sequence.indexOf('\x1b[');
- if (escIndex !== -1) {
- // Send ESC[
- this.emit('keypress', null, {
- name: undefined,
- sequence: '\x1b[',
- ctrl: false,
- meta: false,
- shift: false,
- });
+ this.emit('data', Buffer.from(sequence));
+ }
- // Send the rest character by character
- const rest = sequence.substring(escIndex + 2);
- for (const char of rest) {
- this.emit('keypress', null, {
- name: /[a-zA-Z0-9]/.test(char) ? char : undefined,
- sequence: char,
- ctrl: false,
- meta: false,
- shift: false,
- });
- }
- }
+ // 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));
}
}
@@ -304,4 +277,37 @@ describe('KeypressContext - Kitty Protocol', () => {
);
});
});
+
+ describe('paste mode', () => {
+ it('should handle multiline paste as a single event', async () => {
+ const keyHandler = vi.fn();
+ const pastedText = 'This \nis \na \nmultiline \npaste.';
+
+ const { result } = renderHook(() => useKeypressContext(), {
+ wrapper,
+ });
+
+ act(() => {
+ result.current.subscribe(keyHandler);
+ });
+
+ // Simulate a bracketed paste event
+ act(() => {
+ stdin.sendPaste(pastedText);
+ });
+
+ await waitFor(() => {
+ // Expect the handler to be called exactly once for the entire paste
+ expect(keyHandler).toHaveBeenCalledTimes(1);
+ });
+
+ // Verify the single event contains the full pasted text
+ expect(keyHandler).toHaveBeenCalledWith(
+ expect.objectContaining({
+ paste: true,
+ sequence: pastedText,
+ }),
+ );
+ });
+ });
});