Replaces global singleton configuration with dependency injection Introduces more modular and testable approach to configuration Removes direct references to calendarConfig in multiple components Adds explicit configuration passing to constructors Improves code maintainability and reduces global state dependencies
299 lines
11 KiB
TypeScript
299 lines
11 KiB
TypeScript
import { describe, it, expect, beforeEach, vi } from 'vitest';
|
|
import { NavigationManager } from '../../src/managers/NavigationManager';
|
|
import { EventBus } from '../../src/core/EventBus';
|
|
import { EventRenderingService } from '../../src/renderers/EventRendererManager';
|
|
import { DateService } from '../../src/utils/DateService';
|
|
import { CalendarConfig } from '../../src/core/CalendarConfig';
|
|
|
|
describe('NavigationManager - Edge Cases', () => {
|
|
let navigationManager: NavigationManager;
|
|
let eventBus: EventBus;
|
|
let dateService: DateService;
|
|
|
|
beforeEach(() => {
|
|
eventBus = new EventBus();
|
|
const config = new CalendarConfig();
|
|
dateService = new DateService(config);
|
|
const mockEventRenderer = {} as EventRenderingService;
|
|
const mockGridRenderer = {} as any;
|
|
const mockNavigationRenderer = {} as any;
|
|
navigationManager = new NavigationManager(eventBus, mockEventRenderer, mockGridRenderer, dateService, mockNavigationRenderer);
|
|
});
|
|
|
|
describe('Week 53 Navigation', () => {
|
|
it('should correctly navigate to week 53 (year 2020)', () => {
|
|
// Dec 28, 2020 is start of week 53
|
|
const week53Start = new Date(2020, 11, 28);
|
|
|
|
const weekNum = dateService.getWeekNumber(week53Start);
|
|
expect(weekNum).toBe(53);
|
|
|
|
const weekBounds = dateService.getWeekBounds(week53Start);
|
|
expect(weekBounds.start.getDate()).toBe(28);
|
|
expect(weekBounds.start.getMonth()).toBe(11); // December
|
|
expect(weekBounds.start.getFullYear()).toBe(2020);
|
|
});
|
|
|
|
it('should navigate from week 53 to week 1 of next year', () => {
|
|
const week53 = new Date(2020, 11, 28); // Week 53, 2020
|
|
|
|
// Add 1 week should go to week 1 of 2021
|
|
const nextWeek = dateService.addWeeks(week53, 1);
|
|
const nextWeekNum = dateService.getWeekNumber(nextWeek);
|
|
|
|
expect(nextWeek.getFullYear()).toBe(2021);
|
|
expect(nextWeekNum).toBe(1);
|
|
});
|
|
|
|
it('should navigate from week 1 back to week 53 of previous year', () => {
|
|
const week1_2021 = new Date(2021, 0, 4); // Monday Jan 4, 2021 (week 1)
|
|
|
|
// Subtract 1 week should go to week 53 of 2020
|
|
const prevWeek = dateService.addWeeks(week1_2021, -1);
|
|
const prevWeekNum = dateService.getWeekNumber(prevWeek);
|
|
|
|
expect(prevWeek.getFullYear()).toBe(2020);
|
|
expect(prevWeekNum).toBe(53);
|
|
});
|
|
|
|
it('should handle years without week 53 (2021)', () => {
|
|
const dec27_2021 = new Date(2021, 11, 27); // Monday Dec 27, 2021
|
|
const weekNum = dateService.getWeekNumber(dec27_2021);
|
|
|
|
expect(weekNum).toBe(52); // No week 53 in 2021
|
|
|
|
const nextWeek = dateService.addWeeks(dec27_2021, 1);
|
|
const nextWeekNum = dateService.getWeekNumber(nextWeek);
|
|
|
|
// ISO week logic: Adding 1 week from Dec 27 gives Jan 3, which is week 1 of 2022
|
|
expect(nextWeekNum).toBe(1); // Week 1 of 2022
|
|
|
|
// Jan 3, 2022 is indeed week 1
|
|
const jan3_2022 = new Date(2022, 0, 3);
|
|
expect(dateService.getWeekNumber(jan3_2022)).toBe(1);
|
|
});
|
|
|
|
it('should correctly identify week 53 in 2026', () => {
|
|
const dec28_2026 = new Date(2026, 11, 28); // Monday Dec 28, 2026
|
|
const weekNum = dateService.getWeekNumber(dec28_2026);
|
|
|
|
expect(weekNum).toBe(53);
|
|
});
|
|
});
|
|
|
|
describe('Year Boundary Navigation', () => {
|
|
it('should navigate across year boundary (Dec -> Jan)', () => {
|
|
const lastWeekDec = new Date(2024, 11, 23); // Dec 23, 2024
|
|
const firstWeekJan = dateService.addWeeks(lastWeekDec, 1);
|
|
|
|
// Adding 1 week gives Dec 30, which is in week 1 of 2025 (ISO week logic)
|
|
expect(firstWeekJan.getMonth()).toBe(11); // Still December
|
|
|
|
const weekNum = dateService.getWeekNumber(firstWeekJan);
|
|
// Week number can be 1 (of next year) or 52 depending on ISO week rules
|
|
expect(weekNum).toBeGreaterThanOrEqual(1);
|
|
});
|
|
|
|
it('should navigate across year boundary (Jan -> Dec)', () => {
|
|
const firstWeekJan = new Date(2024, 0, 1);
|
|
const lastWeekDec = dateService.addWeeks(firstWeekJan, -1);
|
|
|
|
expect(lastWeekDec.getFullYear()).toBe(2023);
|
|
|
|
const weekNum = dateService.getWeekNumber(lastWeekDec);
|
|
expect(weekNum).toBeGreaterThanOrEqual(52);
|
|
});
|
|
|
|
it('should get correct week bounds at year start', () => {
|
|
const jan1_2024 = new Date(2024, 0, 1); // Monday Jan 1, 2024
|
|
const weekBounds = dateService.getWeekBounds(jan1_2024);
|
|
|
|
// Week should start on Monday
|
|
const startDayOfWeek = weekBounds.start.getDay();
|
|
expect(startDayOfWeek).toBe(1); // Monday = 1
|
|
|
|
expect(weekBounds.start.getDate()).toBe(1);
|
|
expect(weekBounds.start.getMonth()).toBe(0); // January
|
|
});
|
|
|
|
it('should get correct week bounds at year end', () => {
|
|
const dec31_2024 = new Date(2024, 11, 31); // Tuesday Dec 31, 2024
|
|
const weekBounds = dateService.getWeekBounds(dec31_2024);
|
|
|
|
// Week should start on Monday (Dec 30, 2024)
|
|
expect(weekBounds.start.getDate()).toBe(30);
|
|
expect(weekBounds.start.getMonth()).toBe(11);
|
|
expect(weekBounds.start.getFullYear()).toBe(2024);
|
|
|
|
// Week should end on Sunday (Jan 5, 2025)
|
|
expect(weekBounds.end.getDate()).toBe(5);
|
|
expect(weekBounds.end.getMonth()).toBe(0); // January
|
|
expect(weekBounds.end.getFullYear()).toBe(2025);
|
|
});
|
|
});
|
|
|
|
describe('DST Transition Navigation', () => {
|
|
it('should navigate across spring DST transition (March 2024)', () => {
|
|
// Spring DST: March 31, 2024, 02:00 -> 03:00
|
|
const beforeDST = new Date(2024, 2, 25); // Week before DST
|
|
const duringDST = dateService.addWeeks(beforeDST, 1);
|
|
|
|
expect(duringDST.getMonth()).toBe(3); // April
|
|
expect(dateService.isValid(duringDST)).toBe(true);
|
|
|
|
const weekBounds = dateService.getWeekBounds(duringDST);
|
|
expect(weekBounds.start.getMonth()).toBeGreaterThanOrEqual(2); // March or April
|
|
});
|
|
|
|
it('should navigate across fall DST transition (October 2024)', () => {
|
|
// Fall DST: October 27, 2024, 03:00 -> 02:00
|
|
const beforeDST = new Date(2024, 9, 21); // Week before DST
|
|
const duringDST = dateService.addWeeks(beforeDST, 1);
|
|
|
|
expect(duringDST.getMonth()).toBe(9); // October
|
|
expect(dateService.isValid(duringDST)).toBe(true);
|
|
|
|
const weekBounds = dateService.getWeekBounds(duringDST);
|
|
expect(weekBounds.end.getMonth()).toBeLessThanOrEqual(10); // October or November
|
|
});
|
|
|
|
it('should maintain week integrity across DST', () => {
|
|
const beforeDST = new Date(2024, 2, 25, 12, 0);
|
|
const afterDST = dateService.addWeeks(beforeDST, 1);
|
|
|
|
// Week bounds should still give 7-day span
|
|
const weekBounds = dateService.getWeekBounds(afterDST);
|
|
const daysDiff = (weekBounds.end.getTime() - weekBounds.start.getTime()) / (1000 * 60 * 60 * 24);
|
|
|
|
// Should be close to 7 days (accounting for DST hour change)
|
|
expect(daysDiff).toBeGreaterThanOrEqual(6.9);
|
|
expect(daysDiff).toBeLessThanOrEqual(7.1);
|
|
});
|
|
});
|
|
|
|
describe('Month Boundary Week Navigation', () => {
|
|
it('should handle week spanning month boundary', () => {
|
|
const endOfMonth = new Date(2024, 0, 29); // Jan 29, 2024 (Monday)
|
|
const weekBounds = dateService.getWeekBounds(endOfMonth);
|
|
|
|
// Week should span into February
|
|
expect(weekBounds.end.getMonth()).toBe(1); // February
|
|
expect(weekBounds.end.getDate()).toBe(4);
|
|
});
|
|
|
|
it('should navigate to next week across month boundary', () => {
|
|
const lastWeekJan = new Date(2024, 0, 29);
|
|
const firstWeekFeb = dateService.addWeeks(lastWeekJan, 1);
|
|
|
|
expect(firstWeekFeb.getMonth()).toBe(1); // February
|
|
expect(firstWeekFeb.getDate()).toBe(5);
|
|
});
|
|
|
|
it('should handle February-March boundary in leap year', () => {
|
|
const lastWeekFeb = new Date(2024, 1, 26); // Feb 26, 2024 (leap year)
|
|
const weekBounds = dateService.getWeekBounds(lastWeekFeb);
|
|
|
|
// Week should span from Feb into March
|
|
expect(weekBounds.start.getMonth()).toBe(1); // February
|
|
expect(weekBounds.end.getMonth()).toBe(2); // March
|
|
});
|
|
});
|
|
|
|
describe('Invalid Date Navigation', () => {
|
|
it('should reject navigation to invalid date', () => {
|
|
const invalidDate = new Date('invalid');
|
|
const validation = dateService.validateDate(invalidDate);
|
|
|
|
expect(validation.valid).toBe(false);
|
|
expect(validation.error).toBeDefined();
|
|
});
|
|
|
|
it('should reject navigation to out-of-bounds date', () => {
|
|
const outOfBounds = new Date(2150, 0, 1);
|
|
const validation = dateService.validateDate(outOfBounds);
|
|
|
|
expect(validation.valid).toBe(false);
|
|
expect(validation.error).toContain('bounds');
|
|
});
|
|
|
|
it('should accept valid date within bounds', () => {
|
|
const validDate = new Date(2024, 6, 15);
|
|
const validation = dateService.validateDate(validDate);
|
|
|
|
expect(validation.valid).toBe(true);
|
|
expect(validation.error).toBeUndefined();
|
|
});
|
|
});
|
|
|
|
describe('Week Number Edge Cases', () => {
|
|
it('should handle first day of year in previous year\'s week', () => {
|
|
// Jan 1, 2023 is a Sunday, part of week 52 of 2022
|
|
const jan1_2023 = new Date(2023, 0, 1);
|
|
const weekNum = dateService.getWeekNumber(jan1_2023);
|
|
|
|
expect(weekNum).toBe(52); // Part of 2022's last week
|
|
});
|
|
|
|
it('should handle last day of year in next year\'s week', () => {
|
|
// Dec 31, 2023 is a Sunday, part of week 52 of 2023
|
|
const dec31_2023 = new Date(2023, 11, 31);
|
|
const weekNum = dateService.getWeekNumber(dec31_2023);
|
|
|
|
expect(weekNum).toBe(52);
|
|
});
|
|
|
|
it('should correctly number weeks in leap year', () => {
|
|
const dates2024 = [
|
|
new Date(2024, 0, 1), // Week 1
|
|
new Date(2024, 6, 1), // Mid-year
|
|
new Date(2024, 11, 31) // Last week
|
|
];
|
|
|
|
dates2024.forEach(date => {
|
|
const weekNum = dateService.getWeekNumber(date);
|
|
expect(weekNum).toBeGreaterThanOrEqual(1);
|
|
expect(weekNum).toBeLessThanOrEqual(53);
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('Navigation Continuity', () => {
|
|
it('should maintain continuity over multiple forward navigations', () => {
|
|
let currentWeek = new Date(2024, 0, 1);
|
|
|
|
for (let i = 0; i < 60; i++) { // Navigate 60 weeks forward
|
|
currentWeek = dateService.addWeeks(currentWeek, 1);
|
|
expect(dateService.isValid(currentWeek)).toBe(true);
|
|
}
|
|
|
|
// Should be in 2025
|
|
expect(currentWeek.getFullYear()).toBe(2025);
|
|
});
|
|
|
|
it('should maintain continuity over multiple backward navigations', () => {
|
|
let currentWeek = new Date(2024, 11, 31);
|
|
|
|
for (let i = 0; i < 60; i++) { // Navigate 60 weeks backward
|
|
currentWeek = dateService.addWeeks(currentWeek, -1);
|
|
expect(dateService.isValid(currentWeek)).toBe(true);
|
|
}
|
|
|
|
// Should be in 2023
|
|
expect(currentWeek.getFullYear()).toBe(2023);
|
|
});
|
|
|
|
it('should return to same week after forward+backward navigation', () => {
|
|
const originalWeek = new Date(2024, 6, 15);
|
|
const weekBoundsOriginal = dateService.getWeekBounds(originalWeek);
|
|
|
|
// Navigate 10 weeks forward, then 10 weeks back
|
|
const forward = dateService.addWeeks(originalWeek, 10);
|
|
const backAgain = dateService.addWeeks(forward, -10);
|
|
|
|
const weekBoundsBack = dateService.getWeekBounds(backAgain);
|
|
|
|
expect(weekBoundsBack.start.getTime()).toBe(weekBoundsOriginal.start.getTime());
|
|
expect(weekBoundsBack.end.getTime()).toBe(weekBoundsOriginal.end.getTime());
|
|
});
|
|
});
|
|
});
|