import { describe, expect, it } from "vitest"; import { ApiError } from "@/lib/api"; import { describeApiError, isSessionExpired } from "@/lib/api-errors"; import { fromWallTime, toWallTime } from "@/lib/dates"; import { dateBlock, describeWhen, isPast, occurrence, repeatLabel, todayMarkerIndex } from "@/lib/event-display"; import { makeEvent } from "@/lib/test-fixtures"; const now = new Date("2026-09-27T10:00:00Z"); describe("dateBlock", () => { it("gives weekday, day and month in German, Berlin time", () => { // 23:30 UTC on 2 Oct is already Saturday 3 Oct in Berlin. expect(dateBlock(new Date("2026-10-02T23:30:00Z"))).toEqual({ weekday: "Sa", day: "3", month: "Okt" }); }); }); describe("describeWhen", () => { it("shows a same-day time range", () => { expect(describeWhen(makeEvent(), now)).toBe("19:00–21:00 Uhr"); }); it("shows a multi-day timed event with both dates", () => { const event = makeEvent({ start: fromWallTime("2026-10-03", "19:00"), end: fromWallTime("2026-10-05", "12:00") }); expect(describeWhen(event, now)).toBe("3. Okt., 19:00 – 5. Okt., 12:00"); }); it("shows whole-day events, with the range when they span days", () => { const oneDay = makeEvent({ wholeDay: true, start: fromWallTime("2026-10-03", "00:00"), end: fromWallTime("2026-10-03", "23:59") }); const weekend = makeEvent({ wholeDay: true, start: fromWallTime("2026-10-17", "00:00"), end: fromWallTime("2026-10-19", "23:59") }); expect(describeWhen(oneDay, now)).toBe("Ganztägig"); expect(describeWhen(weekend, now)).toBe("Ganztägig · 17.–19. Okt."); }); }); describe("occurrence", () => { it("moves a birthday's start and end to this year's date together", () => { const birthday = makeEvent({ wholeDay: true, repeatFrequency: "YEARLY", start: fromWallTime("1994-10-03", "00:00"), end: fromWallTime("1994-10-03", "23:59"), }); const { start, end } = occurrence(birthday, now); expect(toWallTime(start)).toEqual({ date: "2026-10-03", time: "00:00" }); expect(toWallTime(end)).toEqual({ date: "2026-10-03", time: "23:59" }); expect(describeWhen(birthday, now)).toBe("Ganztägig"); }); }); describe("isPast", () => { it("is true only for non-repeating events that ended before today", () => { const old = { start: fromWallTime("2026-07-18", "15:00"), end: fromWallTime("2026-07-18", "22:00") }; expect(isPast(makeEvent(old), now)).toBe(true); expect(isPast(makeEvent({ ...old, repeatFrequency: "YEARLY" }), now)).toBe(false); expect(isPast(makeEvent(), now)).toBe(false); }); }); describe("todayMarkerIndex", () => { const past = makeEvent({ eventId: 1, start: fromWallTime("2026-07-18", "15:00"), end: fromWallTime("2026-07-18", "22:00") }); const today = makeEvent({ eventId: 2, start: fromWallTime("2026-09-27", "08:00"), end: fromWallTime("2026-09-27", "09:00") }); const later = makeEvent({ eventId: 3 }); it("sits before the first event not yet over, ascending", () => { expect(todayMarkerIndex([past, today, later], "start_asc", now)).toBe(1); }); it("sits before the first past event, descending", () => { expect(todayMarkerIndex([later, today, past], "start_desc", now)).toBe(2); }); it("goes to the end or the start when everything is on one side", () => { expect(todayMarkerIndex([past], "start_asc", now)).toBe(1); expect(todayMarkerIndex([later], "start_asc", now)).toBe(0); expect(todayMarkerIndex([later], "start_desc", now)).toBe(1); }); it("has no place in a list sorted by creation, or an empty one", () => { expect(todayMarkerIndex([past, later], "created_asc", now)).toBeNull(); expect(todayMarkerIndex([], "start_asc", now)).toBeNull(); }); }); describe("repeatLabel", () => { it("translates known frequencies and passes unknown ones through", () => { expect(repeatLabel("")).toBe(""); expect(repeatLabel("YEARLY")).toBe("jährlich"); expect(repeatLabel("HOURLY")).toBe("hourly"); }); }); describe("describeApiError", () => { it("explains each kind of failure in German", () => { expect(describeApiError(new ApiError(0, {}))).toMatch(/Keine Verbindung/); expect(describeApiError(new ApiError(403, {}))).toMatch(/Berechtigung/); expect(describeApiError(new ApiError(500, {}))).toMatch(/\(500\)/); expect(describeApiError(new ApiError(200, {}, "Der Termin wurde nicht gespeichert."))).toBe( "Der Termin wurde nicht gespeichert.", ); expect(describeApiError(new Error("x"))).toMatch(/Unerwarteter Fehler/); }); it("recognises only 401 as an expired session", () => { expect(isSessionExpired(new ApiError(401, {}))).toBe(true); expect(isSessionExpired(new ApiError(403, {}))).toBe(false); }); });