| 1 | import { describe, expect, it } from "vite-plus/test"; |
| 2 | |
| 3 | import { resolveSnoozePresets, snoozeWakeDescription } from "./Sidebar.snooze"; |
| 4 | |
| 5 | // Local-time constructor so preset math is timezone-stable in tests. |
| 6 | function localDate(year: number, month: number, day: number, hour: number, minute = 0): Date { |
| 7 | return new Date(year, month - 1, day, hour, minute, 0, 0); |
| 8 | } |
| 9 | |
| 10 | describe("resolveSnoozePresets", () => { |
| 11 | it("offers one hour, three hours, evening, tomorrow, and next week in the morning", () => { |
| 12 | // Wednesday 2026-04-08 10:00 local. |
| 13 | const presets = resolveSnoozePresets(localDate(2026, 4, 8, 10), "locale"); |
| 14 | expect(presets.map((preset) => preset.id)).toEqual([ |
| 15 | "hour", |
| 16 | "three-hours", |
| 17 | "evening", |
| 18 | "tomorrow", |
| 19 | "next-week", |
| 20 | ]); |
| 21 | const threeHours = presets.find((preset) => preset.id === "three-hours"); |
| 22 | expect(new Date(threeHours!.snoozedUntil).getHours()).toBe(13); |
| 23 | const evening = presets.find((preset) => preset.id === "evening"); |
| 24 | expect(new Date(evening!.snoozedUntil).getHours()).toBe(18); |
| 25 | const tomorrow = presets.find((preset) => preset.id === "tomorrow"); |
| 26 | const tomorrowDate = new Date(tomorrow!.snoozedUntil); |
| 27 | expect(tomorrowDate.getDate()).toBe(9); |
| 28 | expect(tomorrowDate.getHours()).toBe(9); |
| 29 | const nextWeek = presets.find((preset) => preset.id === "next-week"); |
| 30 | const nextWeekDate = new Date(nextWeek!.snoozedUntil); |
| 31 | expect(nextWeekDate.getDay()).toBe(1); |
| 32 | expect(nextWeekDate.getDate()).toBe(13); |
| 33 | }); |
| 34 | |
| 35 | it("whenLabel complements the label instead of repeating it", () => { |
| 36 | const presets = resolveSnoozePresets(localDate(2026, 4, 8, 10), "locale"); |
| 37 | for (const preset of presets) { |
| 38 | // Day words live in the label column; the time column is time-only |
| 39 | // (plus a weekday for next week, which names a different day). |
| 40 | expect(preset.whenLabel.toLowerCase()).not.toContain("tomorrow"); |
| 41 | } |
| 42 | const tomorrow = presets.find((preset) => preset.id === "tomorrow"); |
| 43 | expect(tomorrow!.whenLabel).toMatch(/9/); |
| 44 | const nextWeek = presets.find((preset) => preset.id === "next-week"); |
| 45 | expect(nextWeek!.whenLabel).toMatch(/Mon/); |
| 46 | }); |
| 47 | |
| 48 | it("drops the evening preset once evening is near or past", () => { |
| 49 | expect( |
| 50 | resolveSnoozePresets(localDate(2026, 4, 8, 17, 30), "locale").map((preset) => preset.id), |
| 51 | ).toEqual(["hour", "three-hours", "tomorrow", "next-week"]); |
| 52 | expect( |
| 53 | resolveSnoozePresets(localDate(2026, 4, 8, 21), "locale").map((preset) => preset.id), |
| 54 | ).toEqual(["hour", "three-hours", "tomorrow", "next-week"]); |
| 55 | }); |
| 56 | |
| 57 | it("puts next week a full week out when today is Monday", () => { |
| 58 | // Monday 2026-04-06. |
| 59 | const presets = resolveSnoozePresets(localDate(2026, 4, 6, 10), "locale"); |
| 60 | const nextWeek = new Date(presets.find((preset) => preset.id === "next-week")!.snoozedUntil); |
| 61 | expect(nextWeek.getDay()).toBe(1); |
| 62 | expect(nextWeek.getDate()).toBe(13); |
| 63 | }); |
| 64 | it("formats preset times with the selected clock preference", () => { |
| 65 | const twelveHour = resolveSnoozePresets(localDate(2026, 4, 8, 10), "12-hour"); |
| 66 | const twentyFourHour = resolveSnoozePresets(localDate(2026, 4, 8, 10), "24-hour"); |
| 67 | |
| 68 | expect(twelveHour.find((preset) => preset.id === "evening")!.whenLabel).toMatch(/PM/i); |
| 69 | expect(twentyFourHour.find((preset) => preset.id === "evening")!.whenLabel).toBe("18:00"); |
| 70 | }); |
| 71 | }); |
| 72 | |
| 73 | describe("snoozeWakeDescription", () => { |
| 74 | const now = localDate(2026, 4, 8, 10); |
| 75 | |
| 76 | it("uses bare time today, 'tomorrow' next day, weekday within the week", () => { |
| 77 | expect( |
| 78 | snoozeWakeDescription(localDate(2026, 4, 8, 18).toISOString(), now, "locale"), |
| 79 | ).not.toContain("tomorrow"); |
| 80 | expect(snoozeWakeDescription(localDate(2026, 4, 9, 9).toISOString(), now, "locale")).toContain( |
| 81 | "tomorrow", |
| 82 | ); |
| 83 | expect(snoozeWakeDescription(localDate(2026, 4, 13, 9).toISOString(), now, "locale")).toMatch( |
| 84 | /Mon/, |
| 85 | ); |
| 86 | }); |
| 87 | |
| 88 | it("formats wake descriptions with the selected clock preference", () => { |
| 89 | expect(snoozeWakeDescription(localDate(2026, 4, 8, 18).toISOString(), now, "12-hour")).toMatch( |
| 90 | /PM/i, |
| 91 | ); |
| 92 | expect(snoozeWakeDescription(localDate(2026, 4, 8, 18).toISOString(), now, "24-hour")).toBe( |
| 93 | "18:00", |
| 94 | ); |
| 95 | }); |
| 96 | }); |