Files
edentext/tests/unit/selection-format.test.ts
Steffen Becker f7ee9546a4 refactor: what the selection formats becomes a shared reader
Font, size, mark colour and block attributes all answer the same question — the
one value the selection agrees on, blank when it mixes. One `uniform` walk now
serves all four, framework-free, so any chrome can read it inside its own
reactive wrapper.

The classic toolbar keeps its own copies for now: it is the baseline the ribbon
is compared against, and it should adopt these only once that comparison ends.
2026-08-12 01:22:52 +02:00

98 lines
3.8 KiB
TypeScript

// What a toolbar shows for the selection. Drives a real body editor, so the
// readers see the same state the ribbon does.
import { describe, it, expect } from 'vitest';
import { Editor } from '@tiptap/core';
import { extensions } from '../../src/lib/editor/extensions';
import {
DEFAULT_EDITOR_FONT, uniformFont, uniformFontSize, uniformMarkColor, uniformBlockAttr,
} from '../../src/lib/utils/selectionFormat';
type N = any;
function makeEditor(content: N) {
const el = document.createElement('div');
document.body.appendChild(el);
return new Editor({ element: el, extensions, content });
}
const run = (text: string, attrs?: N): N => ({
type: 'text', text, ...(attrs ? { marks: [{ type: 'textStyle', attrs }] } : {}),
});
const doc = (...paras: N[]): N => ({ type: 'doc', content: paras });
const para = (attrs: N, ...content: N[]): N => ({ type: 'paragraph', attrs, content });
describe('uniformFont', () => {
it('falls back to the editor default where no run sets one', () => {
const editor = makeEditor(doc(para({}, run('plain'))));
editor.commands.selectAll();
expect(uniformFont(editor.state)).toBe(DEFAULT_EDITOR_FONT);
editor.destroy();
});
it('reports one font across a selection and blanks a mixed one', () => {
const editor = makeEditor(doc(para({}, run('aa', { fontFamily: 'Arial' }), run('bb', { fontFamily: 'Arial' }))));
editor.commands.selectAll();
expect(uniformFont(editor.state)).toBe('Arial');
const mixed = makeEditor(doc(para({}, run('aa', { fontFamily: 'Arial' }), run('bb', { fontFamily: 'Georgia' }))));
mixed.commands.selectAll();
expect(uniformFont(mixed.state)).toBe('');
editor.destroy();
mixed.destroy();
});
});
describe('uniformFontSize', () => {
it('prefers a run size over the block size', () => {
const editor = makeEditor(doc(para({ fontSize: '20pt' }, run('big', { fontSize: '9pt' }))));
editor.commands.selectAll();
expect(uniformFontSize(editor.state)).toBe('9pt');
editor.destroy();
});
it('reads the block size for a run that carries none', () => {
const editor = makeEditor(doc(para({ fontSize: '20pt' }, run('plain'))));
editor.commands.selectAll();
expect(uniformFontSize(editor.state)).toBe('20pt');
editor.destroy();
});
});
describe('uniformMarkColor', () => {
it('is null without a colour, the colour with one, blank when mixed', () => {
const none = makeEditor(doc(para({}, run('plain'))));
none.commands.selectAll();
expect(uniformMarkColor(none.state, 'textStyle')).toBeNull();
const one = makeEditor(doc(para({}, run('red', { color: '#ff0000' }))));
one.commands.selectAll();
expect(uniformMarkColor(one.state, 'textStyle')).toBe('#ff0000');
const two = makeEditor(doc(para({}, run('r', { color: '#ff0000' }), run('b', { color: '#0000ff' }))));
two.commands.selectAll();
expect(uniformMarkColor(two.state, 'textStyle')).toBe('');
none.destroy(); one.destroy(); two.destroy();
});
});
describe('uniformBlockAttr', () => {
it('agrees across blocks and blanks when they differ', () => {
const same = makeEditor(doc(para({ lineHeight: '1.5' }, run('a')), para({ lineHeight: '1.5' }, run('b'))));
same.commands.selectAll();
expect(uniformBlockAttr(same.state, 'lineHeight', '1')).toBe('1.5');
const differ = makeEditor(doc(para({ lineHeight: '1.5' }, run('a')), para({ lineHeight: '2' }, run('b'))));
differ.commands.selectAll();
expect(uniformBlockAttr(differ.state, 'lineHeight', '1')).toBe('');
same.destroy(); differ.destroy();
});
it('reports the fallback for a block that never sets the attribute', () => {
const editor = makeEditor(doc(para({}, run('a'))));
editor.commands.selectAll();
expect(uniformBlockAttr(editor.state, 'lineHeight', '1')).toBe('1');
editor.destroy();
});
});