"""Catalogue de thèmes visuels (document_engine/themes/) — conversion du contenu-seed en éléments "à plat" pour l'aperçu (seed_blocks_to_elements), et respect par CHAQUE thème du catalogue des règles structurelles du moteur (ex. un mini-jeu seul sur sa page, voir routes/document/ document_element_add.py) — vérifié ici statiquement sur les données du catalogue, sans passer par une route HTTP.""" from typing import Any import document_engine as doc_engine def test_get_document_theme_entry_returns_none_for_an_unknown_id() -> None: assert doc_engine.get_document_theme_entry("n-importe-quoi") is None def test_get_document_theme_entry_returns_the_matching_entry() -> None: entry = doc_engine.get_document_theme_entry("securite-incendie") assert entry is not None assert entry["id"] == "securite-incendie" assert entry["name"] == "Sécurité Incendie" assert len(entry["seed_pages"]) > 0 def test_securite_incendie_title_page_is_vertically_centered() -> None: """Retour utilisateur du 24/09/2026 : la page de garde doit être centrée verticalement, comme une page de titre — les pages suivantes restent alignées en haut (comportement par défaut).""" entry = doc_engine.get_document_theme_entry("securite-incendie") assert entry is not None assert entry["seed_pages"][0]["vertical_align"] == "center" assert entry["seed_pages"][1].get("vertical_align", "top") == "top" def test_seed_blocks_to_elements_assigns_unique_synthetic_ids() -> None: blocks: list[dict[str, Any]] = [ {"kind": "titre", "attributes": {"content": "Titre"}}, { "kind": "row", "attributes": {"gap": 10}, "children": [ {"kind": "paragraphe", "attributes": {"content": "A"}}, {"kind": "paragraphe", "attributes": {"content": "B"}}, ], }, ] elements = doc_engine.seed_blocks_to_elements(blocks) ids = [el["id"] for el in elements] assert len(ids) == len(set(ids)) assert len(elements) == 4 row = next(el for el in elements if el["kind"] == "row") children = [el for el in elements if el["parent_id"] == row["id"]] assert len(children) == 2 def test_seed_blocks_to_elements_merges_onto_default_attributes() -> None: elements = doc_engine.seed_blocks_to_elements([{"kind": "titre", "attributes": {"content": "Contenu seul"}}]) assert elements[0]["attributes"]["content"] == "Contenu seul" assert elements[0]["attributes"]["style"] == "titre1" def test_seed_blocks_to_elements_renders_without_error() -> None: theme = doc_engine.get_document_theme_entry("securite-incendie") assert theme is not None elements = doc_engine.seed_blocks_to_elements(theme["seed_pages"][0]["blocks"]) html = doc_engine.render_document(elements) assert "docText" in html def test_every_theme_seed_page_has_a_valid_vertical_align() -> None: for theme in doc_engine.DOCUMENT_THEMES: for seed_page in theme["seed_pages"]: align = seed_page.get("vertical_align", "top") assert align in doc_engine.VERTICAL_ALIGNS, f"thème {theme['id']!r} : vertical_align invalide {align!r}" def test_every_theme_seed_page_respects_the_minigame_exclusivity_rule() -> None: """Un mini-jeu doit toujours être SEUL sur sa page (règle appliquée côté route pour un ajout manuel, voir routes/document/ document_element_add.py) — le contenu-seed d'un thème la respecte dès sa conception puisque document_engine.replace_document_content ne la revérifie pas elle-même (contenu fourni par le thème, pas par l'utilisateur).""" for theme in doc_engine.DOCUMENT_THEMES: for seed_page in theme["seed_pages"]: page_blocks = seed_page["blocks"] minigame_blocks = [b for b in page_blocks if b["kind"] in doc_engine.MINIGAME_KINDS] if minigame_blocks: assert len(page_blocks) == 1, ( f"thème {theme['id']!r} : une page avec mini-jeu doit contenir uniquement ce mini-jeu" )