Supprime le type d'écran "document" et recentre le produit sur le jeu 2D
Build and deploy / test-python (push) Successful in 9m41s
Build and deploy / test-js (push) Successful in 1m0s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped

Le document de cadrage produit cible des formateurs non techniques créant
des serious games/quiz gamifiés — l'éditeur générique "document" (blocs de
logique en nœuds, timeline d'animation, définitions d'objets/relations,
templates réutilisables) est une complexité hors cible que l'effort
d'ingénierie récent avait déjà abandonnée au profit du jeu_2d.

- Onboarding : ne garde que le parcours "RPG" (jeu_2d), retire
  Quiz/Embranchement/Créer mon jeu de A à Z (tous document-only)
- Suppression en bloc des modules exclusifs au document : routes/elements,
  routes/element_types, routes/objects, routes/legacy_actions,
  screens/elements, screens/element_types, screens/widgets,
  screens/legacy_actions, le rendu render_element_html.py et son cluster,
  templates/screen_edit.html, templates/game_dashboard.html,
  flow-editor.js/tabs-and-blocks.js/animation-timeline.js
- Dashboard toujours simplifié (un seul mode possible désormais)
- Tests document-only supprimés, tests de logique partagée (flow,
  événements personnalisés, animations) retargetés sur des écrans jeu_2d
- Aucune régression jeu_2d : 299 tests passent

Carte d'onboarding retravaillée : argumentaire RH non technique (liste à
coche, badge "Compatible LMS"), taille et interaction de retournement
ajustées.
This commit is contained in:
william
2026-09-04 20:56:32 +02:00
parent 00f7191a8b
commit 50835a18e2
221 changed files with 1239 additions and 12245 deletions
+26 -77
View File
@@ -1,88 +1,47 @@
"""Position/déplacement d'élément + condition de collision — Phase 5 du
plan d'extension du moteur. Comme le reste du graphe de logique côté
client, le comportement RÉEL (déplacement CSS effectif, chevauchement de
rectangles via getBoundingClientRect) n'est pas testable sans navigateur
(voir play_js_bundle() — on vérifie que la logique existe dans le JS
livré et que les nœuds persistent leurs champs, pas qu'ils s'exécutent)."""
import json
import re
from conftest import play_js_bundle, screen_edit_js_bundle
"""Position/déplacement d'objet de scène — Phase 5 du plan d'extension du
moteur, porté sur jeu_2d (voir OBJECT_ACTION_PROPERTIES, screens/scenes/
flow_palette.py) depuis l'éditeur document d'origine (retiré). Comme le
reste du graphe de logique côté client, le comportement RÉEL (déplacement
CSS effectif, chevauchement de rectangles via getBoundingClientRect) n'est
pas testable sans navigateur (voir play_js_bundle() — on vérifie que la
logique existe dans le JS livré et que les nœuds persistent leurs champs,
pas qu'ils s'exécutent). Le nœud de condition de collision entre deux
objets de scène (cond_element_a/cond_element_b) est déjà couvert par
test_scene_edit_view.py::test_flow_node_collision_condition_targets_scene_objects."""
from conftest import play_js_bundle
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 _create_screen_and_object(slug):
screen_id = screens.create_screen(slug, "Accueil", kind="jeu_2d")
obj_id = screens.add_scene_object(slug, screen_id)
return screen_id, obj_id
def _add_flow_node(client, slug, screen_id, **fields):
resp = client.post(
f"/game/{slug}/screens/{screen_id}/flow/nodes/add",
data=json.dumps(fields),
content_type="application/json",
)
assert resp.status_code == 200
return resp.get_json()["id"]
def test_position_action_node_persists_absolute_property(client, game):
screen_id, element_id = _create_element(client, game)
node_id = _add_flow_node(
client, game, screen_id, node_type="action", action_type="modifier_element",
target_element_id=element_id, element_property="pos_x", element_value="42",
def test_position_action_node_persists_absolute_property(game):
screen_id, obj_id = _create_screen_and_object(game)
node_id = screens.add_flow_node(
game, screen_id, node_type="action", action_type="modifier_objet_scene",
target_object_id=obj_id, element_property="pos_x", element_value="42",
)
node = screens.get_flow_node(game, node_id)
assert node["element_property"] == "pos_x"
assert node["element_value"] == "42"
assert node["target_object_id"] == obj_id
def test_position_action_node_persists_relative_property(client, game):
screen_id, element_id = _create_element(client, game)
node_id = _add_flow_node(
client, game, screen_id, node_type="action", action_type="modifier_element",
target_element_id=element_id, element_property="pos_y_relatif", element_value="-5",
def test_position_action_node_persists_relative_property(game):
screen_id, obj_id = _create_screen_and_object(game)
node_id = screens.add_flow_node(
game, screen_id, node_type="action", action_type="modifier_objet_scene",
target_object_id=obj_id, element_property="pos_y_relatif", element_value="-5",
)
node = screens.get_flow_node(game, node_id)
assert node["element_property"] == "pos_y_relatif"
assert node["element_value"] == "-5"
def test_collision_condition_node_persists_both_elements(client, game):
screen_id, element_a = _create_element(client, game)
resp = client.post(f"/game/{game}/screens/{screen_id}/elements/add", data={"widget": "conteneur"}, follow_redirects=False)
element_b = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
node_id = _add_flow_node(
client, game, screen_id, node_type="condition", cond_source="collision",
cond_element_a=element_a, cond_element_b=element_b,
)
node = screens.get_flow_node(game, node_id)
assert node["cond_source"] == "collision"
assert node["cond_element_a"] == element_a
assert node["cond_element_b"] == element_b
assert node["cond_field"] is None
assert node["cond_operator"] is None
def test_deleting_element_cleans_up_collision_condition_nodes(client, game):
"""Un nœud de collision qui référence un élément supprimé n'a plus de
sens (voir delete_element.py) — même nettoyage que pour un déclencheur/
une action ciblant un élément supprimé."""
screen_id, element_a = _create_element(client, game)
resp = client.post(f"/game/{game}/screens/{screen_id}/elements/add", data={"widget": "conteneur"}, follow_redirects=False)
element_b = int(re.search(r"selected=(\d+)", resp.headers["Location"]).group(1))
node_id = _add_flow_node(
client, game, screen_id, node_type="condition", cond_source="collision",
cond_element_a=element_a, cond_element_b=element_b,
)
client.post(f"/game/{game}/elements/{element_a}/delete", follow_redirects=False)
assert screens.get_flow_node(game, node_id) is None
def test_play_page_exposes_position_and_collision_runtime(client, game):
js = play_js_bundle()
assert "pos_x" in js
@@ -90,13 +49,3 @@ def test_play_page_exposes_position_and_collision_runtime(client, game):
assert "elementsOverlap" in js
assert "getBoundingClientRect" in js
assert "'collision'" in js
def test_screen_edit_exposes_collision_condition_editor(client, game):
html = client.get(f"/game/{game}/screens/{_create_element(client, game)[0]}/edit").get_data(as_text=True)
assert "nf-cond-collision-fields" in html
assert "nf-cond-element-a" in html
assert "nf-cond-element-b" in html
js = screen_edit_js_bundle()
assert "isCollision" in js
assert "cond_element_a" in js