"""Tests des corrections suite au retour utilisateur du 21/08 : - le format d'aperçu (Portrait/Paysage/Carré) d'un écran est mémorisé côté serveur, au lieu de repartir sur Portrait à chaque fois que le panneau est redemandé au serveur (ajout/suppression d'élément, changement de sélection) ; - l'échelle (transform:scale) d'un élément posé directement sur un écran vit sur un cadre intermédiaire (.canvasElementInner / .playElementInner), à la taille exacte du cadre de positionnement/animation (.canvasElement / .playElement) mais distinct de lui — ni sur la balise intérieure générée par render_element_html (sinon l'effet restait visuellement "coincé" à l'intérieur d'un cadre immobile), ni sur le cadre extérieur lui-même (sinon ce "transform" entrait en collision avec celui posé par Animate.css pendant une animation, qui l'écrasait) ; - cette même zone de jeu garde la proportion de l'écran en mode Jouer (voir #playFrame dans play.html), pour que les positions/tailles en % ne se déforment pas selon la fenêtre du joueur.""" 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 _add_element(client, slug, screen_id, widget): 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 test_screen_defaults_to_portrait(client, game): screen_id = _create_screen(client, game) html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert 'aspect-ratio:9/16;' in html assert 'aspectBtn active" data-aspect="9/16"' in html def test_aspect_choice_persists_across_reload(client, game): screen_id = _create_screen(client, game) resp = client.post( f"/game/{game}/screens/{screen_id}/aspect", data='{"aspect": "16/9"}', content_type="application/json", ) assert resp.status_code == 200 html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert 'aspect-ratio:16/9;' in html assert 'aspectBtn active" data-aspect="16/9"' in html def test_aspect_choice_survives_element_add_and_delete(client, game): """Le vrai bug rapporté : l'écran repartait sur Portrait dès qu'on posait ou supprimait un élément, car ces deux actions redemandent le panneau au serveur.""" screen_id = _create_screen(client, game) client.post( f"/game/{game}/screens/{screen_id}/aspect", data='{"aspect": "1/1"}', content_type="application/json", ) el_id = _add_element(client, game, screen_id, "bouton") html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert 'aspect-ratio:1/1;' in html client.post(f"/game/{game}/elements/{el_id}/delete", follow_redirects=False) html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert 'aspect-ratio:1/1;' in html def test_unknown_aspect_value_is_ignored(client, game): screen_id = _create_screen(client, game) resp = client.post( f"/game/{game}/screens/{screen_id}/aspect", data='{"aspect": "not-a-ratio"}', content_type="application/json", ) assert resp.status_code == 200 html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() assert 'aspect-ratio:9/16;' in html def test_scale_lives_on_outer_frame_for_top_level_element(client, game): """Un conteneur posé directement sur l'écran : l'échelle doit être sur le cadre intermédiaire .canvasElementInner, pas sur le cadre extérieur .canvasElement (id="el-", cible d'Animate.css — un "transform" posé là entrerait en collision avec celui d'une animation en cours) ni sur la balise intérieure générée par render_element_html (sinon l'effet restait visuellement clippé à l'intérieur d'un cadre qui, lui, ne grossissait pas).""" screen_id = _create_screen(client, game) el_id = _add_element(client, game, screen_id, "conteneur") client.post( f"/game/{game}/elements/{el_id}/save", data={"ctrl_scale": "1.5"}, ) html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode() # Le cadre extérieur (id="el-", cible Animate.css) ne porte PAS le transform... outer_match = re.search(rf'id="el-{el_id}"[^>]*style="([^"]*)"', html) assert outer_match, "cadre .canvasElement introuvable" assert "transform" not in outer_match.group(1) # ...le cadre intermédiaire .canvasElementInner le porte... inner_html = html.split(f'id="el-{el_id}"', 1)[1] canvas_inner_match = re.search(r'class="canvasElementInner" style="([^"]*)"', inner_html) assert canvas_inner_match, "cadre .canvasElementInner introuvable" assert "transform:scale(1.5)" in canvas_inner_match.group(1) # ...et la balise intérieure (générée par render_element_html) ne le # répète pas une seconde fois. content_style_match = re.search(r'class="canvasElementInner"[^>]*>\s*