// A tab stop's leader: the character it repeats across its gap (Word w:tab w:leader,
// ODF style:leader-text).
import { describe, it, expect } from 'vitest';
import { unzipSync, strFromU8 } from 'fflate';
import { buildOdt } from '../../src/lib/export/odt';
import { importOdt } from '../../src/lib/import/odt';
import { buildDocx } from '../../src/lib/export/docx';
import { importDocx } from '../../src/lib/import/docx';
import { parseTabStops, formatTabStops } from '../../src/lib/editor/extensions/tabStops';
const doc: any = {
type: 'doc',
content: [
{ type: 'paragraph', attrs: { tabStops: '6l.;12r_' }, content: [{ type: 'text', text: 'a\tb\tc' }] },
{ type: 'paragraph', attrs: { tabStops: '4c' }, content: [{ type: 'text', text: 'x\ty' }] },
],
};
const stopsOfFirst = (d: any) => d.content?.[0]?.attrs?.tabStops ?? null;
describe('tab leaders', () => {
it('round-trips through the attr form', () => {
expect(parseTabStops('6l.;12r_')).toEqual([
{ pos: 6, align: 'left', leader: '.' },
{ pos: 12, align: 'right', leader: '_' },
]);
expect(formatTabStops(parseTabStops('6l.;12r_'))).toBe('6l.;12r_');
// A character we can't render is dropped rather than approximated.
expect(parseTabStops('6l~')[0].leader).toBe(null);
});
it('rides ODF style:leader-text and comes back', async () => {
const bytes = await buildOdt(doc);
const content = strFromU8(unzipSync(bytes)['content.xml']);
expect(content).toContain('style:leader-style="dotted" style:leader-text="."');
expect(content).toContain('style:leader-style="solid" style:leader-text="_"');
// The stop without one stays bare.
expect(content).toContain('');
expect(stopsOfFirst(importOdt(bytes).content)).toBe('6l.;12r_');
});
it('keeps two stops at one position apart', async () => {
// odf-kit mints one automatic style per option set, so without the leader riding
// style:type both paragraphs would share a style — and with it one leader.
const mixed: any = { type: 'doc', content: [
{ type: 'paragraph', attrs: { tabStops: '6l.' }, content: [{ type: 'text', text: 'a\tb' }] },
{ type: 'paragraph', attrs: { tabStops: '6l' }, content: [{ type: 'text', text: 'c\td' }] },
] };
const content = strFromU8(unzipSync(await buildOdt(mixed))['content.xml']);
expect(content).toContain('');
expect(content).toContain('');
});
it('rides a DOCX w:leader and comes back', async () => {
const bytes = await buildDocx(doc);
expect(strFromU8(unzipSync(bytes)['word/document.xml'])).toContain('w:leader="dot"');
expect(stopsOfFirst(importDocx(bytes).content)).toBe('6l.;12r_');
});
});