138 lines
4.6 KiB
TypeScript
138 lines
4.6 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { isRecurringDay, getRecurringHoursForDay } from "../allocation/recurrence.js";
|
|
import { RecurrenceFrequency } from "@planarchy/shared";
|
|
import type { RecurrencePattern } from "@planarchy/shared";
|
|
|
|
const monday = new Date("2026-03-09"); // Monday
|
|
const tuesday = new Date("2026-03-10"); // Tuesday
|
|
const wednesday = new Date("2026-03-11"); // Wednesday
|
|
const friday = new Date("2026-03-13"); // Friday
|
|
const nextMonday = new Date("2026-03-16"); // Monday (next week)
|
|
const twoWeeksMonday = new Date("2026-03-23"); // Monday (two weeks after)
|
|
|
|
const allocationStart = new Date("2026-03-09"); // Monday
|
|
|
|
describe("isRecurringDay", () => {
|
|
describe("WEEKLY", () => {
|
|
const pattern: RecurrencePattern = {
|
|
frequency: RecurrenceFrequency.WEEKLY,
|
|
weekdays: [1, 3, 5], // Mon, Wed, Fri
|
|
};
|
|
|
|
it("returns true for days matching weekday list", () => {
|
|
expect(isRecurringDay(monday, pattern, allocationStart)).toBe(true);
|
|
expect(isRecurringDay(wednesday, pattern, allocationStart)).toBe(true);
|
|
expect(isRecurringDay(friday, pattern, allocationStart)).toBe(true);
|
|
});
|
|
|
|
it("returns false for days not in weekday list", () => {
|
|
expect(isRecurringDay(tuesday, pattern, allocationStart)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("BIWEEKLY", () => {
|
|
const pattern: RecurrencePattern = {
|
|
frequency: RecurrenceFrequency.BIWEEKLY,
|
|
weekdays: [1], // Monday only
|
|
interval: 2,
|
|
};
|
|
|
|
it("returns true on the allocation start week (week 0)", () => {
|
|
expect(isRecurringDay(monday, pattern, allocationStart)).toBe(true);
|
|
});
|
|
|
|
it("returns false on the next week (week 1, odd)", () => {
|
|
expect(isRecurringDay(nextMonday, pattern, allocationStart)).toBe(false);
|
|
});
|
|
|
|
it("returns true two weeks after start (week 2, even)", () => {
|
|
expect(isRecurringDay(twoWeeksMonday, pattern, allocationStart)).toBe(true);
|
|
});
|
|
|
|
it("returns false for non-matching weekdays even in active weeks", () => {
|
|
expect(isRecurringDay(tuesday, pattern, allocationStart)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("MONTHLY", () => {
|
|
const pattern: RecurrencePattern = {
|
|
frequency: RecurrenceFrequency.MONTHLY,
|
|
monthDay: 15,
|
|
};
|
|
|
|
it("returns true on the specified day of month", () => {
|
|
expect(isRecurringDay(new Date("2026-03-15"), pattern, allocationStart)).toBe(true);
|
|
});
|
|
|
|
it("returns false on other days", () => {
|
|
expect(isRecurringDay(new Date("2026-03-14"), pattern, allocationStart)).toBe(false);
|
|
});
|
|
|
|
it("returns false when monthDay is undefined", () => {
|
|
const noDay: RecurrencePattern = {
|
|
frequency: RecurrenceFrequency.MONTHLY,
|
|
};
|
|
expect(isRecurringDay(new Date("2026-03-15"), noDay, allocationStart)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("CUSTOM", () => {
|
|
const pattern: RecurrencePattern = {
|
|
frequency: RecurrenceFrequency.CUSTOM,
|
|
};
|
|
|
|
it("always returns true", () => {
|
|
expect(isRecurringDay(monday, pattern, allocationStart)).toBe(true);
|
|
expect(isRecurringDay(tuesday, pattern, allocationStart)).toBe(true);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("getRecurringHoursForDay", () => {
|
|
const weeklyPattern: RecurrencePattern = {
|
|
frequency: RecurrenceFrequency.WEEKLY,
|
|
weekdays: [1, 3, 5], // Mon, Wed, Fri
|
|
};
|
|
|
|
it("returns default hours on recurring days", () => {
|
|
expect(getRecurringHoursForDay(monday, weeklyPattern, 8, allocationStart)).toBe(8);
|
|
});
|
|
|
|
it("returns 0 on non-recurring days", () => {
|
|
expect(getRecurringHoursForDay(tuesday, weeklyPattern, 8, allocationStart)).toBe(0);
|
|
});
|
|
|
|
it("uses pattern.hoursPerDay when set", () => {
|
|
const patternWithHours: RecurrencePattern = {
|
|
...weeklyPattern,
|
|
hoursPerDay: 4,
|
|
};
|
|
expect(getRecurringHoursForDay(monday, patternWithHours, 8, allocationStart)).toBe(4);
|
|
});
|
|
|
|
it("returns 0 before pattern startDate", () => {
|
|
const patternWithStart: RecurrencePattern = {
|
|
...weeklyPattern,
|
|
startDate: new Date("2026-03-16"),
|
|
};
|
|
expect(getRecurringHoursForDay(monday, patternWithStart, 8, allocationStart)).toBe(0);
|
|
});
|
|
|
|
it("returns 0 after pattern endDate", () => {
|
|
const patternWithEnd: RecurrencePattern = {
|
|
...weeklyPattern,
|
|
endDate: new Date("2026-03-08"),
|
|
};
|
|
expect(getRecurringHoursForDay(monday, patternWithEnd, 8, allocationStart)).toBe(0);
|
|
});
|
|
|
|
it("returns hours within pattern date bounds", () => {
|
|
const bounded: RecurrencePattern = {
|
|
...weeklyPattern,
|
|
startDate: new Date("2026-03-01"),
|
|
endDate: new Date("2026-03-31"),
|
|
};
|
|
expect(getRecurringHoursForDay(monday, bounded, 8, allocationStart)).toBe(8);
|
|
});
|
|
});
|