mirror of
https://github.com/deunix-educ/aventuriers-des-mots.git
synced 2026-10-13 02:11:58 +02:00
Add several boards per level, CM1/CM2 levels and the CE1 'plage des sons' pilot board (0.6.0)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -18,9 +18,9 @@ func test_unknown_ids_give_no_path() -> void:
|
||||
assert_eq(SoundCatalog.music_path("inconnue"), "")
|
||||
|
||||
|
||||
func test_music_follows_the_level() -> void:
|
||||
assert_eq(SoundCatalog.music_for_level("CE1"), "forest")
|
||||
assert_eq(SoundCatalog.music_for_level("CE2"), "marsh")
|
||||
func test_music_follows_the_backdrop() -> void:
|
||||
assert_eq(SoundCatalog.resolve_music("auto", "forest"), "forest")
|
||||
assert_eq(SoundCatalog.resolve_music("auto", "marsh"), "marsh")
|
||||
|
||||
|
||||
func test_there_are_five_music_tracks_and_an_automatic_choice() -> void:
|
||||
@@ -30,9 +30,9 @@ func test_there_are_five_music_tracks_and_an_automatic_choice() -> void:
|
||||
|
||||
|
||||
func test_resolve_music_follows_choice_or_level() -> void:
|
||||
assert_eq(SoundCatalog.resolve_music("night", "CE1"), "night")
|
||||
assert_eq(SoundCatalog.resolve_music("auto", "CE2"), "marsh")
|
||||
assert_eq(SoundCatalog.resolve_music("n_importe_quoi", "CE1"), "forest")
|
||||
assert_eq(SoundCatalog.resolve_music("night", "forest"), "night")
|
||||
assert_eq(SoundCatalog.resolve_music("auto", "beach"), "river")
|
||||
assert_eq(SoundCatalog.resolve_music("n_importe_quoi", "inconnu"), "forest")
|
||||
|
||||
|
||||
func test_every_music_choice_has_a_label() -> void:
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
extends GutTest
|
||||
## Coherence des « tableaux » (data/curriculum/) avec le reste des donnees.
|
||||
|
||||
|
||||
func test_school_levels_match_the_schemas() -> void:
|
||||
for schema_name in ["save", "rule", "curriculum", "dictation", "exercise_multiple_choice"]:
|
||||
var schema := DataLoader.load_schema(schema_name)
|
||||
var enum_values: Array = []
|
||||
if schema_name == "save":
|
||||
enum_values = schema["properties"]["school_level"]["enum"]
|
||||
else:
|
||||
enum_values = schema["properties"]["level"]["enum"]
|
||||
assert_eq(enum_values, SchoolLevels.ALL as Array, "schema %s" % schema_name)
|
||||
|
||||
|
||||
func test_backdrop_enum_matches_available_art() -> void:
|
||||
var schema := DataLoader.load_schema("curriculum")
|
||||
var enum_values: Array = schema["properties"]["backdrop"]["enum"]
|
||||
var kinds: Array = BackdropKinds.kinds()
|
||||
enum_values.sort()
|
||||
kinds.sort()
|
||||
assert_eq(enum_values, kinds)
|
||||
for kind in BackdropKinds.kinds():
|
||||
assert_true(
|
||||
ResourceLoader.exists(BackdropKinds.TEXTURES[kind]), "decor manquant : %s" % kind
|
||||
)
|
||||
assert_true(SoundCatalog.MUSIC_IDS.has(BackdropKinds.music_for(kind)))
|
||||
|
||||
|
||||
func test_every_rule_belongs_to_exactly_one_region_of_its_level() -> void:
|
||||
var owners := {}
|
||||
for level in SchoolLevels.ALL:
|
||||
for region in ExerciseEngine.get_regions_for_level(level):
|
||||
assert_between(
|
||||
ExerciseEngine.get_regions_for_level(level).size(), 1, 3, "3 tableaux maximum"
|
||||
)
|
||||
for rule_id: String in region["rule_ids"]:
|
||||
assert_false(owners.has(rule_id), "regle dans deux tableaux : %s" % rule_id)
|
||||
owners[rule_id] = region["region_id"]
|
||||
var rule := ExerciseEngine.get_rule(rule_id)
|
||||
assert_false(rule.is_empty(), "regle inconnue : %s" % rule_id)
|
||||
assert_eq(rule.get("level", ""), level, "niveau de %s" % rule_id)
|
||||
for mission in ExerciseEngine.get_missions():
|
||||
assert_true(
|
||||
owners.has(mission["rule_id"]), "mission hors tableau : %s" % mission["mission_id"]
|
||||
)
|
||||
|
||||
|
||||
func test_regions_have_a_child_facing_title_and_unique_order() -> void:
|
||||
for level in SchoolLevels.ALL:
|
||||
var orders := {}
|
||||
for region in ExerciseEngine.get_regions_for_level(level):
|
||||
assert_true(region.has("region_title"), "titre manquant : %s" % region["region_id"])
|
||||
var order := int(region.get("order", 1))
|
||||
assert_false(orders.has(order), "numero de tableau en double dans %s" % level)
|
||||
orders[order] = true
|
||||
|
||||
|
||||
func test_school_levels_helpers() -> void:
|
||||
assert_eq(SchoolLevels.previous("CE1"), "")
|
||||
assert_eq(SchoolLevels.previous("CM1"), "CE2")
|
||||
assert_eq(SchoolLevels.previous("n_importe_quoi"), "")
|
||||
assert_true(SchoolLevels.rank("CM2") > SchoolLevels.rank("CE2"))
|
||||
@@ -0,0 +1 @@
|
||||
uid://domp0yvjne65v
|
||||
@@ -13,20 +13,24 @@ const MIN_GAP_X := 0.2
|
||||
const MIN_GAP_Y := 0.3
|
||||
|
||||
|
||||
func test_mission_markers_do_not_overlap_within_a_level() -> void:
|
||||
for level in ["CE1", "CE2"]:
|
||||
var missions := ExerciseEngine.get_missions_for_level(level)
|
||||
assert_false(missions.is_empty(), "aucune mission pour %s" % level)
|
||||
for i in missions.size():
|
||||
for j in range(i + 1, missions.size()):
|
||||
var a: Dictionary = missions[i]["map_position"]
|
||||
var b: Dictionary = missions[j]["map_position"]
|
||||
var dx: float = abs(float(a["x"]) - float(b["x"]))
|
||||
var dy: float = abs(float(a["y"]) - float(b["y"]))
|
||||
assert_true(
|
||||
dx >= MIN_GAP_X or dy >= MIN_GAP_Y,
|
||||
(
|
||||
"%s et %s sont trop proches (dx=%s, dy=%s)"
|
||||
% [missions[i]["mission_id"], missions[j]["mission_id"], dx, dy]
|
||||
),
|
||||
)
|
||||
func test_mission_markers_do_not_overlap_within_a_region() -> void:
|
||||
var checked_regions := 0
|
||||
for level in SchoolLevels.ALL:
|
||||
for region in ExerciseEngine.get_regions_for_level(level):
|
||||
var missions := ExerciseEngine.get_missions_for_region(region["region_id"])
|
||||
assert_false(missions.is_empty(), "aucune mission pour %s" % region["region_id"])
|
||||
checked_regions += 1
|
||||
for i in missions.size():
|
||||
for j in range(i + 1, missions.size()):
|
||||
var a: Dictionary = missions[i]["map_position"]
|
||||
var b: Dictionary = missions[j]["map_position"]
|
||||
var dx: float = abs(float(a["x"]) - float(b["x"]))
|
||||
var dy: float = abs(float(a["y"]) - float(b["y"]))
|
||||
assert_true(
|
||||
dx >= MIN_GAP_X or dy >= MIN_GAP_Y,
|
||||
(
|
||||
"%s et %s sont trop proches (dx=%s, dy=%s)"
|
||||
% [missions[i]["mission_id"], missions[j]["mission_id"], dx, dy]
|
||||
),
|
||||
)
|
||||
assert_gt(checked_regions, 1)
|
||||
|
||||
@@ -15,7 +15,7 @@ func test_unknown_rule_returns_empty_dictionary() -> void:
|
||||
|
||||
func test_missions_are_loaded_and_sorted() -> void:
|
||||
var missions := ExerciseEngine.get_missions()
|
||||
assert_eq(missions.size(), 21)
|
||||
assert_eq(missions.size(), 30)
|
||||
var ids: Array = missions.map(func(m: Dictionary) -> String: return m["mission_id"])
|
||||
assert_eq(ids, ids.duplicate()) # trivialement vrai, documente l'ordre stable
|
||||
assert_true(ids.has("mission_01_pre_des_mots"))
|
||||
@@ -24,11 +24,23 @@ func test_missions_are_loaded_and_sorted() -> void:
|
||||
|
||||
|
||||
func test_missions_are_filtered_by_the_level_of_their_rule() -> void:
|
||||
assert_eq(ExerciseEngine.get_missions_for_level("CE1").size(), 9)
|
||||
assert_eq(ExerciseEngine.get_missions_for_level("CE1").size(), 18)
|
||||
assert_eq(ExerciseEngine.get_missions_for_level("CE2").size(), 12)
|
||||
assert_eq(ExerciseEngine.get_missions_for_level("CM1").size(), 0)
|
||||
|
||||
|
||||
func test_regions_are_ordered_and_hold_their_missions() -> void:
|
||||
var ce1 := ExerciseEngine.get_regions_for_level("CE1")
|
||||
assert_eq(ce1.size(), 2)
|
||||
assert_eq(ce1[0]["region_id"], "foret_des_accords")
|
||||
assert_eq(ce1[1]["region_id"], "plage_des_sons")
|
||||
assert_eq(ExerciseEngine.get_missions_for_region("foret_des_accords").size(), 9)
|
||||
assert_eq(ExerciseEngine.get_missions_for_region("plage_des_sons").size(), 9)
|
||||
assert_eq(ExerciseEngine.backdrop_for_rule("regle_s_ss"), "beach")
|
||||
assert_eq(ExerciseEngine.backdrop_for_rule("regle_pluriel_nom"), "forest")
|
||||
assert_eq(ExerciseEngine.backdrop_for_rule("regle_homophone_a_a"), "marsh")
|
||||
|
||||
|
||||
func test_reward_catalog_is_loaded_and_sorted_by_threshold() -> void:
|
||||
var items := ExerciseEngine.get_reward_items()
|
||||
assert_eq(items.size(), 10)
|
||||
|
||||
@@ -70,3 +70,19 @@ func test_profile_deletion_belongs_to_the_adult_space() -> void:
|
||||
assert_true(ProfileManager.list_profiles().has(other_id), "les autres profils sont intacts")
|
||||
|
||||
ProfileManager.delete_profile(other_id)
|
||||
|
||||
|
||||
func test_profile_creation_offers_only_levels_that_have_content() -> void:
|
||||
var scene: Control = add_child_autofree(
|
||||
load("res://scenes/profiles/profile_create.tscn").instantiate()
|
||||
)
|
||||
await wait_process_frames(1)
|
||||
var offered: Array[String] = []
|
||||
for button in scene.get_node("%LevelBox").get_children():
|
||||
offered.append(button.get_meta("level"))
|
||||
var expected: Array[String] = []
|
||||
for level in SchoolLevels.ALL:
|
||||
if not ExerciseEngine.get_regions_for_level(level).is_empty():
|
||||
expected.append(level)
|
||||
assert_eq(offered, expected)
|
||||
assert_true(offered.has("CE1") and offered.has("CE2"))
|
||||
|
||||
@@ -24,6 +24,7 @@ func test_ce1_profile_sees_only_ce1_missions_and_no_toggle() -> void:
|
||||
|
||||
assert_eq(map.get_node("%MapArea").get_child_count(), 9)
|
||||
assert_false(map.get_node("%LevelToggleButton").visible)
|
||||
GameManager.map_region_id = ""
|
||||
|
||||
|
||||
func test_ce2_profile_sees_ce2_missions_and_can_review_ce1() -> void:
|
||||
@@ -31,20 +32,40 @@ func test_ce2_profile_sees_ce2_missions_and_can_review_ce1() -> void:
|
||||
await wait_process_frames(1)
|
||||
|
||||
assert_eq(map.get_node("%MapArea").get_child_count(), 12)
|
||||
assert_eq(map.get_node("%TitleLabel").text, UIText.get_text("world_map.title_ce2"))
|
||||
assert_eq(map.get_node("%TitleLabel").text, "Le marais des homophones")
|
||||
var toggle: Button = map.get_node("%LevelToggleButton")
|
||||
assert_true(toggle.visible)
|
||||
|
||||
toggle.pressed.emit()
|
||||
await wait_process_frames(1)
|
||||
assert_eq(map.get_node("%MapArea").get_child_count(), 9)
|
||||
assert_eq(map.get_node("%TitleLabel").text, UIText.get_text("world_map.title"))
|
||||
assert_eq(map.get_node("%TitleLabel").text, "La forêt des accords")
|
||||
assert_eq(toggle.text, "Retour au CE2")
|
||||
|
||||
toggle.pressed.emit()
|
||||
await wait_process_frames(1)
|
||||
assert_eq(map.get_node("%MapArea").get_child_count(), 12)
|
||||
|
||||
|
||||
func test_ce1_profile_can_move_between_its_regions() -> void:
|
||||
var map := _open_map_for("CE1")
|
||||
await wait_process_frames(1)
|
||||
assert_eq(map.get_node("%TitleLabel").text, "La forêt des accords")
|
||||
var button: Button = map.get_node("%RegionButton")
|
||||
assert_true(button.visible)
|
||||
assert_eq(map.get_node("%MapArea").get_child_count(), 9)
|
||||
|
||||
button.pressed.emit()
|
||||
await wait_process_frames(1)
|
||||
assert_eq(map.get_node("%TitleLabel").text, "La plage des sons")
|
||||
assert_eq(map.get_node("%MapArea").get_child_count(), 9)
|
||||
|
||||
button.pressed.emit()
|
||||
await wait_process_frames(1)
|
||||
assert_eq(map.get_node("%TitleLabel").text, "La forêt des accords")
|
||||
GameManager.map_region_id = ""
|
||||
|
||||
|
||||
func test_map_offers_a_way_back_to_the_profile_selection() -> void:
|
||||
var map := _open_map_for("CE1")
|
||||
await wait_process_frames(1)
|
||||
|
||||
Reference in New Issue
Block a user