17112c06a8
Deploy / deploy (push) Successful in 1m26s
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
106 lines
2.9 KiB
TypeScript
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');
|
|
});
|
|
});
|