Ajoute la fonctionnalite quiz autonome/plein ecran a la boite a quiz
Introduit la double categorie de modeles (boite de dialogue / page de quiz plein ecran) avec plein ecran, minuteur, score integre et ecran de resultat pour les modeles page ; ajoute les modeles "Manga" (boite et page) et "Classique" (page), pilotables aussi par l'assistant IA Ruby. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
559331f9cf
commit
7db4803b93
@@ -182,6 +182,29 @@ def test_system_prompt_asks_ruby_to_clarify_interagir_vs_immediate_action():
|
||||
assert "APPUYER SUR UNE TOUCHE" in ai.chat._SYSTEM_PROMPT
|
||||
|
||||
|
||||
def test_system_prompt_documents_the_quiz_box_config_tool():
|
||||
"""Demande explicite : "Ruby dois pouvoir aussi piloter ces réglages"
|
||||
(plein écran/minuteur/modèle d'un quiz autonome) — pas seulement
|
||||
l'éditeur manuel."""
|
||||
prompt = ai.chat._SYSTEM_PROMPT
|
||||
assert "set_quiz_box_config" in prompt
|
||||
for keyword in ("fullscreen", "timer_mode", "dialog_template", "page_template"):
|
||||
assert keyword in prompt, keyword
|
||||
|
||||
|
||||
def test_system_prompt_asks_ruby_to_clarify_the_timer_before_enabling_it():
|
||||
assert "jamais imposé" in ai.chat._SYSTEM_PROMPT
|
||||
|
||||
|
||||
def test_system_prompt_documents_the_two_quiz_template_categories():
|
||||
"""Demande explicite : "Ruby doit également comprendre cette
|
||||
distinction" (modèles "boîte de dialogue" hors plein écran vs modèles
|
||||
"page de quiz" en plein écran, structures HTML totalement séparées)."""
|
||||
prompt = ai.chat._SYSTEM_PROMPT
|
||||
assert "page de quiz" in prompt
|
||||
assert "manga" in prompt
|
||||
|
||||
|
||||
def test_run_chat_turn_passes_the_scene_state_in_the_system_prompt(client, tmp_game_slug_cleanup, monkeypatch):
|
||||
slug, screen_id, conversation_id = _create_jeu2d_game_with_conversation(client, tmp_game_slug_cleanup)
|
||||
screens.add_scene_object(slug, screen_id, kind="personnage")
|
||||
|
||||
@@ -168,6 +168,34 @@ def test_dispatch_set_object_collision_updates_the_settings(client, tmp_game_slu
|
||||
assert settings["shape"] == "cercle"
|
||||
|
||||
|
||||
def test_dispatch_set_quiz_box_config_updates_the_settings(client, tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "Ruby dois pouvoir aussi piloter ces réglages"
|
||||
(plein écran/minuteur/modèle d'une "❓ Boîte à quiz", voir
|
||||
screens/rendering/quiz_box_config.py) — pas seulement l'éditeur
|
||||
manuel."""
|
||||
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
|
||||
object_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
dispatch_tool(slug, screen_id, 1, "set_quiz_box_config", {
|
||||
"object_id": object_id, "fullscreen": True, "timer_mode": "question",
|
||||
"timer_seconds": 15, "dialog_template": "defaut", "page_template": "manga",
|
||||
})
|
||||
obj = screens.get_scene_object(slug, object_id)
|
||||
config = screens.resolve_quiz_box_config(obj)
|
||||
assert config == {
|
||||
"fullscreen": True, "timer_mode": "question", "timer_seconds": 15,
|
||||
"dialog_template": "defaut", "page_template": "manga",
|
||||
}
|
||||
|
||||
|
||||
def test_dispatch_set_quiz_box_config_defaults_to_no_fullscreen_no_timer(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
|
||||
object_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
dispatch_tool(slug, screen_id, 1, "set_quiz_box_config", {"object_id": object_id})
|
||||
obj = screens.get_scene_object(slug, object_id)
|
||||
config = screens.resolve_quiz_box_config(obj)
|
||||
assert config == screens.DEFAULT_QUIZ_BOX_CONFIG
|
||||
|
||||
|
||||
def test_dispatch_create_global_variable_calls_db_directly(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
|
||||
result = dispatch_tool(slug, screen_id, 1, "create_global_variable", {"name": "score_ia", "var_type": "nombre_entier"})
|
||||
@@ -417,3 +445,18 @@ def test_set_object_role_enum_matches_the_source_constant():
|
||||
def test_set_object_collision_shape_enum_matches_the_source_constant():
|
||||
schema = _tool("set_object_collision")["input_schema"]["properties"]["shape"]
|
||||
assert schema["enum"] == list(screens.COLLISION_SHAPES)
|
||||
|
||||
|
||||
def test_set_quiz_box_config_timer_mode_enum_matches_the_source_constant():
|
||||
schema = _tool("set_quiz_box_config")["input_schema"]["properties"]["timer_mode"]
|
||||
assert schema["enum"] == list(screens.TIMER_MODES)
|
||||
|
||||
|
||||
def test_set_quiz_box_config_dialog_template_enum_matches_the_source_constant():
|
||||
schema = _tool("set_quiz_box_config")["input_schema"]["properties"]["dialog_template"]
|
||||
assert schema["enum"] == list(screens.QUIZ_BOX_DIALOG_TEMPLATES)
|
||||
|
||||
|
||||
def test_set_quiz_box_config_page_template_enum_matches_the_source_constant():
|
||||
schema = _tool("set_quiz_box_config")["input_schema"]["properties"]["page_template"]
|
||||
assert schema["enum"] == list(screens.QUIZ_BOX_PAGE_TEMPLATES)
|
||||
|
||||
@@ -0,0 +1,630 @@
|
||||
"""Réglages "❓ Boîte à quiz" propres au quiz AUTONOME/plein écran (demande
|
||||
explicite : "étendre les fonctionnalités de la boîte à quiz pour les RH
|
||||
qui veulent juste faire un quiz") — voir screens/rendering/
|
||||
quiz_box_config.py, screens/rendering/quiz_box_templates.py. DISTINCT de
|
||||
"🎨 Style" (dialogue_box_style.py, partagé avec dialogue_box/score_widget,
|
||||
voir test_dialogue_box.py) : ce fichier ne teste que plein écran/
|
||||
minuteur/modèle."""
|
||||
import db
|
||||
import screens
|
||||
|
||||
|
||||
def _create_jeu2d_game(tmp_game_slug_cleanup, name="pytest_quiz_config"):
|
||||
slug = tmp_game_slug_cleanup(db.create_game(name))
|
||||
screen_id = screens.create_screen(slug, "Scène 1", kind="jeu_2d")
|
||||
return slug, screen_id
|
||||
|
||||
|
||||
# ---------- Sanitizer/résolution ----------
|
||||
|
||||
def test_default_quiz_box_config_has_no_fullscreen_no_timer_and_the_defaut_template(tmp_game_slug_cleanup):
|
||||
""""Aucun" minuteur/plein écran désactivé par défaut — demande
|
||||
explicite : jamais imposé, seulement au choix du créateur."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
config = screens.resolve_quiz_box_config(obj)
|
||||
assert config == screens.DEFAULT_QUIZ_BOX_CONFIG
|
||||
assert config["fullscreen"] is False
|
||||
assert config["timer_mode"] == "aucun"
|
||||
assert config["dialog_template"] == "defaut"
|
||||
assert config["page_template"] == "classique"
|
||||
|
||||
|
||||
def test_sanitize_quiz_box_config_clamps_timer_seconds(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
config = screens.set_scene_object_quiz_config(slug, obj_id, {"timer_seconds": 999999})
|
||||
assert config["timer_seconds"] == 600
|
||||
config = screens.set_scene_object_quiz_config(slug, obj_id, {"timer_seconds": 0})
|
||||
assert config["timer_seconds"] == 3
|
||||
|
||||
|
||||
def test_sanitize_quiz_box_config_rejects_unknown_timer_mode_and_templates(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
config = screens.set_scene_object_quiz_config(slug, obj_id, {
|
||||
"timer_mode": "toutes-les-secondes", "dialog_template": "arc-en-ciel", "page_template": "aquarelle",
|
||||
})
|
||||
assert config["timer_mode"] == "aucun"
|
||||
assert config["dialog_template"] == "defaut"
|
||||
assert config["page_template"] == "classique"
|
||||
|
||||
|
||||
def test_sanitize_quiz_box_config_keeps_dialog_and_page_template_independent(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : les deux réglages sont conservés
|
||||
INDÉPENDAMMENT — un modèle "boîte" invalide ne doit jamais affecter le
|
||||
modèle "page" valide, et réciproquement ("verre" n'existe plus depuis
|
||||
la suppression des modèles "boîte" non-base, sert ici justement à
|
||||
vérifier qu'une valeur invalide retombe sur "defaut" SANS toucher au
|
||||
page_template valide à côté)."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
config = screens.set_scene_object_quiz_config(slug, obj_id, {"dialog_template": "verre", "page_template": "manga"})
|
||||
assert config["dialog_template"] == "defaut"
|
||||
assert config["page_template"] == "manga"
|
||||
|
||||
|
||||
def test_sanitize_quiz_box_config_ignores_a_non_dict_input(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
config = screens.set_scene_object_quiz_config(slug, obj_id, "pas-un-dict")
|
||||
assert config == screens.DEFAULT_QUIZ_BOX_CONFIG
|
||||
|
||||
|
||||
def test_set_scene_object_quiz_config_persists_and_sanitizes(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {
|
||||
"fullscreen": True, "timer_mode": "question", "timer_seconds": 15,
|
||||
"dialog_template": "defaut", "page_template": "manga",
|
||||
})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
config = screens.resolve_quiz_box_config(obj)
|
||||
assert config == {
|
||||
"fullscreen": True, "timer_mode": "question", "timer_seconds": 15,
|
||||
"dialog_template": "defaut", "page_template": "manga",
|
||||
}
|
||||
|
||||
|
||||
def test_set_scene_object_quiz_config_returns_none_for_unknown_object(tmp_game_slug_cleanup):
|
||||
slug, _ = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
assert screens.set_scene_object_quiz_config(slug, 999999, {}) is None
|
||||
|
||||
|
||||
# ---------- Route ----------
|
||||
|
||||
def test_quiz_config_route_persists_config_and_returns_rendered_html(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
resp = client.post(
|
||||
f"/game/{slug}/scene-objects/{obj_id}/quiz-config",
|
||||
data='{"config": {"fullscreen": false, "timer_mode": "quiz", "timer_seconds": 30, "dialog_template": "defaut"}}',
|
||||
content_type="application/json",
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = resp.get_json()
|
||||
assert body["config"]["fullscreen"] is False
|
||||
assert body["config"]["dialog_template"] == "defaut"
|
||||
assert 'class="quizBoxWidget"' in body["rendered_html"]
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
assert screens.resolve_quiz_box_config(obj)["dialog_template"] == "defaut"
|
||||
|
||||
|
||||
def test_quiz_config_route_page_template_only_applies_when_fullscreen(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
resp = client.post(
|
||||
f"/game/{slug}/scene-objects/{obj_id}/quiz-config",
|
||||
data='{"config": {"fullscreen": true, "page_template": "manga"}}',
|
||||
content_type="application/json",
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
body = resp.get_json()
|
||||
assert body["config"]["page_template"] == "manga"
|
||||
assert "quizBoxWidget--manga" in body["rendered_html"]
|
||||
|
||||
|
||||
def test_quiz_config_route_404s_for_missing_object(client, game):
|
||||
resp = client.post(f"/game/{game}/scene-objects/999999/quiz-config", data='{"config": {}}', content_type="application/json")
|
||||
assert resp.status_code == 404
|
||||
|
||||
|
||||
# ---------- Rendu — "contrat" de points d'ancrage (voir §3 du plan) :
|
||||
# chaque modèle doit exposer les mêmes data-quiz-role, quelle que soit sa
|
||||
# présentation, pour que le moteur JS partagé fonctionne quel que soit le
|
||||
# modèle choisi ----------
|
||||
|
||||
def test_render_quiz_box_defaut_is_unchanged_for_a_non_reconfigured_object(tmp_game_slug_cleanup):
|
||||
"""Non-régression explicite : un quiz existant (jamais reconfiguré)
|
||||
doit rester visuellement identique à avant cette extension."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert "quizBoxWidget--" not in html
|
||||
assert 'data-quiz-role="header"' in html
|
||||
assert 'data-quiz-role="question"' in html
|
||||
assert 'data-quiz-role="choices"' in html
|
||||
assert "Quête : Titre de la quête" in html
|
||||
assert html.count("quizBoxChoiceBtn") == 2
|
||||
assert "quizBoxFooter" not in html
|
||||
|
||||
|
||||
def test_render_quiz_box_header_and_topbar_share_the_same_header_row(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "le score doit être à l'intérieur de la boîte
|
||||
à quiz, pas à l'extérieur" — le score (topbar) et le titre (header)
|
||||
doivent être frères DANS le même bandeau ("quizBoxHeaderRow"), jamais
|
||||
deux bandes empilées de couleurs différentes."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
for template in screens.QUIZ_BOX_DIALOG_TEMPLATES:
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"dialog_template": template})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
row_start = html.index("quizBoxHeaderRow")
|
||||
row_end = html.index("quizBoxBody") # le bandeau se termine avant le corps
|
||||
assert row_start < html.index('data-quiz-role="header"', row_start) < row_end, template
|
||||
assert row_start < html.index('data-quiz-role="topbar"', row_start) < row_end, template
|
||||
|
||||
|
||||
def test_render_quiz_box_every_dialog_template_exposes_the_required_data_quiz_role_anchors(tmp_game_slug_cleanup):
|
||||
"""Contrat commun (voir §3 du plan) — vérifié ici pour les modèles
|
||||
"boîte de dialogue" (fullscreen=False, voir le test jumeau pour les
|
||||
modèles "page de quiz")."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
for template in screens.QUIZ_BOX_DIALOG_TEMPLATES:
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"dialog_template": template})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
for role in ("header", "question", "choices", "topbar", "score", "timer"):
|
||||
assert f'data-quiz-role="{role}"' in html, f"modèle {template!r} : ancrage {role!r} manquant"
|
||||
assert f'data-quiz-template="{template}"' in html
|
||||
assert 'class="quizBoxWidget' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_every_page_template_exposes_the_required_data_quiz_role_anchors(tmp_game_slug_cleanup):
|
||||
"""Même contrat, pour les modèles "page de quiz" (fullscreen=True) —
|
||||
"topbar" n'est PAS exigé ici (voir le contrat assoupli dans
|
||||
screens/rendering/quiz_box_templates.py : un modèle "page" n'a pas
|
||||
besoin de regrouper score/minuteur à masquer, il n'existe QUE plein
|
||||
écran)."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True})
|
||||
for template in screens.QUIZ_BOX_PAGE_TEMPLATES:
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": template})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
for role in ("header", "question", "choices", "score", "timer"):
|
||||
assert f'data-quiz-role="{role}"' in html, f"modèle {template!r} : ancrage {role!r} manquant"
|
||||
assert f'data-quiz-template="{template}"' in html
|
||||
assert 'class="quizBoxWidget' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_reflects_fullscreen_and_timer_data_attributes(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "timer_mode": "question", "timer_seconds": 42})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert 'data-fullscreen="1"' in html
|
||||
assert 'data-timer-mode="question"' in html
|
||||
assert 'data-timer-seconds="42"' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_manga_shows_aucun_under_temps_when_no_timer_is_configured(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "garde le temps mais si il n'y a pas de chrono
|
||||
marque en dessous de temps aucun" — la plaque "Temps" reste TOUJOURS
|
||||
visible (jamais masquée), avec "Aucun" en son absence."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "timer_mode": "aucun", "page_template": "manga"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<span class="mangaPlateValue quizBoxTimerDisplay" data-quiz-role="timer">Aucun</span>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_manga_leaves_the_timer_value_empty_when_a_timer_is_configured(tmp_game_slug_cleanup):
|
||||
"""Un vrai minuteur est rempli par le moteur JS (compte à rebours), pas
|
||||
par le rendu serveur — jamais "Aucun" affiché en même temps qu'un
|
||||
minuteur réellement configuré."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "timer_mode": "question", "timer_seconds": 20, "page_template": "manga"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<span class="mangaPlateValue quizBoxTimerDisplay" data-quiz-role="timer"></span>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_manga_has_a_progress_stepper_placeholder(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "il n'y a pas le steper" — le HTML doit exposer
|
||||
un conteneur `data-manga-role="progress"` que le JS (forgeQuizMangaOnShow,
|
||||
static/js/play/quiz-box-templates.js) remplit au jeu (nombre de questions
|
||||
inconnu à la création de l'objet)."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "manga"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<div class="mangaProgress" data-manga-role="progress"></div>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_manga_has_an_always_empty_result_zone_placeholder(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "il faut aussi adapter le modele manga pour
|
||||
avoir sa page de résultat" — voir forgeQuizMangaOnQuizComplete,
|
||||
static/js/play/quiz-box-templates.js, qui remplit/affiche ce
|
||||
conteneur EN JEU seulement (toujours vide côté rendu serveur)."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "manga"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<div class="mangaResultZone" data-manga-role="result"></div>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_manga_dialogue_applies_its_modifier_class_and_frame(tmp_game_slug_cleanup):
|
||||
""""Manga" (boîte de dialogue) — demande explicite : "il faut créer un
|
||||
modele de boite themé mangas qui va avec le mode de page mangas",
|
||||
validé via une maquette interactive. Reste une petite carte flottante
|
||||
(fullscreen=False), structure PARTAGÉE avec les autres modèles
|
||||
"boîte" (contrat déjà vérifié génériquement, voir
|
||||
test_render_quiz_box_every_dialog_template_exposes_the_required_data_quiz_role_anchors)."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"dialog_template": "manga_dialogue"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert 'class="quizBoxWidget quizBoxWidget--mangaDialogue"' in html
|
||||
assert '<div class="quizMangaDialogueFrame">' in html
|
||||
assert 'data-quiz-template="manga_dialogue"' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_manga_dialogue_has_an_always_visible_footer_band(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "le modele dois etre une copie conforme a la
|
||||
maquete au pixel pret" — un bandeau de pied de page sombre reste
|
||||
TOUJOURS visible (même principe que le modèle "page" "Classique"),
|
||||
même avant toute réponse (le bouton "Continuer →" du moteur partagé,
|
||||
lui, n'apparaît qu'après coup, posé par-dessus ce bandeau en CSS)."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"dialog_template": "manga_dialogue"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<div class="quizMangaDialogueFooterBand" aria-hidden="true"></div>' in html
|
||||
# "Titre de la quête" seul (jamais "Quête : Titre de la quête") — le
|
||||
# préfixe "Quête : " est posé en CSS (::before), pas dans le HTML,
|
||||
# sinon il apparaîtrait EN DOUBLE dans l'aperçu éditeur.
|
||||
assert '>Titre de la quête<' in html
|
||||
assert '>Quête : Titre de la quête<' not in html
|
||||
|
||||
|
||||
def test_render_quiz_box_manga_dialogue_shows_a_disabled_continue_button_placeholder(tmp_game_slug_cleanup):
|
||||
"""Demande explicite (répétée : "il est ou le bouton continuer !!!!!",
|
||||
puis "le bouton continuer est toujours visible mais activer seulement
|
||||
quand un choix est fait") — le vrai bouton, comme ce repère d'aperçu
|
||||
éditeur, est maintenant TOUJOURS présent dès la question (posé
|
||||
désactivé par forgeShowQuizBox, activé par forgeQuizBoxAnswer une
|
||||
fois répondu — voir static/js/play/dialogue-box-controller.js)."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"dialog_template": "manga_dialogue"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<button type="button" class="quizBoxContinueBtn" disabled>Continuer →</button>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_manga_dialogue_body_has_no_extra_wrapper_around_the_continue_button(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "le contener qui contien les choix a les bord
|
||||
du bas qui ne sont pas pointue" — le coin dentelé du bas est un
|
||||
simple `background` (dégradé) sur .quizBoxBody, PAS un clip-path ni
|
||||
un décor séparé à part (deux essais précédents, tous deux abandonnés :
|
||||
l'un rognait, l'autre emprisonnait dans un contexte d'empilement, le
|
||||
bouton "Continuer →" du moteur JS partagé — voir static/style.css).
|
||||
Le HTML reste donc le plus simple possible : le bouton reste un
|
||||
DESCENDANT DIRECT de .quizBoxChoices, sans wrapper intermédiaire."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"dialog_template": "manga_dialogue"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert "quizMangaDialogueBodyBg" not in html
|
||||
assert (
|
||||
'<div class="quizBoxChoices" data-quiz-role="choices">'
|
||||
'<button type="button" class="quizBoxChoiceBtn" disabled>Choix A</button>'
|
||||
'<button type="button" class="quizBoxChoiceBtn" disabled>Choix B</button>'
|
||||
'<button type="button" class="quizBoxContinueBtn" disabled>Continuer →</button>'
|
||||
'</div>'
|
||||
) in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_is_the_default_page_template(tmp_game_slug_cleanup):
|
||||
""""Classique" — modèle de PAGE DE QUIZ DE BASE, devient le réglage
|
||||
par défaut (au même titre que "defaut"/Classique côté "boîte de
|
||||
dialogue") : un créateur qui active juste "Plein écran" sans jamais
|
||||
toucher aux modèles obtient ce modèle sobre, jamais "manga"."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
config = screens.resolve_quiz_box_config(obj)
|
||||
assert config["page_template"] == "classique"
|
||||
html = screens.render_scene_object(obj)
|
||||
assert 'class="quizBoxWidget quizBoxWidget--classique"' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_uses_the_object_name_field_as_its_title(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "aujourd'hui un quiz n'a pas de titre mais il y
|
||||
a un champs nom qui peut servir de titre pour le modele" — pas de
|
||||
nouveau champ, le champ "Nom" (déjà existant, "ℹ️ Informations")
|
||||
devient le titre affiché dans le bandeau de ce modèle."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_name(slug, obj_id, "Formation Sécurité Incendie")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "classique"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<span class="classiqueHeaderTitle">Formation Sécurité Incendie</span>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_falls_back_to_a_generic_title_without_a_name(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "classique"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<span class="classiqueHeaderTitle">Quiz</span>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_shows_aucun_under_temps_when_no_timer_is_configured(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "classique", "timer_mode": "aucun"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<span class="classiqueChipValue quizBoxTimerDisplay" data-quiz-role="timer">Aucun</span>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_timer_and_score_chips_share_the_exact_same_pill_container(tmp_game_slug_cleanup):
|
||||
"""Demande explicite (répétée) : "le conteneur de temps + aucun
|
||||
doivent avoir la background mais pas le mot aucun" — UN SEUL fond
|
||||
(celui de ".classiqueChip", identique pour "Temps" et "Score", avec
|
||||
ou sans minuteur configuré) ; la pastille parasite que
|
||||
".quizBoxTimerDisplay" pose d'ordinaire autour de sa propre valeur
|
||||
(voir static/style.css) est neutralisée pour ce modèle — un bug de
|
||||
classe partagée qui collait un second fond/coin arrondi UNIQUEMENT
|
||||
autour du mot "Aucun", jamais autour de "Score : 0"."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "classique", "timer_mode": "aucun"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert 'class="classiqueChip classiqueChip--timer"' in html
|
||||
assert 'class="classiqueChip classiqueChip--score"' in html
|
||||
assert 'class="classiqueChipValue quizBoxTimerDisplay"' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_has_an_always_empty_result_zone_placeholder(tmp_game_slug_cleanup):
|
||||
"""Écran de résultat de fin de quiz (demande explicite : "elle affiche
|
||||
un conteneur qui reaffiche le contenu du quiz au lieu du resultat" —
|
||||
fonctionnalité désormais réellement implémentée, voir
|
||||
forgeQuizClassiqueOnQuizComplete/forgeQuizResultRestart, static/js/
|
||||
play/quiz-box-templates.js et dialogue-box-controller.js) : le HTML
|
||||
expose toujours ce conteneur vide, rempli/affiché EN JEU seulement."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "classique"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<div class="classiqueResultZone" data-classique-role="result"></div>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_has_an_always_visible_footer_band(tmp_game_slug_cleanup):
|
||||
"""Demande explicite : "il manque le footer foncé avec le bouton" —
|
||||
un bandeau de pied de page (couleur footer_bg du panneau "🎨 Style")
|
||||
reste TOUJOURS visible, même avant toute réponse (le bouton
|
||||
"Continuer →" du moteur partagé, lui, n'apparaît qu'après coup, posé
|
||||
par-dessus ce bandeau en CSS)."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "classique"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<div class="classiqueFooterBand" aria-hidden="true"></div>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_has_a_progress_stepper_placeholder(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "classique"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<div class="classiqueProgress" data-classique-role="progress"></div>' in html
|
||||
|
||||
|
||||
def test_render_quiz_box_classique_ships_an_always_empty_explain_placeholder(tmp_game_slug_cleanup):
|
||||
"""Encart d'explication réservé pour une fonctionnalité FUTURE non
|
||||
encore implémentée (demande explicite : "tu peut le mettre dans le
|
||||
modele mais ne fonctionnera pas pour le moment") — présent dans le
|
||||
HTML mais toujours vide aujourd'hui, aucune donnée ne l'alimente."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "classique"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert '<div class="classiqueExplain" data-quiz-role="explain"></div>' in html
|
||||
|
||||
|
||||
def test_quiz_box_style_panel_colors_still_apply_regardless_of_template(tmp_game_slug_cleanup):
|
||||
"""Le panneau "🎨 Style" (couleurs/police) continue de s'appliquer à
|
||||
TOUS les modèles — un modèle change la disposition/décoration, jamais
|
||||
une raison d'ignorer les couleurs déjà choisies par le créateur."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_dialogue_box_style(slug, obj_id, {"text_color": "#123456"})
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"dialog_template": "defaut"})
|
||||
obj = screens.get_scene_object(slug, obj_id)
|
||||
html = screens.render_scene_object(obj)
|
||||
assert "#123456" in html
|
||||
|
||||
|
||||
# ---------- Payload ----------
|
||||
|
||||
def test_full_game_payload_exposes_quiz_config_for_quiz_box(tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True, "page_template": "manga"})
|
||||
payload = screens.full_game_payload(slug)
|
||||
scene = next(s for s in payload["screens"] if s["id"] == screen_id)
|
||||
box = next(o for o in scene["elements"] if o["kind"] == "quiz_box")
|
||||
assert box["quiz_config"]["fullscreen"] is True
|
||||
assert box["quiz_config"]["page_template"] == "manga"
|
||||
|
||||
|
||||
def test_full_game_payload_exposes_any_quiz_box_fullscreen_per_screen(tmp_game_slug_cleanup):
|
||||
"""Bug signalé : "il faut régler la caméra sur plein écran et
|
||||
désactiver ce réglage quand la boîte à quiz est plein écran" — ce
|
||||
drapeau permet à templates/play.html de rendre l'ÉCRAN lui-même en
|
||||
plein viewport (voir .playScene--quizFullscreen), pas seulement le
|
||||
widget."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
payload = screens.full_game_payload(slug)
|
||||
scene = next(s for s in payload["screens"] if s["id"] == screen_id)
|
||||
assert scene["any_quiz_box_fullscreen"] is False
|
||||
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True})
|
||||
payload = screens.full_game_payload(slug)
|
||||
scene = next(s for s in payload["screens"] if s["id"] == screen_id)
|
||||
assert scene["any_quiz_box_fullscreen"] is True
|
||||
|
||||
|
||||
def test_play_route_renders_the_screen_itself_fullscreen_when_a_quiz_box_is_fullscreen(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True})
|
||||
html = client.get(f"/game/{slug}/play").get_data(as_text=True)
|
||||
assert "playScene--quizFullscreen" in html
|
||||
# Le réglage "Caméra (px)" (scene_width/scene_height) ne doit plus du
|
||||
# tout dimensionner l'écran dans ce cas.
|
||||
assert "width:960px" not in html
|
||||
|
||||
|
||||
def test_play_route_keeps_the_camera_size_without_a_fullscreen_quiz_box(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
screens.add_scene_object(slug, screen_id, kind="quiz_box") # pas plein écran
|
||||
html = client.get(f"/game/{slug}/play").get_data(as_text=True)
|
||||
# La règle CSS elle-même (toujours présente dans <style>) ne compte
|
||||
# pas — seule la classe posée sur LA DIV de cet écran importe ici.
|
||||
assert f'data-screen-id="{screen_id}"' in html
|
||||
assert 'class="playScreen playScene "' in html
|
||||
assert "width:960px; height:540px;" in html
|
||||
|
||||
|
||||
# ---------- Éditeur — panneau propriétés (rendu, pas de logique JS ici) ----------
|
||||
|
||||
def test_scene_edit_view_renders_dialog_template_gallery_when_not_fullscreen(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
html = client.get(f"/game/{slug}/screens/{screen_id}/edit?selected={obj_id}").get_data(as_text=True)
|
||||
assert 'id="quiz-fullscreen"' in html
|
||||
assert 'id="quiz-timer-mode"' in html
|
||||
assert 'id="quiz-timer-seconds"' in html
|
||||
assert 'id="quizDialogTemplateGrid"' in html
|
||||
assert 'id="quizPageTemplateGrid"' not in html
|
||||
for template in screens.QUIZ_BOX_DIALOG_TEMPLATES:
|
||||
assert f"selectQuizBoxDialogTemplate({obj_id}, '{template}')" in html
|
||||
|
||||
|
||||
def test_scene_edit_view_renders_page_template_gallery_when_fullscreen(client, tmp_game_slug_cleanup):
|
||||
"""Demande explicite : les modèles "page de quiz" ne se voient QUE
|
||||
quand la boîte à quiz est plein écran — jamais les deux galeries en
|
||||
même temps."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True})
|
||||
html = client.get(f"/game/{slug}/screens/{screen_id}/edit?selected={obj_id}").get_data(as_text=True)
|
||||
assert 'id="quizPageTemplateGrid"' in html
|
||||
assert 'id="quizDialogTemplateGrid"' not in html
|
||||
for template in screens.QUIZ_BOX_PAGE_TEMPLATES:
|
||||
assert f"selectQuizBoxPageTemplate({obj_id}, '{template}')" in html
|
||||
|
||||
|
||||
def test_scene_edit_view_does_not_render_quiz_box_options_for_a_dialogue_box(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="dialogue_box")
|
||||
html = client.get(f"/game/{slug}/screens/{screen_id}/edit?selected={obj_id}").get_data(as_text=True)
|
||||
assert 'id="quiz-fullscreen"' not in html
|
||||
assert 'id="quizDialogTemplateGrid"' not in html
|
||||
assert 'id="quizPageTemplateGrid"' not in html
|
||||
|
||||
|
||||
# ---------- "Plein écran" dans L'ÉDITEUR (demande explicite : "elle doit
|
||||
# occuper tout l'écran", "être visible par défaut", "la notion de caméra
|
||||
# disparaît" — pas seulement au jeu) ----------
|
||||
|
||||
def test_scene_edit_view_marks_a_fullscreen_quiz_box_for_the_editor_preview(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True})
|
||||
html = client.get(f"/game/{slug}/screens/{screen_id}/edit").get_data(as_text=True)
|
||||
assert f'id="el-{obj_id}"' in html
|
||||
# La classe qui fait occuper tout le canevas (voir static/style.css
|
||||
# .canvasElement.isQuizFullscreenPreview) doit être posée sur le
|
||||
# conteneur de CET objet précis.
|
||||
import re
|
||||
m = re.search(r'<div class="canvasElement[^"]*"\s+id="el-' + str(obj_id) + '"', html)
|
||||
assert m and "isQuizFullscreenPreview" in m.group(0)
|
||||
|
||||
|
||||
def test_scene_edit_view_canvas_itself_fills_the_frame_when_a_quiz_box_is_fullscreen(client, tmp_game_slug_cleanup):
|
||||
"""Bug signalé : "il ne devait plus y avoir de notion de caméra" —
|
||||
masquer seulement le cadre "🎥 Champ de la caméra" ne suffisait pas,
|
||||
le CANEVAS restait lui-même dimensionné en pixels fixes
|
||||
(world_width/world_height, une notion de caméra/scène nominale),
|
||||
laissant une bande vide visible autour de la boîte plein écran."""
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
obj_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, obj_id, {"fullscreen": True})
|
||||
html = client.get(f"/game/{slug}/screens/{screen_id}/edit").get_data(as_text=True)
|
||||
import re
|
||||
m = re.search(r'<div class="canvas" id="canvas" style="([^"]*)"', html)
|
||||
assert m
|
||||
style = m.group(1)
|
||||
assert "width:100%; height:100%;" in style
|
||||
assert "px" not in style.split(";")[0] and "px" not in style.split(";")[1]
|
||||
|
||||
|
||||
def test_scene_edit_view_canvas_keeps_pixel_size_without_a_fullscreen_quiz_box(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
screens.add_scene_object(slug, screen_id, kind="quiz_box") # pas plein écran
|
||||
html = client.get(f"/game/{slug}/screens/{screen_id}/edit").get_data(as_text=True)
|
||||
import re
|
||||
m = re.search(r'<div class="canvas" id="canvas" style="([^"]*)"', html)
|
||||
assert m
|
||||
assert "width:960px; height:540px;" in m.group(1)
|
||||
|
||||
|
||||
def test_scene_edit_view_hides_the_camera_frame_when_a_quiz_box_is_fullscreen(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
# Un fond plus grand que la scène nominale, pour que "🎥 Champ de la
|
||||
# caméra" soit normalement affiché (voir resolve_scene_world_size).
|
||||
fond_id = screens.add_scene_object(slug, screen_id, kind="fond")
|
||||
screens.update_scene_object_geometry(slug, fond_id, x=0, y=0, width=2000, height=2000)
|
||||
html_before = client.get(f"/game/{slug}/screens/{screen_id}/edit").get_data(as_text=True)
|
||||
assert "sceneCameraFrame" in html_before
|
||||
|
||||
quiz_id = screens.add_scene_object(slug, screen_id, kind="quiz_box")
|
||||
screens.set_scene_object_quiz_config(slug, quiz_id, {"fullscreen": True})
|
||||
html_after = client.get(f"/game/{slug}/screens/{screen_id}/edit").get_data(as_text=True)
|
||||
assert "sceneCameraFrame" not in html_after
|
||||
|
||||
|
||||
def test_scene_edit_view_keeps_the_camera_frame_when_no_quiz_box_is_fullscreen(client, tmp_game_slug_cleanup):
|
||||
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
|
||||
fond_id = screens.add_scene_object(slug, screen_id, kind="fond")
|
||||
screens.update_scene_object_geometry(slug, fond_id, x=0, y=0, width=2000, height=2000)
|
||||
screens.add_scene_object(slug, screen_id, kind="quiz_box") # pas plein écran
|
||||
html = client.get(f"/game/{slug}/screens/{screen_id}/edit").get_data(as_text=True)
|
||||
assert "sceneCameraFrame" in html
|
||||
Reference in New Issue
Block a user