- Read-only share page: a calm paper-toned page rendered natively (no iframe), with outline tabs, Print / PDF, numbered points with accent numerals, type tags, notes and verse quotes; works on phones - Printable / PDF page restyled to match (serif, hanging numerals, tidy spacing, footer) - Word outline restyled: eyebrow + title block, bold main points, accent numerals, set-off verse quotes, tidy margins Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
67 lines
3.6 KiB
JavaScript
67 lines
3.6 KiB
JavaScript
import { afterEach, beforeEach, expect, test, vi } from 'vitest';
|
||
import { outlineShareText, printOutline, shareOutline } from './outlineExport.js';
|
||
import { buildOutlineHtml } from './bibleUtils.jsx';
|
||
|
||
const outline = {
|
||
title: 'Titus 1 — Elders', subtitle: 'Titus 1:5–9',
|
||
tree: [{ id: 'a', text: 'Appoint elders', ref: 'Titus 1:5', kind: 'application', minutes: 4, note: 'Why it matters', children: [{ id: 'b', text: 'Blameless', ref: '', children: [] }] }],
|
||
};
|
||
beforeEach(() => { vi.stubGlobal('navigator', { ...navigator, share: undefined, clipboard: { writeText: vi.fn(() => Promise.resolve()) } }); });
|
||
afterEach(() => { vi.unstubAllGlobals(); vi.restoreAllMocks(); });
|
||
|
||
test('the share text has the title, passage and the outline', () => {
|
||
const text = outlineShareText(outline);
|
||
expect(text.startsWith('Titus 1 — Elders\nTitus 1:5–9\n\nI. [Application] Appoint elders (Titus 1:5) · 4 min')).toBe(true);
|
||
expect(text).toContain('A. Blameless');
|
||
});
|
||
|
||
test('uses the system share sheet when there is one', async () => {
|
||
const share = vi.fn(() => Promise.resolve());
|
||
vi.stubGlobal('navigator', { share, clipboard: { writeText: vi.fn() } });
|
||
expect(await shareOutline(outline)).toBe('shared');
|
||
expect(share).toHaveBeenCalledWith(expect.objectContaining({ title: 'Titus 1 — Elders — Outline' }));
|
||
});
|
||
|
||
test('closing the share sheet is not an error, and a broken one falls back to copying', async () => {
|
||
vi.stubGlobal('navigator', { share: vi.fn(() => Promise.reject(Object.assign(new Error('x'), { name: 'AbortError' }))), clipboard: { writeText: vi.fn() } });
|
||
expect(await shareOutline(outline)).toBe('cancelled');
|
||
const writeText = vi.fn(() => Promise.resolve());
|
||
vi.stubGlobal('navigator', { share: vi.fn(() => Promise.reject(new Error('nope'))), clipboard: { writeText } });
|
||
expect(await shareOutline(outline)).toBe('copied');
|
||
expect(writeText).toHaveBeenCalled();
|
||
});
|
||
|
||
test('without a share sheet it copies, and says so when even that fails', async () => {
|
||
expect(await shareOutline(outline)).toBe('copied');
|
||
vi.stubGlobal('navigator', { clipboard: { writeText: vi.fn(() => Promise.reject(new Error('denied'))) } });
|
||
expect(await shareOutline(outline)).toBe('failed');
|
||
});
|
||
|
||
test('print writes a page of just the outline, and reports a blocked window', () => {
|
||
const doc = { write: vi.fn(), close: vi.fn() };
|
||
vi.spyOn(window, 'open').mockReturnValue({ document: doc, focus: vi.fn(), print: vi.fn() });
|
||
expect(printOutline(outline)).toBe(true);
|
||
const html = doc.write.mock.calls[0][0];
|
||
expect(html).toContain('Appoint elders');
|
||
expect(html).toContain('About 4 minutes');
|
||
window.open.mockReturnValue(null);
|
||
expect(printOutline(outline)).toBe(false);
|
||
});
|
||
|
||
test('the printable page escapes text and shows notes', () => {
|
||
const html = buildOutlineHtml({ title: 'A <b>', tree: [{ id: 'x', text: '<i>hi</i>', ref: '', note: 'line', children: [] }] });
|
||
expect(html).toContain('A <b>');
|
||
expect(html).toContain('<i>hi</i>');
|
||
expect(html).toContain('class="o-note"');
|
||
});
|
||
|
||
test('the Word document builds, with and without the extras', async () => {
|
||
const { buildOutlineDocxBlob } = await import('./docxExport.js');
|
||
const blob = await buildOutlineDocxBlob({
|
||
...outline,
|
||
tree: [{ ...outline.tree[0], scripture: { ref: 'Titus 1:5', text: 'appoint elders', translation: 'BSB' }, attachments: [{ id: 'x', type: 'word', label: 'presbyteros', detail: 'elder' }] }, { id: 'z', text: '', ref: '', children: [] }],
|
||
});
|
||
expect(blob.size).toBeGreaterThan(1000);
|
||
expect((await buildOutlineDocxBlob({ title: 'Empty', tree: [] })).size).toBeGreaterThan(500);
|
||
});
|