From b0d11eb83137a179aace1a9b4eff3467b0ff01b7 Mon Sep 17 00:00:00 2001 From: Denis Defolie Date: Sun, 11 Oct 2026 18:08:54 +0200 Subject: [PATCH] Add regression tests for the sentence-correction tap-and-retype flow Investigating a manual bug report ("typing petits as the correction for petit doesn't validate"): both a direct test of sentence_correction.tscn and a full exercise_host integration test (mission_02_pont_des_phrases) tap the faulty token, type its exact expected correction, and submit -- both pass, confirming the validator and the scene wiring correctly accept "petits" when the word is tapped once and the correction is typed before pressing Valider. Caught along the way: a first draft of the direct-scene test used a lambda connected to answer_submitted to capture its parameters into outer local variables -- GDScript closures capture locals by value at creation time, so reassigning them inside the lambda never reaches the outer scope. Switched to GUT's assert_signal_emitted_with_parameters, the supported way to inspect signal arguments in a test. No fix applied yet: these tests confirm the intended interaction works, but don't rule out a real fragility noticed while reading the code -- toggling a token off deletes its LineEdit (and whatever was typed in it) immediately, so an accidental second tap on the same word silently discards the correction. Needs the user's exact click sequence to confirm before changing behavior. Co-Authored-By: Claude Sonnet 5 --- .../test_exercise_host_sentence_correction.gd | 60 +++++++++++++++++++ ...t_exercise_host_sentence_correction.gd.uid | 1 + .../test_sentence_correction_scene.gd | 38 ++++++++++++ .../test_sentence_correction_scene.gd.uid | 1 + 4 files changed, 100 insertions(+) create mode 100644 tests/exercises/test_exercise_host_sentence_correction.gd create mode 100644 tests/exercises/test_exercise_host_sentence_correction.gd.uid create mode 100644 tests/exercises/test_sentence_correction_scene.gd create mode 100644 tests/exercises/test_sentence_correction_scene.gd.uid diff --git a/tests/exercises/test_exercise_host_sentence_correction.gd b/tests/exercises/test_exercise_host_sentence_correction.gd new file mode 100644 index 0000000..ed72b57 --- /dev/null +++ b/tests/exercises/test_exercise_host_sentence_correction.gd @@ -0,0 +1,60 @@ +extends GutTest +## Reproduction d'un bug signale manuellement via exercise_host (pas +## seulement la scene isolee) : toucher "petit" puis taper "petits" sur +## la mission "Le pont des phrases". + +var _profile_id: String = "" + + +func before_each() -> void: + _profile_id = ProfileManager.create_profile("Test", "CE1") + ProfileManager.select_profile(_profile_id) + GameManager.pending_mission_id = "mission_02_pont_des_phrases" + + +func after_each() -> void: + ProfileManager.delete_profile(_profile_id) + GameManager.pending_mission_id = "" + + +func test_correct_word_tap_and_retype_validates_through_exercise_host() -> void: + var host: Control = load("res://scenes/exercises/exercise_host.tscn").instantiate() + add_child_autofree(host) + await wait_process_frames(1) + + var overlay: Control = host.get_node("%OverlayContainer") + overlay.get_child(0).continued.emit() + await wait_process_frames(1) + + var exercise_scene: Control = host.get_node("%ExerciseContainer").get_child(0) + var exercise: Dictionary = host.get_current_exercise() + assert_eq(exercise["type"], "sentence_correction") + + var faulty_index := -1 + for i in (exercise["tokens"] as Array).size(): + if (exercise["tokens"][i] as Dictionary).get("correct", true) == false: + faulty_index = i + assert_ne(faulty_index, -1, "l'exercice doit avoir un mot fautif") + var expected: String = exercise["tokens"][faulty_index]["expected"] + + var tokens_box: Control = exercise_scene.get_node("%TokensBox") + var faulty_button: Button = tokens_box.get_child(faulty_index) + faulty_button.toggled.emit(true) + await wait_process_frames(1) + + var corrections_box: Control = exercise_scene.get_node("%CorrectionsBox") + assert_eq(corrections_box.get_child_count(), 1) + var edit: LineEdit = corrections_box.get_child(0) + edit.text = expected + + exercise_scene.get_node("%ValidateButton").pressed.emit() + await wait_process_frames(1) + + # Le panneau de retour doit annoncer une reussite ("Bravo !"), pas une + # erreur, et la tentative doit etre enregistree comme reussie. + var feedback: Control = overlay.get_child(overlay.get_child_count() - 1) + var title_label: Label = feedback.get_node("%TitleLabel") + assert_eq(title_label.text, UIText.get_text("feedback_panel.correct_title")) + + var skill_state := ProgressManager.get_skill_state("accord_groupe_nominal") + assert_eq(skill_state["recent_results"], [true]) diff --git a/tests/exercises/test_exercise_host_sentence_correction.gd.uid b/tests/exercises/test_exercise_host_sentence_correction.gd.uid new file mode 100644 index 0000000..27feb17 --- /dev/null +++ b/tests/exercises/test_exercise_host_sentence_correction.gd.uid @@ -0,0 +1 @@ +uid://bcl8njonlx8m0 diff --git a/tests/exercises/test_sentence_correction_scene.gd b/tests/exercises/test_sentence_correction_scene.gd new file mode 100644 index 0000000..3244195 --- /dev/null +++ b/tests/exercises/test_sentence_correction_scene.gd @@ -0,0 +1,38 @@ +extends GutTest +## Reproduction d'un bug signale manuellement : taper la correction attendue +## ("petits") sur le mot fautif ("petit") de +## ce1_accord_adjectif_correction_01 n'etait pas reconnu comme juste. + + +func test_tapping_the_faulty_word_and_typing_its_correction_validates() -> void: + var scene: Control = load("res://scenes/exercises/sentence_correction.tscn").instantiate() + add_child_autofree(scene) + await wait_process_frames(1) + + var exercise: Dictionary = DataLoader.read_json( + "res://data/exercises/ce1/ce1_accord_adjectif_correction_01.json" + ) + scene.load_exercise(exercise) + await wait_process_frames(1) + + var tokens_box: Control = scene.get_node("%TokensBox") + assert_eq(tokens_box.get_child_count(), 4) + var faulty_button: Button = tokens_box.get_child(1) + assert_eq(faulty_button.text, "petit") + + faulty_button.toggled.emit(true) + await wait_process_frames(1) + + var corrections_box: Control = scene.get_node("%CorrectionsBox") + assert_eq(corrections_box.get_child_count(), 1, "un champ de correction doit apparaitre") + var edit: LineEdit = corrections_box.get_child(0) + edit.text = "petits" + + watch_signals(scene) + scene.get_node("%ValidateButton").pressed.emit() + await wait_process_frames(1) + + assert_signal_emitted_with_parameters(scene, "answer_submitted", [[1], {1: "petits"}]) + + var result := AnswerValidator.validate_sentence_correction(exercise, [1], {1: "petits"}) + assert_true(result.is_correct, "'petits' doit etre accepte comme correction de 'petit'") diff --git a/tests/exercises/test_sentence_correction_scene.gd.uid b/tests/exercises/test_sentence_correction_scene.gd.uid new file mode 100644 index 0000000..66f13d3 --- /dev/null +++ b/tests/exercises/test_sentence_correction_scene.gd.uid @@ -0,0 +1 @@ +uid://c7si2246mty63