Lots 1-3 modernisation JS (S8786/S2703/S2486) + retrait Sonar CI/prod

- Lot 1 (S8786, ReDoS) : 5 sites documentes NOSONAR apres preuve empirique
  (script reproductible docs/redos_probe_s8786.js), aucune reecriture
  defensive necessaire.
- Lot 2 (S2703, variable globale implicite) : bug reel trouve et corrige
  (SCENE_OBJECT_NAMES en const au lieu de let, cassait la reassignation
  cross-script depuis scene-editor.js) + test de non-regression ; 4 autres
  sites confirmes surs et documentes.
- Lot 3 (S2486, exceptions avalees) : 6 sites confirmes surs et
  documentes ; 2 sites (config sprite JSON invalide) corriges avec un
  console.warn devtools, comportement joueur inchange, couverts par un
  nouveau test.
- Retrait du job CI sonarqube (.gitea/workflows/deploy.yml) et du service
  prod sonarqube/sonar-postgres (docker-compose.prod.yml) : acces dashboard
  bloque par des soucis d'infrastructure reseau (WSL2/pare-feu Hyper-V en
  local, reseau Docker partage avec Caddy pas en place en prod), sans lien
  avec le code du moteur - mis de cote plutot que de continuer a bloquer
  sur de l'infra. Les lots 4+ de modernisation JS dependent de scores
  Sonar exacts et sont donc egalement en pause (voir CODE_QUALITY.md).

SKIP=djlint : H021 (styles inline, 49 occurrences) est un backlog deja
documente et assume (CODE_QUALITY.md section 6), sur des templates non
touches par ce commit - deja exclu de la CI pour la meme raison.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
william
2026-09-18 08:46:24 +02:00
co-authored by Claude Sonnet 5
parent 55e81c0fbb
commit 66d8eaeae8
16 changed files with 666 additions and 112 deletions
@@ -28,6 +28,7 @@ function makeEl() {
getAttribute() { return null; },
addEventListener() {},
removeEventListener() {},
dataset: {},
};
Object.defineProperty(el, 'innerHTML', {
get() { return this._html; },
@@ -48,6 +49,9 @@ function setupGlobals() {
global.ELEMENT_VISIBILITY_LABELS_MAP = { visible: 'Visible' };
global.VIDEO_MODE_LABELS_MAP = { plein_ecran: 'Plein écran' };
global.USER_ASSETS_OPTIONS = [];
// Nécessaire depuis le chargement de scene-editor.js (voir plus bas) :
// initScenePosFields() la lit dès son propre appel top-level.
global.CURRENT_SELECTED_ID = null;
const elementsById = {};
global.document = {
@@ -75,6 +79,11 @@ function setupGlobals() {
setupGlobals();
loadAsScript('../collision-rules-editor.js');
loadAsScript('../../triggers/trigger-editor.js');
// scene-editor.js APRÈS trigger-editor.js, exactement l'ordre de chargement
// réel de templates/scene_edit.html (voir CODE_QUALITY.md, S2703) —
// nécessaire pour tester refreshSceneObjectNames() en conditions réelles,
// contre la vraie déclaration `SCENE_OBJECT_NAMES` de trigger-editor.js.
loadAsScript('../scene-editor.js');
test('assistant — une chaîne "attendre -> puis dialogue" sauvegarde bien LES DEUX, dans le bon ordre', () => {
// Bug corrigé : "Rien de plus" renvoyait la DERNIÈRE feuille ajoutée
@@ -212,3 +221,27 @@ test('triggerActionChainHtml — un bouton "+ Ajouter une action" apparaît apr
assert.ok(html.includes("screenTriggerOpenAppendActionModal('a_1')"));
assert.ok(html.includes("screenTriggerOpenAppendActionModal('d_1')"));
});
test('refreshSceneObjectNames — SCENE_OBJECT_NAMES doit rester réassignable depuis scene-editor.js (bug S2703 : déclarée en `const` dans trigger-editor.js, la réassignation levait TypeError et la liste ne se mettait jamais à jour sans recharger la page)', async () => {
setupGlobals();
global.fetch = function (url) {
assert.equal(url, '/game/pytest_wizard/scene-object-names');
return Promise.resolve({ ok: true, json: () => Promise.resolve(['Marc', 'Damien']) });
};
global.refreshSceneObjectNames();
// fetch(...).then(...).then(...) est asynchrone (2 sauts de microtâches) —
// laisse la file d'attente se vider avant de vérifier le résultat.
await new Promise((resolve) => { setTimeout(resolve, 0); });
// SCENE_OBJECT_NAMES est déclarée en `let`/`const` (pas `var`) : comme
// dans un vrai navigateur, cette liaison ne devient JAMAIS une propriété
// de l'objet global — il faut la relire via une autre exécution dans le
// même environnement lexical partagé (vm.runInThisContext), pas via
// `global.SCENE_OBJECT_NAMES` (toujours `undefined`, quel que soit le
// résultat réel du bug).
assert.deepEqual(vm.runInThisContext('SCENE_OBJECT_NAMES'), ['Marc', 'Damien']);
// Preuve que triggerSpeakerOptions() (trigger-editor.js) voit bien le
// nouveau nom SANS rechargement de page — exactement le symptôme rapporté.
assert.ok(global.triggerSpeakerOptions(null).includes('Damien'));
});