import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { COLORS } from '../src/renderer.js'; import { ThemeManager } from '../src/themes.js'; import { Scene, Color } from '../tests/__mocks__/three.js'; // Snapshot of original COLORS to restore between tests (shared mutable state) const ORIGINAL_COLORS = { wall: { ...COLORS.wall }, floor: { ...COLORS.floor }, ceiling: COLORS.ceiling, door: COLORS.door, window: COLORS.window, windowFrame: COLORS.windowFrame, grid: COLORS.grid, selected: COLORS.selected }; function restoreColors() { Object.assign(COLORS.wall, ORIGINAL_COLORS.wall); Object.assign(COLORS.floor, ORIGINAL_COLORS.floor); COLORS.ceiling = ORIGINAL_COLORS.ceiling; COLORS.door = ORIGINAL_COLORS.door; COLORS.window = ORIGINAL_COLORS.window; COLORS.windowFrame = ORIGINAL_COLORS.windowFrame; COLORS.grid = ORIGINAL_COLORS.grid; COLORS.selected = ORIGINAL_COLORS.selected; } function makeMockRenderer() { const scene = new Scene(); // Add mock lights to the scene scene.add({ isAmbientLight: true, intensity: 0.6, traverse: (fn) => fn({ isAmbientLight: true, intensity: 0.6 }) }); scene.add({ isDirectionalLight: true, intensity: 0.8, traverse: (fn) => fn({ isDirectionalLight: true, intensity: 0.8 }) }); return { scene, currentFloor: 0, _clearFloor: vi.fn(), showFloor: vi.fn() }; } describe('ThemeManager', () => { let tm, mockRenderer; beforeEach(() => { restoreColors(); mockRenderer = makeMockRenderer(); tm = new ThemeManager(mockRenderer); }); afterEach(() => { restoreColors(); }); describe('getThemes', () => { it('returns array of theme descriptors', () => { const themes = tm.getThemes(); expect(themes.length).toBeGreaterThanOrEqual(4); for (const theme of themes) { expect(theme).toHaveProperty('id'); expect(theme).toHaveProperty('name'); expect(theme).toHaveProperty('swatch'); expect(typeof theme.id).toBe('string'); expect(typeof theme.name).toBe('string'); expect(theme.swatch).toMatch(/^#[0-9a-f]{6}$/i); } }); it('includes expected theme ids', () => { const ids = tm.getThemes().map(t => t.id); expect(ids).toContain('default'); expect(ids).toContain('modern'); expect(ids).toContain('warm'); expect(ids).toContain('dark'); expect(ids).toContain('scandinavian'); }); }); describe('constructor', () => { it('starts with default theme', () => { expect(tm.currentTheme).toBe('default'); }); }); describe('applyTheme', () => { it('updates currentTheme property', () => { tm.applyTheme('dark'); expect(tm.currentTheme).toBe('dark'); }); it('does nothing for invalid theme id', () => { tm.applyTheme('nonexistent'); expect(tm.currentTheme).toBe('default'); }); it('mutates the shared COLORS object', () => { const origExterior = COLORS.wall.exterior; tm.applyTheme('dark'); expect(COLORS.wall.exterior).toBe(0x3a3a3a); // Restore for other tests tm.applyTheme('default'); expect(COLORS.wall.exterior).toBe(origExterior); }); it('calls _clearFloor and showFloor on renderer', () => { tm.applyTheme('modern'); expect(mockRenderer._clearFloor).toHaveBeenCalled(); expect(mockRenderer.showFloor).toHaveBeenCalledWith(0); }); it('updates scene background', () => { tm.applyTheme('dark'); expect(mockRenderer.scene.background._hex).toBe(0x222222); }); }); });