test_serve_app_js_fin_mise_a_jour.py
Rama main · réplica de lectura del repositorio original.
"""Une conclusion sans relance rend les contrôles et le libellé du bouton."""
import json
import re
import shutil
import subprocess
import unittest
from pathlib import Path
class TestsFinMiseAJour(unittest.TestCase):
@classmethod
def setUpClass(cls):
cls.node = shutil.which("node")
if cls.node is None:
raise unittest.SkipTest("node introuvable")
source = (Path(__file__).parent / "serve_app.js").read_text(encoding="utf-8")
motifs = [
r"^const I18N = \{.*?^\};",
r"^function t\([^\n]*$",
r"^const CONTROLES_GELES_PENDANT_MAJ = [^\n]*;$",
*[rf"^function {nom}\(.*?^\}}" for nom in (
"tf", "libellePhase", "montrerTravail", "montrerMaj", "gelerPendantMaj")],
r'^\$\("update"\)\.onclick = async \(\) => \{.*?^\};',
r'^async function etatDuTravail\(.*?^\}',
]
cls.code = "\n".join(re.search(motif, source, re.DOTALL | re.MULTILINE).group(0)
for motif in motifs)
def executer(self, langue, actif, retard=False, refus=False, retard_post=False,
reessai=False):
script = """
const elements = {};
const $ = id => elements[id] ||= {
disabled: false, dataset: {}, textContent: '', hidden: false,
classList: {add() {}, remove() {}}, removeAttribute() {}
};
let _lang = LANGUE, actualisationLocale = false;
let travailVisible = false, travailEnCours = true, miseAJourAttente = null;
let generationMaj = 0, repondreTravail, repondreMaj;
let rechargements = 0;
const intervallesAnnules = [];
const delais = [];
const rechargerEnArrierePlan = () => rechargements++;
const setInterval = () => 7;
const clearInterval = id => intervallesAnnules.push(id);
const setTimeout = rappel => delais.push(rappel);
const fetch = async url => url === '/api/travail'
? new Promise(resolve => repondreTravail = resolve)
: (RETARD_POST ? new Promise(resolve => repondreMaj = resolve)
: (REFUS ? {error: 'Conclusion occupée'} : {ok: true, version: '0.99.0'}));
const lireJSON = async reponse => reponse;
const alert = () => { if (!REFUS) throw Error('alerte inattendue'); };
""".replace("LANGUE", json.dumps(langue)) + self.code + """
(async () => {
montrerMaj({version: '0.99.0'});
const lecture = RETARD ? etatDuTravail() : null;
const demande = $('update').onclick();
let postProtege = null;
if (RETARD_POST) {
const travail = etatDuTravail();
if (repondreTravail) repondreTravail({travail: {
quoi: 'Ancien échec', cle: 'phase.update_noop', fait: 1, total: 1,
termine: '2026-10-02T00:00:00Z'}});
await travail;
postProtege = $('update').disabled && !!$('update').dataset.encours;
repondreMaj({ok: true, version: '0.99.0'});
}
await demande;
const geles = CONTROLES_GELES_PENDANT_MAJ.every(id => $(id).disabled);
let ancienneIgnoree = null;
if (RETARD) {
repondreTravail({travail: {quoi: 'Ancien échec', cle: 'phase.update_noop',
fait: 1, total: 1, termine: '2026-10-02T00:00:00Z'}});
await lecture;
ancienneIgnoree = $('update').disabled
&& CONTROLES_GELES_PENDANT_MAJ.every(id => $(id).disabled)
&& miseAJourAttente === 7 && intervallesAnnules.length === 0;
}
if (REFUS) {
console.log(JSON.stringify({bouton: {disabled: $('update').disabled,
texte: $('update').textContent, encours: !!$('update').dataset.encours}}));
return;
}
montrerTravail({quoi: "Échec de l'arrêt", cle: 'phase.update_noop',
fait: 1, total: 1, termine: '2026-10-02T00:00:00Z', actif: ACTIF});
if (REESSAI) {
await $('update').onclick();
const attenteReessai = miseAJourAttente;
const annulesAvant = intervallesAnnules.length;
delais[0]();
const ancienDelaiIgnore = $('update').disabled && !!$('update').dataset.encours
&& CONTROLES_GELES_PENDANT_MAJ.every(id => $(id).disabled)
&& miseAJourAttente === attenteReessai && intervallesAnnules.length === annulesAvant;
delais[1]();
const nouveauDelaiApplique = !$('update').disabled && !$('update').dataset.encours
&& CONTROLES_GELES_PENDANT_MAJ.every(id => !$(id).disabled)
&& miseAJourAttente === null && intervallesAnnules.length === annulesAvant + 1;
console.log(JSON.stringify({ancienDelaiIgnore, nouveauDelaiApplique}));
return;
}
console.log(JSON.stringify({geles, ancienneIgnoree, postProtege,
controles: CONTROLES_GELES_PENDANT_MAJ.map(id => $(id).disabled),
bouton: {disabled: $('update').disabled, texte: $('update').textContent,
encours: !!$('update').dataset.encours},
refresh: $('refresh').disabled, phase: $('phase').textContent,
attente: miseAJourAttente, intervallesAnnules, rechargements}));
})().catch(erreur => {console.error(erreur); process.exitCode = 1;});
""".replace("ACTIF", json.dumps(actif))
script = script.replace("RETARD_POST", json.dumps(retard_post))
script = script.replace("REESSAI", json.dumps(reessai))
script = script.replace("RETARD", json.dumps(retard)).replace("REFUS", json.dumps(refus))
resultat = subprocess.run([self.node, "-"], input=script, capture_output=True,
text=True, encoding="utf-8", timeout=15)
self.assertEqual(resultat.returncode, 0, resultat.stderr)
return json.loads(resultat.stdout)
def verifier(self, actif):
for langue, texte in (("fr", "Installer 0.99.0"), ("en", "Install 0.99.0")):
with self.subTest(langue=langue):
resultat = self.executer(langue, actif)
self.assertTrue(resultat["geles"])
self.assertEqual(resultat["controles"], [False] * 4)
self.assertEqual(resultat["bouton"], {
"disabled": False, "texte": texte, "encours": False})
self.assertEqual(resultat["refresh"], actif)
self.assertEqual(resultat["phase"], "Échec de l'arrêt")
self.assertIsNone(resultat["attente"])
self.assertEqual(resultat["intervallesAnnules"], [7])
self.assertEqual(resultat["rechargements"], int(not actif))
def test_echec_sans_autre_travail(self):
self.verifier(False)
def test_echec_pendant_un_telechargement(self):
self.verifier(True)
def test_reponse_en_vol_avant_le_clic_ne_debloque_pas_le_reessai(self):
resultat = self.executer("en", True, retard=True)
self.assertTrue(resultat["ancienneIgnoree"])
self.assertEqual(resultat["bouton"], {
"disabled": False, "texte": "Install 0.99.0", "encours": False})
self.assertEqual(resultat["intervallesAnnules"], [7])
def test_reessai_refuse_retablit_le_libelle_du_bouton(self):
resultat = self.executer("fr", True, refus=True)
self.assertEqual(resultat["bouton"], {
"disabled": False, "texte": "Installer 0.99.0", "encours": False})
def test_sonde_pendant_le_post_ne_debloque_pas_le_bouton(self):
resultat = self.executer("en", True, retard_post=True)
self.assertTrue(resultat["postProtege"])
self.assertTrue(resultat["geles"])
def test_ancien_delai_ne_debloque_pas_le_reessai(self):
resultat = self.executer("fr", True, reessai=True)
self.assertTrue(resultat["ancienDelaiIgnore"])
self.assertTrue(resultat["nouveauDelaiApplique"])
if __name__ == "__main__":
unittest.main()