- Refactor leave-planner.worker.ts to support cancellation of ongoing calculations. - Introduce `calculatePlanningAsyncInternal` for asynchronous planning calculations. - Add cancellation handling in the worker message event. - Update planning-date.ts with new utility functions for date manipulation. - Extend translation-service.ts and translation files (en.json, de.json, es.json, fr.json) to include new strings related to planning horizon and cancellation. - Create documentation for the cancellation feature in planner-cancellation.md. - Add unit tests for cancellation functionality in leave-planner-cancellation.spec.ts and leave-planner.worker.spec.ts. #57 #58
131 lines
4.7 KiB
TypeScript
131 lines
4.7 KiB
TypeScript
import { TestBed } from '@angular/core/testing';
|
|
import { provideZonelessChangeDetection } from '@angular/core';
|
|
import { CalendarSettingsService } from './calendar-settings-service';
|
|
import { vi } from 'vitest';
|
|
|
|
describe('CalendarSettingsService', () => {
|
|
let service: CalendarSettingsService;
|
|
|
|
beforeEach(() => {
|
|
localStorage.clear();
|
|
vi.useFakeTimers({ toFake: ['Date'] });
|
|
vi.setSystemTime(new Date('2026-10-04T22:30:00Z'));
|
|
TestBed.configureTestingModule({
|
|
providers: [provideZonelessChangeDetection()],
|
|
});
|
|
service = TestBed.inject(CalendarSettingsService);
|
|
});
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers();
|
|
vi.restoreAllMocks();
|
|
localStorage.clear();
|
|
});
|
|
|
|
it('should be created', () => {
|
|
expect(service).toBeTruthy();
|
|
});
|
|
|
|
it('defaults to the Paris date and displays the day before the anniversary as the last planning date', () => {
|
|
expect(service.planningDates()).toEqual({
|
|
start: '2026-10-05',
|
|
lastPlanningDate: '2027-10-04',
|
|
});
|
|
expect(service.planningHorizon()).toEqual({ start: '2026-10-05', end: '2027-10-05' });
|
|
expect(localStorage.getItem('calendarSettings.planningDates')).toBeNull();
|
|
});
|
|
|
|
it('saves and restores fixed historical dates before applying a later default', () => {
|
|
service.setPlanningDates('2024-02-29', '2026-02-28');
|
|
vi.setSystemTime(new Date('2029-08-01T12:00:00Z'));
|
|
const restored = TestBed.runInInjectionContext(() => new CalendarSettingsService());
|
|
expect(restored.planningDates()).toEqual({
|
|
start: '2024-02-29',
|
|
lastPlanningDate: '2026-02-28',
|
|
});
|
|
expect(restored.planningHorizon()).toEqual({ start: '2024-02-29', end: '2026-03-01' });
|
|
});
|
|
|
|
it('uses the existing February 29 anniversary convention for the unsaved default', () => {
|
|
vi.setSystemTime(new Date('2024-02-29T12:00:00Z'));
|
|
const leapDay = TestBed.runInInjectionContext(() => new CalendarSettingsService());
|
|
expect(leapDay.planningDates()).toEqual({
|
|
start: '2024-02-29',
|
|
lastPlanningDate: '2025-02-27',
|
|
});
|
|
expect(leapDay.planningHorizon()).toEqual({ start: '2024-02-29', end: '2025-02-28' });
|
|
});
|
|
|
|
it.each([
|
|
['2024-02-29', '2024-03-01'],
|
|
['2026-03-29', '2026-03-30'],
|
|
['2026-10-25', '2026-10-26'],
|
|
['2026-12-31', '2027-01-01'],
|
|
])(
|
|
'converts a single inclusive planning date %s across leap days, clock changes and years',
|
|
(date, end) => {
|
|
const previousTimeZone = process.env['TZ'];
|
|
process.env['TZ'] = 'Pacific/Auckland';
|
|
try {
|
|
service.setPlanningDates(date, date);
|
|
expect(service.planningHorizon()).toEqual({ start: date, end });
|
|
} finally {
|
|
if (previousTimeZone === undefined) delete process.env['TZ'];
|
|
else process.env['TZ'] = previousTimeZone;
|
|
}
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
['2024-02-30', '2024-03-01', 'invalidDate'],
|
|
['2024-02-29', '2024-13-01', 'invalidDate'],
|
|
['29/02/2024', '2024-03-01', 'invalidDate'],
|
|
['', '2024-03-01', 'invalidDate'],
|
|
['2024-03-02', '2024-03-01', 'horizonOrder'],
|
|
])(
|
|
'keeps invalid input visible without replacing the last valid saved dates (%s, %s)',
|
|
(start, last, error) => {
|
|
service.setPlanningDates('2024-02-29', '2027-02-28');
|
|
service.setPlanningDates(start, last);
|
|
expect(service.planningDates()).toEqual({ start, lastPlanningDate: last });
|
|
expect(service.planningHorizon()).toBeNull();
|
|
expect(service.planningHorizonError()).toBe(error);
|
|
const restored = TestBed.runInInjectionContext(() => new CalendarSettingsService());
|
|
expect(restored.planningHorizon()).toEqual({ start: '2024-02-29', end: '2027-03-01' });
|
|
},
|
|
);
|
|
|
|
it.each([
|
|
'{',
|
|
'null',
|
|
'{}',
|
|
'{"start":"2024-02-30","lastPlanningDate":"2024-03-01"}',
|
|
'{"start":"2024-03-02","lastPlanningDate":"2024-03-01"}',
|
|
])('uses an unsaved default when saved dates are unreadable: %s', (saved) => {
|
|
vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
localStorage.setItem('calendarSettings.planningDates', saved);
|
|
const restored = TestBed.runInInjectionContext(() => new CalendarSettingsService());
|
|
expect(restored.planningHorizon()).toEqual({ start: '2026-10-05', end: '2027-10-05' });
|
|
expect(localStorage.getItem('calendarSettings.planningDates')).toBe(saved);
|
|
});
|
|
|
|
it('should initialize with default values', () => {
|
|
expect(service.get('showWeekNumbers')()).toBe(false);
|
|
});
|
|
|
|
it('should allow updating showWeekNumbers', () => {
|
|
service.get('showWeekNumbers').set(true);
|
|
expect(service.get('showWeekNumbers')()).toBe(true);
|
|
});
|
|
|
|
it('should provide settings array for UI iteration', () => {
|
|
const settingsArray = service.settingsArray;
|
|
expect(settingsArray.length).toBe(1);
|
|
expect(settingsArray[0].id).toBe('showWeekNumbers');
|
|
});
|
|
|
|
it('should return correct descriptions', () => {
|
|
expect(service.getDescription('showWeekNumbers')).toBe('Afficher les numéros de semaine');
|
|
});
|
|
});
|