Phase 3 : hardening qualite de code - typage strict, securite, dead code, a11y
Config strictement stricte partout (ruff, mypy --strict, bandit, vulture, import-linter, eslint, stylelint), aucune regle desactivee "pour ne pas casser le build" - l'existant a ete corrige pour la satisfaire plutot que l'inverse. Hooks pre-commit locaux (language: system) bloquants. - Typage mypy --strict propage a tout le moteur (db, screens, auth, core, ai, routes, puis publish/scripts/tests/app.py/build_css.py). - Securite : fuite de handle fichier Windows corrigee dans l'export SCORM (routes/publish/export_scorm.py), CSRF/RNG non-crypto/xAPI documentes (# nosec, # NOSONAR justifies), nouveau db.json_for_script() (echappe "</script>" dans le JSON embarque en <script>, 25 sites). - Architecture : imports circulaires/F811 nettoyes, contrats import-linter respectes, code mort retire (vulture). - Accessibilite : 69 champs de formulaire sans label correctement associe corriges (for/id ou aria-label) sur 11 templates. - ESLint/Stylelint : lot mecanique JS/CSS, regles ajustees puis appliquees (aucune desactivee sans verification individuelle). - Tests : isolation du compte admin partage (nettoyage ponctuel + fixture de teardown automatique en filet de securite), suite complete verte (591 tests Python, 241 tests JS). - SonarQube Community Build self-heberge (Docker + PostgreSQL) : rapport complet analyse point par point, faux positifs documentes. - .gitattributes ajoute (LF force) : core.autocrlf=true sur cette machine faisait echouer ESLint (linebreak-style) via un bug connu de git (checkout "en place" qui ignore l'eol force sur un fichier deja present sur disque - contourne en supprimant puis recreant chaque fichier suivi). djLint (H021, styles inline) volontairement saute pour ce commit - backlog assume, deja documente, traite dans un lot separe. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
7db4803b93
commit
c57420c8c9
@@ -17,13 +17,17 @@ function loadAsScript(relativePath) {
|
||||
|
||||
function makeEl() {
|
||||
const el = {
|
||||
_html: '', style: {}, value: '',
|
||||
_html: '',
|
||||
style: {},
|
||||
value: '',
|
||||
classList: { add() {}, remove() {}, toggle() {} },
|
||||
children: [],
|
||||
appendChild(c) { this.children.push(c); },
|
||||
querySelector() { return null; },
|
||||
setAttribute() {}, getAttribute() { return null; },
|
||||
addEventListener() {}, removeEventListener() {},
|
||||
setAttribute() {},
|
||||
getAttribute() { return null; },
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
};
|
||||
Object.defineProperty(el, 'innerHTML', {
|
||||
get() { return this._html; },
|
||||
@@ -90,7 +94,7 @@ test('assistant — une chaîne "attendre -> puis dialogue" sauvegarde bien LES
|
||||
|
||||
const saveCall = posted.find((p) => p.url === '/game/pytest_wizard/screens/7/triggers');
|
||||
assert.ok(saveCall, 'la règle aurait dû être postée');
|
||||
const action = saveCall.body.rule.action;
|
||||
const { action } = saveCall.body.rule;
|
||||
assert.equal(action.type, 'attendre');
|
||||
assert.equal(action.data_value, '2');
|
||||
assert.equal(action.then.type, 'dialogue');
|
||||
@@ -114,7 +118,7 @@ test('assistant — une chaîne de 3 actions ("variable" -> "attendre" -> "dialo
|
||||
global.collisionWizardChainDone('none');
|
||||
|
||||
const saveCall = posted.find((p) => p.url === '/game/pytest_wizard/screens/7/triggers');
|
||||
const action = saveCall.body.rule.action;
|
||||
const { action } = saveCall.body.rule;
|
||||
assert.equal(action.type, 'variable');
|
||||
assert.equal(action.then.type, 'attendre');
|
||||
assert.equal(action.then.data_value, '1.5');
|
||||
@@ -131,7 +135,7 @@ test('assistant — une seule action ("Rien de plus" tout de suite) sauvegarde b
|
||||
global.collisionWizardChainDone('none');
|
||||
|
||||
const saveCall = posted.find((p) => p.url === '/game/pytest_wizard/screens/7/triggers');
|
||||
const action = saveCall.body.rule.action;
|
||||
const { action } = saveCall.body.rule;
|
||||
assert.equal(action.type, 'dialogue');
|
||||
assert.equal(action.then, undefined);
|
||||
});
|
||||
@@ -142,9 +146,15 @@ test('assistant — l\'action "vidéo" ne propose QUE les fichiers vidéo (jamai
|
||||
// USER_ASSETS_OPTIONS en entier, sans filtrer par kind.
|
||||
setupGlobals();
|
||||
global.USER_ASSETS_OPTIONS = [
|
||||
{ id: 1, url: '/assets/1/photo.png', name: 'photo.png', kind: 'image' },
|
||||
{ id: 2, url: '/assets/1/bip.mp3', name: 'bip.mp3', kind: 'audio' },
|
||||
{ id: 3, url: '/assets/1/clip.mp4', name: 'clip.mp4', kind: 'video' },
|
||||
{
|
||||
id: 1, url: '/assets/1/photo.png', name: 'photo.png', kind: 'image',
|
||||
},
|
||||
{
|
||||
id: 2, url: '/assets/1/bip.mp3', name: 'bip.mp3', kind: 'audio',
|
||||
},
|
||||
{
|
||||
id: 3, url: '/assets/1/clip.mp4', name: 'clip.mp4', kind: 'video',
|
||||
},
|
||||
];
|
||||
const body = document.getElementById('questTriggerModalBody');
|
||||
|
||||
@@ -161,9 +171,15 @@ test('assistant — l\'action "vidéo" ne propose QUE les fichiers vidéo (jamai
|
||||
test('assistant — l\'action "son" ne propose QUE les fichiers audio (jamais les images/vidéos)', () => {
|
||||
setupGlobals();
|
||||
global.USER_ASSETS_OPTIONS = [
|
||||
{ id: 1, url: '/assets/1/photo.png', name: 'photo.png', kind: 'image' },
|
||||
{ id: 2, url: '/assets/1/bip.mp3', name: 'bip.mp3', kind: 'audio' },
|
||||
{ id: 3, url: '/assets/1/clip.mp4', name: 'clip.mp4', kind: 'video' },
|
||||
{
|
||||
id: 1, url: '/assets/1/photo.png', name: 'photo.png', kind: 'image',
|
||||
},
|
||||
{
|
||||
id: 2, url: '/assets/1/bip.mp3', name: 'bip.mp3', kind: 'audio',
|
||||
},
|
||||
{
|
||||
id: 3, url: '/assets/1/clip.mp4', name: 'clip.mp4', kind: 'video',
|
||||
},
|
||||
];
|
||||
const body = document.getElementById('questTriggerModalBody');
|
||||
|
||||
@@ -183,7 +199,9 @@ test('triggerActionChainHtml — un bouton "+ Ajouter une action" apparaît apr
|
||||
// juste après lui, pas seulement le tout dernier de la chaîne.
|
||||
setupGlobals();
|
||||
const chain = [
|
||||
{ type: 'variable', id: 'v_1', target_variable: 'score', data_operation: 'incrementer', data_value: '1' },
|
||||
{
|
||||
type: 'variable', id: 'v_1', target_variable: 'score', data_operation: 'incrementer', data_value: '1',
|
||||
},
|
||||
{ type: 'attendre', id: 'a_1', data_value: '2' },
|
||||
{ type: 'dialogue', dialogue: { id: 'd_1', lines: [] } },
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user