From 5e17e42dfa25128780e8c976eca5093d0404f6f2 Mon Sep 17 00:00:00 2001 From: william Date: Thu, 3 Sep 2026 12:27:50 +0200 Subject: [PATCH] =?UTF-8?q?Test=20bout=20en=20bout=20:=20collision=20->=20?= =?UTF-8?q?qu=C3=AAte=20->=20bo=C3=AEte=20de=20dialogue?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Vérifie explicitement le lien moteur demandé : une règle de collision "quete" retrouve la quête, récupère les répliques de son statut actuel, les affiche dans l'ordre dans le widget "💬 Boîte de dialogue" posé sur la scène, avance au clic sur "Suivant", disparaît à la dernière réplique — déjà implémenté (collision-rules-controller.js + dialogue-box-controller.js), ce test couvre la CHAÎNE COMPLÈTE plutôt que chaque brique isolément. Co-Authored-By: Claude Sonnet 5 --- ...llision-quete-dialogue-integration.test.js | 108 ++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 static/js/play/__tests__/collision-quete-dialogue-integration.test.js diff --git a/static/js/play/__tests__/collision-quete-dialogue-integration.test.js b/static/js/play/__tests__/collision-quete-dialogue-integration.test.js new file mode 100644 index 00000000..dc0ce522 --- /dev/null +++ b/static/js/play/__tests__/collision-quete-dialogue-integration.test.js @@ -0,0 +1,108 @@ +// Bout en bout : une règle de collision "quete" doit réellement faire +// apparaître/avancer/disparaître la boîte de dialogue posée sur la scène +// — pas seulement chaque brique testée isolément (voir +// collision-rules-controller.test.js et dialogue-box-controller.test.js). +// Reproduit le scénario signalé : un objet "Mr. Pinguin" avec une règle +// "à la collision -> déclencher la quête « test »", une quête au statut +// "nouvelle" avec 2 répliques, un widget "💬 Boîte de dialogue" posé sur +// la même scène. +const test = require('node:test'); +const assert = require('node:assert/strict'); + +global.window = global.window || {}; +global.gameData = {}; +global.screensData = []; +global.heldKeys = new Set(); + +const actionsMod = require('../actions.js'); +Object.assign(global, actionsMod); +require('../personnage-controller.js'); +const { forgeDialogueBoxAdvance } = require('../dialogue-box-controller.js'); // pas mocké : la vraie logique métier + +const { forgeCollisionRuleTick } = require('../collision-rules-controller.js'); + +function fakeEl(style) { + const classes = new Set(); + const children = []; + return { + style: style, + classList: { add: c => classes.add(c), remove: c => classes.delete(c), contains: c => classes.has(c) }, + children, + appendChild(child) { children.push(child); }, + querySelector(sel) { + if (sel === '.dialogueBoxWidget') return children.find(c => c._isDialogueBoxWidget); + if (sel === '[data-dialogue-role="header"]') return this._header; + if (sel === '[data-dialogue-role="body"]') return this._body; + return undefined; + }, + getAttribute(attr) { return attr === 'data-object-id' ? this._objectId : null; }, + }; +} + +test('collision "quete" -> boîte de dialogue affiche la 1ère réplique, "Suivant" avance, se masque à la fin', () => { + const player = { id: 1, kind: 'personnage', personnage_role: 'joueur' }; + const mrPinguin = { + id: 2, kind: 'personnage', + collision_rules: [{ trigger: 'collision', action: { type: 'quete', quete_id: 99 } }], + }; + const box = { id: 3, kind: 'dialogue_box' }; + + const playerEl = fakeEl({ left: '0px', top: '0px', width: '20px', height: '20px' }); + const pinguinEl = fakeEl({ left: '5px', top: '5px', width: '20px', height: '20px' }); // chevauche playerEl + const boxWidget = fakeEl({}); + boxWidget._isDialogueBoxWidget = true; + boxWidget._objectId = '3'; + boxWidget._header = { textContent: '' }; + boxWidget._body = { textContent: '' }; + + global.window.currentScreenId = 1; + global.gameData = { + personnage_animations: {}, + quests: [{ + id: 99, status: 'nouvelle', + dialogues: { + nouvelle: [ + { speaker: 'Mr. Pinguin', text: 'Bonjour !' }, + { speaker: 'Charlie', text: 'Salut !' }, + ], + en_cours: [], terminee: [], + }, + }], + }; + + const previousDoc = global.document; + global.document = { + querySelector(sel) { + // forgeDialogueBoxWidgetOnCurrentScreen() cherche + // '.playScreen[data-screen-id="1"] .dialogueBoxWidget' — à vérifier + // AVANT les sélecteurs [data-object-id="..."] ci-dessous (le "1" du + // numéro de scène apparaît aussi comme sous-chaîne de cette + // requête, source d'ambiguïté sinon). + if (sel.indexOf('.dialogueBoxWidget') !== -1 && sel.indexOf('data-object-id') === -1) return boxWidget; + if (sel.indexOf('data-object-id="1"') !== -1) return playerEl; + if (sel.indexOf('data-object-id="2"') !== -1) return pinguinEl; + // forgeDialogueBoxWidgetById('3') — appelé par le clic "Suivant" + if (sel.indexOf('data-object-id="3"') !== -1) return boxWidget; + return null; + }, + }; + try { + const screenData = { elements: [player, mrPinguin, box] }; + + forgeCollisionRuleTick(screenData); // entrée en collision -> déclenche la quête + assert.equal(boxWidget._header.textContent, 'Mr. Pinguin'); + assert.equal(boxWidget._body.textContent, 'Bonjour !'); + assert.equal(boxWidget.classList.contains('is-active'), true); + + forgeCollisionRuleTick(screenData); // toujours en collision -> ne redéclenche pas la quête + assert.equal(boxWidget._header.textContent, 'Mr. Pinguin'); // pas repartie de la 1ère réplique + + forgeDialogueBoxAdvance('3'); // clic "Suivant" (bouton du widget) + assert.equal(boxWidget._header.textContent, 'Charlie'); + assert.equal(boxWidget._body.textContent, 'Salut !'); + assert.equal(boxWidget.classList.contains('is-active'), true); + + forgeDialogueBoxAdvance('3'); // dernière réplique consommée -> la modale disparaît + assert.equal(boxWidget.classList.contains('is-active'), false); + } finally { global.document = previousDoc; } +});