"""Ombre portée (panneau de propriétés, tout widget) : 6 sous-réglages (décalage X/Y, flou, étendue, couleur, opacité) combinés en une seule valeur CSS box-shadow.""" import re import screens def _create_element(client, slug, widget="conteneur"): resp = client.post(f"/game/{slug}/screens/new", data={"name": "Accueil"}, follow_redirects=False) screen_id = int(re.search(r"/screens/(\d+)/edit", resp.headers["Location"]).group(1)) resp = client.post(f"/game/{slug}/screens/{screen_id}/elements/add", data={"widget": widget}, follow_redirects=False) element_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1)) return screen_id, element_id def test_freshly_created_element_has_no_shadow_baked_in(client, game): """Régression : le réglage par défaut du contrôle "shadow" est un dict composite (voir c_shadow.py), pas une valeur CSS directement écrivable — sans une exclusion dédiée dans default_style_for_widget.py, il était écrit TEL QUEL (repr Python d'un dict) dans le style de TOUT élément fraîchement créé, une valeur CSS invalide qui cassait "box-shadow" (et donnait l'impression que "les propriétés ne sont pas prises en compte") sur chaque nouvel élément, quel que soit son widget.""" _screen_id, element_id = _create_element(client, game) el = screens.get_element(game, element_id) assert "box-shadow" not in el["style"] html = client.get(f"/game/{game}/runtime-payload").get_json() rendered_html = None for s in html["screens"]: for e in s["elements"]: if e["id"] == element_id: rendered_html = e["rendered_html"] assert rendered_html is not None assert "{" not in rendered_html.split("style=")[1].split(">")[0] def test_already_corrupted_style_does_not_break_the_whole_style_attribute(client, game): """Filet de sécurité (voir style_string.py) : un élément déjà touché par le bug ci-dessus avant son correctif (un dict resté tel quel dans son style stocké) doit continuer à s'afficher — sans lui, une seule valeur invalide au milieu de l'attribut style pouvait tout casser.""" import json import db _screen_id, element_id = _create_element(client, game, "texte") el = screens.get_element(game, element_id) corrupted_style = dict(el["style"]) corrupted_style["box-shadow"] = {"x": 0, "y": 4, "blur": 12, "spread": 0, "color": "#000000", "opacity": 0.4} conn = db.connect(game) conn.execute("UPDATE _screen_elements SET style = ? WHERE id = ?", (json.dumps(corrupted_style), element_id)) conn.commit() conn.close() html = client.get(f"/game/{game}/screens/{_screen_id}/edit").data.decode() idx = html.find(f'id="elt-{element_id}"') style_attr = html[max(0, idx - 400):idx] assert "{" not in style_attr.split('style="')[-1] def test_shadow_controls_appear_in_the_properties_panel(client, game): screen_id, element_id = _create_element(client, game) html = client.get(f"/game/{game}/screens/{screen_id}/edit?selected={element_id}").data.decode() assert "ctrl_box-shadow_x" in html assert "ctrl_box-shadow_color" in html def test_saving_a_shadow_composes_box_shadow_css(client, game): screen_id, element_id = _create_element(client, game) client.post(f"/game/{game}/elements/{element_id}/save", data={ "ctrl_box-shadow_x": "2", "ctrl_box-shadow_y": "6", "ctrl_box-shadow_blur": "20", "ctrl_box-shadow_spread": "3", "ctrl_box-shadow_color": "#ff0000", "ctrl_box-shadow_opacity": "0.5", }) el = screens.get_element(game, element_id) assert el["style"]["box-shadow"] == "2px 6px 20px 3px rgba(255, 0, 0, 0.5)" def test_shadow_value_round_trips_back_into_the_form(client, game): screen_id, element_id = _create_element(client, game) client.post(f"/game/{game}/elements/{element_id}/save", data={ "ctrl_box-shadow_x": "2", "ctrl_box-shadow_y": "6", "ctrl_box-shadow_blur": "20", "ctrl_box-shadow_spread": "3", "ctrl_box-shadow_color": "#ff0000", "ctrl_box-shadow_opacity": "0.5", }) html = client.get(f"/game/{game}/screens/{screen_id}/edit?selected={element_id}").data.decode() assert 'name="ctrl_box-shadow_x" value="2.0"' in html or 'name="ctrl_box-shadow_x" value="2"' in html assert 'name="ctrl_box-shadow_color" value="#ff0000"' in html def test_all_zero_offsets_remove_the_shadow(client, game): screen_id, element_id = _create_element(client, game) client.post(f"/game/{game}/elements/{element_id}/save", data={ "ctrl_box-shadow_x": "2", "ctrl_box-shadow_y": "6", "ctrl_box-shadow_blur": "20", "ctrl_box-shadow_spread": "3", "ctrl_box-shadow_color": "#ff0000", "ctrl_box-shadow_opacity": "0.5", }) client.post(f"/game/{game}/elements/{element_id}/save", data={ "ctrl_box-shadow_x": "0", "ctrl_box-shadow_y": "0", "ctrl_box-shadow_blur": "0", "ctrl_box-shadow_spread": "0", "ctrl_box-shadow_color": "#ff0000", "ctrl_box-shadow_opacity": "0.5", }) el = screens.get_element(game, element_id) assert "box-shadow" not in el["style"]