85 lines
5.3 KiB
GDScript
85 lines
5.3 KiB
GDScript
extends SceneTree
|
|
|
|
var failures := 0
|
|
|
|
|
|
func _initialize() -> void:
|
|
call_deferred("_run")
|
|
|
|
|
|
func _run() -> void:
|
|
var host := Control.new()
|
|
host.size = Vector2(240, 720)
|
|
root.add_child(host)
|
|
var selector = load("res://scripts/ui/study_selector.gd").new()
|
|
selector.size = host.size
|
|
host.add_child(selector)
|
|
await process_frame
|
|
selector._show_stage("step3")
|
|
selector._refresh_grid_columns()
|
|
await process_frame
|
|
|
|
_assert(selector.stage_scroll != null, "study selection should scroll vertically on short web viewports")
|
|
_assert(selector.stage_scroll.horizontal_scroll_mode == ScrollContainer.SCROLL_MODE_DISABLED, "study selection should never introduce horizontal scrolling")
|
|
_assert(selector.mode_grid.columns == 1, "phone-width mode cards should use one column")
|
|
_assert(selector.stage_content.get_combined_minimum_size().x <= host.size.x, "phone-width study content should fit inside the web canvas (minimum %.1f px)" % selector.stage_content.get_combined_minimum_size().x)
|
|
for card in selector.responsive_cards:
|
|
_assert(card.custom_minimum_size.x == 0.0, "phone-width cards should release their desktop minimum width")
|
|
for row in selector.responsive_card_rows:
|
|
_assert(row.vertical, "phone-width card content should stack instead of clipping")
|
|
_assert(selector.mode_view.find_children("*", "RichTextLabel", true, false).is_empty(), "study modes should not render the legacy rich-text simulation note")
|
|
var dynamic_icon := selector.mode_view.find_child("DynamicQuestionsIcon", true, false) as TextureRect
|
|
_assert(dynamic_icon != null, "dynamic questions should expose its mode icon")
|
|
if dynamic_icon != null:
|
|
_assert(dynamic_icon.texture.resource_path == "res://assets/icons/lucide/activity.svg", "dynamic questions should use a web-safe vector icon")
|
|
selector._show_stage("question_bank")
|
|
await process_frame
|
|
_assert(selector.question_bank_launch_view.visible and not selector.mode_view.visible, "direct question bank navigation should show only the focused launch state")
|
|
_assert(selector.launch_retry_button != null and selector.launch_error_label != null, "question bank launch should retain retry and error controls")
|
|
|
|
var main_screen = load("res://scripts/ui/main.gd").new()
|
|
main_screen.size = host.size
|
|
host.add_child(main_screen)
|
|
await process_frame
|
|
main_screen._update_responsive_layout()
|
|
_assert(main_screen.theme_button.custom_minimum_size == Vector2(44, 44), "the theme toggle should keep a stable square footprint")
|
|
_assert(main_screen.theme_button.size.x >= 44.0 and main_screen.theme_button.size.y >= 44.0, "the theme toggle should not collapse into an oval")
|
|
_assert(main_screen.theme_button.icon_alignment == HORIZONTAL_ALIGNMENT_CENTER, "the theme toggle icon should remain centered")
|
|
_assert(main_screen.theme_button.get_index() < main_screen.account_button.get_index(), "the theme toggle should appear before the account dropdown")
|
|
_assert(main_screen.account_chevron_button.get_index() == main_screen.account_button.get_index() + 1, "the account avatar and chevron should remain adjacent")
|
|
_assert(main_screen.header_panel.find_child("HeaderStats", true, false) == null, "the application header should not display rating or role labels")
|
|
var pause_menu_has_profile := false
|
|
for menu_button in main_screen.app_menu.find_children("*", "Button", true, false):
|
|
if menu_button.text == "Profile":
|
|
pause_menu_has_profile = true
|
|
_assert(not pause_menu_has_profile, "the paused-run menu should not duplicate profile navigation")
|
|
_assert(main_screen.session_expiry_dialog != null, "the application shell should construct a global session-expiry prompt")
|
|
main_screen.session_expiry_dialog._show_warning(120)
|
|
_assert(main_screen.session_expiry_dialog.visible, "the session-expiry prompt should cover every authenticated screen")
|
|
_assert(main_screen.session_expiry_dialog.message_label.text.contains("02:00"), "the session-expiry prompt should display a live countdown")
|
|
main_screen.session_expiry_dialog._refresh_started()
|
|
_assert(main_screen.session_expiry_dialog.stay_button.disabled, "the renewal action should lock while a refresh request is active")
|
|
main_screen.session_expiry_dialog._refresh_failed("Connection interrupted.", false)
|
|
_assert(not main_screen.session_expiry_dialog.stay_button.disabled, "a transient renewal failure should permit retry before expiry")
|
|
var cancel_event := InputEventAction.new()
|
|
cancel_event.action = "ui_cancel"
|
|
cancel_event.pressed = true
|
|
main_screen.session_expiry_dialog._unhandled_input(cancel_event)
|
|
_assert(main_screen.session_expiry_dialog.visible, "the session-expiry prompt should not be dismissible with Escape")
|
|
main_screen.session_expiry_dialog.dismiss_for_logout()
|
|
_assert(not main_screen.account_chevron_button.visible, "nonessential account controls should collapse on phone-width headers")
|
|
var question_bank_nav := main_screen.sidebar.find_child("NavQuestionBank", true, false) as Button
|
|
_assert(question_bank_nav != null and question_bank_nav.text == "Question Bank", "the Practice navigation item should be replaced by Question Bank")
|
|
_assert(main_screen.sidebar.find_child("NavPractice", true, false) == null, "the sidebar should not retain a Practice navigation item")
|
|
|
|
host.queue_free()
|
|
await process_frame
|
|
quit(1 if failures > 0 else 0)
|
|
|
|
|
|
func _assert(condition: bool, message: String) -> void:
|
|
if condition:
|
|
return
|
|
failures += 1
|
|
push_error(message)
|