mirror of
https://github.com/Horizontal42/summarum.git
synced 2026-11-10 21:31:59 +03:00
test: Add tests for SumEditor class
This commit adds a missing test file (`src/ui/editor.test.ts`) covering initialization, properties, and core behaviors of the `SumEditor` class. To avoid heavy DOM and browser environment dependencies in Vitest, `@codemirror/view` and fundamental DOM functionalities are thoroughly mocked. Co-authored-by: Horizontal42 <75383519+Horizontal42@users.noreply.github.com>
This commit is contained in:
parent
bede889ced
commit
ef63edcb15
1 changed files with 159 additions and 0 deletions
159
src/ui/editor.test.ts
Normal file
159
src/ui/editor.test.ts
Normal file
|
|
@ -0,0 +1,159 @@
|
||||||
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
|
import { SumEditor } from './editor';
|
||||||
|
|
||||||
|
vi.mock('@codemirror/view', () => {
|
||||||
|
class MockEditorView {
|
||||||
|
static updateListener = { of: vi.fn() };
|
||||||
|
static theme = vi.fn();
|
||||||
|
static lineWrapping = {};
|
||||||
|
static decorations = { from: vi.fn() };
|
||||||
|
|
||||||
|
state = {
|
||||||
|
doc: {
|
||||||
|
length: 10,
|
||||||
|
toString: vi.fn(() => "mock doc"),
|
||||||
|
lines: 2,
|
||||||
|
line: vi.fn((n) => ({ text: `line ${n}`, from: (n-1)*10, to: n*10 })),
|
||||||
|
lineAt: vi.fn((_pos) => ({ number: 1, from: 0, to: 10 })),
|
||||||
|
iterLines: vi.fn(function*() { yield "line 1"; yield "line 2"; }),
|
||||||
|
},
|
||||||
|
selection: { main: { empty: true, from: 0, to: 0, head: 0 } },
|
||||||
|
};
|
||||||
|
dom = { addEventListener: vi.fn() };
|
||||||
|
scrollDOM = { addEventListener: vi.fn() };
|
||||||
|
dispatch = vi.fn();
|
||||||
|
focus = vi.fn();
|
||||||
|
lineBlockAt = vi.fn(() => ({ top: 10 }));
|
||||||
|
documentTop = 0;
|
||||||
|
|
||||||
|
constructor(config: any) {
|
||||||
|
if (config.state?.doc) {
|
||||||
|
this.state.doc.toString = vi.fn(() => config.state.doc);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
EditorView: MockEditorView,
|
||||||
|
ViewPlugin: { fromClass: vi.fn() },
|
||||||
|
Decoration: { mark: vi.fn(), line: vi.fn(), none: {} },
|
||||||
|
keymap: { of: vi.fn() },
|
||||||
|
drawSelection: vi.fn(),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
vi.mock('@codemirror/state', () => ({
|
||||||
|
EditorState: {
|
||||||
|
create: vi.fn((cfg) => cfg),
|
||||||
|
},
|
||||||
|
StateEffect: {
|
||||||
|
define: vi.fn(() => ({ of: vi.fn() })),
|
||||||
|
},
|
||||||
|
StateField: {
|
||||||
|
define: vi.fn(),
|
||||||
|
},
|
||||||
|
RangeSetBuilder: class {
|
||||||
|
add = vi.fn();
|
||||||
|
finish = vi.fn();
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@codemirror/commands', () => ({
|
||||||
|
defaultKeymap: [],
|
||||||
|
history: vi.fn(),
|
||||||
|
historyKeymap: [],
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock('@codemirror/autocomplete', () => ({
|
||||||
|
autocompletion: vi.fn(),
|
||||||
|
acceptCompletion: vi.fn(),
|
||||||
|
}));
|
||||||
|
|
||||||
|
describe('SumEditor', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.stubGlobal('requestAnimationFrame', (cb: Function) => cb());
|
||||||
|
vi.stubGlobal('document', {
|
||||||
|
createDocumentFragment: vi.fn(() => ({
|
||||||
|
appendChild: vi.fn(),
|
||||||
|
})),
|
||||||
|
createElement: vi.fn((tag) => ({
|
||||||
|
tagName: tag,
|
||||||
|
className: '',
|
||||||
|
textContent: '',
|
||||||
|
dataset: {},
|
||||||
|
style: {},
|
||||||
|
})),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
afterEach(() => {
|
||||||
|
vi.unstubAllGlobals();
|
||||||
|
});
|
||||||
|
|
||||||
|
const createMocks = () => {
|
||||||
|
const parent = { getBoundingClientRect: () => ({ top: 0 }) } as any;
|
||||||
|
const resultsEl = {
|
||||||
|
addEventListener: vi.fn(),
|
||||||
|
replaceChildren: vi.fn(),
|
||||||
|
getBoundingClientRect: () => ({ top: 0 }),
|
||||||
|
clientHeight: 500,
|
||||||
|
} as any;
|
||||||
|
const engine = { completions: vi.fn(() => []) } as any;
|
||||||
|
const cb = { onChange: vi.fn(), onCopy: vi.fn(), onResults: vi.fn(), onSelection: vi.fn() };
|
||||||
|
const evaluateDoc = vi.fn(() => []);
|
||||||
|
return { parent, resultsEl, engine, cb, evaluateDoc };
|
||||||
|
};
|
||||||
|
|
||||||
|
it('instantiates and triggers evaluation on init', () => {
|
||||||
|
const { parent, resultsEl, engine, cb, evaluateDoc } = createMocks();
|
||||||
|
const editor = new SumEditor(parent, resultsEl, engine, cb, evaluateDoc, 'init text');
|
||||||
|
expect(editor).toBeDefined();
|
||||||
|
expect(editor.getText()).toBe('init text');
|
||||||
|
expect(evaluateDoc).toHaveBeenCalledWith('init text');
|
||||||
|
expect(cb.onResults).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('setText dispatches changes to the view', () => {
|
||||||
|
const { parent, resultsEl, engine, cb, evaluateDoc } = createMocks();
|
||||||
|
const editor = new SumEditor(parent, resultsEl, engine, cb, evaluateDoc, '');
|
||||||
|
editor.setText('new text');
|
||||||
|
expect(editor.view.dispatch).toHaveBeenCalledWith({
|
||||||
|
changes: { from: 0, to: 10, insert: 'new text' }
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('refresh re-evaluates the current text and renders results', () => {
|
||||||
|
const { parent, resultsEl, engine, cb, evaluateDoc } = createMocks();
|
||||||
|
const editor = new SumEditor(parent, resultsEl, engine, cb, evaluateDoc, 'text');
|
||||||
|
editor.refresh();
|
||||||
|
expect(evaluateDoc).toHaveBeenCalledTimes(2); // init + refresh
|
||||||
|
});
|
||||||
|
|
||||||
|
it('focus focuses the view', () => {
|
||||||
|
const { parent, resultsEl, engine, cb, evaluateDoc } = createMocks();
|
||||||
|
const editor = new SumEditor(parent, resultsEl, engine, cb, evaluateDoc, '');
|
||||||
|
editor.focus();
|
||||||
|
expect(editor.view.focus).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('goToLine calculates correctly and dispatches selection', () => {
|
||||||
|
const { parent, resultsEl, engine, cb, evaluateDoc } = createMocks();
|
||||||
|
const editor = new SumEditor(parent, resultsEl, engine, cb, evaluateDoc, '');
|
||||||
|
editor.goToLine(2);
|
||||||
|
expect(editor.view.dispatch).toHaveBeenCalledWith({
|
||||||
|
selection: { anchor: 10, head: 20 },
|
||||||
|
scrollIntoView: true
|
||||||
|
});
|
||||||
|
expect(editor.view.focus).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('getSheetWithResults formats lines with their results', () => {
|
||||||
|
const { parent, resultsEl, engine, cb, evaluateDoc } = createMocks();
|
||||||
|
// evaluateDoc will return mock results
|
||||||
|
evaluateDoc.mockReturnValue([{ text: 'result 1', tokens: [] }, { text: 'result 2', tokens: [] }] as any);
|
||||||
|
const editor = new SumEditor(parent, resultsEl, engine, cb, evaluateDoc, '');
|
||||||
|
|
||||||
|
const sheet = editor.getSheetWithResults();
|
||||||
|
// doc mock returns line 1 and line 2
|
||||||
|
expect(sheet).toBe('line 1 = result 1\nline 2 = result 2');
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
Add table
Reference in a new issue