"""Tests de la "condition de visibilité" (panneau de propriétés, tout widget) : masque un élément en mode jouable selon une variable globale ou le champ d'un objet de données. Jamais appliquée dans l'éditeur.""" import re import db import screens def _create_screen(client, slug, name="Accueil"): resp = client.post(f"/game/{slug}/screens/new", data={"name": name}, follow_redirects=False) return int(re.search(r"/screens/(\d+)/edit", resp.headers["Location"]).group(1)) def _add_element(client, slug, screen_id, widget="texte"): resp = client.post(f"/game/{slug}/screens/{screen_id}/elements/add", data={"widget": widget}, follow_redirects=False) return int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1)) def _rendered_html(client, slug): payload = client.get(f"/game/{slug}/runtime-payload").get_json() return payload["screens"][0]["elements"][0]["rendered_html"] def test_variable_mode_hides_and_shows_element(client, game): screen_id = _create_screen(client, game) element_id = _add_element(client, game, screen_id) client.post(f"/game/{game}/variables/new", data={"name": "porte_ouverte", "type": "booleen", "default_value": "0"}) client.post(f"/game/{game}/elements/{element_id}/save", data={ "ctrl_visibilite_cond_mode": "variable", "ctrl_visibilite_cond_variable": "porte_ouverte", "ctrl_visibilite_cond_operateur": "egal", "ctrl_visibilite_cond_valeur": "1", }) html = _rendered_html(client, game) assert "display:none" in html db.update_global_variable_value(game, "porte_ouverte", "1") html = _rendered_html(client, game) assert "display:none" not in html def test_visibility_condition_never_hides_in_the_editor(client, game): screen_id = _create_screen(client, game) element_id = _add_element(client, game, screen_id) client.post(f"/game/{game}/variables/new", data={"name": "porte_ouverte", "type": "booleen", "default_value": "0"}) client.post(f"/game/{game}/elements/{element_id}/save", data={ "ctrl_visibilite_cond_mode": "variable", "ctrl_visibilite_cond_variable": "porte_ouverte", "ctrl_visibilite_cond_operateur": "egal", "ctrl_visibilite_cond_valeur": "1", }) html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert 'class="visibilityGated"' not in html assert f'id="el-{element_id}"' in html def test_objet_mode_uses_most_recent_row_outside_a_repeater(client, game): screen_id = _create_screen(client, game) element_id = _add_element(client, game, screen_id) resp = client.post(f"/game/{game}/objects/new", data={ "object_name": "Partie", "field_name[]": ["score"], "field_type[]": ["nombre_entier"], "field_relation[]": [""], "field_required[]": ["0"], "field_min[]": [""], "field_max[]": [""], }, follow_redirects=False) def_id = int(resp.headers["Location"].rstrip("/").split("/")[-1]) client.post(f"/game/{game}/objects/{def_id}/data/new", data={"score": "3"}) client.post(f"/game/{game}/elements/{element_id}/save", data={ "ctrl_visibilite_cond_mode": "objet", "ctrl_visibilite_cond_definition_id": str(def_id), "ctrl_visibilite_cond_champ": "score", "ctrl_visibilite_cond_operateur": "superieur", "ctrl_visibilite_cond_valeur": "10", }) assert "display:none" in _rendered_html(client, game) client.post(f"/game/{game}/objects/{def_id}/data/1/edit", data={"score": "20"}) assert "display:none" not in _rendered_html(client, game) def test_visible_special_render_widget_carries_refresh_marker(client, game): """Régression : une icône (special_render, pas le chemin générique) actuellement VISIBLE sous condition doit quand même porter le marqueur "visibilityGated" — sinon refreshRuntimeData() (play.html) ne sait jamais qu'il faut la réévaluer après une action "Modifier une donnée/variable", et elle reste figée dans son état précédent. Cas concret remonté : une icône "enveloppe fermée" (visible tant que is_opened=0) reste affichée EN MÊME TEMPS que l'icône "enveloppe ouverte" (qui, elle, se met à jour normalement puisqu'un élément CACHÉ porte toujours son marqueur via son placeholder).""" screen_id = _create_screen(client, game) element_id = _add_element(client, game, screen_id, "icone") client.post(f"/game/{game}/variables/new", data={"name": "porte_ouverte", "type": "booleen", "default_value": "0"}) client.post(f"/game/{game}/elements/{element_id}/save", data={ "ctrl_visibilite_cond_mode": "variable", "ctrl_visibilite_cond_variable": "porte_ouverte", "ctrl_visibilite_cond_operateur": "egal", "ctrl_visibilite_cond_valeur": "0", }) html = _rendered_html(client, game) assert "display:none" not in html assert "visibilityGated" in html def test_objet_mode_inside_a_repeater_evaluates_each_row_independently(client, game): """Régression : une "Mail card" (élément de jeu réutilisable posé dans un Répéteur d'e-mails) doit afficher l'icône "ouvert"/"fermé" qui correspond à SA PROPRE ligne — pas toujours la même ligne (la plus récente de l'objet) pour tous les exemplaires du Répéteur.""" resp = client.post(f"/game/{game}/objects/new", data={ "object_name": "Email", "field_name[]": ["sujet", "is_opened"], "field_type[]": ["texte", "booleen"], "field_relation[]": ["", ""], "field_required[]": ["0", "0"], "field_min[]": ["", ""], "field_max[]": ["", ""], }, follow_redirects=False) def_id = int(resp.headers["Location"].rstrip("/").split("/")[-1]) client.post(f"/game/{game}/objects/{def_id}/data/new", data={"sujet": "Fermé"}) client.post(f"/game/{game}/objects/{def_id}/data/new", data={"sujet": "Ouvert", "is_opened": "on"}) resp = client.post(f"/game/{game}/element-types", data={"name": "Mail card", "icon": "📧"}, follow_redirects=False) et = next(t for t in screens.list_element_types(game) if t["name"] == "Mail card") template_screen_id = et["template_screen_id"] closed_icon_id = _add_element(client, game, template_screen_id, "icone") client.post(f"/game/{game}/elements/{closed_icon_id}/save", data={ "ctrl_visibilite_cond_mode": "objet", "ctrl_visibilite_cond_definition_id": str(def_id), "ctrl_visibilite_cond_champ": "is_opened", "ctrl_visibilite_cond_operateur": "egal", "ctrl_visibilite_cond_valeur": "0", }) open_icon_id = _add_element(client, game, template_screen_id, "icone") client.post(f"/game/{game}/elements/{open_icon_id}/save", data={ "ctrl_visibilite_cond_mode": "objet", "ctrl_visibilite_cond_definition_id": str(def_id), "ctrl_visibilite_cond_champ": "is_opened", "ctrl_visibilite_cond_operateur": "egal", "ctrl_visibilite_cond_valeur": "1", }) screen_id = _create_screen(client, game, "Boîte de réception") repeater_id = _add_element(client, game, screen_id, "repeteur_donnees") client.post(f"/game/{game}/elements/{repeater_id}/save", data={ "ctrl_definition_id": str(def_id), "ctrl_template_type_id": str(et["id"]), }) html = _rendered_html(client, game) items = html.split('class="repeaterItem"') assert len(items) == 3 row_closed, row_open = items[1], items[2] assert row_closed.count('class="visibilityGated"') == 1 assert row_open.count('class="visibilityGated"') == 1 assert row_closed != row_open