import { describe, it, expect, vi, beforeEach } from 'vitest'; import { configStore, serializeConfig, deserializeConfig } from '@/store/useConfigStore'; // Mock window.history const mockReplaceState = vi.fn(); const mockPushState = vi.fn(); const mockAddEventListener = vi.fn(); const mockRemoveEventListener = vi.fn(); vi.stubGlobal('window', { location: { href: 'http://localhost:3000', search: '', }, history: { replaceState: mockReplaceState, pushState: mockPushState, state: {}, }, addEventListener: mockAddEventListener, removeEventListener: mockRemoveEventListener, }); describe('URL Serialization', () => { describe('serializeConfig', () => { it('should serialize colors, persona, and payloads to base64', () => { const encoded = serializeConfig({ activeColors: { primary: '#ff0000', secondary: '#00ff00', accent: '#808080' }, activePersonaAttire: 'Emarati Kandura', activeBody: 'basic', activePayloads: [{ id: 'cam1', type: 'camera', position: 'head' }], isHydrated: true, }); expect(typeof encoded).toBe('string'); // Verify it decodes correctly const decoded = JSON.parse(atob(encoded)); expect(decoded.c.primary).toBe('#ff0000'); expect(decoded.p).toBe('Emarati Kandura'); expect(decoded.y[0].id).toBe('cam1'); }); }); describe('deserializeConfig', () => { it('should decode base64 and return valid state', () => { const original = { activeColors: { primary: '#ff0000', secondary: '#00ff00', accent: '#808080' }, activePersonaAttire: 'Industrial Vest', activeBody: 'edu' as const, activePayloads: [ { id: 'cam1', type: 'camera', position: 'head' }, { id: 'light1', type: 'light', position: 'arm' }, ], isHydrated: true, }; const encoded = serializeConfig(original); const decoded = deserializeConfig(encoded); expect(decoded).not.toBeNull(); expect(decoded?.activeColors).toEqual(original.activeColors); expect(decoded?.activePersonaAttire).toBe(original.activePersonaAttire); expect(decoded?.activePayloads).toEqual(original.activePayloads); }); it('should return null for malformed base64', () => { expect(deserializeConfig('!!!invalid-base64!!!')).toBeNull(); }); it('should return null for valid base64 but invalid JSON', () => { expect(deserializeConfig(btoa('not json'))).toBeNull(); }); it('should return null for valid JSON but wrong schema', () => { expect(deserializeConfig(btoa(JSON.stringify({ wrong: 'schema' })))).toBeNull(); }); }); }); describe('Store integration with URL', () => { beforeEach(() => { vi.clearAllMocks(); configStore.getState().reset(); }); it('should create encoded URL with config', () => { configStore.getState().setColors({ primary: '#ff0000' }); configStore.getState().setPersonaAttire('Test'); configStore.getState().addPayload({ id: 'p1', type: 'camera', position: 'head' }); const state = configStore.getState(); const encoded = serializeConfig(state); expect(encoded).toBeTruthy(); expect(encoded.length).toBeGreaterThan(0); }); it('should preserve state through serialize/deserialize cycle', () => { configStore.getState().setColors({ primary: '#abc123', secondary: '#def456', accent: '#9a9a9a' }); configStore.getState().setPersonaAttire('Emarati Kandura'); configStore.getState().addPayload({ id: 'cam1', type: 'PTZ Camera', position: 'head' }); configStore.getState().addPayload({ id: 'light1', type: 'LED Panel', position: 'chest' }); configStore.getState().setHydrated(true); const state = configStore.getState(); const encoded = serializeConfig(state); const decoded = deserializeConfig(encoded); expect(decoded).not.toBeNull(); expect(decoded?.activeColors).toEqual(state.activeColors); expect(decoded?.activePersonaAttire).toBe(state.activePersonaAttire); expect(decoded?.activePayloads).toHaveLength(2); }); });