Skip to content

Commit fbc9ff2

Browse files
committed
Add test coverage
1 parent 4cc474b commit fbc9ff2

File tree

2 files changed

+116
-0
lines changed

2 files changed

+116
-0
lines changed

src/prompt-input/__tests__/prompt-input-token-mode.test.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6510,6 +6510,41 @@ describe('disabled and readonly menu suppression', () => {
65106510
});
65116511
});
65126512

6513+
describe('non-collapsed selection deletion via keyboard', () => {
6514+
test('backspace with selection spanning text and reference removes selected content', () => {
6515+
const ref = React.createRef<PromptInputProps.Ref>();
6516+
const onChange = jest.fn();
6517+
const { wrapper } = renderStatefulTokenMode({
6518+
props: {
6519+
tokens: [
6520+
{ type: 'text', value: 'hello ' },
6521+
{ type: 'reference', id: 'ref-1', label: 'Alice', value: 'user-1', menuId: 'mentions' },
6522+
{ type: 'text', value: ' world' },
6523+
],
6524+
onChange,
6525+
},
6526+
ref,
6527+
});
6528+
act(() => {
6529+
ref.current!.focus();
6530+
});
6531+
act(() => {
6532+
ref.current!.setSelectionRange(3, 13);
6533+
});
6534+
const el = wrapper.findContentEditableElement()!.getElement();
6535+
act(() => {
6536+
el.dispatchEvent(new KeyboardEvent('keydown', { key: 'Backspace', keyCode: 8, bubbles: true }));
6537+
});
6538+
expect(onChange).toHaveBeenCalled();
6539+
const lastTokens = onChange.mock.calls[onChange.mock.calls.length - 1][0].detail.tokens;
6540+
// Reference should be removed since the selection spans it
6541+
expect(lastTokens.find((t: any) => t.type === 'reference')).toBeUndefined();
6542+
// Only text before the selection start should remain
6543+
const textValues = lastTokens.filter((t: any) => t.type === 'text').map((t: any) => t.value);
6544+
expect(textValues.join('')).toBe('hel');
6545+
});
6546+
});
6547+
65136548
describe('classifyChange branches', () => {
65146549
test('external update with different token count triggers structural re-render', () => {
65156550
const onChange = jest.fn();

src/prompt-input/__tests__/token-utils.test.ts

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
getCaretPositionAfterPinnedReorder,
1313
getCaretPositionAfterTokenRemoval,
1414
mergeConsecutiveTextTokens,
15+
removeTokenRange,
1516
validateTrigger,
1617
} from '../core/token-utils';
1718
import { PromptInputProps } from '../interfaces';
@@ -512,3 +513,83 @@ describe('getCaretPositionAfterTokenRemoval - trigger token handling', () => {
512513
expect(getCaretPositionAfterTokenRemoval(16, prev, next)).toBe(15);
513514
});
514515
});
516+
517+
describe('removeTokenRange', () => {
518+
test('returns copy when start >= end', () => {
519+
const tokens: PromptInputProps.InputToken[] = [text('hello')];
520+
expect(removeTokenRange(tokens, 3, 3)).toEqual([text('hello')]);
521+
expect(removeTokenRange(tokens, 5, 2)).toEqual([text('hello')]);
522+
});
523+
524+
test('removes entire text token when fully within range', () => {
525+
const tokens: PromptInputProps.InputToken[] = [text('aaa'), text('bbb'), text('ccc')];
526+
// Remove "bbb" (positions 3-6)
527+
const result = removeTokenRange(tokens, 3, 6);
528+
expect(result).toEqual([text('aaa'), text('ccc')]);
529+
});
530+
531+
test('trims text token at start of range', () => {
532+
const tokens: PromptInputProps.InputToken[] = [text('hello world')];
533+
// Remove "world" (positions 6-11)
534+
const result = removeTokenRange(tokens, 6, 11);
535+
expect(result).toEqual([text('hello ')]);
536+
});
537+
538+
test('trims text token at end of range', () => {
539+
const tokens: PromptInputProps.InputToken[] = [text('hello world')];
540+
// Remove "hello " (positions 0-6)
541+
const result = removeTokenRange(tokens, 0, 6);
542+
expect(result).toEqual([text('world')]);
543+
});
544+
545+
test('trims text token from both sides', () => {
546+
const tokens: PromptInputProps.InputToken[] = [text('hello world')];
547+
// Remove "lo wo" (positions 3-8)
548+
const result = removeTokenRange(tokens, 3, 8);
549+
expect(result).toEqual([text('helrld')]);
550+
});
551+
552+
test('removes reference token when overlapping', () => {
553+
const tokens: PromptInputProps.InputToken[] = [text('hi '), ref('r1', 'Alice', 'alice', 'mentions'), text(' bye')];
554+
// Range spans the reference (positions 3-4)
555+
const result = removeTokenRange(tokens, 3, 4);
556+
expect(result.find(t => t.type === 'reference')).toBeUndefined();
557+
expect(result).toHaveLength(2);
558+
expect(result[0]).toEqual(text('hi '));
559+
expect(result[1]).toEqual(text(' bye'));
560+
});
561+
562+
test('removes break token when overlapping', () => {
563+
const tokens: PromptInputProps.InputToken[] = [text('aaa'), { type: 'break', value: '\n' }, text('bbb')];
564+
// Range spans the break (position 3-4)
565+
const result = removeTokenRange(tokens, 3, 4);
566+
expect(result.find(t => t.type === 'break')).toBeUndefined();
567+
expect(result).toHaveLength(2);
568+
expect(result[0]).toEqual(text('aaa'));
569+
expect(result[1]).toEqual(text('bbb'));
570+
});
571+
572+
test('removes trigger token when overlapping', () => {
573+
const tokens: PromptInputProps.InputToken[] = [
574+
text('hi '),
575+
{ type: 'trigger', value: 'bob', triggerChar: '@', id: 'trig-1' },
576+
];
577+
// Range spans into the trigger (positions 3-7, trigger is @bob = 4 chars)
578+
const result = removeTokenRange(tokens, 3, 7);
579+
expect(result.find(t => t.type === 'trigger')).toBeUndefined();
580+
expect(result).toHaveLength(1);
581+
expect(result[0]).toEqual(text('hi '));
582+
});
583+
584+
test('removes everything when range covers all tokens', () => {
585+
const tokens: PromptInputProps.InputToken[] = [text('aaa'), { type: 'break', value: '\n' }, text('bbb')];
586+
const result = removeTokenRange(tokens, 0, 7);
587+
expect(result).toEqual([]);
588+
});
589+
590+
test('removes text token entirely when trimmed to empty', () => {
591+
const tokens: PromptInputProps.InputToken[] = [text('abc')];
592+
const result = removeTokenRange(tokens, 0, 3);
593+
expect(result).toEqual([]);
594+
});
595+
});

0 commit comments

Comments
 (0)