diff --git a/app/features/calendar/calendar-utils.test.ts b/app/features/calendar/calendar-utils.test.ts new file mode 100644 index 000000000..690c62ca5 --- /dev/null +++ b/app/features/calendar/calendar-utils.test.ts @@ -0,0 +1,102 @@ +import { describe, expect, it } from "vitest"; +import { + calendarEventMaxDate, + calendarEventMinDate, + closeByWeeks, + datesToRegClosesAt, + regClosesAtDate, + regClosesAtToDisplayName, +} from "./calendar-utils"; + +describe("calendar-utils", () => { + it("calendarEventMinDate should return a fixed date", () => { + expect(calendarEventMinDate()).toEqual(new Date(Date.UTC(2015, 4, 28))); + }); + + it("calendarEventMaxDate should return a date one year from now", () => { + const result = calendarEventMaxDate(); + const expected = new Date(); + expected.setFullYear(expected.getFullYear() + 1); + expect(result.getFullYear()).toBe(expected.getFullYear()); + }); + + it("regClosesAtDate should return correct date based on closesAt option", () => { + const startTime = new Date(); + expect(regClosesAtDate({ startTime, closesAt: "5min" })).toEqual( + new Date(startTime.getTime() - 5 * 60 * 1000), + ); + expect(regClosesAtDate({ startTime, closesAt: "1h" })).toEqual( + new Date(startTime.getTime() - 60 * 60 * 1000), + ); + }); + + it("regClosesAtToDisplayName should return correct display name", () => { + expect(regClosesAtToDisplayName("5min")).toBe("5 minutes"); + expect(regClosesAtToDisplayName("1h")).toBe("1 hour"); + }); + + it("datesToRegClosesAt should return correct closesAt option based on date difference", () => { + const startTime = new Date(); + expect( + datesToRegClosesAt({ + startTime, + regClosesAt: new Date(startTime.getTime() - 5 * 60 * 1000), + }), + ).toBe("5min"); + expect( + datesToRegClosesAt({ + startTime, + regClosesAt: new Date(startTime.getTime() - 60 * 60 * 1000), + }), + ).toBe("1h"); + }); + + it("closeByWeeks should return correct week numbers and years (middle of year)", () => { + const result = closeByWeeks({ week: 24, year: 2024 }); + expect(result).toEqual([ + { number: 20, year: 2024 }, + { number: 21, year: 2024 }, + { number: 22, year: 2024 }, + { number: 23, year: 2024 }, + { number: 24, year: 2024 }, // <-- + { number: 25, year: 2024 }, + { number: 26, year: 2024 }, + { number: 27, year: 2024 }, + { number: 28, year: 2024 }, + ]); + }); + + it("closeByWeeks should return correct week numbers and years (start of year)", () => { + const result = closeByWeeks({ week: 1, year: 2023 }); + expect(result).toEqual([ + { number: 49, year: 2022 }, + { number: 50, year: 2022 }, + { number: 51, year: 2022 }, + { number: 52, year: 2022 }, + { number: 1, year: 2023 }, // <-- + { number: 2, year: 2023 }, + { number: 3, year: 2023 }, + { number: 4, year: 2023 }, + { number: 5, year: 2023 }, + ]); + }); + + it("closeByWeeks should return correct week numbers and years (end of year)", () => { + const result = closeByWeeks({ week: 52, year: 2024 }); + expect(result).toEqual([ + { number: 48, year: 2024 }, + { number: 49, year: 2024 }, + { number: 50, year: 2024 }, + { number: 51, year: 2024 }, + { number: 52, year: 2024 }, // <-- + { number: 1, year: 2025 }, + { number: 2, year: 2025 }, + { number: 3, year: 2025 }, + { number: 4, year: 2025 }, + ]); + }); + + it("closeByWeeks should throw if week is out of range", () => { + expect(() => closeByWeeks({ week: 53, year: 2024 })).toThrow(); + }); +}); diff --git a/app/features/calendar/calendar-utils.ts b/app/features/calendar/calendar-utils.ts index aac145b88..b76cd0232 100644 --- a/app/features/calendar/calendar-utils.ts +++ b/app/features/calendar/calendar-utils.ts @@ -115,3 +115,27 @@ export function datesToRegClosesAt({ logger.warn("datesToRegClosesAt: fallback value"); return "0"; } + +export function closeByWeeks(args: { week: number; year: number }) { + if (args.week < 1 || args.week > 52) { + throw new Error("Invalid week number"); + } + + return [-4, -3, -2, -1, 0, 1, 2, 3, 4].map((week) => { + let number = args.week + week; + let year = args.year; + + if (number < 1) { + number = 52 + number; + year--; + } else if (number > 52) { + number = number - 52; + year++; + } + + return { + number, + year, + }; + }); +} diff --git a/app/features/calendar/routes/calendar.tsx b/app/features/calendar/routes/calendar.tsx index 114d5fe5c..96b30a1d0 100644 --- a/app/features/calendar/routes/calendar.tsx +++ b/app/features/calendar/routes/calendar.tsx @@ -1,7 +1,7 @@ import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; import { Link, useLoaderData, useSearchParams } from "@remix-run/react"; import clsx from "clsx"; -import { addDays, addMonths, subDays, subMonths } from "date-fns"; +import { addMonths, subMonths } from "date-fns"; import React from "react"; import { Flipped, Flipper } from "react-flip-toolkit"; import { useTranslation } from "react-i18next"; @@ -49,6 +49,7 @@ import type { SerializeFrom } from "../../../utils/remix"; import * as CalendarRepository from "../CalendarRepository.server"; import { calendarEventTagSchema } from "../actions/calendar.new.server"; import { CALENDAR_EVENT } from "../calendar-constants"; +import { closeByWeeks } from "../calendar-utils"; import { Tags } from "../components/Tags"; import "~/styles/calendar.css"; @@ -157,22 +158,6 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { }; }; -function closeByWeeks(args: { week: number; year: number }) { - const dateFromWeekNumber = weekNumberToDate(args); - - return [-4, -3, -2, -1, 0, 1, 2, 3, 4].map((week) => { - const date = - week < 0 - ? subDays(dateFromWeekNumber, Math.abs(week) * 7) - : addDays(dateFromWeekNumber, week * 7); - - return { - number: dateToWeekNumber(date), - year: date.getFullYear(), - }; - }); -} - function fetchEventsOfWeek(args: { week: number; year: number;