- Home: "Your five minutes": a rotating Observe/Interpret/Apply prompt on the passage whose matching note is thinnest in your own words, a 5:00 clock and a box that appends to that note. Pasting is blocked; sessions show in Insights. - Study: a Reading list tab (R) for the study: books and articles to consult, with a note and a tick; merges across devices. - Starting a study from a reading-plan day now uses the notes template you last picked when starting a study by hand (logic moved to utils/planStudy.js). Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
54 lines
3.0 KiB
JavaScript
54 lines
3.0 KiB
JavaScript
import { test, expect } from 'vitest';
|
|
import { weeklySummary, themeIndex, wordIndex, slippedList, followUpList } from './insights.js';
|
|
import { WEEK_MS } from './recall.js';
|
|
|
|
const NOW = 100 * WEEK_MS;
|
|
const text = (n) => Array.from({ length: n }, (_, i) => `w${i}`).join(' ');
|
|
const chunk = (id, extra = {}) => ({ id, startVerse: 1, endVerse: 5, observation: text(20), editedAt: NOW - 1000, ...extra });
|
|
const study = (chunks, id = 'p1', title = 'Romans') => ({ id, title, translation: 'BSB', chapters: [{ book: 'Romans', bookAbbrev: 'Rom', chapter: 8, chunks }] });
|
|
|
|
test('the weekly summary counts only chunks edited this week', () => {
|
|
const pasted = text(10);
|
|
const projects = [study([
|
|
chunk('a', { teachBack: 'x', recalls: [{ at: NOW - 1000, rating: 'solid' }] }),
|
|
chunk('b', { observation: `${text(10)} ${pasted}`, aiPastes: { observation: [pasted] }, editedAt: NOW - 500 }),
|
|
chunk('old', { editedAt: NOW - 3 * WEEK_MS, recalls: [{ at: NOW - 3 * WEEK_MS, rating: 'solid' }] }),
|
|
])];
|
|
const s = weeklySummary(projects, NOW);
|
|
expect(s).toMatchObject({ chunksTouched: 2, ownWords: 30, recalls: 1, teachBacks: 1 });
|
|
expect(s.ownShare).toBe(75); // 100% and 50%, averaged
|
|
expect(weeklySummary([], NOW).ownShare).toBeNull();
|
|
});
|
|
|
|
test('tags group notes from different studies into themes, most used first', () => {
|
|
const p1 = study([chunk('a', { tags: ['Grace'] }), chunk('b', { tags: ['hope'] })]);
|
|
const p2 = study([chunk('c', { tags: ['grace '] })], 'p2', 'John');
|
|
const themes = themeIndex([p1, p2]);
|
|
expect(themes.map((t) => [t.tag, t.count])).toEqual([['Grace', 2], ['hope', 1]]);
|
|
expect(themes[0].items.map((i) => i.projectTitle)).toEqual(['Romans', 'John']);
|
|
});
|
|
|
|
test('words that come up in more than one place are listed', () => {
|
|
const w = (n, g) => ({ id: n, strongNumber: n, lexeme: g, englishGloss: g });
|
|
const projects = [study([chunk('a', { greekWords: [w('G26', 'agape'), w('G4102', 'pistis')] }), chunk('b', { greekWords: [w('g26', 'agape')] })])];
|
|
const list = wordIndex(projects);
|
|
expect(list).toHaveLength(1);
|
|
expect(list[0]).toMatchObject({ strong: 'G26', count: 2 });
|
|
expect(wordIndex(projects, { minCount: 1 })).toHaveLength(2);
|
|
});
|
|
|
|
test('slipped lines and follow-ups are collected, open follow-ups first', () => {
|
|
const projects = [study([chunk('a', {
|
|
recalls: [{ at: 5, gaps: '- the serpent\nwho spoke', rating: 'partly' }, { at: 9, gaps: '', rating: 'solid' }],
|
|
followUps: [{ id: '1', text: 'done thing', done: true }, { id: '2', text: 'call Sam', done: false }],
|
|
})])];
|
|
expect(slippedList(projects)).toHaveLength(1);
|
|
expect(slippedList(projects)[0].items).toEqual(['the serpent', 'who spoke']);
|
|
expect(followUpList(projects).map((f) => f.text)).toEqual(['call Sam', 'done thing']);
|
|
});
|
|
|
|
test('five-minute sessions this week are counted', () => {
|
|
const p = [study([chunk('a', { fiveMinutes: [{ at: NOW - 1000, field: 'observation' }, { at: NOW - 3 * WEEK_MS, field: 'application' }] })])];
|
|
expect(weeklySummary(p, NOW).fiveMinutes).toBe(1);
|
|
});
|