Files
Forge-Engine/tests/test_scene_object_add_ajax.py
T
williamandClaude Sonnet 5 559331f9cf
Build and deploy / test-python (push) Successful in 10m59s
Build and deploy / test-js (push) Successful in 1m27s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Enrichit les declencheurs/actions de scene (clic/survol/affichage, surbrillance/video/son/visibilite/indication/attendre) et fiabilise la pose d'un fond/decor importe
- Ajoute clic/survol/affichage-ecran comme declencheurs, et surbrillance,
  video, son, visibilite, indication, attendre comme actions, utilisables
  aussi bien par l'editeur manuel (menu lateral Objets/Ecran) que par
  Ruby (IA), avec blocs deplacables/supprimables dans une chaine.
- Corrige plusieurs variantes du bug "impossible de poser un objet hors
  du champ de la camera" (troncature du chainage d'actions a 4 maillons,
  fond importe pose a 128x128 au lieu de sa taille reelle, decalage du
  fond au vrai glisser-depose, redimensionnement manuel jamais propage
  au monde).
- Ajoute un vrai glisser-depose depuis la galerie vers la scene, la
  gestion complete de "Mes assets" (sous-sections Fonds/Decors/Sons/
  Videos, suppression, reclassement fond<->decor sans re-upload).
- Ajoute l'upload de son (limite 3 min) et de video (MP4 uniquement,
  limite 5 min), avec validation de la duree reelle du fichier, et une
  replique audio optionnelle dans une bulle de dialogue.
- Fixe la taille de pose d'un objet/decor importe a 200x200 avec une
  boite de collision de 150x150.
- Filtre le selecteur de fichier des actions son/video par type reel.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-12 00:28:46 +02:00

127 lines
5.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""Ajout d'un objet de scène en AJAX (voir routes/scenes/scene_object_add.py,
addSceneObject() dans static/js/scenes/scene-editor.js) : la réponse
JSON (posée quand l'appelant envoie X-Requested-With: fetch) remplace le
rechargement complet de page — sans ça, poser un objet "vidait" un instant
l'éditeur de collision/quêtes (tout l'état JS repartait de zéro à chaque
ajout)."""
import io
import struct
import auth
import screens
def _png_bytes(width, height):
ihdr = struct.pack(">II", width, height) + b"\x08\x02\x00\x00\x00"
return b"\x89PNG\r\n\x1a\n" + b"\x00\x00\x00\x0d" + b"IHDR" + ihdr + b"\x00\x00\x00\x00"
def _create_jeu2d_game(client, tmp_game_slug_cleanup, name="pytest_add_ajax"):
resp = client.post("/games/new", data={"name": name}, follow_redirects=False)
slug = tmp_game_slug_cleanup(resp.headers["Location"].rstrip("/").split("/")[-1])
screen_id = screens.create_screen(slug, "Scène 1", kind="jeu_2d")
return slug, screen_id
def _add_ajax(client, slug, screen_id, **data):
return client.post(
f"/game/{slug}/screens/{screen_id}/scene-objects/add",
data=data, headers={"X-Requested-With": "fetch"},
)
def test_ajax_add_returns_json_instead_of_redirecting(client, tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
resp = _add_ajax(client, slug, screen_id, kind="personnage")
assert resp.status_code == 200
body = resp.get_json()
assert body["kind"] == "personnage"
assert "rendered_html" in body and body["rendered_html"]
assert body["animations"]
def test_ajax_add_with_drop_position_places_the_object_exactly_there(client, tmp_game_slug_cleanup):
"""Vrai glisser-déposer (demande explicite : "n'importe où sur la
scène") — x/y postés remplacent le défaut 100/100."""
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
resp = _add_ajax(client, slug, screen_id, kind="decor", x="300", y="220")
assert resp.status_code == 200
body = resp.get_json()
assert body["x"] == 300
assert body["y"] == 220
def test_ajax_add_with_drop_position_near_the_edge_is_clamped_into_the_camera(client, tmp_game_slug_cleanup):
"""Un dépôt trop proche d'un bord ne doit jamais laisser l'objet
déborder du cadre visible par la caméra une fois sa taille réelle
connue (même esprit que ai/tools.py::_clamp_to_camera pour l'IA)."""
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
resp = _add_ajax(client, slug, screen_id, kind="decor", x="999999", y="999999")
assert resp.status_code == 200
body = resp.get_json()
assert body["x"] <= 960 - body["width"]
assert body["y"] <= 540 - body["height"]
# ---------- Fond importé ("Mes assets") posé à sa taille RÉELLE — bug :
# "je ne peux pas poser un objet hors du champ de la caméra quand
# j'utilise un fond de scène importé" (sans ça, l'image retombait
# toujours sur 128×128, trop petite pour agrandir le "monde" au-delà de
# la caméra nominale 960×540) ----------
def test_ajax_add_fond_ignores_the_drop_position_and_stays_anchored_at_the_origin(client, tmp_game_slug_cleanup):
"""Bug corrigé ("décalage") : le vrai glisser-déposer envoie une
position EXACTE même pour un fond — sans ce garde-fou, un fond
déposé ailleurs qu'au coin haut-gauche restait décalé, laissant un
bandeau de canevas vide visible entre le bord de la scène et
l'image."""
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
resp = _add_ajax(client, slug, screen_id, kind="fond", x="450", y="300")
assert resp.status_code == 200
body = resp.get_json()
assert (body["x"], body["y"]) == (0, 0)
def test_ajax_add_fond_from_user_asset_uses_the_real_image_dimensions(client, tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
with client.session_transaction() as sess:
user_id = sess["user_id"]
asset_id, _filename = auth.save_user_asset(
user_id, _png_bytes(1920, 1080), ".png", original_name="ciel.png", source="upload", scene_kind="fond",
)
resp = _add_ajax(client, slug, screen_id, kind="fond", asset_id=str(asset_id))
assert resp.status_code == 200
body = resp.get_json()
assert body["width"] == 1920
assert body["height"] == 1080
# ---------- Redimensionnement manuel d'un "fond" — la route /geometry
# doit renvoyer le "monde" recalculé pour que scene-editor.js puisse
# rafraîchir SCENE_WIDTH/SCENE_HEIGHT tout de suite (même bug que
# ci-dessus, mais via le redimensionnement à la souris plutôt qu'un
# import — sans ça, l'agrandissement n'était visible qu'après un
# rechargement complet de la page) ----------
def test_geometry_route_returns_the_recomputed_world_size_for_an_enlarged_fond(client, tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
resp = _add_ajax(client, slug, screen_id, kind="fond")
obj_id = resp.get_json()["id"]
geo = client.post(
f"/game/{slug}/scene-objects/{obj_id}/geometry",
json={"x": 0, "y": 0, "width": 2000, "height": 1200},
)
assert geo.status_code == 200
body = geo.get_json()
assert body["world_width"] >= 2000
assert body["world_height"] >= 1200
def test_non_ajax_add_still_redirects(client, tmp_game_slug_cleanup):
"""Secours pour tout appelant non-JS (aucun connu, mais gratuit à garder) —
comportement inchangé : redirection classique, sans le header AJAX."""
slug, screen_id = _create_jeu2d_game(client, tmp_game_slug_cleanup)
resp = client.post(f"/game/{slug}/screens/{screen_id}/scene-objects/add", data={"kind": "personnage"}, follow_redirects=False)
assert resp.status_code == 302
assert "selected=" in resp.headers["Location"]