Files
study-bible-project/src/utils/insights.test.js
T
nmemmertandClaude Sonnet 5.5 eccc5e509d Studying: daily five minutes, reading list, plan-day studies use your template
- 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>
2026-10-14 13:51:49 -04:00

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);
});