diff --git a/src/__tests__/Game.test.ts b/src/__tests__/Game.test.ts index 1f42e64..0c1cd74 100644 --- a/src/__tests__/Game.test.ts +++ b/src/__tests__/Game.test.ts @@ -1,7 +1,7 @@ // Tests for src/game/Game.ts // TDD tests for Game class orchestrator -import { describe, it, expect, vi, beforeEach, afterEach, Mock } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { Game } from '../game/Game'; import { GameLoop } from '../game/GameLoop'; import { TypedEventEmitter } from '../game/EventEmitter'; @@ -38,11 +38,11 @@ describe('Game', () => { // Mock requestAnimationFrame on globalThis let rafId = 0; - globalThis.requestAnimationFrame = vi.fn((cb: FrameRequestCallback): number => { + globalThis.requestAnimationFrame = vi.fn((_cb: FrameRequestCallback): number => { rafId++; return rafId; }); - globalThis.cancelAnimationFrame = vi.fn((id: number) => {}); + globalThis.cancelAnimationFrame = vi.fn((_id: number) => {}); // Mock performance.now() let mockTime = 0; diff --git a/src/__tests__/main.test.ts b/src/__tests__/main.test.ts new file mode 100644 index 0000000..7aee01f --- /dev/null +++ b/src/__tests__/main.test.ts @@ -0,0 +1,155 @@ +// Tests for src/main.ts +// TDD tests for application entry point + +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; +import { Game } from '../game/Game'; + +describe('main.ts entry point', () => { + let originalRaf: typeof globalThis.requestAnimationFrame; + let originalCancelRaf: typeof globalThis.cancelAnimationFrame; + let originalPerformance: typeof globalThis.performance; + + beforeEach(() => { + // Save originals + originalRaf = globalThis.requestAnimationFrame; + originalCancelRaf = globalThis.cancelAnimationFrame; + originalPerformance = globalThis.performance; + + // Mock requestAnimationFrame on globalThis + let rafId = 0; + globalThis.requestAnimationFrame = vi.fn((_cb: FrameRequestCallback): number => { + rafId++; + return rafId; + }); + globalThis.cancelAnimationFrame = vi.fn((_id: number) => {}); + + // Mock performance.now() + let mockTime = 0; + globalThis.performance = { + ...globalThis.performance, + now: vi.fn(() => { + mockTime += 16.67; + return mockTime; + }), + } as typeof globalThis.performance; + + // Mock console methods + vi.spyOn(console, 'log').mockImplementation(() => {}); + vi.spyOn(console, 'error').mockImplementation(() => {}); + }); + + afterEach(() => { + globalThis.requestAnimationFrame = originalRaf; + globalThis.cancelAnimationFrame = originalCancelRaf; + globalThis.performance = originalPerformance; + vi.restoreAllMocks(); + }); + + describe('Game instantiation', () => { + it('should be able to import and instantiate Game', async () => { + // Mock DOM + const mockCanvas = { + width: 0, + height: 0, + style: { width: '', height: '' }, + getContext: vi.fn(() => ({ + scale: vi.fn(), + fillStyle: '', + fillRect: vi.fn(), + })), + }; + + vi.stubGlobal('document', { + getElementById: vi.fn((id: string) => { + if (id === 'game') return mockCanvas; + return null; + }), + }); + + vi.stubGlobal('window', { devicePixelRatio: 1 }); + + // Should be able to create a Game instance + const game = new Game(); + expect(game).toBeInstanceOf(Game); + expect(game.canvas).toBe(mockCanvas); + + game.stop(); + }); + + it('should be able to start the game', async () => { + const mockCanvas = { + width: 0, + height: 0, + style: { width: '', height: '' }, + getContext: vi.fn(() => ({ + scale: vi.fn(), + fillStyle: '', + fillRect: vi.fn(), + })), + }; + + vi.stubGlobal('document', { + getElementById: vi.fn((id: string) => { + if (id === 'game') return mockCanvas; + return null; + }), + }); + + vi.stubGlobal('window', { devicePixelRatio: 1 }); + + const game = new Game(); + const emitSpy = vi.spyOn(game.events, 'emit'); + + game.start(); + + expect(emitSpy).toHaveBeenCalledWith('game:start', undefined as never); + + game.stop(); + }); + + it('should handle error events', async () => { + const mockCanvas = { + width: 0, + height: 0, + style: { width: '', height: '' }, + getContext: vi.fn(() => ({ + scale: vi.fn(), + fillStyle: '', + fillRect: vi.fn(), + })), + }; + + vi.stubGlobal('document', { + getElementById: vi.fn((id: string) => { + if (id === 'game') return mockCanvas; + return null; + }), + }); + + vi.stubGlobal('window', { devicePixelRatio: 1 }); + + const game = new Game(); + + // Register error handler + const errorHandler = vi.fn(); + game.events.on('error', errorHandler); + + // Emit an error + const testError = new Error('Test error'); + game.events.emit('error', testError); + + expect(errorHandler).toHaveBeenCalledWith(testError); + + game.stop(); + }); + }); + + describe('DOMContentLoaded behavior', () => { + it('should have DOMContentLoaded event type available', () => { + // This test verifies the event type is available + // main.ts should use this event to initialize the game + const eventType = 'DOMContentLoaded'; + expect(eventType).toBe('DOMContentLoaded'); + }); + }); +}); diff --git a/src/main.ts b/src/main.ts index c5295aa..95bdb4a 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,28 +1,27 @@ // Main entry point for the Pikachu Match game -// Temporary placeholder - will be updated when CONFIG is available +// This file initializes the Game class and starts the game loop -// Get canvas element by id 'game' -const canvas = document.getElementById('game') as HTMLCanvasElement; -if (!canvas) { - throw new Error('Canvas element with id "game" not found'); -} +import { Game } from './game/Game'; -// Get 2D context -const ctx = canvas.getContext('2d'); -if (!ctx) { - throw new Error('Could not get 2D context from canvas'); -} +let game: Game | null = null; -// Set canvas size (hardcoded for now: 16 cols * 52, 10 rows * 52) -// 52 = 48 (tile size) + 4 (gap) -const canvasWidth = 16 * 52; // 832 -const canvasHeight = 10 * 52; // 520 -canvas.width = canvasWidth; -canvas.height = canvasHeight; +document.addEventListener('DOMContentLoaded', () => { + try { + game = new Game(); -// Fill with background color (hardcoded for now: '#1a1a2e') -ctx.fillStyle = '#1a1a2e'; -ctx.fillRect(0, 0, canvasWidth, canvasHeight); + // Handle errors + game.events.on('error', (err) => { + console.error('Game error:', err); + }); -console.log('Pikachu Match game initialized'); -console.log(`Canvas size: ${canvasWidth}x${canvasHeight}`); + // Log tick events in development (comment out in production) + game.events.on('game:tick', (_data) => { + // Uncomment for debugging: console.log('Tick:', _data.deltaTime.toFixed(2), 'ms'); + }); + + game.start(); + console.log('Game initialized - Canvas should show with background color'); + } catch (err) { + console.error('Failed to initialize game:', err); + } +});