Outline: per-chunk outlines, richer points, print and share
- One outline for the whole study plus one per chunk (picker at the top); chunk outlines can start from the chunk's verses, each carrying its words - Points can have a type (illustration, application, quote, question, transition), minutes (with a running total), a longer note, verse text fetched from a passage, and cross-references / word studies attached from the chunk - Share (system share sheet, or copy), Print / PDF, Copy as text, Word and Markdown downloads for any one outline - All of it included in the Markdown, HTML/PDF and Word study exports, with each chunk's outline under that chunk Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d273c8cb3e
commit
820886e3ab
12 files changed
+741
-103
No files matched your search
@@ -0,0 +1,56 @@
|
||||
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"');
|
||||
});
|
||||
Reference in new issue
Block a user