Files
Forge-Engine/tests/test_screen_layout_fixes.py
T
williamandwilliam 3f4ebc4527
Build and deploy / build-and-push (push) Successful in 17s
Build and deploy / deploy (push) Successful in 10s
first commit
2026-08-21 16:23:49 +02:00

141 lines
6.6 KiB
Python

"""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-<id>", 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-<id>", 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*<div style="([^"]*)"', inner_html)
assert content_style_match, "balise intérieure introuvable"
assert "transform" not in content_style_match.group(1)
def test_scale_stays_on_own_tag_for_nested_element(client, game):
"""Un élément posé À L'INTÉRIEUR d'un conteneur n'a pas de cadre séparé
— sa propre balise EST déjà tout l'élément, donc l'échelle y reste."""
screen_id = _create_screen(client, game)
parent_id = _add_element(client, game, screen_id, "conteneur")
resp = client.post(f"/game/{game}/elements/{parent_id}/children/add", data={"widget": "bouton"}, follow_redirects=False)
child_id = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
client.post(f"/game/{game}/elements/{child_id}/save", data={"ctrl_scale": "1.3"})
html = client.get(f"/game/{game}/screens/{screen_id}/edit").data.decode()
assert "transform:scale(1.3)" in html
def test_play_payload_includes_preview_aspect_per_screen(client, game):
screen_id = _create_screen(client, game)
client.post(
f"/game/{game}/screens/{screen_id}/aspect",
data='{"aspect": "16/9"}',
content_type="application/json",
)
html = client.get(f"/game/{game}/play").data.decode()
assert '"preview_aspect": "16/9"' in html
assert 'data-aspect="16/9"' in html
def test_play_page_has_letterboxing_frame(client, game):
html = client.get(f"/game/{game}/play").data.decode()
assert 'id="playFrame"' in html
assert "applyScreenAspect" in html