summaryrefslogtreecommitdiff
path: root/packages/cli/src/ui/components/InputPrompt.test.tsx
diff options
context:
space:
mode:
Diffstat (limited to 'packages/cli/src/ui/components/InputPrompt.test.tsx')
-rw-r--r--packages/cli/src/ui/components/InputPrompt.test.tsx379
1 files changed, 379 insertions, 0 deletions
diff --git a/packages/cli/src/ui/components/InputPrompt.test.tsx b/packages/cli/src/ui/components/InputPrompt.test.tsx
index 1a100c36..6b201901 100644
--- a/packages/cli/src/ui/components/InputPrompt.test.tsx
+++ b/packages/cli/src/ui/components/InputPrompt.test.tsx
@@ -570,4 +570,383 @@ describe('InputPrompt', () => {
expect(props.buffer.setText).not.toHaveBeenCalled();
unmount();
});
+
+ describe('cursor-based completion trigger', () => {
+ it('should trigger completion when cursor is after @ without spaces', async () => {
+ // Set up buffer state
+ mockBuffer.text = '@src/components';
+ mockBuffer.lines = ['@src/components'];
+ mockBuffer.cursor = [0, 15];
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: true,
+ suggestions: [{ label: 'Button.tsx', value: 'Button.tsx' }],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ // Verify useCompletion was called with true (should show completion)
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '@src/components',
+ '/test/project/src',
+ true, // shouldShowCompletion should be true
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should trigger completion when cursor is after / without spaces', async () => {
+ mockBuffer.text = '/memory';
+ mockBuffer.lines = ['/memory'];
+ mockBuffer.cursor = [0, 7];
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: true,
+ suggestions: [{ label: 'show', value: 'show' }],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '/memory',
+ '/test/project/src',
+ true, // shouldShowCompletion should be true
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should NOT trigger completion when cursor is after space following @', async () => {
+ mockBuffer.text = '@src/file.ts hello';
+ mockBuffer.lines = ['@src/file.ts hello'];
+ mockBuffer.cursor = [0, 18];
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: false,
+ suggestions: [],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '@src/file.ts hello',
+ '/test/project/src',
+ false, // shouldShowCompletion should be false
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should NOT trigger completion when cursor is after space following /', async () => {
+ mockBuffer.text = '/memory add';
+ mockBuffer.lines = ['/memory add'];
+ mockBuffer.cursor = [0, 11];
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: false,
+ suggestions: [],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '/memory add',
+ '/test/project/src',
+ false, // shouldShowCompletion should be false
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should NOT trigger completion when cursor is not after @ or /', async () => {
+ mockBuffer.text = 'hello world';
+ mockBuffer.lines = ['hello world'];
+ mockBuffer.cursor = [0, 5];
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: false,
+ suggestions: [],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ 'hello world',
+ '/test/project/src',
+ false, // shouldShowCompletion should be false
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should handle multiline text correctly', async () => {
+ mockBuffer.text = 'first line\n/memory';
+ mockBuffer.lines = ['first line', '/memory'];
+ mockBuffer.cursor = [1, 7];
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: false,
+ suggestions: [],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ 'first line\n/memory',
+ '/test/project/src',
+ false, // shouldShowCompletion should be false (isSlashCommand returns false because text doesn't start with /)
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should handle single line slash command correctly', async () => {
+ mockBuffer.text = '/memory';
+ mockBuffer.lines = ['/memory'];
+ mockBuffer.cursor = [0, 7];
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: true,
+ suggestions: [{ label: 'show', value: 'show' }],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '/memory',
+ '/test/project/src',
+ true, // shouldShowCompletion should be true (isSlashCommand returns true AND cursor is after / without space)
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should handle Unicode characters (emojis) correctly in paths', async () => {
+ // Test with emoji in path after @
+ mockBuffer.text = '@src/file๐Ÿ‘.txt';
+ mockBuffer.lines = ['@src/file๐Ÿ‘.txt'];
+ mockBuffer.cursor = [0, 14]; // After the emoji character
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: true,
+ suggestions: [{ label: 'file๐Ÿ‘.txt', value: 'file๐Ÿ‘.txt' }],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '@src/file๐Ÿ‘.txt',
+ '/test/project/src',
+ true, // shouldShowCompletion should be true
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should handle Unicode characters with spaces after them', async () => {
+ // Test with emoji followed by space - should NOT trigger completion
+ mockBuffer.text = '@src/file๐Ÿ‘.txt hello';
+ mockBuffer.lines = ['@src/file๐Ÿ‘.txt hello'];
+ mockBuffer.cursor = [0, 20]; // After the space
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: false,
+ suggestions: [],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '@src/file๐Ÿ‘.txt hello',
+ '/test/project/src',
+ false, // shouldShowCompletion should be false
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should handle escaped spaces in paths correctly', async () => {
+ // Test with escaped space in path - should trigger completion
+ mockBuffer.text = '@src/my\\ file.txt';
+ mockBuffer.lines = ['@src/my\\ file.txt'];
+ mockBuffer.cursor = [0, 16]; // After the escaped space and filename
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: true,
+ suggestions: [{ label: 'my file.txt', value: 'my file.txt' }],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '@src/my\\ file.txt',
+ '/test/project/src',
+ true, // shouldShowCompletion should be true
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should NOT trigger completion after unescaped space following escaped space', async () => {
+ // Test: @path/my\ file.txt hello (unescaped space after escaped space)
+ mockBuffer.text = '@path/my\\ file.txt hello';
+ mockBuffer.lines = ['@path/my\\ file.txt hello'];
+ mockBuffer.cursor = [0, 24]; // After "hello"
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: false,
+ suggestions: [],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '@path/my\\ file.txt hello',
+ '/test/project/src',
+ false, // shouldShowCompletion should be false
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should handle multiple escaped spaces in paths', async () => {
+ // Test with multiple escaped spaces
+ mockBuffer.text = '@docs/my\\ long\\ file\\ name.md';
+ mockBuffer.lines = ['@docs/my\\ long\\ file\\ name.md'];
+ mockBuffer.cursor = [0, 29]; // At the end
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: true,
+ suggestions: [
+ { label: 'my long file name.md', value: 'my long file name.md' },
+ ],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '@docs/my\\ long\\ file\\ name.md',
+ '/test/project/src',
+ true, // shouldShowCompletion should be true
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should handle escaped spaces in slash commands', async () => {
+ // Test escaped spaces with slash commands (though less common)
+ mockBuffer.text = '/memory\\ test';
+ mockBuffer.lines = ['/memory\\ test'];
+ mockBuffer.cursor = [0, 13]; // At the end
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: true,
+ suggestions: [{ label: 'test-command', value: 'test-command' }],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '/memory\\ test',
+ '/test/project/src',
+ true, // shouldShowCompletion should be true
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+
+ it('should handle Unicode characters with escaped spaces', async () => {
+ // Test combining Unicode and escaped spaces
+ mockBuffer.text = '@files/emoji\\ ๐Ÿ‘\\ test.txt';
+ mockBuffer.lines = ['@files/emoji\\ ๐Ÿ‘\\ test.txt'];
+ mockBuffer.cursor = [0, 25]; // After the escaped space and emoji
+
+ mockedUseCompletion.mockReturnValue({
+ ...mockCompletion,
+ showSuggestions: true,
+ suggestions: [
+ { label: 'emoji ๐Ÿ‘ test.txt', value: 'emoji ๐Ÿ‘ test.txt' },
+ ],
+ });
+
+ const { unmount } = render(<InputPrompt {...props} />);
+ await wait();
+
+ expect(mockedUseCompletion).toHaveBeenCalledWith(
+ '@files/emoji\\ ๐Ÿ‘\\ test.txt',
+ '/test/project/src',
+ true, // shouldShowCompletion should be true
+ mockSlashCommands,
+ mockCommandContext,
+ expect.any(Object),
+ );
+
+ unmount();
+ });
+ });
});