Skip to Content

Chapitre 4.5 — Débuguer le runtime

⏱️ TL;DR — Le symptôme est toujours le même : « le module ne remonte rien ». Derrière, une poignée de causes récurrentes — le SCO muet (API jamais trouvée), la session jamais initialisée, le statut oublié, le Terminate manquant, une valeur rejetée silencieusement. La bonne nouvelle : SCORM te donne de quoi savoir précisément ce qui cloche, à condition de le demander. Après chaque appel, GetLastError / LMSGetLastError renvoie un code ; GetErrorString le traduit en message ; GetDiagnostic donne le détail propre au LMS. Ce chapitre te donne un wrapper qui vérifie l’erreur à chaque appel, une checklist de diagnostic, les outils (console, SCORM Cloud, extensions), et déroule un cas concret pas à pas.

🎯 Objectifs

  • Reconnaître le « SCO muet » et ses causes (API introuvable, jamais initialisé).
  • Interroger l’erreur après chaque appel : GetLastError + GetErrorString + GetDiagnostic.
  • Écrire un wrapper qui journalise et détecte les échecs silencieux.
  • Dérouler une checklist « mon module ne remonte rien » et un cas concret de bout en bout.

Le SCO muet

Un « SCO muet », c’est un module qui s’affiche mais ne communique pas avec le LMS. Deux grandes familles :

  1. Il n’a jamais trouvé l’API (chapitre 4.1) : cross-domain, iframe imbriquée non gérée, pop-up bloquée, findAPI() naïf. Rien ne partira jamais, car il n’y a personne à qui parler.
  2. Il a trouvé l’API mais ne s’en sert pas correctement : jamais Initialize, statut jamais posé, Commit/Terminate oubliés, valeurs rejetées. L’API est là, mais le contrat n’est pas honoré.

Le drame du SCO muet, c’est qu’il échoue en silence. Les méthodes SCORM ne lèvent pas d’exception quand elles échouent : elles renvoient "false" (ou une chaîne vide) et posent un code d’erreur que, par défaut, personne ne lit. D’où la règle de survie du débogage SCORM :

💡 RéflexeVérifie l’erreur après CHAQUE appel. Un SetValue qui renvoie "false" n’a rien fait — mais il ne te le dira que si tu appelles GetLastError juste après. Le code qui « a l’air de marcher » sans jamais lire les erreurs est exactement celui qui livre des SCO muets en production.

Lire l’erreur : les trois méthodes

Après chaque appel au runtime, trois méthodes te renseignent :

Méthode (2004 / 1.2)Renvoie
GetLastError() / LMSGetLastError()Le code d’erreur du dernier appel ("0" = pas d’erreur).
GetErrorString(code) / LMSGetErrorString(code)Le libellé standard du code (message court).
GetDiagnostic(code) / LMSGetDiagnostic(code)Un détail propre au LMS (souvent le plus utile).

Le principe : GetLastError te dit s’il y a eu une erreur et laquelle ; GetErrorString la nomme ; GetDiagnostic explique (au niveau de détail que le LMS veut bien fournir).

Quelques codes SCORM 1.2 à reconnaître (la liste complète est dans la spec) :

CodeSignification
0Pas d’erreur — l’appel a réussi.
101Exception générale (souvent : appel avant LMSInitialize).
201Argument invalide (nom d’élément mal formé).
301Non initialisé (lecture/écriture hors session).
401Élément non implémenté par ce LMS.
403Élément en lecture seule (tu as tenté d’écrire un RO).
404Élément en écriture seule (tu as tenté de lire un WO).
405Type de donnée incorrect (mauvais format de valeur).

⚠️ Piège — Ignorer le retour des méthodes et le code d’erreur. LMSSetValue(...) qui renvoie "false" n’a rien écrit : peut-être un 403 (champ RO), un 301 (pas initialisé), un 405 (mauvais type). Sans lire GetLastError/GetDiagnostic, tu passes des heures à chercher « pourquoi le score ne remonte pas » alors que le LMS te l’a dit au premier appel.

Un wrapper qui parle

Le meilleur investissement de débogage : n’appeler jamais le runtime en direct, mais à travers un wrapper qui journalise l’appel, vérifie le code d’erreur et crie en cas d’échec.

// scorm-debug.js — wrapper qui vérifie l'erreur après chaque appel // (version SCORM 1.2 ; adapter les noms de méthodes pour 2004) function lastError(api) { const code = api.LMSGetLastError(); if (code === "0") return null; return { code, label: api.LMSGetErrorString(code), // libellé standard detail: api.LMSGetDiagnostic(code), // détail du LMS }; } function setValue(api, elt, val) { const ok = api.LMSSetValue(elt, String(val)); const err = lastError(api); if (ok !== "true" || err) { console.error( `[SCORM] SetValue("${elt}", "${val}") a ÉCHOUÉ`, err || "(retour false sans code)" ); return false; } console.debug(`[SCORM] SetValue("${elt}", "${val}") OK`); return true; } function getValue(api, elt) { const val = api.LMSGetValue(elt); const err = lastError(api); if (err) { console.error(`[SCORM] GetValue("${elt}") a ÉCHOUÉ`, err); return null; } return val; } function commit(api) { const ok = api.LMSCommit(""); const err = lastError(api); if (ok !== "true" || err) console.error("[SCORM] Commit a ÉCHOUÉ", err); return ok === "true"; }

Avec ce wrapper, un champ mal orthographié, un statut posé avant Initialize, un score au mauvais format : tout apparaît immédiatement dans la console, avec le code et le message. Tu passes du « ça ne marche pas » au « SetValue("cmi.core.score.raw", "85%") → code 405, type incorrect » — un diagnostic, pas une devinette.

Les outils

OutilÀ quoi il sert
Console navigateurVoir tes logs, inspecter la chaîne de frames, tester getScormAPI() à la main. Attention : ouvre la console dans l’iframe du SCO, pas dans la page LMS (sélecteur de contexte / frame).
SCORM Cloud (Rustici)LMS de test de référence : importe ton paquet, lance-le, et regarde le journal des appels runtime (chaque SetValue, chaque valeur, chaque erreur). L’étalon-or pour valider un SCO hors de tout LMS client.
Extension de debug SCORMDes extensions navigateur affichent en direct les appels API et le contenu du modèle CMI, sans toucher au code. Pratiques pour auditer un module compilé (Storyline, Rise) dont tu n’as pas la source.
Suite de tests ADLVérifie la conformité formelle du SCO au standard — utile avant de livrer/certifier.
Logs applicatifsTon wrapper console.debug/console.error : garde-les (désactivables) pour diagnostiquer sur le terrain, chez le client.

🔌 Côté intégration — Avant d’envoyer un paquet à une école, passe-le par SCORM Cloud. Si la complétion et le score y remontent proprement, tu as éliminé 90 % des tickets « ça ne marche pas chez nous » : le problème sera alors du côté de la configuration du LMS client (mode d’ouverture, cross-domain), pas de ton contenu. Un SCO validé sur SCORM Cloud est un argument commercial : « conforme, testé, ça s’importe partout ».

La checklist « mon module ne remonte rien »

Déroule dans l’ordre — chaque étape élimine une famille de causes.

En clair, les six questions à se poser :

  1. getScormAPI() renvoie-t-il un objet ? Non → SCO muet par API introuvable (chapitre 4.1).
  2. Initialize/LMSInitialize a-t-il renvoyé "true" ? Non → session jamais ouverte ; tous les SetValue suivants échoueront en 301/101.
  3. Le statut est-il posé à completed/passed à la fin ? Non → piège n°1 (chapitre 4.4).
  4. Commit puis Terminate/LMSFinish sont-ils appelés, y compris en fermeture d’onglet ? Non → données perdues (chapitre 4.2).
  5. GetLastError est-il "0" après chaque SetValue ? Non → une valeur est rejetée (mauvais nom, mauvais type, champ RO) ; lire GetDiagnostic.
  6. Tout est vert côté SCO mais ça ne remonte toujours pas ? Regarder la config du LMS client (mode d’ouverture, domaine).

Cas concret : le module FormaCampus, pas à pas

Reprenons le fil rouge et déroulons la checklist sur le module qui ne remontait pas la complétion dans le Moodle de l’école.

Étape 1 — API trouvée ? Dans la console (contexte : iframe du SCO), on tape getScormAPI(). Retour : un objet, version: "1.2". ✅ L’API est trouvée. On élimine le cross-domain et la pop-up bloquée (chapitre 4.1).

Étape 2 — Session ouverte ? On instrumente : LMSInitialize("") renvoie "true", GetLastError = "0". ✅ Session ouverte. Le SCO n’est pas muet au sens « jamais initialisé ».

Étape 3 — Statut posé ? On trace les SetValue. On voit :

[SCORM] SetValue("cmi.core.lesson_status", "incomplete") OK ← à l'ouverture ... (aucun autre SetValue sur lesson_status)

Le statut n’est jamais repassé à completed. Le code posait incomplete au départ et l’écran final n’appelait aucune méthode SCORM (juste une animation de félicitations). Bug n°1 confirmé (chapitre 4.4).

Étape 4 — Clôture ? On cherche LMSFinish. Il est câblé sur un bouton « Quitter » que personne ne clique ; à la fermeture d’onglet, rien. ❌ Bug n°2 confirmé (chapitre 4.2).

La correction, minimale et ciblée :

// À la validation du dernier écran (avant l'animation de félicitations) : setValue(api, "cmi.core.lesson_status", "completed"); // wrapper qui vérifie l'erreur setValue(api, "cmi.core.score.raw", String(scoreFinal)); setValue(api, "cmi.core.exit", ""); commit(api); // Clôture garantie, quelle que soit la façon de quitter : function closeSession() { if (closeSession.done) return; closeSession.done = true; commit(api); api.LMSFinish(""); } window.addEventListener("pagehide", closeSession); window.addEventListener("beforeunload", closeSession);

Vérification : on rejoue le module sur SCORM Cloud. Le journal montre SetValue("cmi.core.lesson_status", "completed") suivi d’un Commit et d’un LMSFinish, tous en code "0". La complétion remonte. On réimporte dans le Moodle de l’école : les stagiaires passent en « terminé », les scores apparaissent. Ticket clos.

🧭 Sur FormaCampus — Bilan de la Partie 4 : le module « valide » n’était pas invalide au packaging (Partie 3) — il était incomplet au runtime. Les deux bugs (statut jamais completed, LMSFinish jamais appelé) sont les deux pièges les plus courants du e-learning, et le wrapper de debug les a rendus visibles en trois lignes de console. Leçon d’équipe adoptée chez FormaCampus : aucun SetValue en direct (tout passe par le wrapper qui lit l’erreur) et tout module validé sur SCORM Cloud avant livraison. Zéro ticket « ça ne remonte pas » depuis.

📚 La specGetLastError/LMSGetLastError, GetErrorString, GetDiagnostic et la table des codes d’erreur sont définis dans le livre RTE de chaque version SCORM (ADL, adlnet.gov). Les codes exacts diffèrent entre 1.2 et 2004 : ne pas coder « en dur » un tableau de codes, mais lire GetErrorString(code) et GetDiagnostic(code) renvoyés par le LMS, qui font autorité pour la version en cours.

✏️ Exercices

Exercice 1 — Le score refusé. Un dev jure que « le score ne remonte pas » malgré ce code 1.2 :

api.LMSSetValue("cmi.core.score.raw", "85%");

Sans même lancer, dis ce que renverrait LMSGetLastError juste après, et corrige.

✅ Solution

"85%" n’est pas un nombre : cmi.core.score.raw attend une valeur numérique (ici 0–100). Le LMS rejette la valeur ; LMSSetValue renvoie "false" et LMSGetLastError renvoie 405 (type de donnée incorrect). Rien n’est écrit — d’où « le score ne remonte pas ».

Correction : envoyer un nombre en chaîne, sans symbole.

api.LMSSetValue("cmi.core.score.raw", "85"); // et poser min/max pour lever l'ambiguïté api.LMSSetValue("cmi.core.score.min", "0"); api.LMSSetValue("cmi.core.score.max", "100");

Morale : un wrapper qui lit GetLastError après chaque appel aurait affiché « code 405 » immédiatement.

Exercice 2 — Diagnostiquer un muet. Un module Storyline compilé (pas de source) ne trace rien chez un client. Décris, dans l’ordre, comment tu poses le diagnostic sans modifier le contenu.

✅ Solution

  1. Console dans l’iframe du SCO : taper getScormAPI() (ou tester window.parent.API / API_1484_11 en remontant). S’il n’y a pas d’objet → API introuvable (cross-domain, pop-up, iframe) : on regarde le domaine du SCO vs celui du LMS et le mode d’ouverture.
  2. Si l’API est là, extension de debug SCORM ou journal SCORM Cloud : rejouer le module et regarder les appels — y a-t-il un Initialize ? un SetValue sur le statut ? un Terminate ? avec quels codes d’erreur ?
  3. Selon ce qu’on voit, on conclut : jamais initialisé, statut jamais posé, valeur rejetée, ou clôture absente.

Tout ça sans toucher au .zip : on observe le dialogue runtime, on ne le réécrit pas. Si le module est fautif, on renvoie le diagnostic à l’auteur (ou on recompile côté source).

🧠 Quiz de révision

1. Qu’est-ce qu’un « SCO muet » et quelles en sont les deux grandes causes ?

Un module qui s’affiche mais ne communique pas avec le LMS. Deux familles : (1) API jamais trouvée (cross-domain, iframe, pop-up), (2) API trouvée mais mal utilisée (jamais Initialize, statut oublié, Terminate manquant, valeurs rejetées).

2. Pourquoi les méthodes SCORM sont-elles « silencieuses » en cas d’échec ?

Elles ne lèvent pas d’exception : elles renvoient "false"/chaîne vide et posent un code d’erreur qu’il faut aller lire avec GetLastError. Un code qui ne vérifie jamais l’erreur croit « que ça marche » et livre des SCO muets.

3. Quelles trois méthodes utilise-t-on pour diagnostiquer une erreur ?

GetLastError() (le code ; "0" = pas d’erreur), GetErrorString(code) (le libellé standard), GetDiagnostic(code) (le détail propre au LMS). Préfixées LMS en SCORM 1.2.

4. Où faut-il ouvrir la console pour inspecter un SCO ?

Dans le contexte de l’iframe du SCO, pas dans la page LMS : sélectionner la bonne frame dans les outils de développement. Sinon window.parent, getScormAPI() et les logs du SCO ne correspondent pas à ce qu’on croit inspecter.

5. Pourquoi tester un paquet sur SCORM Cloud avant de le livrer ?

C’est un LMS de test de référence qui journalise chaque appel runtime et ses erreurs. Si complétion et score y remontent, on a éliminé l’essentiel des tickets « ça ne marche pas » ; un éventuel problème restant sera alors du côté configuration du LMS client, pas du contenu.


Partie suivante : Partie 5 — SCORM 2004 : séquencement — au-delà d’un SCO isolé, comment SCORM 2004 orchestre plusieurs activités : objectifs, règles de séquencement, rollup et navigation.

Last updated on