Files
neuf-www/web/src/lib/event.test.ts
T

196 lines
5.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import {
type EventFragment,
getEventPig,
getFutureOccurrences,
getSingularEvents,
organizeEventsByDate,
organizeEventsInCalendar,
sortSingularEvents,
} from "./event.ts";
// "now" is tirsdag 2026-07-07 12:00 in Oslo
beforeEach(() => {
vi.useFakeTimers();
vi.setSystemTime(new Date("2026-07-07T10:00:00Z"));
});
afterEach(() => {
vi.useRealTimers();
});
const makeEvent = (id: string, starts: string[]) => ({
id,
occurrences: starts.map((start, i) => ({
id: `${id}-${i}`,
start,
end: null,
})),
});
describe("getSingularEvents", () => {
it("flattens one copy per occurrence", () => {
const a = makeEvent("a", ["2026-07-07T18:00:00Z", "2026-07-08T18:00:00Z"]);
const b = makeEvent("b", ["2026-07-09T18:00:00Z"]);
const singular = getSingularEvents([a, b]);
expect(singular).toHaveLength(3);
expect(singular.map((e) => e.occurrence.id)).toEqual(["a-0", "a-1", "b-0"]);
expect(singular[0].id).toBe("a");
expect("occurrence" in a).toBe(false);
});
});
describe("sortSingularEvents", () => {
it("sorts by occurrence start", () => {
const events = getSingularEvents([
makeEvent("late", ["2026-07-09T18:00:00Z"]),
makeEvent("early", ["2026-07-07T18:00:00Z"]),
makeEvent("mid", ["2026-07-08T18:00:00Z"]),
]);
expect(sortSingularEvents(events).map((e) => e.id)).toEqual([
"early",
"mid",
"late",
]);
});
});
describe("organizeEventsInCalendar", () => {
it("nests by yearMonth/week/day and seeds empty days", () => {
const events = getSingularEvents([
makeEvent("a", ["2026-07-07T18:00:00Z"]),
]);
const calendar = organizeEventsInCalendar(events);
expect(Object.keys(calendar)).toEqual(["2026-07"]);
const weeks = Object.values(calendar["2026-07"]);
expect(weeks).toHaveLength(1);
const week = weeks[0];
// the whole week (man 6. søn 12. juli) is pre-seeded
expect(Object.keys(week)).toHaveLength(7);
expect(week["2026-07-07"].map((e) => e.id)).toEqual(["a"]);
expect(week["2026-07-06"]).toEqual([]);
expect(week["2026-07-12"]).toEqual([]);
});
it("splits a week across the year boundary under a shared week key", () => {
const events = getSingularEvents([
makeEvent("romjul", ["2026-12-30T20:00:00Z"]),
makeEvent("nyttaar", ["2027-01-02T20:00:00Z"]),
]);
const calendar = organizeEventsInCalendar(events);
// uke 53 (man 28.12. søn 3.1.) spans both months; each yearMonth
// bucket holds only its own days, under the same week number
expect(Object.keys(calendar)).toEqual(["2026-12", "2027-01"]);
expect(Object.keys(calendar["2026-12"])).toEqual(["53"]);
expect(Object.keys(calendar["2027-01"])).toEqual(["53"]);
expect(Object.keys(calendar["2026-12"]["53"])).toEqual([
"2026-12-28",
"2026-12-29",
"2026-12-30",
"2026-12-31",
]);
expect(Object.keys(calendar["2027-01"]["53"])).toEqual([
"2027-01-01",
"2027-01-02",
"2027-01-03",
]);
expect(calendar["2026-12"]["53"]["2026-12-30"].map((e) => e.id)).toEqual([
"romjul",
]);
expect(calendar["2027-01"]["53"]["2027-01-02"].map((e) => e.id)).toEqual([
"nyttaar",
]);
});
it("seeds a full 7-day week through the DST fall-back", () => {
// DST ends søndag 2026-10-25 (a 25-hour day in Oslo)
const events = getSingularEvents([
makeEvent("a", ["2026-10-21T18:00:00Z"]),
]);
const calendar = organizeEventsInCalendar(events);
const weeks = Object.values(calendar["2026-10"]);
expect(weeks).toHaveLength(1);
expect(Object.keys(weeks[0])).toEqual([
"2026-10-19",
"2026-10-20",
"2026-10-21",
"2026-10-22",
"2026-10-23",
"2026-10-24",
"2026-10-25",
]);
expect(weeks[0]["2026-10-21"].map((e) => e.id)).toEqual(["a"]);
});
});
describe("organizeEventsByDate", () => {
it("groups by Oslo date, sorted within the day", () => {
const events = getSingularEvents([
makeEvent("kveld", ["2026-07-07T20:00:00Z"]),
makeEvent("torsdag", ["2026-07-09T18:00:00Z"]),
makeEvent("ettermiddag", ["2026-07-07T16:00:00Z"]),
]);
const byDate = organizeEventsByDate(events);
expect(Object.keys(byDate)).toEqual(["2026-07-07", "2026-07-09"]);
expect(byDate["2026-07-07"].map((e) => e.id)).toEqual([
"ettermiddag",
"kveld",
]);
});
});
describe("getFutureOccurrences", () => {
it("drops past days and sorts ascending", () => {
const event = makeEvent("a", [
"2026-07-14T18:00:00Z",
"2026-07-01T18:00:00Z",
"2026-07-07T18:00:00Z",
]);
expect(getFutureOccurrences(event).map((o) => o.start)).toEqual([
"2026-07-07T18:00:00Z",
"2026-07-14T18:00:00Z",
]);
});
});
describe("getEventPig", () => {
const pigEvent = (pig: unknown, categoryPigs: string[] = []) =>
({
pig,
categories: categoryPigs.map((p) => ({ pig: p })),
}) as unknown as EventFragment;
it("returns an explicit valid pig", () => {
expect(getEventPig(pigEvent("dance"))).toBe("dance");
});
it("returns null for empty, missing or unknown pig", () => {
expect(getEventPig(pigEvent(""))).toBeNull();
expect(getEventPig(pigEvent(null))).toBeNull();
expect(getEventPig(pigEvent("notapig"))).toBeNull();
});
it("picks a valid category pig for automatic", () => {
expect(getEventPig(pigEvent("automatic", ["music", "bogus"]))).toBe(
"music"
);
expect(["music", "drink"]).toContain(
getEventPig(pigEvent("automatic", ["music", "drink"]))
);
expect(getEventPig(pigEvent("automatic"))).toBeNull();
});
});