extends SceneTree # Drives the flashcards study screen's reveal/grading render logic and keyboard mapping # directly (no network): the screen is added to the tree so _ready() builds its controls and # wires the FlashcardService signal connections, but no method that would send an HTTP # request (submit_review/request_next_card) is invoked. var failures := 0 func _initialize() -> void: var Study := load("res://scripts/ui/flashcards_study.gd") var screen = Study.new() screen._build() # Keyboard grading shortcuts: 1-4 map to Again/Hard/Good/Easy; everything else is inert. _assert(Study.rating_for_keycode(KEY_1) == "again", "1 should grade Again") _assert(Study.rating_for_keycode(KEY_2) == "hard", "2 should grade Hard") _assert(Study.rating_for_keycode(KEY_3) == "good", "3 should grade Good") _assert(Study.rating_for_keycode(KEY_4) == "easy", "4 should grade Easy") _assert(Study.rating_for_keycode(KEY_5) == "", "keys outside 1-4 should not grade a card") # Basic card: front-first, then reveal shows answer + extra and the four graded options. screen.current_note = {"type": "basic", "prompt": "2 + 2 = ?", "answer": "4", "extra": "Basic arithmetic"} screen.current_card = {"id": "card-1", "variant": 0, "revision": 1} screen.rating_options = [ {"rating": "again", "intervalLabel": "<1m"}, {"rating": "hard", "intervalLabel": "6m"}, {"rating": "good", "intervalLabel": "1d"}, {"rating": "easy", "intervalLabel": "4d"} ] screen._render_card() _assert(screen.front_label.text == "2 + 2 = ?", "the front should show only the prompt before reveal") _assert(screen.tap_hint_label.visible, "the tap-to-reveal hint should be visible before the answer is shown") _assert(not screen.grade_row.visible, "grading buttons should be hidden before reveal") _assert(not screen.back_label.visible, "the back should stay hidden before reveal") screen._reveal() _assert(screen.back_label.text == "4", "reveal should show the answer on its own, separate from the extra pearl") _assert(screen.extra_label.text == "Basic arithmetic" and screen.extra_label.visible, "the extra takeaway should render in its own labeled section") _assert(screen.answer_caption.visible and screen.extra_caption.visible, "revealing a card with an extra should show both the answer and takeaway captions") _assert(screen.grade_row.visible, "grading buttons should appear after reveal") _assert(not screen.tap_hint_label.visible, "the tap-to-reveal hint should hide once the card is revealed") _assert(screen.grade_buttons["good"].text.contains("1d"), "each grade button should show its computed next interval") # Cloze card: only the card's own variant is hidden on the front; back reveals everything. screen.current_note = {"type": "cloze", "prompt": "{{c1::Paris}} is the capital of France", "answer": "", "extra": "European capitals are a classic Step 3 target"} screen.current_card = {"id": "card-2", "variant": 1, "revision": 1} screen.revealed = false screen._render_card() _assert(screen.front_label.text == "[...] is the capital of France", "cloze front should hide only its own variant") screen._reveal() _assert(screen.back_label.text == "Paris is the capital of France", "cloze back should reveal every deletion") _assert(screen.extra_label.text == "European capitals are a classic Step 3 target" and screen.extra_label.visible, "a cloze note's extra takeaway should also render (previously silently dropped since only basic notes combined answer+extra into one label)") # A card with no extra should hide the takeaway section entirely rather than show an empty caption. screen.current_note = {"type": "basic", "prompt": "What is 1 + 1?", "answer": "2", "extra": ""} screen.current_card = {"id": "card-3", "variant": 0, "revision": 1} screen.revealed = false screen._render_card() screen._reveal() _assert(not screen.extra_label.visible and not screen.extra_caption.visible, "a card with no extra takeaway should hide that section instead of showing an empty caption") # Tapping the card panel reveals the answer, same as pressing Space. screen.current_note = {"type": "basic", "prompt": "Tap test", "answer": "Tapped", "extra": ""} screen.current_card = {"id": "card-4", "variant": 0, "revision": 1} screen.revealed = false screen._render_card() var click := InputEventMouseButton.new() click.button_index = MOUSE_BUTTON_LEFT click.pressed = true screen._on_card_panel_input(click) _assert(screen.revealed and screen.back_label.text == "Tapped", "clicking the card panel should reveal the answer") # A second tap after reveal (or while browsing history) is a no-op - it must not re-trigger # any reveal side effects (grading is guarded elsewhere; this only checks idempotence). screen.back_label.text = "Tapped" screen._on_card_panel_input(click) _assert(screen.back_label.text == "Tapped", "tapping an already-revealed card should be a no-op") screen.free() quit(1 if failures > 0 else 0) func _assert(condition: bool, message: String) -> void: if condition: return failures += 1 push_error(message)