Files
study-bible-project/src/utils/readerMarks.test.js
T
nmemmertandClaude Sonnet 5.5 760c1e568b
Build and Push Docker Image / build-and-push (push) Failing after 19s
Sync bookmarks, highlights and display preferences to the account
Bookmarks and text highlights are saved per user and merged across devices
(union on first contact, newer server copy when this device is clean). Theme,
default translation, verse layout, commentary source and reading position sync
too, each with its own timestamp so the newest change to each setting wins.
The Reader never moves under you while it is open. Signing out clears this
device's marks and forgets sync timestamps.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-09-29 10:45:23 -04:00

39 lines
2.2 KiB
JavaScript

import { test, expect } from 'vitest';
import { emptyMarks, isEmptyMarks, marksEqual, mergeMarks, normalizeMarks } from './readerMarks.js';
const hl = (over = {}) => ({ id: 'h1', book: 'JHN', chapter: 3, verse: 16, startOffset: 4, endOffset: 20, color: '#fef08a', ...over });
test('normalizeMarks tolerates junk', () => {
expect(normalizeMarks(null)).toEqual(emptyMarks());
expect(normalizeMarks({ bookmarks: [], highlights: 'x' })).toEqual(emptyMarks());
expect(normalizeMarks({ bookmarks: { a: '#fff' }, highlights: [hl(), null, { nope: 1 }] }).highlights).toEqual([hl()]);
});
test('isEmptyMarks', () => {
expect(isEmptyMarks(emptyMarks())).toBe(true);
expect(isEmptyMarks({ bookmarks: { 'JHN-3-16': '#fff' }, highlights: [] })).toBe(false);
});
test('mergeMarks keeps everything from both devices; the server wins a colour clash', () => {
const local = { bookmarks: { 'JHN-3-16': '#111111', 'GEN-1-1': '#222222' }, highlights: [hl({ id: 'L' })] };
const server = { bookmarks: { 'JHN-3-16': '#999999', 'ROM-8-1': '#333333' }, highlights: [hl({ id: 'S', verse: 17 })] };
const merged = mergeMarks(local, server);
expect(merged.bookmarks).toEqual({ 'JHN-3-16': '#999999', 'GEN-1-1': '#222222', 'ROM-8-1': '#333333' });
expect(merged.highlights.map((h) => h.id).sort()).toEqual(['L', 'S']);
});
test('mergeMarks does not duplicate a highlight both devices made on the same words', () => {
const merged = mergeMarks({ bookmarks: {}, highlights: [hl({ id: 'local-id' })] }, { bookmarks: {}, highlights: [hl({ id: 'server-id' })] });
expect(merged.highlights).toHaveLength(1);
expect(merged.highlights[0].id).toBe('server-id');
});
test('marksEqual compares by value, not by reference or order', () => {
const a = { bookmarks: { x: '#1', y: '#2' }, highlights: [hl({ id: '1' }), hl({ id: '2', verse: 2 })] };
const b = { bookmarks: { y: '#2', x: '#1' }, highlights: [hl({ id: '2', verse: 2 }), hl({ id: '1' })] };
expect(marksEqual(a, b)).toBe(true);
expect(marksEqual(a, { ...b, bookmarks: { ...b.bookmarks, x: '#9' } })).toBe(false);
expect(marksEqual(a, { ...b, highlights: [hl({ id: '1' })] })).toBe(false);
expect(marksEqual(a, { ...b, highlights: [hl({ id: '1', color: '#000000' }), hl({ id: '2', verse: 2 })] })).toBe(false);
});