- Commentary and study aids wait for 15 words of your own Observe notes. - Insights page: this week's own work, themes across studies, words you keep studying, what slipped in recalls, open follow-ups. - Recall can record what slipped; session notes (teach it back, who came, group notes, follow-ups); "Pray about this" from Apply. - Compare any two passages side by side. - Handout layouts: standard, questions only, large print, note-taking. - Search understands "in <book>", "last week/month/year" and a year. - Home shows the last backup age and nudges after two weeks. - Phone: the stage bar scrolls instead of clipping Script and Export. Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
65 lines
3.5 KiB
JavaScript
65 lines
3.5 KiB
JavaScript
import { expect, test } from 'vitest';
|
|
import { searchMarks, searchNotes, searchProjects } from './personalSearch.js';
|
|
|
|
const project = {
|
|
id: 'p1', title: 'Romans study',
|
|
chapters: [{
|
|
book: 'Romans', bookAbbrev: 'ROM', chapter: '8', verses: [{ number: 1, text: 'a' }, { number: 2, text: 'b' }],
|
|
chunks: [
|
|
{ id: 'c1', startVerse: 1, endVerse: 2, observation: 'No condemnation for those in Christ.', interpretation: '', application: '', generalNotes: '', finalScript: '' },
|
|
{ id: 'c2', startVerse: 2, endVerse: 2, observation: '', interpretation: 'The Spirit of life sets us free', application: '', generalNotes: '', finalScript: '' },
|
|
],
|
|
}],
|
|
};
|
|
|
|
test('finds notes, one hit per chunk, saying which field matched', () => {
|
|
const hits = searchNotes([project], 'spirit');
|
|
expect(hits).toHaveLength(1);
|
|
expect(hits[0]).toMatchObject({ projectId: 'p1', chunkId: 'c2', field: 'Interpretation' });
|
|
expect(hits[0].snippet).toContain('Spirit of life');
|
|
});
|
|
|
|
test('needs three letters before searching notes', () => {
|
|
expect(searchNotes([project], 'sp')).toEqual([]);
|
|
});
|
|
|
|
test('finds projects by title, passage or tag', () => {
|
|
const index = [{ id: 'a', title: 'Romans study', chapterSummary: 'Romans 8', tags: ['grace'] }, { id: 'b', title: 'Psalms', chapterSummary: 'Psalms 23', tags: [] }];
|
|
expect(searchProjects(index, 'rom').map((e) => e.id)).toEqual(['a']);
|
|
expect(searchProjects(index, 'grace').map((e) => e.id)).toEqual(['a']);
|
|
expect(searchProjects(index, 'zzz')).toEqual([]);
|
|
});
|
|
|
|
test('finds highlights by their words or colour name, and bookmarks by reference', () => {
|
|
const marks = {
|
|
bookmarks: { 'JHN-3-16': '#fef08a' },
|
|
highlights: [{ id: 'h1', book: 'ROM', chapter: 8, verse: 28, color: '#bbf7d0', text: 'all things work together', startOffset: 0, endOffset: 5 }],
|
|
labels: { '#bbf7d0': 'Promise' },
|
|
};
|
|
expect(searchMarks(marks, 'together')[0]).toMatchObject({ kind: 'highlight', bookName: 'Romans', verse: 28 });
|
|
expect(searchMarks(marks, 'promise')[0]).toMatchObject({ kind: 'highlight' });
|
|
expect(searchMarks(marks, 'john 3')[0]).toMatchObject({ kind: 'bookmark', chapter: 3, verse: 16 });
|
|
});
|
|
|
|
import { parseSearchQuery as parseQ } from './personalSearch.js';
|
|
|
|
test('search understands "in <book>" and time words', () => {
|
|
const now = new Date('2026-10-14T12:00:00Z').getTime();
|
|
const q = parseQ('grace in Romans last year', now);
|
|
expect(q.text).toBe('grace');
|
|
expect(q.book).toBe('Romans');
|
|
expect(q.since).toBe(now - 365 * 86400000);
|
|
expect(parseQ('hope 2025', now)).toMatchObject({ text: 'hope', since: new Date(2025, 0, 1).getTime(), until: new Date(2026, 0, 1).getTime() });
|
|
expect(parseQ('plain words', now)).toMatchObject({ text: 'plain words', book: null, since: null });
|
|
});
|
|
|
|
test('a passage and date search filters notes, even with no words to match', () => {
|
|
const now = new Date('2026-10-14T12:00:00Z').getTime();
|
|
const day = 86400000;
|
|
const make = (book, editedAt, observation) => ({ id: book + editedAt, title: 'S', lastEdited: now, chapters: [{ book, bookAbbrev: book.slice(0, 3), chapter: '1', chunks: [{ id: `${book}${editedAt}`, startVerse: 1, endVerse: 3, observation, editedAt }] }] });
|
|
const projects = [make('Romans', now - 10 * day, 'grace abounds'), make('Romans', now - 400 * day, 'grace long ago'), make('John', now - 10 * day, 'grace again')];
|
|
expect(searchNotes(projects, 'grace in Romans last year', { now })).toHaveLength(1);
|
|
expect(searchNotes(projects, 'in John last month', { now })).toHaveLength(1);
|
|
expect(searchNotes(projects, 'grace', { now })).toHaveLength(3);
|
|
});
|