Files
vu/tests/app-state.test.ts
will 17112c06a8
Deploy / deploy (push) Successful in 1m26s
Make the Today link context-sensitive to time of day
When already viewing today, clicking Today now snaps the selected hour
to whatever hour would be default for the current wall-clock time
(same as a fresh visit to /vu); when viewing another date it still just
jumps to today while preserving the current hour, as before. The link's
href is built from the same todayTargetHour() decision as the click
handler, so right-click -> open in new tab lands on the same target
instead of following a stale href.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CbHcmjnqQPJZ1i2yoebmp5
2026-08-28 16:52:09 -04:00

106 lines
2.9 KiB
TypeScript

import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
import { defaultHourForTime, goToToday, getState, setDate, setSelectedHour, todayTargetHour } from '../src/app-state';
function at(hour: number, minute = 0): Date {
const d = new Date();
d.setHours(hour, minute, 0, 0);
return d;
}
describe('defaultHourForTime', () => {
it.each([
[0, 30, 'matins'],
[3, 0, 'matins'],
[5, 59, 'matins'],
[6, 0, 'lauds'],
[6, 30, 'lauds'],
[7, 59, 'lauds'],
[8, 0, 'prime'],
[8, 30, 'prime'],
[8, 59, 'prime'],
[9, 0, 'terce'],
[10, 0, 'terce'],
[11, 59, 'terce'],
[12, 0, 'sext'],
[13, 0, 'sext'],
[14, 59, 'sext'],
[15, 0, 'none'],
[16, 0, 'none'],
[16, 59, 'none'],
[17, 0, 'vespers'],
[18, 0, 'vespers'],
[20, 59, 'vespers'],
[21, 0, 'compline'],
[22, 0, 'compline'],
[23, 59, 'compline'],
] as const)('%i:%i -> %s', (hour, minute, expected) => {
expect(defaultHourForTime(at(hour, minute))).toBe(expected);
});
});
describe('goToToday', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date(2026, 7, 28, 10, 0, 0)); // 2026-08-28 10:00 local -> terce
});
afterEach(() => {
vi.useRealTimers();
});
it('jumps to today and preserves the current hour when on a different date', () => {
setDate('2026-08-01');
setSelectedHour('vespers');
goToToday();
expect(getState().date).toBe('2026-08-28');
expect(getState().selectedHour).toBe('vespers');
});
it('snaps the hour to the current time-of-day default when already on today', () => {
setDate('2026-08-28');
setSelectedHour('vespers');
goToToday();
expect(getState().date).toBe('2026-08-28');
expect(getState().selectedHour).toBe('terce');
});
it('is idempotent-safe: a second click after jumping to today snaps the hour', () => {
setDate('2026-08-01');
setSelectedHour('vespers');
goToToday(); // -> today, hour still 'vespers'
goToToday(); // -> already today, snaps hour to time-of-day default
expect(getState().date).toBe('2026-08-28');
expect(getState().selectedHour).toBe('terce');
});
});
describe('todayTargetHour', () => {
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date(2026, 7, 28, 10, 0, 0)); // 2026-08-28 10:00 local -> terce
});
afterEach(() => {
vi.useRealTimers();
});
it('matches goToToday, so the Today link href agrees with its click behavior', () => {
// Different date: href should preserve the current hour, same as goToToday().
setDate('2026-08-01');
setSelectedHour('vespers');
expect(todayTargetHour()).toBe('vespers');
// Already on today: href should point at the time-of-day default,
// same as goToToday() — this is what a right-click "open in new tab"
// relies on, since it follows the href without running the click handler.
setDate('2026-08-28');
expect(todayTargetHour()).toBe('terce');
});
});