import { test, describe, beforeEach, afterEach } from 'node:test'; import { strict as assert } from 'node:assert'; import { createMockDOM, cleanupMockDOM, createLogCapture } from '../utils/test-helpers.js'; import { sampleJSONContent, gameTestData } from '../fixtures/content-samples.js'; import { readFileSync } from 'fs'; import path from 'path'; describe('GameLoader - Tests Unitaires', () => { let GameLoader; let logCapture; beforeEach(() => { createMockDOM(); logCapture = createLogCapture(); // Mock GameModules avec des jeux de test global.GameModules = { WhackAMole: class { constructor(options) { this.container = options.container; this.content = options.content; this.onScoreUpdate = options.onScoreUpdate; this.onGameEnd = options.onGameEnd; } start() { this.started = true; } destroy() { this.destroyed = true; } restart() { this.restarted = true; } }, MemoryMatch: class { constructor(options) { this.container = options.container; this.content = options.content; } start() { this.started = true; } destroy() { this.destroyed = true; } }, QuizGame: class { constructor(options) { this.container = options.container; this.content = options.content; } start() { this.started = true; } destroy() { this.destroyed = true; } } }; // Mock ContentModules global.ContentModules = { TestContent: sampleJSONContent }; // Charger GameLoader const loaderPath = path.resolve(process.cwd(), 'js/core/game-loader.js'); const code = readFileSync(loaderPath, 'utf8'); const testCode = code .replace(/window\./g, 'global.') .replace(/typeof window !== 'undefined'/g, 'true'); eval(testCode); GameLoader = global.GameLoader; }); afterEach(() => { logCapture.restore(); cleanupMockDOM(); }); describe('Construction et initialisation', () => { test('devrait créer une instance GameLoader', () => { const loader = new GameLoader(); assert.ok(loader instanceof GameLoader); assert.equal(loader.currentGame, null); assert.equal(loader.currentGameType, null); }); }); describe('Chargement de jeux', () => { test('loadGame devrait charger un jeu avec succès', async () => { const loader = new GameLoader(); const container = { innerHTML: '' }; const scoreCallback = (score) => {}; const endCallback = () => {}; const game = await loader.loadGame( 'whack-a-mole', 'test-content', container, scoreCallback, endCallback ); assert.ok(game); assert.ok(game instanceof global.GameModules.WhackAMole); assert.equal(game.container, container); assert.equal(game.content, sampleJSONContent); assert.equal(loader.currentGame, game); assert.equal(loader.currentGameType, 'whack-a-mole'); }); test('loadGame devrait nettoyer le jeu précédent', async () => { const loader = new GameLoader(); const container = { innerHTML: '' }; // Charger un premier jeu const game1 = await loader.loadGame('whack-a-mole', 'test-content', container); game1.start(); // Charger un second jeu const game2 = await loader.loadGame('memory-match', 'test-content', container); assert.equal(game1.destroyed, true); assert.equal(loader.currentGame, game2); assert.ok(game2 instanceof global.GameModules.MemoryMatch); }); test('loadGame devrait échouer si le jeu n\'existe pas', async () => { const loader = new GameLoader(); const container = { innerHTML: '' }; try { await loader.loadGame('nonexistent-game', 'test-content', container); assert.fail('Should have thrown an error'); } catch (error) { assert.ok(error.message.includes('Jeu non trouvé')); } }); test('loadGame devrait échouer si le contenu n\'existe pas', async () => { const loader = new GameLoader(); const container = { innerHTML: '' }; try { await loader.loadGame('whack-a-mole', 'nonexistent-content', container); assert.fail('Should have thrown an error'); } catch (error) { assert.ok(error.message.includes('Contenu non trouvé')); } }); }); describe('Conversion de noms', () => { test('getGameClassName devrait convertir les noms de jeu', () => { const loader = new GameLoader(); assert.equal(loader.getGameClassName('whack-a-mole'), 'WhackAMole'); assert.equal(loader.getGameClassName('memory-match'), 'MemoryMatch'); assert.equal(loader.getGameClassName('quiz-game'), 'QuizGame'); assert.equal(loader.getGameClassName('fill-the-blank'), 'FillTheBlank'); }); test('getContentModuleName devrait convertir les noms de contenu', () => { const loader = new GameLoader(); assert.equal(loader.getContentModuleName('test-content'), 'TestContent'); assert.equal(loader.getContentModuleName('sbs-level-7-8-new'), 'SBSLevel78New'); assert.equal(loader.getContentModuleName('english-class-demo'), 'EnglishClassDemo'); }); test('toPascalCase devrait convertir correctement', () => { const loader = new GameLoader(); assert.equal(loader.toPascalCase('hello-world'), 'HelloWorld'); assert.equal(loader.toPascalCase('test-file-name'), 'TestFileName'); assert.equal(loader.toPascalCase('simple'), 'Simple'); assert.equal(loader.toPascalCase(''), ''); }); }); describe('Gestion du cycle de vie des jeux', () => { test('cleanup devrait détruire le jeu actuel', () => { const loader = new GameLoader(); const mockGame = { destroy: () => { mockGame.destroyed = true; }, destroyed: false }; loader.currentGame = mockGame; loader.currentGameType = 'test-game'; loader.cleanup(); assert.equal(mockGame.destroyed, true); assert.equal(loader.currentGame, null); assert.equal(loader.currentGameType, null); }); test('cleanup devrait être sûr si pas de jeu actuel', () => { const loader = new GameLoader(); // Ne devrait pas lever d'erreur loader.cleanup(); assert.equal(loader.currentGame, null); assert.equal(loader.currentGameType, null); }); test('restartCurrentGame devrait redémarrer le jeu actuel', () => { const loader = new GameLoader(); const mockGame = { restart: () => { mockGame.restarted = true; }, restarted: false }; loader.currentGame = mockGame; loader.restartCurrentGame(); assert.equal(mockGame.restarted, true); }); test('restartCurrentGame devrait échouer si pas de jeu actuel', () => { const loader = new GameLoader(); try { loader.restartCurrentGame(); assert.fail('Should have thrown an error'); } catch (error) { assert.ok(error.message.includes('Aucun jeu actuel')); } }); }); describe('Validation et contraintes', () => { test('validateGameContent devrait valider le contenu minimal', () => { const loader = new GameLoader(); const validContent = { vocabulary: { 'test': 'test' } }; const invalidContent1 = {}; const invalidContent2 = { vocabulary: {} }; const invalidContent3 = null; assert.doesNotThrow(() => loader.validateGameContent(validContent, 'test-game')); assert.throws(() => loader.validateGameContent(invalidContent1, 'test-game')); assert.throws(() => loader.validateGameContent(invalidContent2, 'test-game')); assert.throws(() => loader.validateGameContent(invalidContent3, 'test-game')); }); test('checkGameRequirements devrait vérifier les exigences spécifiques', () => { const loader = new GameLoader(); // Contenu avec vocabulaire suffisant const goodContent = { vocabulary: gameTestData.whackAMole.vocabulary }; // Contenu avec vocabulaire insuffisant const poorContent = { vocabulary: { 'only': 'one' } }; assert.doesNotThrow(() => loader.checkGameRequirements(goodContent, 'whack-a-mole')); assert.throws(() => loader.checkGameRequirements(poorContent, 'whack-a-mole')); }); }); describe('Gestion des erreurs', () => { test('devrait logger les erreurs de chargement', async () => { const loader = new GameLoader(); const container = { innerHTML: '' }; try { await loader.loadGame('nonexistent-game', 'test-content', container); } catch (error) { // Expected } const errorLogs = logCapture.getLogs('ERROR'); assert.ok(errorLogs.length > 0); assert.ok(errorLogs.some(log => log.message.includes('Erreur lors du chargement'))); }); test('devrait gérer les erreurs de construction de jeu', async () => { const loader = new GameLoader(); const container = { innerHTML: '' }; // Mock un jeu qui lève une erreur à la construction global.GameModules.BrokenGame = class { constructor() { throw new Error('Construction failed'); } }; try { await loader.loadGame('broken-game', 'test-content', container); assert.fail('Should have thrown an error'); } catch (error) { assert.ok(error.message.includes('Erreur lors de la création')); } }); }); describe('État et informations', () => { test('getCurrentGameInfo devrait retourner les informations du jeu actuel', () => { const loader = new GameLoader(); const mockGame = {}; loader.currentGame = mockGame; loader.currentGameType = 'test-game'; const info = loader.getCurrentGameInfo(); assert.equal(info.gameType, 'test-game'); assert.equal(info.game, mockGame); assert.equal(typeof info.loadTime, 'number'); }); test('getCurrentGameInfo devrait retourner null si pas de jeu', () => { const loader = new GameLoader(); const info = loader.getCurrentGameInfo(); assert.equal(info, null); }); test('isGameLoaded devrait retourner l\'état correct', () => { const loader = new GameLoader(); assert.equal(loader.isGameLoaded(), false); loader.currentGame = {}; assert.equal(loader.isGameLoaded(), true); loader.currentGame = null; assert.equal(loader.isGameLoaded(), false); }); }); });