- Convert site from royal-blue theme to black/white monochrome (desaturate all blue-hued colors; keep WhatsApp green and error red) - Swap blue circular brand logo for mono YS mark; regenerate favicon/PWA icons - Add 6 Pudu robots (CC1 Pro, MT1 Max, MT1 Vac, T600, T600 Underride, BG1 Pro) - Fix favicon.ico to RGBA (Next 16 Turbopack decode) and Stripe apiVersion type Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
115 lines
4.0 KiB
TypeScript
115 lines
4.0 KiB
TypeScript
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);
|
|
});
|
|
});
|