Invite-only sign-up, present mode, series, handouts, templates, dictation, progress, sharing
Build and Push Docker Image / build-and-push (push) Successful in 6m47s
Build and Push Docker Image / build-and-push (push) Successful in 6m47s
- Admin: sign-up Open / Invite only / Closed with single-use invite links - Present mode for scripts (auto-scroll) and outlines (step through, pace vs planned minutes) - Series: group studies on the dashboard, filter by series, calendar file for a study's sessions - Lesson handout: printable page per session (passage, outline, questions, writing lines) - Save a chunk's notes as your own study template (synced) - Voice dictation for notes and scripts (browser speech recognition) - Reading progress: map of the 66 books and a calendar of days read, synced and merged across devices - Export highlights and bookmarks as Markdown or Word, grouped by named colour - Verse of the day push to the ntfy topic (needs IQBIBLE_API_KEY) - Sharing: co-teacher editing via single-use invite links; read-only links can expire and take a passcode Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
88bc59bac0
commit
54cb1c217d
73 files changed
+2739
-134
No files matched your search
@@ -0,0 +1,48 @@
|
||||
import { test, expect } from 'vitest';
|
||||
import { buildHandoutHtml, outlineForSession, questionsFor, sessionsFor } from './handout.js';
|
||||
|
||||
const chunk = (id, s, e, x = {}) => ({ id, startVerse: s, endVerse: e, observation: '', interpretation: '', application: '', ...x });
|
||||
const project = {
|
||||
title: 'Romans 8 <study>', series: 'Romans', translation: 'BSB',
|
||||
outline: [{ id: 'o1', text: 'No condemnation', ref: '', chunkId: 'a', children: [{ id: 'o2', text: 'The Spirit gives life', ref: 'Rom 8:2', chunkId: null, children: [] }] }],
|
||||
chapters: [{ book: 'Romans', bookAbbrev: 'ROM', chapter: '8', verses: [{ number: 1, text: 'There is now no condemnation.' }, { number: 2, text: 'For the law of the Spirit of life.' }, { number: 12, text: 'So then, brothers.' }], chunks: [
|
||||
chunk('a', 1, 2, { episodeNumber: '1', episodeTitle: 'Free at last', discussionQuestions: '1. What is condemnation?\n- Why does it end?', application: 'Rest in grace' }),
|
||||
chunk('b', 12, 12, { application: '- Walk by the Spirit\n- Put sin to death' }),
|
||||
] }],
|
||||
};
|
||||
|
||||
test('sessions are numbered in order with their passages', () => {
|
||||
expect(sessionsFor(project).map((s) => [s.number, s.reference])).toEqual([[1, 'Romans 8:1–2'], [2, 'Romans 8:12']]);
|
||||
});
|
||||
|
||||
test('questions written for a session are used; otherwise the Application notes become prompts', () => {
|
||||
expect(questionsFor(project.chapters[0].chunks[0])).toEqual({ kind: 'questions', items: ['What is condemnation?', 'Why does it end?'] });
|
||||
expect(questionsFor(project.chapters[0].chunks[1])).toEqual({ kind: 'think', items: ['Walk by the Spirit', 'Put sin to death'] });
|
||||
expect(questionsFor(chunk('z', 1, 1))).toEqual({ kind: 'none', items: [] });
|
||||
});
|
||||
|
||||
test('a session uses the study outline points linked to it', () => {
|
||||
expect(outlineForSession(project, project.chapters[0].chunks[0])).toHaveLength(1);
|
||||
expect(outlineForSession(project, project.chapters[0].chunks[1])).toEqual([]);
|
||||
expect(outlineForSession(project, { id: 'a', outline: [{ id: 'x', text: 'Own', children: [] }] })[0].id).toBe('x');
|
||||
});
|
||||
|
||||
test('the handout has a page per session with passage, outline and questions, and escapes text', () => {
|
||||
const html = buildHandoutHtml(project);
|
||||
expect(html.match(/class="page"/g)).toHaveLength(2);
|
||||
expect(html).toContain('Session 1: Free at last');
|
||||
expect(html).toContain('<sup>1</sup> There is now no condemnation.');
|
||||
expect(html).toContain('The Spirit gives life');
|
||||
expect(html).toContain('Discussion questions');
|
||||
expect(html).toContain('Think about it');
|
||||
expect(html).toContain('Romans 8 <study>'); // no raw markup from the title
|
||||
expect(html).not.toContain('<study>');
|
||||
expect(html).toContain('class="rule"');
|
||||
});
|
||||
|
||||
test('one session only, and no writing lines when switched off', () => {
|
||||
const html = buildHandoutHtml(project, ['b'], { answerLines: false });
|
||||
expect(html.match(/class="page"/g)).toHaveLength(1);
|
||||
expect(html).toContain('Session 2');
|
||||
expect(html).not.toContain('class="rule"');
|
||||
});
|
||||
Reference in new issue
Block a user