"""Tests du widget Onglets — ajouter/renommer/supprimer un onglet, contenu par onglet (un vrai conteneur enfant, voir screens/elements/add_tab.py). À ne pas confondre avec tests/test_onglets.py, qui teste l'action de flux existante "activer_onglet" (2.3, montrer un élément et masquer ses frères) — un mécanisme manuel plus bas niveau, toujours disponible séparément, que ce widget ne remplace pas.""" import re 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 _create_onglets(client, slug, screen_id): resp = client.post(f"/game/{slug}/screens/{screen_id}/elements/add", data={"widget": "onglets"}, follow_redirects=False) return int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1)) def _tab_rename_ids(html): """Ids des formulaires de renommage DANS le bloc "Onglets" du panneau de propriétés — pas le premier /elements//rename de la page (le champ "Nommer" générique de l'élément actuellement sélectionné), ni une occurrence de "Onglets (" dans une liste déroulante d'éléments de la Logique de la scène (ex. ""), qui existe aussi sur la page — seul l'en-tête du groupe de réglages ("🗂️ Onglets (") repère sans ambiguïté le bloc de propriétés lui-même.""" block = html.split(">🗂️ Onglets (", 1)[-1] return [int(x) for x in re.findall(r"/elements/(\d+)/rename", block)] def test_new_tabs_widget_has_no_tabs_yet(client, game): screen_id = _create_screen(client, game) _create_onglets(client, game, screen_id) html = client.get(f"/game/{game}/play").data.decode() assert "forgeTabBtn" not in html assert "ajoute au moins un onglet" in html def test_add_tab_creates_a_named_child_container(client, game): screen_id = _create_screen(client, game) onglets_id = _create_onglets(client, game, screen_id) resp = client.post(f"/game/{game}/elements/{onglets_id}/tabs/add", follow_redirects=False) assert resp.status_code == 302 assert f"selected={onglets_id}" in resp.headers["Location"] # reste sur le widget Onglets, pas le nouvel onglet html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert ">Onglet 1<" in html # nom par défaut, affiché tel quel comme libellé de l'onglet def test_second_tab_is_named_onglet_2_and_starts_hidden(client, game): screen_id = _create_screen(client, game) onglets_id = _create_onglets(client, game, screen_id) client.post(f"/game/{game}/elements/{onglets_id}/tabs/add") client.post(f"/game/{game}/elements/{onglets_id}/tabs/add") html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert ">Onglet 1<" in html assert ">Onglet 2<" in html # Seul le premier onglet est visible par défaut (le second est en display:none). assert 'style="display:none;"' in html def test_renaming_a_tab_changes_the_visible_label(client, game): screen_id = _create_screen(client, game) onglets_id = _create_onglets(client, game, screen_id) client.post(f"/game/{game}/elements/{onglets_id}/tabs/add") html = client.get(f"/game/{game}/screens/{screen_id}/edit?selected={onglets_id}").data.decode() tab_id = _tab_rename_ids(html)[0] client.post(f"/game/{game}/elements/{tab_id}/rename", data={"name": "Statistiques"}) html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert ">Statistiques<" in html assert ">Onglet 1<" not in html def test_deleting_a_tab_removes_it_and_reselects_the_tabs_widget(client, game): screen_id = _create_screen(client, game) onglets_id = _create_onglets(client, game, screen_id) client.post(f"/game/{game}/elements/{onglets_id}/tabs/add") client.post(f"/game/{game}/elements/{onglets_id}/tabs/add", follow_redirects=False) html = client.get(f"/game/{game}/screens/{screen_id}/edit?selected={onglets_id}").data.decode() tab_ids = _tab_rename_ids(html) assert len(tab_ids) == 2 resp = client.post(f"/game/{game}/elements/{tab_ids[1]}/delete", follow_redirects=False) assert resp.status_code == 302 assert f"selected={onglets_id}" in resp.headers["Location"] html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert html.count("forgeShowTab(this)") == 1 def test_tab_content_holds_a_real_container_children_can_be_added_to(client, game): """Régression : chaque onglet doit se comporter comme un vrai conteneur — on doit pouvoir y poser un Répéteur/Conteneur/etc. via "Ajouter DANS ce conteneur", exactement comme pour n'importe quel autre conteneur.""" screen_id = _create_screen(client, game) onglets_id = _create_onglets(client, game, screen_id) client.post(f"/game/{game}/elements/{onglets_id}/tabs/add") html = client.get(f"/game/{game}/screens/{screen_id}/edit?selected={onglets_id}").data.decode() tab_id = _tab_rename_ids(html)[0] resp = client.post(f"/game/{game}/elements/{tab_id}/children/add", data={"widget": "titre"}, follow_redirects=False) assert resp.status_code == 302 child_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1)) client.post(f"/game/{game}/elements/{child_id}/save", data={"ctrl_content": "Bienvenue dans cet onglet"}) html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert "Bienvenue dans cet onglet" in html def test_deleting_the_tabs_widget_cascades_to_its_tabs(client, game): screen_id = _create_screen(client, game) onglets_id = _create_onglets(client, game, screen_id) client.post(f"/game/{game}/elements/{onglets_id}/tabs/add") client.post(f"/game/{game}/elements/{onglets_id}/delete") html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert "forgeTabBtn" not in html assert ">Onglet 1<" not in html