import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { renderHook, act } from '@testing-library/react' import { useShowWeekNumbers, getLastLeaveType, setLastLeaveType, } from './useCalendarPreferences' const SHOW_WEEK_NUMBERS_KEY = 'calendar_show_week_numbers' const LAST_LEAVE_TYPE_KEY = 'calendar_last_leave_type' // Node's own experimental `localStorage`/`sessionStorage` globals (active in // newer Node runtimes without a valid --localstorage-file) shadow jsdom's // working implementation and throw on every call. Stub a real in-memory // Storage for these tests, matching the idiom used in WorkspaceContext.test.tsx. function createStorageMock(): Storage { let store: Record = {} return { getItem: (key: string) => (key in store ? store[key]! : null), setItem: (key: string, value: string) => { store[key] = value }, removeItem: (key: string) => { delete store[key] }, clear: () => { store = {} }, key: (index: number) => Object.keys(store)[index] ?? null, get length() { return Object.keys(store).length }, } } describe('useShowWeekNumbers', () => { beforeEach(() => { vi.stubGlobal('localStorage', createStorageMock()) }) afterEach(() => { vi.unstubAllGlobals() }) it('defaults to false when nothing is stored', () => { const { result } = renderHook(() => useShowWeekNumbers()) expect(result.current[0]).toBe(false) }) it('reads the persisted value on mount', () => { localStorage.setItem(SHOW_WEEK_NUMBERS_KEY, 'true') const { result } = renderHook(() => useShowWeekNumbers()) expect(result.current[0]).toBe(true) }) it('treats any non-"true" stored value as false', () => { localStorage.setItem(SHOW_WEEK_NUMBERS_KEY, 'garbage') const { result } = renderHook(() => useShowWeekNumbers()) expect(result.current[0]).toBe(false) }) it('persists the new value and updates state when the setter is called', () => { const { result } = renderHook(() => useShowWeekNumbers()) act(() => { result.current[1](true) }) expect(localStorage.getItem(SHOW_WEEK_NUMBERS_KEY)).toBe('true') expect(result.current[0]).toBe(true) act(() => { result.current[1](false) }) expect(localStorage.getItem(SHOW_WEEK_NUMBERS_KEY)).toBe('false') expect(result.current[0]).toBe(false) }) it('syncs across instances via the custom preferences-changed event', () => { const { result: a } = renderHook(() => useShowWeekNumbers()) const { result: b } = renderHook(() => useShowWeekNumbers()) act(() => { a.current[1](true) }) expect(b.current[0]).toBe(true) }) it('syncs across tabs via the native storage event', () => { const { result } = renderHook(() => useShowWeekNumbers()) act(() => { window.dispatchEvent( new StorageEvent('storage', { key: SHOW_WEEK_NUMBERS_KEY, newValue: 'true' }), ) }) expect(result.current[0]).toBe(true) }) it('ignores storage events for unrelated keys', () => { const { result } = renderHook(() => useShowWeekNumbers()) act(() => { window.dispatchEvent( new StorageEvent('storage', { key: 'some_other_key', newValue: 'true' }), ) }) expect(result.current[0]).toBe(false) }) it('removes its listeners on unmount', () => { const removeSpy = vi.spyOn(window, 'removeEventListener') const { unmount } = renderHook(() => useShowWeekNumbers()) unmount() expect(removeSpy).toHaveBeenCalledWith('storage', expect.any(Function)) expect(removeSpy).toHaveBeenCalledWith('calendar:preferences-changed', expect.any(Function)) removeSpy.mockRestore() }) }) describe('getLastLeaveType / setLastLeaveType', () => { beforeEach(() => { vi.stubGlobal('sessionStorage', createStorageMock()) }) afterEach(() => { vi.unstubAllGlobals() }) it('returns null when nothing is stored', () => { expect(getLastLeaveType()).toBeNull() }) it('returns null when the stored value is not a valid leave type', () => { sessionStorage.setItem(LAST_LEAVE_TYPE_KEY, 'not-a-real-type') expect(getLastLeaveType()).toBeNull() }) it('round-trips a valid leave type', () => { setLastLeaveType('vacation') expect(sessionStorage.getItem(LAST_LEAVE_TYPE_KEY)).toBe('vacation') expect(getLastLeaveType()).toBe('vacation') }) it('accepts every valid leave type', () => { for (const type of ['sjukpenning', 'lagsta', 'vacation'] as const) { setLastLeaveType(type) expect(getLastLeaveType()).toBe(type) } }) })