import { test, expect } from 'vitest'; import { pickRecall, 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(40), editedAt: NOW - 2 * WEEK_MS, ...extra }); const study = (chunks, id = 'p1') => ({ id, title: 'Romans', translation: 'BSB', chapters: [{ book: 'Romans', bookAbbrev: 'Rom', chapter: 8, chunks }] }); test('picks a studied chunk that has enough of your own writing', () => { const pick = pickRecall([study([chunk('a')])], NOW); expect(pick).toMatchObject({ projectId: 'p1', projectTitle: 'Romans' }); expect(pick.chunk.id).toBe('a'); }); test('skips thin notes, pasted notes and chunks edited this week', () => { const pasted = text(40); const projects = [study([ chunk('thin', { observation: text(10) }), chunk('pasted', { observation: pasted, aiPastes: { observation: [pasted] } }), chunk('fresh', { editedAt: NOW - 1000 }), ])]; expect(pickRecall(projects, NOW)).toBeNull(); }); test('never-recalled chunks come before ones recalled long ago', () => { const old = chunk('old', { recalls: [{ at: NOW - 3 * WEEK_MS, text: 'x', rating: 'solid' }] }); expect(pickRecall([study([old, chunk('new')])], NOW).chunk.id).toBe('new'); }); test('one recall a week: nothing is offered after you have done one', () => { const done = chunk('done', { recalls: [{ at: NOW - 1000, text: 'x', rating: 'partly' }] }); expect(pickRecall([study([done, chunk('other')])], NOW)).toBeNull(); expect(pickRecall([study([done, chunk('other')])], NOW + WEEK_MS)).not.toBeNull(); }); import { recallHistory, recallSummary } from './recall.js'; test('history lists every recall newest first, with where it came from', () => { const a = chunk('a', { recalls: [{ at: NOW - 3 * WEEK_MS, text: 'old', rating: 'forgot' }, { at: NOW - 1000, text: 'new', rating: 'solid' }] }); const h = recallHistory([study([a])]); expect(h.map((x) => x.text)).toEqual(['new', 'old']); expect(h[0]).toMatchObject({ projectTitle: 'Romans', chunkId: 'a' }); expect(h[0].reference).toContain('Romans'); }); test('the summary counts ratings and marks each of the last eight weeks by its weakest recall', () => { const history = [ { at: NOW - 1000, rating: 'solid' }, { at: NOW - 2000, rating: 'partly' }, { at: NOW - 2 * WEEK_MS + 1000, rating: 'forgot' }, ]; const { counts, total, recent } = recallSummary(history, NOW); expect(counts).toEqual({ solid: 1, partly: 1, forgot: 1 }); expect(total).toBe(3); expect(recent).toHaveLength(8); expect(recent[7].rating).toBe('partly'); // this week: the weaker of solid and partly expect(recent[6].rating).toBe('forgot'); // last week expect(recent[0].rating).toBeNull(); });