char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Aleksander Grygierui: Refactor data-attrs constants, enum for bool strings (#27002)bdffafa5d

char/aliceai-t5-moe
4.5 KiB161 linesraw
1// Guards the editing-key contract of the ChatFormInputRich:
2// undo/redo is replayed from source snapshots (the token rebuilds destroy
3// the native undo stack), and Tab is NOT intercepted (WCAG 2.1.2 no
4// keyboard trap), matching the plain textarea.
5
6import ChatFormInputRichHarness from './components/ChatFormInputRichHarness.svelte';
7import { tick } from 'svelte';
8import { describe, expect, it } from 'vitest';
9import { render } from 'vitest-browser-svelte';
10
11const SOURCE = 'see [docs](file:///a/b)';
12
13function editableIn(container: HTMLElement): HTMLElement {
14	const el = container.querySelector('[role="textbox"]');
15
16	if (!(el instanceof HTMLElement)) throw new Error('ChatFormInputRich not rendered');
17
18	return el;
19}
20
21function type(root: HTMLElement, text: string, inputType = 'insertText') {
22	root.appendChild(document.createTextNode(text));
23	root.dispatchEvent(new InputEvent('input', { bubbles: true, inputType }));
24}
25
26function keydown(root: HTMLElement, init: KeyboardEventInit) {
27	const event = new KeyboardEvent('keydown', { bubbles: true, cancelable: true, ...init });
28
29	root.dispatchEvent(event);
30
31	return event;
32}
33
34describe('ChatFormInputRich undo/redo', () => {
35	it('undoes and redoes an edit across a badge-containing buffer', async () => {
36		const screen = render(ChatFormInputRichHarness, { value: SOURCE });
37
38		await tick();
39
40		const root = editableIn(screen.container);
41
42		type(root, ' more');
43		await tick();
44		expect(screen.component.getValue()).toBe(`${SOURCE} more`);
45
46		const undoEvent = keydown(root, { ctrlKey: true, key: 'z' });
47
48		await tick();
49		expect(undoEvent.defaultPrevented).toBe(true);
50		expect(screen.component.getValue()).toBe(SOURCE);
51
52		const redoEvent = keydown(root, { ctrlKey: true, key: 'z', shiftKey: true });
53
54		await tick();
55		expect(redoEvent.defaultPrevented).toBe(true);
56		expect(screen.component.getValue()).toBe(`${SOURCE} more`);
57	});
58
59	it('redoes with Ctrl+Y as well', async () => {
60		const screen = render(ChatFormInputRichHarness, { value: SOURCE });
61
62		await tick();
63
64		const root = editableIn(screen.container);
65
66		type(root, ' more');
67		await tick();
68		keydown(root, { key: 'z', metaKey: true });
69		await tick();
70		expect(screen.component.getValue()).toBe(SOURCE);
71
72		keydown(root, { ctrlKey: true, key: 'y' });
73		await tick();
74		expect(screen.component.getValue()).toBe(`${SOURCE} more`);
75	});
76
77	it('coalesces a typing burst into one undo step', async () => {
78		const screen = render(ChatFormInputRichHarness, { value: 'abc' });
79
80		await tick();
81
82		const root = editableIn(screen.container);
83
84		type(root, 'd');
85		type(root, 'e');
86		await tick();
87		expect(screen.component.getValue()).toBe('abcde');
88
89		keydown(root, { ctrlKey: true, key: 'z' });
90		await tick();
91		expect(screen.component.getValue()).toBe('abc');
92	});
93
94	it('keeps a newline as its own undo step', async () => {
95		const screen = render(ChatFormInputRichHarness, { value: 'abc' });
96
97		await tick();
98
99		const root = editableIn(screen.container);
100
101		type(root, 'd');
102		type(root, '\n', 'insertLineBreak');
103		await tick();
104		expect(screen.component.getValue()).toBe('abcd\n');
105
106		keydown(root, { ctrlKey: true, key: 'z' });
107		await tick();
108		expect(screen.component.getValue()).toBe('abcd');
109
110		keydown(root, { ctrlKey: true, key: 'z' });
111		await tick();
112		expect(screen.component.getValue()).toBe('abc');
113	});
114
115	it('is a no-op when there is nothing to undo', async () => {
116		const screen = render(ChatFormInputRichHarness, { value: 'abc' });
117
118		await tick();
119
120		const root = editableIn(screen.container);
121		const event = keydown(root, { ctrlKey: true, key: 'z' });
122
123		await tick();
124
125		expect(event.defaultPrevented).toBe(true);
126		expect(screen.component.getValue()).toBe('abc');
127	});
128
129	it('abandons the redo branch after a fresh edit', async () => {
130		const screen = render(ChatFormInputRichHarness, { value: 'abc' });
131
132		await tick();
133
134		const root = editableIn(screen.container);
135
136		type(root, 'd');
137		await tick();
138		keydown(root, { ctrlKey: true, key: 'z' });
139		await tick();
140		expect(screen.component.getValue()).toBe('abc');
141
142		type(root, 'e');
143		await tick();
144		keydown(root, { ctrlKey: true, key: 'z', shiftKey: true });
145		await tick();
146		expect(screen.component.getValue()).toBe('abce');
147	});
148});
149
150describe('ChatFormInputRich Tab key', () => {
151	it('does not trap Tab (focus can leave the editable)', async () => {
152		const screen = render(ChatFormInputRichHarness, { value: SOURCE });
153
154		await tick();
155
156		const root = editableIn(screen.container);
157		const event = keydown(root, { key: 'Tab' });
158
159		expect(event.defaultPrevented).toBe(false);
160	});
161});