2025-12-25 15:59:20 +00:00
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
|
|
|
|
|
|
// Mock BroadcastChannel before importing stores
|
|
|
|
|
if (typeof global.BroadcastChannel === 'undefined') {
|
|
|
|
|
(global as any).BroadcastChannel = class MockBroadcastChannel {
|
|
|
|
|
postMessage = vi.fn();
|
|
|
|
|
close = vi.fn();
|
|
|
|
|
addEventListener = vi.fn();
|
|
|
|
|
removeEventListener = vi.fn();
|
|
|
|
|
onmessage = null;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
import { useUIStore } from './ui';
|
|
|
|
|
|
|
|
|
|
// Mock window.matchMedia
|
|
|
|
|
Object.defineProperty(window, 'matchMedia', {
|
|
|
|
|
writable: true,
|
|
|
|
|
value: vi.fn().mockImplementation((query) => ({
|
|
|
|
|
matches: false,
|
|
|
|
|
media: query,
|
|
|
|
|
onchange: null,
|
|
|
|
|
addListener: vi.fn(),
|
|
|
|
|
removeListener: vi.fn(),
|
|
|
|
|
addEventListener: vi.fn(),
|
|
|
|
|
removeEventListener: vi.fn(),
|
|
|
|
|
dispatchEvent: vi.fn(),
|
|
|
|
|
})),
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
// Mock crypto.randomUUID
|
|
|
|
|
Object.defineProperty(global, 'crypto', {
|
|
|
|
|
value: {
|
2026-01-13 18:47:57 +00:00
|
|
|
randomUUID: () => `test-uuid-${Math.random().toString(36).substring(7)}`,
|
2025-12-25 15:59:20 +00:00
|
|
|
},
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('UIStore', () => {
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
// Reset store state
|
|
|
|
|
useUIStore.setState({
|
|
|
|
|
theme: 'system',
|
|
|
|
|
language: 'en',
|
|
|
|
|
sidebarOpen: true,
|
|
|
|
|
notifications: [],
|
|
|
|
|
});
|
|
|
|
|
vi.clearAllMocks();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('Initial State', () => {
|
|
|
|
|
it('should have correct initial state', () => {
|
|
|
|
|
const state = useUIStore.getState();
|
|
|
|
|
expect(state.theme).toBe('system');
|
|
|
|
|
expect(state.language).toBe('en');
|
|
|
|
|
expect(state.sidebarOpen).toBe(true);
|
|
|
|
|
expect(state.notifications).toEqual([]);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('setTheme', () => {
|
|
|
|
|
it('should set theme to light', () => {
|
|
|
|
|
useUIStore.getState().setTheme('light');
|
|
|
|
|
expect(useUIStore.getState().theme).toBe('light');
|
|
|
|
|
expect(document.documentElement.classList.contains('dark')).toBe(false);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set theme to dark', () => {
|
|
|
|
|
useUIStore.getState().setTheme('dark');
|
|
|
|
|
expect(useUIStore.getState().theme).toBe('dark');
|
|
|
|
|
expect(document.documentElement.classList.contains('dark')).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set theme to system', () => {
|
|
|
|
|
useUIStore.getState().setTheme('system');
|
|
|
|
|
expect(useUIStore.getState().theme).toBe('system');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('setLanguage', () => {
|
|
|
|
|
it('should set language to en', () => {
|
|
|
|
|
useUIStore.getState().setLanguage('en');
|
|
|
|
|
expect(useUIStore.getState().language).toBe('en');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should set language to fr', () => {
|
|
|
|
|
useUIStore.getState().setLanguage('fr');
|
|
|
|
|
expect(useUIStore.getState().language).toBe('fr');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('setSidebarOpen', () => {
|
|
|
|
|
it('should set sidebar open state', () => {
|
|
|
|
|
useUIStore.getState().setSidebarOpen(false);
|
|
|
|
|
expect(useUIStore.getState().sidebarOpen).toBe(false);
|
|
|
|
|
|
|
|
|
|
useUIStore.getState().setSidebarOpen(true);
|
|
|
|
|
expect(useUIStore.getState().sidebarOpen).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('addNotification', () => {
|
|
|
|
|
it('should add a notification', () => {
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message: 'Test notification',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notifications = useUIStore.getState().notifications;
|
|
|
|
|
expect(notifications).toHaveLength(1);
|
|
|
|
|
expect(notifications[0].message).toBe('Test notification');
|
|
|
|
|
expect(notifications[0].type).toBe('success');
|
|
|
|
|
expect(notifications[0].id).toBeDefined();
|
|
|
|
|
expect(notifications[0].timestamp).toBeDefined();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should add multiple notifications', () => {
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'info',
|
|
|
|
|
message: 'First notification',
|
|
|
|
|
});
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'warning',
|
|
|
|
|
message: 'Second notification',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notifications = useUIStore.getState().notifications;
|
|
|
|
|
expect(notifications).toHaveLength(2);
|
|
|
|
|
expect(notifications[0].message).toBe('First notification');
|
|
|
|
|
expect(notifications[1].message).toBe('Second notification');
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('removeNotification', () => {
|
|
|
|
|
it('should remove a notification by id', () => {
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message: 'Test notification',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notifications = useUIStore.getState().notifications;
|
|
|
|
|
const notificationId = notifications[0].id;
|
|
|
|
|
|
|
|
|
|
useUIStore.getState().removeNotification(notificationId);
|
|
|
|
|
|
|
|
|
|
expect(useUIStore.getState().notifications).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not remove notification with wrong id', () => {
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'success',
|
|
|
|
|
message: 'Test notification',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
useUIStore.getState().removeNotification('wrong-id');
|
|
|
|
|
|
|
|
|
|
expect(useUIStore.getState().notifications).toHaveLength(1);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('markNotificationAsRead', () => {
|
|
|
|
|
it('should mark notification as read', () => {
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'info',
|
|
|
|
|
message: 'Test notification',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notifications = useUIStore.getState().notifications;
|
|
|
|
|
const notificationId = notifications[0].id;
|
|
|
|
|
|
|
|
|
|
expect(notifications[0].read).toBeUndefined();
|
|
|
|
|
|
|
|
|
|
useUIStore.getState().markNotificationAsRead(notificationId);
|
|
|
|
|
|
|
|
|
|
const updatedNotifications = useUIStore.getState().notifications;
|
|
|
|
|
expect(updatedNotifications[0].read).toBe(true);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
it('should not mark other notifications as read', () => {
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'info',
|
|
|
|
|
message: 'First notification',
|
|
|
|
|
});
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'warning',
|
|
|
|
|
message: 'Second notification',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const notifications = useUIStore.getState().notifications;
|
|
|
|
|
const firstId = notifications[0].id;
|
|
|
|
|
|
|
|
|
|
useUIStore.getState().markNotificationAsRead(firstId);
|
|
|
|
|
|
|
|
|
|
const updatedNotifications = useUIStore.getState().notifications;
|
|
|
|
|
expect(updatedNotifications[0].read).toBe(true);
|
|
|
|
|
expect(updatedNotifications[1].read).toBeUndefined();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
describe('clearNotifications', () => {
|
|
|
|
|
it('should clear all notifications', () => {
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'info',
|
|
|
|
|
message: 'First notification',
|
|
|
|
|
});
|
|
|
|
|
useUIStore.getState().addNotification({
|
|
|
|
|
type: 'warning',
|
|
|
|
|
message: 'Second notification',
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
expect(useUIStore.getState().notifications).toHaveLength(2);
|
|
|
|
|
|
|
|
|
|
useUIStore.getState().clearNotifications();
|
|
|
|
|
|
|
|
|
|
expect(useUIStore.getState().notifications).toHaveLength(0);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
});
|