Files
Forge-Engine/tests/test_scene_objects.py
T
william c504ace167
Build and deploy / test-python (push) Successful in 5m56s
Build and deploy / test-js (push) Successful in 51s
Build and deploy / build-and-push (push) Skipped
Build and deploy / deploy (push) Skipped
Enrichit le reporting SCORM/xAPI et met en conformité RGAA le player
SCORM/xAPI :
- Ajoute l'export SCORM 2004 (3rd/4th edition) au choix, en plus du 1.2
  par défaut : sépare completion_status/success_status (un échec reste
  "completed" au lieu de retomber à tort en "incomplete" comme force la
  1.2), remonte aussi cmi.interactions.n.* par question répondue.
- Fournit cmi.core.score.min/max (1.2 et 2004), calculé depuis les
  récompenses de quiz du jeu, pour que le LMS affiche un vrai pourcentage
  au lieu du score brut à tort étiqueté "%".
- Libellés de verbes xAPI en français en plus de l'anglais.

Accessibilité (RGAA/WCAG 2.1 AA) sur le player :
- Navigation clavier des objets de scène "au clic"/"au survol"
  (tabindex, role, Entrée/Espace, focus/blur).
- alt sur les images (nom auteur ou décoratif), aria-hidden sur les
  icônes seules, role="dialog"/aria-live sur les boîtes de dialogue/quiz.
- Landmark <main> + titre de page, respect de prefers-reduced-motion.
- Le quiz n'avance plus automatiquement après un délai fixe : bouton
  "Continuer →" explicite (RGAA 2.2.1).
- Avertissement de contraste dans l'éditeur de style de dialogue.
- Déclaration d'accessibilité téléchargeable depuis la modale d'export.
2026-09-06 00:17:51 +02:00

120 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.
"""Objets de scène (_scene_objects) — fondations de l'éditeur de scène 2D
(écrans "jeu_2d", voir screens/screens_repo/ensure_schema.py). Positions
en PIXELS fixes (pas des %, voir ensure_scene_schema.py). kind="personnage"
réutilise TELLE QUELLE la structure _personnage_data et les fonctions
resolve_personnage_* déjà écrites pour le widget "personnage" de
l'éditeur document (Phase 8) — même bibliothèque Forge, même moteur
d'animation côté client, juste une autre table de stockage."""
import db
import screens
def _create_jeu2d_game(tmp_game_slug_cleanup, name="pytest_scene_test"):
slug = tmp_game_slug_cleanup(db.create_game(name))
screen_id = screens.create_screen(slug, "Scène 1", kind="jeu_2d")
return slug, screen_id
def test_scene_object_defaults_to_forge_personnage(tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id)
obj = screens.get_scene_object(slug, obj_id)
assert obj["kind"] == "personnage"
assert obj["attributes"]["_personnage_data"]
data = screens.resolve_personnage_data(obj)
assert data["source"] == "forge"
assert data["forge_character"] in screens.SPRITE_LIBRARY
def test_add_scene_object_with_specific_forge_character(tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id, kind="personnage", forge_character="zombie")
obj = screens.get_scene_object(slug, obj_id)
assert screens.resolve_personnage_data(obj)["forge_character"] == "zombie"
def test_add_decor_scene_object(tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id, kind="decor")
obj = screens.get_scene_object(slug, obj_id)
assert obj["kind"] == "decor"
def test_scene_object_geometry_is_pixels_not_percent(tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id)
screens.update_scene_object_geometry(slug, obj_id, x=320, y=200, width=128, height=128)
obj = screens.get_scene_object(slug, obj_id)
assert (obj["x"], obj["y"], obj["width"], obj["height"]) == (320, 200, 128, 128)
def test_list_scene_objects_ordered_by_z_index(tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
first = screens.add_scene_object(slug, screen_id)
second = screens.add_scene_object(slug, screen_id)
objs = screens.list_scene_objects(slug, screen_id)
assert [o["id"] for o in objs] == [first, second]
def test_swap_scene_object_forge_character(tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id, forge_character="male-adventurer")
screens.set_scene_object_personnage_data(slug, obj_id, {"source": "forge", "forge_character": "robot"})
obj = screens.get_scene_object(slug, obj_id)
assert screens.resolve_personnage_data(obj)["forge_character"] == "robot"
def test_render_scene_object_shows_idle_frame_pixel_positioned(tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id, forge_character="zombie")
screens.update_scene_object_geometry(slug, obj_id, x=50, y=60, width=64, height=64)
obj = screens.get_scene_object(slug, obj_id)
html = screens.render_scene_object(obj)
assert "/static/characters/zombie/idle.png" in html
assert "left:50" in html and "top:60" in html
assert f'data-object-id="{obj_id}"' in html
assert 'data-personnage="1"' in html
def test_render_scene_object_alt_uses_the_authored_name_rgaa(tmp_game_slug_cleanup):
"""RGAA/WCAG 1.1.1 : le nom donné par l'auteur (panneau "ℹ️
Informations") sert d'alternative textuelle — jamais aucun attribut
alt du tout n'était généré avant."""
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id, forge_character="zombie")
screens.set_scene_object_name(slug, obj_id, "Zombie du hangar")
obj = screens.get_scene_object(slug, obj_id)
html = screens.render_scene_object(obj)
assert 'alt="Zombie du hangar"' in html
def test_render_scene_object_alt_is_empty_when_unnamed_rgaa(tmp_game_slug_cleanup):
"""alt="" (décoratif) plutôt qu'aucun attribut du tout quand l'auteur
n'a pas nommé l'objet."""
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id, forge_character="zombie")
obj = screens.get_scene_object(slug, obj_id)
html = screens.render_scene_object(obj)
assert 'alt=""' in html
def test_render_scene_object_fond_alt_is_always_empty_rgaa(tmp_game_slug_cleanup):
"""Un "fond" est toujours décoratif — alt="" même s'il est nommé."""
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id, kind="fond", background_slug="foret")
screens.set_scene_object_name(slug, obj_id, "Grand décor de fond")
obj = screens.get_scene_object(slug, obj_id)
html = screens.render_scene_object(obj)
assert 'alt=""' in html
def test_delete_scene_object_cleans_up_referencing_flow_nodes(tmp_game_slug_cleanup):
slug, screen_id = _create_jeu2d_game(tmp_game_slug_cleanup)
obj_id = screens.add_scene_object(slug, screen_id)
node_id = screens.add_flow_node(
slug, screen_id, node_type="action", action_type="jouer_animation_sprite",
target_object_id=obj_id, data_value='{"animation":"idle","fps":8,"loop":true}',
)
screens.delete_scene_object(slug, obj_id)
assert screens.get_flow_node(slug, node_id) is None