Checking every word of a 460-page file costs half a second, and at mount it was paid before the document was on screen. The pass now waits for idle time (2 s timeout), as does the one a language or dictionary change asks for; the check after an edit is unchanged. The file is named 200 ms earlier and settles 700 ms earlier, with the same page count, spacers, page numbers and references.
72 lines
2.7 KiB
TypeScript
72 lines
2.7 KiB
TypeScript
// The check after an edit looks at the edited paragraph only; the others keep their
|
|
// squiggles. Drives a real body editor with a stub checker that flags xx-words.
|
|
import { describe, it, expect, vi, afterEach } from 'vitest';
|
|
import { Editor } from '@tiptap/core';
|
|
|
|
const { checks } = vi.hoisted(() => ({ checks: [] as string[] }));
|
|
vi.mock('../../src/lib/spell/controller', () => ({
|
|
spellController: {
|
|
isEnabled: () => true,
|
|
check: (w: string) => { checks.push(w); return !w.startsWith('xx'); },
|
|
suggest: () => [],
|
|
getLanguage: () => 'en-US',
|
|
addWord: () => {},
|
|
ignoreWord: () => {},
|
|
subscribe: () => () => {},
|
|
},
|
|
}));
|
|
|
|
const { extensions } = await import('../../src/lib/editor/extensions');
|
|
const { spellErrorAt } = await import('../../src/lib/editor/extensions/spellCheck');
|
|
|
|
const para = (text: string) => ({ type: 'paragraph', content: [{ type: 'text', text }] });
|
|
|
|
async function makeEditor() {
|
|
vi.useFakeTimers({ toFake: ['setTimeout', 'clearTimeout'] });
|
|
const el = document.createElement('div');
|
|
document.body.appendChild(el);
|
|
const editor = new Editor({
|
|
element: el,
|
|
extensions,
|
|
content: { type: 'doc', content: [para('alpha xxone beta'), para('gamma xxtwo delta')] },
|
|
});
|
|
await Promise.resolve();
|
|
vi.advanceTimersByTime(1); // the mount's whole-document check, which waits for idle time
|
|
checks.length = 0;
|
|
return editor;
|
|
}
|
|
|
|
// 'alpha xxone beta' fills 1..17, so the second paragraph's text starts at 19.
|
|
const XXONE = 8;
|
|
const XXTWO = 26;
|
|
|
|
afterEach(() => vi.useRealTimers());
|
|
|
|
describe('spell recheck after an edit', () => {
|
|
it('checks the edited paragraph only and keeps the other squiggles', async () => {
|
|
const editor = await makeEditor();
|
|
expect(spellErrorAt(editor.state, XXONE)).toEqual({ from: 7, to: 12 });
|
|
|
|
editor.view.dispatch(editor.state.tr.insertText('q', 24)); // gamma → gammaq
|
|
vi.advanceTimersByTime(400);
|
|
expect(checks).toEqual(['gammaq', 'xxtwo', 'delta']);
|
|
expect(spellErrorAt(editor.state, XXONE)).toEqual({ from: 7, to: 12 });
|
|
expect(spellErrorAt(editor.state, XXTWO + 1)).toEqual({ from: 26, to: 31 });
|
|
editor.destroy();
|
|
});
|
|
|
|
it('drops a squiggle the edit fixed, and re-reads a paragraph a deletion emptied into', async () => {
|
|
const editor = await makeEditor();
|
|
editor.view.dispatch(editor.state.tr.insertText('two', 25, 30)); // xxtwo → two
|
|
vi.advanceTimersByTime(400);
|
|
expect(spellErrorAt(editor.state, XXTWO)).toBeNull();
|
|
expect(spellErrorAt(editor.state, XXONE)).toEqual({ from: 7, to: 12 });
|
|
|
|
checks.length = 0;
|
|
editor.view.dispatch(editor.state.tr.delete(19, 25)); // 'gamma ' gone: a point, not a range
|
|
vi.advanceTimersByTime(400);
|
|
expect(checks).toEqual(['two', 'delta']);
|
|
editor.destroy();
|
|
});
|
|
});
|