Skip to content

Commit 24915a0

Browse files
committed
Improve testability through insertText, dropdown selectors and imports updated, enhanced trigger state management, improved test coverage
1 parent 7ce28a5 commit 24915a0

20 files changed

+6231
-4520
lines changed

src/prompt-input/__integ__/prompt-input-token-mode.test.ts

Lines changed: 140 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,36 @@ class PromptInputTokenModePage extends BasePageObject {
5252
return sel ? sel.toString().replace(/\u200B/g, '') : '';
5353
});
5454
}
55+
56+
getParagraphStructure(): Promise<
57+
Array<{ childCount: number; hasTrailingBR: boolean; firstChildType: string; firstChildText: string }>
58+
> {
59+
return this.browser.execute((selector: string) => {
60+
const editable = document.querySelector(selector);
61+
if (!editable) {
62+
return [];
63+
}
64+
const paragraphs = editable.querySelectorAll('p');
65+
const result: Array<{
66+
childCount: number;
67+
hasTrailingBR: boolean;
68+
firstChildType: string;
69+
firstChildText: string;
70+
}> = [];
71+
for (let i = 0; i < paragraphs.length; i++) {
72+
const p = paragraphs[i];
73+
const firstChild = p.firstChild;
74+
const lastChild = p.lastChild;
75+
result.push({
76+
childCount: p.childNodes.length,
77+
hasTrailingBR: !!(lastChild && lastChild.nodeName === 'BR'),
78+
firstChildType: firstChild ? firstChild.nodeName : 'none',
79+
firstChildText: (firstChild?.textContent ?? '').replace(/\u200B/g, ''),
80+
});
81+
}
82+
return result;
83+
}, contentEditableSelector);
84+
}
5585
}
5686

5787
const setupTest = (testFn: (page: PromptInputTokenModePage) => Promise<void>) => {
@@ -171,15 +201,13 @@ const setupTest = (testFn: (page: PromptInputTokenModePage) => Promise<void>) =>
171201
'backspace through trigger with filter text removes all of it',
172202
setupTest(async page => {
173203
await page.focusInput();
174-
await page.keys(['h', 'i', ' ', '@', 'a', 'l', 'i']);
175-
await page.pause(300);
204+
await page.keys(['h', 'i', ' ', '@', 'a']);
176205

177-
await page.keys(['Escape']);
178-
await page.pause(100);
179-
await page.keys(['Backspace', 'Backspace', 'Backspace', 'Backspace']);
180-
await page.pause(300);
206+
// Backspace filter char, then backspace trigger char
207+
await page.keys(['Backspace']);
208+
await page.keys(['Backspace']);
181209

182-
expect((await page.getEditorText()).trim()).toBe('hi');
210+
expect(await page.getEditorText()).toBe('hi ');
183211
expect(await page.getCaretOffset()).toBe(3);
184212
})
185213
);
@@ -383,7 +411,7 @@ const setupTest = (testFn: (page: PromptInputTokenModePage) => Promise<void>) =>
383411
await page.pause(200);
384412

385413
const text = await page.getEditorText();
386-
expect(text.trim()).toBe('hi');
414+
expect(text).toBe(' hi');
387415
expect(text).not.toContain('Jane Smith');
388416
})
389417
);
@@ -403,7 +431,7 @@ const setupTest = (testFn: (page: PromptInputTokenModePage) => Promise<void>) =>
403431
await page.pause(200);
404432

405433
const text = await page.getEditorText();
406-
expect(text.trim()).toBe('hi');
434+
expect(text).toBe('hi ');
407435
expect(text).not.toContain('Jane Smith');
408436
expect(await page.getCaretOffset()).toBe(3);
409437
})
@@ -455,3 +483,106 @@ const setupTest = (testFn: (page: PromptInputTokenModePage) => Promise<void>) =>
455483
})
456484
);
457485
});
486+
487+
(isReact18 ? describe : describe.skip)(
488+
'PromptInput token mode - typing into empty line (isTypingIntoEmptyLine)',
489+
() => {
490+
test(
491+
'typing on a new line after shift+enter replaces trailing BR with text node',
492+
setupTest(async page => {
493+
await page.focusInput();
494+
await page.keys(['h', 'e', 'l', 'l', 'o']);
495+
await page.pause(100);
496+
497+
await page.keys(['Shift', 'Enter', 'Shift']);
498+
await page.pause(100);
499+
500+
// Before typing: second paragraph should have a trailing BR (empty line)
501+
const beforeStructure = await page.getParagraphStructure();
502+
expect(beforeStructure.length).toBe(2);
503+
expect(beforeStructure[1].hasTrailingBR).toBe(true);
504+
505+
// Type on the empty second line
506+
await page.keys(['w']);
507+
await page.pause(200);
508+
509+
// After typing: second paragraph should have a text node, no trailing BR
510+
const afterStructure = await page.getParagraphStructure();
511+
expect(afterStructure.length).toBe(2);
512+
expect(afterStructure[1].hasTrailingBR).toBe(false);
513+
expect(afterStructure[1].firstChildType).toBe('#text');
514+
expect(afterStructure[1].firstChildText).toContain('w');
515+
expect(await page.getCaretOffset()).toBe(6);
516+
})
517+
);
518+
519+
test(
520+
'typing trigger on empty line after shift+enter opens menu and replaces BR',
521+
setupTest(async page => {
522+
await page.focusInput();
523+
await page.keys(['h', 'e', 'l', 'l', 'o']);
524+
await page.pause(100);
525+
526+
await page.keys(['Shift', 'Enter', 'Shift']);
527+
await page.pause(100);
528+
529+
await page.keys(['@']);
530+
await page.pause(300);
531+
532+
await expect(page.isMenuOpen()).resolves.toBe(true);
533+
534+
// The second paragraph should now contain a trigger element, not a trailing BR
535+
const structure = await page.getParagraphStructure();
536+
expect(structure.length).toBe(2);
537+
expect(structure[1].hasTrailingBR).toBe(false);
538+
})
539+
);
540+
541+
test(
542+
'typing into completely empty input replaces trailing BR with text node',
543+
setupTest(async page => {
544+
await page.focusInput();
545+
546+
// Before typing: single paragraph with trailing BR
547+
const beforeStructure = await page.getParagraphStructure();
548+
expect(beforeStructure.length).toBe(1);
549+
expect(beforeStructure[0].hasTrailingBR).toBe(true);
550+
551+
await page.keys(['a']);
552+
await page.pause(200);
553+
554+
// After typing: paragraph has text node, no trailing BR
555+
const afterStructure = await page.getParagraphStructure();
556+
expect(afterStructure.length).toBe(1);
557+
expect(afterStructure[0].hasTrailingBR).toBe(false);
558+
expect(afterStructure[0].firstChildType).toBe('#text');
559+
expect(afterStructure[0].firstChildText).toBe('a');
560+
expect(await page.getCaretOffset()).toBe(1);
561+
})
562+
);
563+
}
564+
);
565+
566+
(isReact18 ? describe : describe.skip)('PromptInput token mode - mouseup selection normalization', () => {
567+
test(
568+
'clicking on a reference token and dragging produces a valid selection',
569+
setupTest(async page => {
570+
await page.focusInput();
571+
await page.keys(['h', 'i', ' ']);
572+
await page.keys(['@']);
573+
await page.pause(200);
574+
await page.keys(['ArrowDown', 'Enter']);
575+
await page.pause(200);
576+
await page.keys([' ', 'b', 'y', 'e']);
577+
await page.pause(200);
578+
579+
// Click at the start of the input to position caret
580+
await page.click(contentEditableSelector);
581+
await page.pause(100);
582+
583+
// The caret should be at a valid position (not inside a reference's internal structure)
584+
const offset = await page.getCaretOffset();
585+
expect(offset).toBeGreaterThanOrEqual(0);
586+
})
587+
);
588+
});

0 commit comments

Comments
 (0)