import { describe, it, expect, vi, beforeEach } from 'vitest'; // ─── Test: DebugTab barrel export ───────────────────────────────────────────── // Verifies that the DebugTab component is properly exported from the barrel // and that all section components are importable. describe('DebugTab module structure', () => { it('exports DebugTab from barrel index', async () => { const mod = await import('./DebugTab'); expect(mod.DebugTab).toBeDefined(); expect(typeof mod.DebugTab).toBe('function'); }); it('exports GameStateDebugSection', async () => { const mod = await import('./DebugTab/GameStateDebugSection'); expect(mod.GameStateDebugSection).toBeDefined(); expect(typeof mod.GameStateDebugSection).toBe('function'); }); it('exports DisciplineDebugSection', async () => { const mod = await import('./DebugTab/DisciplineDebugSection'); expect(mod.DisciplineDebugSection).toBeDefined(); expect(typeof mod.DisciplineDebugSection).toBe('function'); }); it('exports AttunementDebugSection', async () => { const mod = await import('./DebugTab/AttunementDebugSection'); expect(mod.AttunementDebugSection).toBeDefined(); expect(typeof mod.AttunementDebugSection).toBe('function'); }); it('exports ElementDebugSection', async () => { const mod = await import('./DebugTab/ElementDebugSection'); expect(mod.ElementDebugSection).toBeDefined(); expect(typeof mod.ElementDebugSection).toBe('function'); }); it('exports GolemDebugSection', async () => { const mod = await import('./DebugTab/GolemDebugSection'); expect(mod.GolemDebugSection).toBeDefined(); expect(typeof mod.GolemDebugSection).toBe('function'); }); it('exports PactDebugSection', async () => { const mod = await import('./DebugTab/PactDebugSection'); expect(mod.PactDebugSection).toBeDefined(); expect(typeof mod.PactDebugSection).toBe('function'); }); it('exports SpireDebugSection', async () => { const mod = await import('./DebugTab/SpireDebugSection'); expect(mod.SpireDebugSection).toBeDefined(); expect(typeof mod.SpireDebugSection).toBe('function'); }); it('exports AchievementDebugSection', async () => { const mod = await import('./DebugTab/AchievementDebugSection'); expect(mod.AchievementDebugSection).toBeDefined(); expect(typeof mod.AchievementDebugSection).toBe('function'); }); }); // ─── Test: Barrel export includes DebugTab ──────────────────────────────────── describe('Tab barrel export', () => { it('includes DebugTab in the tabs index', async () => { const mod = await import('@/components/game/tabs'); expect(mod.DebugTab).toBeDefined(); expect(typeof mod.DebugTab).toBe('function'); }); }); // ─── Test: Store interactions used by DebugTab sections ─────────────────────── describe('GameStateDebugSection store interactions', () => { beforeEach(() => { vi.clearAllMocks(); }); it('resetGame action is callable', () => { const mockReset = vi.fn(); // Simulate what GameStateDebugSection does on reset mockReset(); expect(mockReset).toHaveBeenCalledTimes(1); }); it('gatherMana action is callable N times for bulk add', () => { const mockGather = vi.fn(); const amount = 100; for (let i = 0; i < amount; i++) { mockGather(); } expect(mockGather).toHaveBeenCalledTimes(amount); }); it('togglePause action is callable', () => { const mockToggle = vi.fn(); mockToggle(); expect(mockToggle).toHaveBeenCalledTimes(1); }); it('debugSetFloor action is callable with floor number', () => { const mockSetFloor = vi.fn(); mockSetFloor(100); expect(mockSetFloor).toHaveBeenCalledWith(100); }); it('resetFloorHP action is callable', () => { const mockResetHP = vi.fn(); mockResetHP(); expect(mockResetHP).toHaveBeenCalledTimes(1); }); }); describe('DisciplineDebugSection store interactions', () => { it('activate action is callable', () => { const mockActivate = vi.fn(); mockActivate('meditation'); expect(mockActivate).toHaveBeenCalledWith('meditation'); }); it('deactivate action is callable', () => { const mockDeactivate = vi.fn(); mockDeactivate('meditation'); expect(mockDeactivate).toHaveBeenCalledWith('meditation'); }); it('XP can be added to discipline via setState', () => { const disciplines: Record = { meditation: { xp: 0, paused: false }, }; const id = 'meditation'; const amount = 100; disciplines[id] = { ...disciplines[id], xp: disciplines[id].xp + amount }; expect(disciplines[id].xp).toBe(100); }); }); describe('AttunementDebugSection store interactions', () => { it('debugUnlockAttunement is callable', () => { const mockUnlock = vi.fn(); mockUnlock('invoker'); expect(mockUnlock).toHaveBeenCalledWith('invoker'); }); it('addAttunementXP is callable', () => { const mockAddXP = vi.fn(); mockAddXP('enchanter', 100); expect(mockAddXP).toHaveBeenCalledWith('enchanter', 100); }); }); describe('ElementDebugSection store interactions', () => { it('unlockElement is callable with zero cost', () => { const mockUnlock = vi.fn(); mockUnlock('fire', 0); expect(mockUnlock).toHaveBeenCalledWith('fire', 0); }); it('addElementMana is callable', () => { const mockAdd = vi.fn(); mockAdd('fire', 10, 50); expect(mockAdd).toHaveBeenCalledWith('fire', 10, 50); }); }); describe('GolemDebugSection store interactions', () => { it('setEnabledGolems is callable with all golem IDs', () => { const mockSet = vi.fn(); const allIds = ['stoneGolem', 'fireGolem']; mockSet(allIds); expect(mockSet).toHaveBeenCalledWith(allIds); }); it('setEnabledGolems is callable with empty array to disable all', () => { const mockSet = vi.fn(); mockSet([]); expect(mockSet).toHaveBeenCalledWith([]); }); }); describe('PactDebugSection store interactions', () => { it('addSignedPact is callable', () => { const mockAdd = vi.fn(); mockAdd(10); expect(mockAdd).toHaveBeenCalledWith(10); }); it('removePact is callable', () => { const mockRemove = vi.fn(); mockRemove(10); expect(mockRemove).toHaveBeenCalledWith(10); }); it('debugSetSignedPacts is callable', () => { const mockSet = vi.fn(); mockSet([10, 20, 30]); expect(mockSet).toHaveBeenCalledWith([10, 20, 30]); }); }); describe('SpireDebugSection store interactions', () => { it('enterSpireMode is callable', () => { const mockEnter = vi.fn(); mockEnter(); expect(mockEnter).toHaveBeenCalledTimes(1); }); it('exitSpireMode is callable', () => { const mockExit = vi.fn(); mockExit(); expect(mockExit).toHaveBeenCalledTimes(1); }); it('setMaxFloorReached is callable', () => { const mockSet = vi.fn(); mockSet(50); expect(mockSet).toHaveBeenCalledWith(50); }); }); describe('AchievementDebugSection store interactions', () => { it('can set all achievements as unlocked via setState', () => { const allIds = ['firstBlood', 'floorClimber']; const newState = { achievements: { unlocked: allIds, progress: Object.fromEntries(allIds.map(id => [id, 100])), }, }; expect(newState.achievements.unlocked).toEqual(allIds); expect(Object.keys(newState.achievements.progress)).toEqual(allIds); }); it('can reset all achievements via setState', () => { const newState = { achievements: { unlocked: [], progress: {}, }, }; expect(newState.achievements.unlocked).toEqual([]); expect(newState.achievements.progress).toEqual({}); }); }); // ─── Test: DebugTab component displayName ───────────────────────────────────── describe('DebugTab component metadata', () => { it('DebugTab has correct displayName', async () => { const { DebugTab } = await import('./DebugTab'); expect(DebugTab.displayName).toBe('DebugTab'); }); it('GameStateDebugSection has correct displayName', async () => { const { GameStateDebugSection } = await import('./DebugTab/GameStateDebugSection'); expect(GameStateDebugSection.displayName).toBe('GameStateDebugSection'); }); it('DisciplineDebugSection has correct displayName', async () => { const { DisciplineDebugSection } = await import('./DebugTab/DisciplineDebugSection'); expect(DisciplineDebugSection.displayName).toBe('DisciplineDebugSection'); }); it('AttunementDebugSection has correct displayName', async () => { const { AttunementDebugSection } = await import('./DebugTab/AttunementDebugSection'); expect(AttunementDebugSection.displayName).toBe('AttunementDebugSection'); }); it('ElementDebugSection has correct displayName', async () => { const { ElementDebugSection } = await import('./DebugTab/ElementDebugSection'); expect(ElementDebugSection.displayName).toBe('ElementDebugSection'); }); it('GolemDebugSection has correct displayName', async () => { const { GolemDebugSection } = await import('./DebugTab/GolemDebugSection'); expect(GolemDebugSection.displayName).toBe('GolemDebugSection'); }); it('PactDebugSection has correct displayName', async () => { const { PactDebugSection } = await import('./DebugTab/PactDebugSection'); expect(PactDebugSection.displayName).toBe('PactDebugSection'); }); it('SpireDebugSection has correct displayName', async () => { const { SpireDebugSection } = await import('./DebugTab/SpireDebugSection'); expect(SpireDebugSection.displayName).toBe('SpireDebugSection'); }); it('AchievementDebugSection has correct displayName', async () => { const { AchievementDebugSection } = await import('./DebugTab/AchievementDebugSection'); expect(AchievementDebugSection.displayName).toBe('AchievementDebugSection'); }); }); // ─── Test: File size limits ─────────────────────────────────────────────────── // Note: 400-line limit is enforced by pre-commit hook (check-file-size.js). // These tests verify the source files are importable; line count enforcement // is handled by the hook, not by runtime tests. describe('File size limits (400 lines max)', () => { it('DebugTab.tsx is importable and under 400 lines (enforced by pre-commit hook)', async () => { const fs = await import('fs'); const path = await import('path'); const filePath = path.join(__dirname, 'DebugTab.tsx'); const content = fs.readFileSync(filePath, 'utf-8'); const lines = content.split('\n').length; expect(lines).toBeLessThan(400); }); it('GameStateDebugSection.tsx is under 400 lines', async () => { const fs = await import('fs'); const path = await import('path'); const filePath = path.join(__dirname, 'DebugTab', 'GameStateDebugSection.tsx'); const content = fs.readFileSync(filePath, 'utf-8'); const lines = content.split('\n').length; expect(lines).toBeLessThan(400); }); it('DisciplineDebugSection.tsx is under 400 lines', async () => { const fs = await import('fs'); const path = await import('path'); const filePath = path.join(__dirname, 'DebugTab', 'DisciplineDebugSection.tsx'); const content = fs.readFileSync(filePath, 'utf-8'); const lines = content.split('\n').length; expect(lines).toBeLessThan(400); }); it('PactDebugSection.tsx is under 400 lines', async () => { const fs = await import('fs'); const path = await import('path'); const filePath = path.join(__dirname, 'DebugTab', 'PactDebugSection.tsx'); const content = fs.readFileSync(filePath, 'utf-8'); const lines = content.split('\n').length; expect(lines).toBeLessThan(400); }); });