extends MainWindowTestSuite ## The main window holds the top bar, the quiz and the popups opened from the top bar. ## These tests check that the parts are where they belong and still work together: ## clicks reach the right scene, popups dim the whole window, and syncing, changing ## server and the keyboard shortcuts reach both the main window and the quiz. const MAIN_WINDOW_CHILDREN: Array[String] = [ "Background", "TopBar", "Quiz", "PopupBackdrop", "SearchPopup", "SyncPopup", "QRPopup", "ShortcutsPopup", "StatisticsView", "AboutPopup", "LogPopup", "SoundtrackStatsPopup", "SettingsPopup", ] ## The parts of the quiz and the scene each one comes from. const QUIZ_PART_SCENES: Dictionary = { "MusicListPanel": "res://quiz/MusicListPanel.tscn", "PlayersPanel": "res://quiz/PlayersPanel.tscn", "InspirationList": "res://quiz/InspirationList.tscn", "NowPlaying": "res://quiz/NowPlaying.tscn", "QuizToolbar": "res://quiz/QuizToolbar.tscn", "SongControls": "res://quiz/SongControls.tscn", "MusicPlayer": "res://player/MusicPlayer.tscn", } ## The quiz buttons that are disabled while the server syncs. const LOCKED_BY_SYNC: Array[String] = [ "QuizToolbar/SoundTestButton", "QuizToolbar/ResetPlaylistButton", "SongControls/ShowAnswerButton", "SongControls/NextButton", ] const TOP_BAR_CHILDREN: Array[String] = [ "TopPanelContainer", "Title", "SyncButton", "SettingsButton", "ShortcutsButton", "DebugLabel", "UpdateLabel", "OfflineLabel", "AboutButton", "QRButton", "QuizTab", "StatisticsTab", ] ## Each top bar button and the popup it opens in the main window. const TOP_BAR_POPUPS: Dictionary = { "SyncButton": "SyncPopup", "SettingsButton": "SettingsPopup", "ShortcutsButton": "ShortcutsPopup", "AboutButton": "AboutPopup", "QRButton": "QRPopup", } func _top_bar_button(button_name: String) -> Button: return _window.get_node("TopBar/" + button_name) func _children(node: Node) -> Array[String]: var names: Array[String] = [] for child: Node in node.get_children(): names.append(String(child.name)) return names func _center(control: Control) -> Vector2: return control.get_global_rect().get_center() func test_main_window_holds_only_top_bar_quiz_and_its_popups() -> void: assert_array(_children(_window)).contains_exactly_in_any_order(MAIN_WINDOW_CHILDREN) func test_top_bar_is_drawn_below_the_quiz() -> void: # The quiz popups' backdrop has to cover the top bar too assert_int(_window.get_node("TopBar").get_index()) \ .is_less(_window.get_node("Quiz").get_index()) func test_quiz_parts_are_their_own_scenes() -> void: for node_name: String in QUIZ_PART_SCENES: assert_str(_quiz.get_node(node_name).scene_file_path) \ .is_equal(QUIZ_PART_SCENES[node_name]) func test_quiz_downloads_songs_with_a_song_loader() -> void: assert_object(_quiz.song_loader).is_instanceof(SongLoader) func test_quiz_settings_button_sits_under_the_player_bar() -> void: var player: Control = _quiz.get_node("MusicPlayer") var bar: Rect2 = (player.get_node("PlayerBar") as Control).get_global_rect() var button: Rect2 = (_quiz.get_node("QuizSettingsButton") as Control).get_global_rect() assert_float(button.position.y).is_greater_equal(bar.end.y) assert_float(button.position.x).is_equal(player.get_global_rect().position.x) func test_top_bar_holds_only_title_and_buttons() -> void: assert_array(_children(_window.get_node("TopBar"))) \ .contains_exactly_in_any_order(TOP_BAR_CHILDREN) func test_quiz_and_top_bar_let_clicks_through_where_they_are_empty() -> void: assert_int(_quiz.mouse_filter).is_equal(Control.MOUSE_FILTER_IGNORE) assert_int((_window.get_node("TopBar") as Control).mouse_filter) \ .is_equal(Control.MOUSE_FILTER_IGNORE) func test_clicking_each_top_bar_button_opens_its_popup() -> void: for button_name: String in TOP_BAR_POPUPS: var popup: Node = _window.get_node(TOP_BAR_POPUPS[button_name]) await _click_at(_center(_top_bar_button(button_name))) assert_bool(popup.visible).override_failure_message( "%s did not open %s" % [button_name, popup.name]).is_true() if popup is BasePopup: popup.close() else: popup.visible = false await _runner.simulate_frames(1) func test_clicking_quiz_buttons_plays_and_shows_answer() -> void: await _click_at(_center(_quiz.get_node("SongControls/NextButton"))) await _runner.simulate_frames(2) assert_int(Playlist.currently_playing_song).is_equal(0) assert_str((_quiz.get_node("NowPlaying/SoundtrackLabel") as Label).text) \ .is_not_equal("Chrono Trigger") await _click_at(_center(_quiz.get_node("SongControls/ShowAnswerButton"))) assert_str((_quiz.get_node("NowPlaying/SoundtrackLabel") as Label).text) \ .is_equal("Chrono Trigger") func test_clicking_a_song_in_the_music_list_plays_it() -> void: _next_song_is(SONG_INFO[1]) await _play_first_song() _press("SongControls/NextButton") # Changing song waits a moment for the old one to stop await get_tree().create_timer(0.7).timeout assert_int(Playlist.currently_playing_song).is_equal(1) var rows: Array[Node] = _quiz.get_node("MusicListPanel/ScrollContainer/MusicList") \ .get_children() var first_row: Control = rows.filter( func(row: SongRow) -> bool: return row.song_object.song_number == 0)[0] await _click_at(first_row.get_global_rect().position + Vector2(20, first_row.size.y / 2)) await get_tree().create_timer(0.7).timeout assert_int(Playlist.currently_playing_song).is_equal(0) func test_points_from_the_player_list_reach_the_song_and_log() -> void: await _play_first_song() var crono: PlayerObject = _add_player("Crono") (_player_row(crono).get_node("HBoxContainer/AddPoint") as BaseButton).pressed.emit() assert_array(Playlist.song_object_array[0].players_given_point).contains_exactly([crono.id]) var log_rows: Node = _window.get_node("%LogPopup").get_node("%LogVBoxContainer") assert_int(log_rows.get_child_count()) \ .is_equal(1) func test_character_button_in_the_player_list_picks_a_new_character() -> void: var crono: PlayerObject = _add_player("Crono") var character_select: BasePopup = _quiz.get_node("CharacterSelectPopup") (_player_row(crono).get_node("HBoxContainer/Character") as BaseButton).pressed.emit() assert_bool(character_select.visible).is_true() var texture: ImageTexture = ImageTexture.create_from_image( Image.create(4, 4, false, Image.FORMAT_RGBA8)) character_select.character_selected.emit(texture) assert_object(crono.character).is_same(texture) func test_sound_test_button_fetches_the_sound_test_song() -> void: _server.respond_bytes("/music/soundTest", FileAccess.get_file_as_bytes("res://tests/data/short.mp3")) await _click_at(_center(_quiz.get_node("QuizToolbar/SoundTestButton"))) assert_int(_server.count("/music/soundTest")).is_equal(1) func test_loaded_song_list_shows_in_the_music_list() -> void: # No new song finishes downloading, so only loading the list can show the songs _server.hold("/music/rand") _server.respond_json("/music/list", [ {"SongNo": 0, "Soundtrack": "Chrono Trigger", "Song": "Corridors of Time"}, {"SongNo": 1, "Soundtrack": "Final Fantasy VI", "Song": "Dancing Mad"}, ]) Playlist.is_song_downloading = false _quiz.song_loader.load_song_list() var rows: Array[Node] = _quiz.get_node("MusicListPanel/ScrollContainer/MusicList") \ .get_children() assert_array(rows.map(func(row: SongRow) -> int: return row.song_object.song_number)) \ .contains_exactly([0, 1]) func test_quiz_popup_backdrop_covers_the_top_bar() -> void: var character_select: CharacterSelectPopup = _quiz.get_node("CharacterSelectPopup") character_select.show_grid("Crono") await _runner.simulate_frames(1) var backdrop: Control = _quiz.get_node("PopupBackdrop") assert_bool(backdrop.visible).is_true() assert_bool(backdrop.get_global_rect().encloses( (_window.get_node("TopBar/TopPanelContainer") as Control).get_global_rect())) \ .is_true() func test_click_on_top_bar_while_quiz_popup_is_open_only_closes_it() -> void: var character_select: CharacterSelectPopup = _quiz.get_node("CharacterSelectPopup") var about: BasePopup = _window.get_node("AboutPopup") character_select.show_grid("Crono") await _runner.simulate_frames(1) await _click_at(_center(_top_bar_button("AboutButton"))) assert_bool(character_select.visible).is_false() assert_bool(about.visible).is_false() func test_sync_disables_quiz_buttons_until_finished() -> void: _server.respond_bytes("/sync", PackedByteArray()) _server.respond_json("/sync/progress", {"progress": 10, "time_spent": "00:00:01"}) var sync_popup: BasePopup = _window.get_node("SyncPopup") _top_bar_button("SyncButton").pressed.emit() sync_popup.sync_timer.stop() for button_name: String in LOCKED_BY_SYNC: assert_bool((_quiz.get_node(button_name) as Button).disabled) \ .override_failure_message(button_name + " not disabled").is_true() assert_bool(_top_bar_button("SyncButton").disabled).is_false() _server.respond_json("/sync/progress", UiTour.SYNC_RESULT) var reloads_before: int = _server.count("/music/all/order") sync_popup._on_sync_timeout() await _runner.simulate_frames(2) for button_name: String in LOCKED_BY_SYNC: assert_bool((_quiz.get_node(button_name) as Button).disabled) \ .override_failure_message(button_name + " still disabled").is_false() # The search list in the main window and the quiz playlist are both reloaded assert_int(_server.count("/music/all/order")).is_equal(reloads_before + 1) assert_int(_server.count("/music/reset")).is_equal(1) func test_changing_server_reloads_search_and_quiz() -> void: var search_loads: int = _server.count("/music/all/order") var inspiration_loads: int = _server.count("/music/all") var player: PlayerObject = _add_player("Crono") player.add_point() _window.get_node("SettingsPopup").server_changed.emit() assert_int(_server.count("/music/all/order")).is_equal(search_loads + 1) assert_int(_server.count("/music/all")).is_equal(inspiration_loads + 1) assert_int(_server.count("/music/reset")).is_equal(1) assert_int(player.player_score).is_equal(0) func test_alt_k_opens_shortcuts_and_alt_l_opens_log() -> void: await _press_keys([KEY_ALT, KEY_K]) assert_bool((_window.get_node("ShortcutsPopup") as Control).visible).is_true() await _press_keys([KEY_ALT, KEY_L]) assert_bool((_window.get_node("LogPopup") as Control).visible).is_true() func test_alt_down_lowers_the_volume() -> void: var bus: int = AudioServer.get_bus_index("Master") var volume_db: float = AudioServer.get_bus_volume_db(bus) var slider: HSlider = _quiz.get_node("MusicPlayer").volume_slider var volume: float = slider.value await _press_keys([KEY_ALT, KEY_DOWN]) assert_float(slider.value).is_less(volume) AudioServer.set_bus_volume_db(bus, volume_db) func test_alt_enter_switches_fullscreen_and_settings_shows_it() -> void: var settings: BasePopup = _window.get_node("SettingsPopup") var was_fullscreen: bool = Settings.fullscreen await _press_keys([KEY_ALT, KEY_ENTER]) settings.open() assert_bool(Settings.fullscreen).is_equal(!was_fullscreen) assert_bool(settings.fullscreen_button.button_pressed).is_equal(!was_fullscreen) await _press_keys([KEY_ALT, KEY_ENTER]) assert_bool(Settings.fullscreen).is_equal(was_fullscreen) func test_debug_label_is_in_the_top_bar_and_stays_on_the_statistics_tab() -> void: var label: Label = _window.get_node("TopBar/DebugLabel") assert_bool(label.visible).is_equal(Settings.is_debug) _top_bar_button("StatisticsTab").pressed.emit() assert_bool(label.is_visible_in_tree()).is_equal(Settings.is_debug) func test_search_is_in_the_quiz_toolbar_and_opens_the_search_popup() -> void: assert_object(_window.get_node_or_null("TopBar/SearchButton")).is_null() await _click_at(_center(_quiz.get_node("QuizToolbar/SearchButton"))) assert_bool((_window.get_node("SearchPopup") as Control).visible).is_true() func test_alt_s_opens_search() -> void: await _press_keys([KEY_ALT, KEY_S]) assert_bool((_window.get_node("SearchPopup") as Control).visible).is_true() func test_music_player_fits_between_the_song_buttons_and_quiz_settings() -> void: var bar: Rect2 = _quiz.get_node("MusicPlayer/PlayerBar").get_global_rect() var above: Rect2 = _quiz.get_node("SongControls/NextButton").get_global_rect() var below: Rect2 = _quiz.get_node("QuizSettingsButton").get_global_rect() assert_float(bar.position.y).is_greater_equal(above.end.y + 4) assert_float(bar.end.y).is_less_equal(below.position.y - 2) # As wide as the music list above it var list: Rect2 = _quiz.get_node("MusicListPanel").get_global_rect() assert_float(bar.position.x).is_equal(list.position.x)