"""Tests E2E de conformité CSP : page de dons, anti-flash de thème et bouton PWA. Couvre les corrections COR-4/COR-7 de l'audit : scripts inline noncés, remplacement des `onclick` par des addEventListener, et de `style="display: none;"` par la classe utilitaire `.is-hidden`. """ import pytest from playwright.sync_api import expect def _aller_page_dons(page, base_url): """Charge dons.php ; saute le test si les dons sont désactivés sur l'instance.""" reponse = page.goto(base_url + "/dons.php", wait_until="domcontentloaded") if reponse is None or reponse.status != 200: pytest.skip(f"dons.php indisponible (HTTP {reponse.status if reponse else '?'})") def _collecter_erreurs_console(page): """Installe la collecte des erreurs console/JS ; retourne la liste à vérifier.""" erreurs = [] page.on("console", lambda msg: erreurs.append(msg.text) if msg.type == "error" else None) page.on("pageerror", lambda exc: erreurs.append(str(exc))) return erreurs def test_dons_scripts_inline_ont_un_nonce(page, base_url): """Tous les scripts inline de dons.php portent le nonce CSP (COR-7).""" _aller_page_dons(page, base_url) sans_nonce = page.locator("script:not([src]):not([nonce])").evaluate_all( "(els) => els.map(e => e.outerHTML.slice(0, 80))" ) assert sans_nonce == [], f"Scripts inline sans nonce : {sans_nonce}" onclick = page.locator("[onclick]").count() assert onclick == 0, f"{onclick} élément(s) avec un attribut onclick (bloqué par la CSP)" def test_dons_anti_flash_theme_sans_erreur(page, base_url): """Le script anti-flash noncé s'exécute : thème sombre appliqué dès le chargement.""" page.add_init_script("localStorage.setItem('theme', 'dark');") erreurs = _collecter_erreurs_console(page) _aller_page_dons(page, base_url) assert page.locator("html").get_attribute("data-theme") == "dark", ( "data-theme absent : le script anti-flash a été bloqué par la CSP" ) assert erreurs == [], f"Erreurs console sur dons.php : {erreurs}" def test_dons_onglets_stripe(page, base_url): """Les onglets Don ponctuel / Don mensuel fonctionnent via addEventListener.""" _aller_page_dons(page, base_url) onglets = page.locator(".donation-tabs .tab-btn") if onglets.count() == 0: pytest.skip("Stripe désactivé sur cette instance (STRIPE_ENABLED=false)") onglet_mensuel = page.locator('.tab-btn[data-tab="monthly"]') onglet_ponctuel = page.locator('.tab-btn[data-tab="onetime"]') # État initial : don ponctuel actif expect(page.locator("#onetime-tab")).to_have_class("tab-content active") expect(page.locator("#monthly-tab")).to_have_class("tab-content") # dispatch_event plutôt que click : les polices chargées depuis cdnjs décalent # la mise en page et rendent le clic aux coordonnées intermittent. Le but est # de vérifier le câblage addEventListener (bloqué avant la correction CSP). onglet_mensuel.dispatch_event("click") expect(page.locator("#monthly-tab")).to_have_class("tab-content active") expect(page.locator("#onetime-tab")).to_have_class("tab-content") expect(onglet_mensuel).to_have_class("tab-btn active") # Retour au don ponctuel onglet_ponctuel.dispatch_event("click") expect(page.locator("#onetime-tab")).to_have_class("tab-content active") expect(page.locator("#monthly-tab")).to_have_class("tab-content") def test_bouton_install_pwa_utilise_is_hidden(page, base_url): """Le bouton d'installation PWA est masqué par .is-hidden, sans style inline (COR-4).""" _aller_page_dons(page, base_url) bouton = page.locator("#install-pwa") assert bouton.count() == 1, "Bouton #install-pwa absent du header" assert bouton.get_attribute("style") is None, "Attribut style résiduel (bloqué par la CSP)" assert bouton.get_attribute("nonce") is None, "Attribut nonce résiduel sur un non-script" assert bouton.evaluate("(e) => e.classList.contains('is-hidden')") assert not bouton.is_visible(), "Le bouton PWA devrait être masqué sans beforeinstallprompt"