// @ts-nocheck import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; // Stub foundry global for conditional base class beforeEach(() => { vi.stubGlobal('Hooks', { on: vi.fn(() => 99), off: vi.fn() }); vi.stubGlobal('game', { user: { setFlag: vi.fn(), getFlag: vi.fn(() => null) } }); }); afterEach(() => { vi.unstubAllGlobals(); vi.clearAllMocks(); }); import { ScenePresetPanel } from '../../../../src/ui/gm/ScenePresetPanel.js'; describe('ScenePresetPanel', () => { let adapter; let scenePresetManager; let panel; beforeEach(() => { adapter = { scenes: { current: vi.fn(() => ({ id: 'scene1', name: 'Test Scene' })) }, i18n: { localize: vi.fn((key) => key) }, notifications: { info: vi.fn() }, }; scenePresetManager = { list: vi.fn(() => [ { name: 'Preset 1' }, { name: 'Preset 2' }, ]), _getSceneFlagData: vi.fn(() => ({})), _getAutoApplyConfig: vi.fn(() => ({ enabled: false, presetName: null, preDelay: 0 })), configureAutoApply: vi.fn().mockResolvedValue(undefined), }; panel = new ScenePresetPanel(adapter, scenePresetManager); }); describe('constructor', () => { it('is side-effect-free: does not call Hooks.on', () => { expect(Hooks.on).not.toHaveBeenCalled(); }); it('stores adapter and scenePresetManager references', () => { expect(panel._adapter).toBe(adapter); expect(panel._scenePresetManager).toBe(scenePresetManager); }); it('initializes _element to null', () => { expect(panel._element).toBeNull(); }); it('initializes _isOpen to false', () => { expect(panel._isOpen).toBe(false); }); it('initializes _currentScene to null', () => { expect(panel._currentScene).toBeNull(); }); it('initializes handlers to null', () => { expect(panel._clickHandler).toBeNull(); expect(panel._changeHandler).toBeNull(); expect(panel._inputHandler).toBeNull(); }); it('sets MAX_PREDELAY to 5000', () => { expect(panel._MAX_PREDELAY).toBe(5000); }); it('sets MIN_PREDELAY to 0', () => { expect(panel._MIN_PREDELAY).toBe(0); }); }); describe('init()', () => { it('creates the DOM element', () => { panel.init(); expect(panel._element).toBeInstanceOf(HTMLElement); expect(panel._element.className).toBe('directors-board__preset-panel'); }); it('sets role attribute to region', () => { panel.init(); expect(panel._element.getAttribute('role')).toBe('region'); }); it('sets aria-label using i18n', () => { panel.init(); expect(adapter.i18n.localize).toHaveBeenCalledWith('video-view-manager.scenePresetPanel.title'); expect(panel._element.getAttribute('aria-label')).toBe('video-view-manager.scenePresetPanel.title'); }); it('sets aria-expanded to false initially', () => { panel.init(); expect(panel._element.getAttribute('aria-expanded')).toBe('false'); }); it('sets display to none initially', () => { panel.init(); expect(panel._element.style.display).toBe('none'); }); it('sets up event listeners', () => { panel.init(); expect(panel._clickHandler).toBeDefined(); expect(panel._inputHandler).toBeDefined(); }); it('calls _refresh() to populate initial content', () => { const refreshSpy = vi.spyOn(panel, '_refresh'); panel.init(); expect(refreshSpy).toHaveBeenCalled(); }); }); describe('element getter', () => { it('returns the panel element after init', () => { panel.init(); expect(panel.element).toBe(panel._element); }); it('returns null before init', () => { expect(panel.element).toBeNull(); }); }); describe('toggle()', () => { beforeEach(() => { panel.init(); }); it('opens the panel when closed', () => { panel._isOpen = false; panel._element.style.display = 'none'; panel.toggle(); expect(panel._isOpen).toBe(true); expect(panel._element.style.display).toBe('block'); }); it('closes the panel when open', () => { panel._isOpen = true; panel._element.style.display = 'block'; panel.toggle(); expect(panel._isOpen).toBe(false); expect(panel._element.style.display).toBe('none'); }); it('is a no-op when element is null', () => { panel._element = null; panel.toggle(); expect(panel._isOpen).toBe(false); }); }); describe('open()', () => { beforeEach(() => { panel.init(); }); it('sets _isOpen to true', () => { panel.open(); expect(panel._isOpen).toBe(true); }); it('sets display to block', () => { panel.open(); expect(panel._element.style.display).toBe('block'); }); it('sets aria-expanded to true', () => { panel.open(); expect(panel._element.getAttribute('aria-expanded')).toBe('true'); }); it('calls _refresh()', () => { const refreshSpy = vi.spyOn(panel, '_refresh'); panel.open(); expect(refreshSpy).toHaveBeenCalled(); }); it('is a no-op when element is null', () => { panel._element = null; panel.open(); expect(panel._isOpen).toBe(false); }); }); describe('close()', () => { beforeEach(() => { panel.init(); }); it('sets _isOpen to false', () => { panel._isOpen = true; panel.close(); expect(panel._isOpen).toBe(false); }); it('sets display to none', () => { panel._element.style.display = 'block'; panel.close(); expect(panel._element.style.display).toBe('none'); }); it('sets aria-expanded to false', () => { panel._element.setAttribute('aria-expanded', 'true'); panel.close(); expect(panel._element.getAttribute('aria-expanded')).toBe('false'); }); it('is a no-op when element is null', () => { panel._element = null; panel.close(); expect(panel._isOpen).toBe(false); }); }); describe('_refresh()', () => { beforeEach(() => { panel.init(); }); it('is a no-op when element is null', async () => { panel._element = null; await panel._refresh(); // Should not throw }); it('builds empty HTML when no scene is current', async () => { adapter.scenes.current.mockReturnValue(null); await panel._refresh(); expect(panel._element.innerHTML).toContain('noScene'); }); it('stores current scene and builds HTML with scene', async () => { const mockScene = { id: 'scene1', name: 'Test Scene' }; adapter.scenes.current.mockReturnValue(mockScene); await panel._refresh(); expect(panel._currentScene).toBe(mockScene); expect(scenePresetManager.list).toHaveBeenCalled(); }); it('updates toggle aria-pressed state based on auto-apply enabled', async () => { scenePresetManager._getAutoApplyConfig.mockReturnValue({ enabled: true, presetName: null, preDelay: 0 }); await panel._refresh(); const toggle = panel._element.querySelector('[data-action="toggle-auto-apply"]'); expect(toggle).not.toBeNull(); expect(toggle.getAttribute('aria-pressed')).toBe('true'); }); }); describe('_buildEmptyHtml()', () => { beforeEach(() => { panel.init(); }); it('returns HTML with no scene message', () => { const html = panel._buildEmptyHtml(); expect(html).toContain('noScene'); expect(html).toContain('directors-board__preset-panel-title'); }); it('uses i18n for message', () => { panel._buildEmptyHtml(); expect(adapter.i18n.localize).toHaveBeenCalledWith('video-view-manager.scenePresetPanel.noScene'); }); it('escapes HTML in message', () => { adapter.i18n.localize = vi.fn(() => ''); const html = panel._buildEmptyHtml(); expect(html).not.toContain('' }], }); expect(html).not.toContain(''); expect(result).not.toContain('<'); expect(result).not.toContain('>'); expect(result).not.toContain('"'); }); }); });