- Share on the Outline page opens a sheet: create / copy / turn off a read-only link, share the link with the system share sheet, or send the outline as text - The link has its own token (separate from the whole-study share link) and the public endpoint returns only the outlines, rebuilt field by field: no observations, interpretations, applications, scripts or word notes - Opening the link shows the outline(s) with no sign-in, a switcher between the whole-study and per-chunk outlines, and Print / PDF - New outline_share_token column (added automatically on start) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
43 lines
2.4 KiB
JavaScript
43 lines
2.4 KiB
JavaScript
import { expect, test } from 'vitest';
|
||
import { buildOutlinePayload } from './outlineShare.js';
|
||
|
||
const node = (id, extra = {}) => ({ id, text: id, ref: '', kind: '', minutes: null, note: '', scripture: null, attachments: [], children: [], ...extra });
|
||
const project = {
|
||
title: 'Titus', translation: 'BSB',
|
||
outline: [node('a', { minutes: 5, kind: 'illustration', children: [node('a1')] })],
|
||
chapters: [{
|
||
book: 'Titus', bookAbbrev: 'TIT', chapter: '1', verses: [{ number: 1, text: 'secret verse text' }],
|
||
chunks: [
|
||
{ id: 'c1', startVerse: 1, endVerse: 4, observation: 'PRIVATE OBSERVATION', finalScript: 'PRIVATE SCRIPT', greekWords: [{ lexeme: 'x' }], outline: [node('b')] },
|
||
{ id: 'c2', startVerse: 5, endVerse: 5, observation: 'more private', outline: [] },
|
||
],
|
||
}],
|
||
};
|
||
|
||
test('shares the outlines only, with each chunk labelled by its passage', () => {
|
||
const p = buildOutlinePayload(project);
|
||
expect(p.title).toBe('Titus');
|
||
expect(p.outlines.map((o) => [o.scope, o.label])).toEqual([['study', 'Whole study'], ['c1', 'Titus 1:1–4']]);
|
||
expect(p.outlines[0].tree[0]).toMatchObject({ text: 'a', minutes: 5, kind: 'illustration' });
|
||
expect(p.outlines[0].tree[0].children[0].text).toBe('a1');
|
||
});
|
||
|
||
test('nothing from the rest of the study leaks into what is shared', () => {
|
||
const json = JSON.stringify(buildOutlinePayload(project));
|
||
for (const secret of ['PRIVATE OBSERVATION', 'PRIVATE SCRIPT', 'secret verse text', 'more private', 'greekWords']) expect(json).not.toContain(secret);
|
||
});
|
||
|
||
test('unknown fields on a point are dropped, and bad values are cleaned', () => {
|
||
const p = buildOutlinePayload({ title: 'T', outline: [{ id: 'x', text: 'hi', evil: '<script>', kind: 'bogus', minutes: -4, scripture: { text: 5 }, attachments: [{ type: 'word', label: 'L' }, null], children: 'nope' }], chapters: [] });
|
||
expect(p.outlines[0].tree[0]).toEqual({
|
||
id: 'x', text: 'hi', ref: '', kind: '', minutes: null, note: '', scripture: null,
|
||
attachments: [{ id: '', type: 'word', label: 'L' }], children: [],
|
||
});
|
||
});
|
||
|
||
test('a study with no outlines shares none, and a huge outline is capped', () => {
|
||
expect(buildOutlinePayload({ title: 'T', chapters: [] }).outlines).toEqual([]);
|
||
const big = { title: 'T', outline: Array.from({ length: 3000 }, (_, i) => node(`n${i}`)), chapters: [] };
|
||
expect(buildOutlinePayload(big).outlines[0].tree).toHaveLength(2000);
|
||
});
|