extends MainWindowTestSuite ## Soundtrack covers on the statistics cards, in the popup and next to the answer in the ## quiz, loaded for the cards on screen; changing a cover from the popup; and editing a ## soundtrack's alternative names, which search then finds it by. const STATISTICS: Array[Dictionary] = [ {"soundtrack_uuid": "st-1", "soundtrack_name": "Chrono Trigger", "soundtrack_played": 5, "cover_modified": 100, "alternative_names": [], "songs": [ {"song_name": "Corridors of Time", "times_played": 1}, ]}, {"soundtrack_uuid": "st-2", "soundtrack_name": "Terranigma", "soundtrack_played": 0, "songs": []}, ] var _view: StatisticsView var _popup: SoundtrackStatsPopup var _png: PackedByteArray func _before_window_starts() -> void: _server.respond_json("/statistics/soundtracks", STATISTICS) var image: Image = Image.create(6, 8, false, Image.FORMAT_RGB8) _png = image.save_png_to_buffer() _server.respond_bytes("/soundtracks/st-1/cover", _png) # The soundtrack list tells the quiz which soundtracks have covers _server.respond_json("/soundtracks", [ {"uuid": "st-1", "name": "Chrono Trigger", "cover_modified": 100}, {"uuid": "st-2", "name": "Terranigma"}, ]) _next_song_is({"SongNo": 0, "Soundtrack": "Chrono Trigger", "SoundtrackUuid": "st-1", "Song": "Corridors of Time"}) func before_test() -> void: # Covers are kept next to the settings, which the tests keep in a folder of their own OS.set_environment(Settings.CONFIG_DIR_ENV, create_temp_dir("covers_%d" % randi())) await super() _view = _window.get_node("StatisticsView") _popup = _window.get_node("SoundtrackStatsPopup") func _open_statistics() -> void: _window.show_tab(&"statistics") await _runner.simulate_frames(3) func _card(soundtrack_name: String) -> SoundtrackCard: for card: SoundtrackCard in _view.cards: if card.soundtrack.soundtrack_name == soundtrack_name: return card return null func test_card_shows_its_cover() -> void: await _open_statistics() var card: SoundtrackCard = _card("Chrono Trigger") assert_object(card.cover_picture.texture).is_not_null() assert_bool(card.cover_placeholder.visible).is_false() func test_card_without_a_cover_shows_initials_and_asks_nothing() -> void: await _open_statistics() var card: SoundtrackCard = _card("Terranigma") assert_object(card.cover_picture.texture).is_null() assert_bool(card.cover_placeholder.visible).is_true() assert_str(card.cover_placeholder.text).is_equal("T") assert_int(_server.count("/soundtracks/st-2/cover")).is_equal(0) func test_cover_missing_on_the_server_keeps_the_initials() -> void: _server.respond_bytes("/soundtracks/st-1/cover", PackedByteArray(), 404) await _open_statistics() assert_bool(_card("Chrono Trigger").cover_placeholder.visible).is_true() assert_str(_card("Chrono Trigger").cover_placeholder.text).is_equal("CT") func test_covers_load_for_cards_on_screen_as_they_scroll_in() -> void: var many: Array[Dictionary] = [] for i: int in 40: many.append({"soundtrack_uuid": "many-%02d" % i, "soundtrack_name": "Game %02d" % i, "soundtrack_played": 1, "cover_modified": 100, "songs": []}) _server.respond_bytes("/soundtracks/many-%02d/cover" % i, _png) _server.respond_json("/statistics/soundtracks", many) await _open_statistics() var asked: Callable = func() -> int: return _server.requested_paths.filter( func(path: String) -> bool: return path.begins_with("/soundtracks/many-")).size() var on_screen: int = asked.call() assert_int(on_screen).is_greater(0) assert_int(on_screen).is_less(40) var scroll_bar: VScrollBar = _view.card_scroll.get_v_scroll_bar() scroll_bar.value = scroll_bar.max_value await _runner.simulate_frames(2) assert_int(asked.call()).is_greater(on_screen) func test_popup_shows_the_cover_large() -> void: await _open_statistics() _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) assert_object(_popup.cover_picture.texture).is_not_null() assert_bool(_popup.cover_placeholder.visible).is_false() assert_str(_popup.names_label.text).is_equal("No other names yet") func test_editing_names_saves_them_and_search_finds_the_new_name() -> void: _server.respond_json("/soundtracks/st-1/alternative-names", {"uuid": "st-1", "name": "Chrono Trigger", "alternative_names": ["CT", "クロノ・トリガー"]}) await _open_statistics() _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) _popup.edit_names() assert_bool(_popup.names_editor.visible).is_true() _popup.new_name_field.text = "CT" _popup.add_name() _popup.new_name_field.text = "クロノ・トリガー" _popup.save_names() var index: int = _server.requested_paths.find("/soundtracks/st-1/alternative-names") assert_int(index).is_not_equal(-1) assert_int(_server.requested_methods[index]).is_equal(HTTPClient.METHOD_PUT) var body: Dictionary = JSON.parse_string(_server.requested_bodies[index]) assert_array(body.names).contains_exactly(["CT", "クロノ・トリガー"]) assert_bool(_popup.names_editor.visible).is_false() assert_str(_popup.names_label.text).is_equal("CT, クロノ・トリガー") _view.search_field.text = "クロノ" _view.apply_filter() var shown: Array = _view.cards.filter(func(card: SoundtrackCard) -> bool: return card.visible) assert_array(shown.map(func(card: SoundtrackCard) -> String: return card.soundtrack.soundtrack_name)).contains_exactly(["Chrono Trigger"]) func test_editor_skips_empty_repeated_and_own_names() -> void: await _open_statistics() _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) _popup.edit_names() for name: String in [" ", "CT", "ct", "Chrono Trigger"]: _popup.new_name_field.text = name _popup.add_name() assert_int(_popup.editor_names.get_child_count()).is_equal(1) func test_removing_a_name_in_the_editor() -> void: await _open_statistics() _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) _popup.edit_names() _popup.new_name_field.text = "CT" _popup.add_name() _popup.remove_name("CT") assert_int(_popup.editor_names.get_child_count()).is_equal(0) func test_failed_save_keeps_the_editor_open() -> void: _server.respond_json("/soundtracks/st-1/alternative-names", {}, 500) await _open_statistics() _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) _popup.edit_names() _popup.new_name_field.text = "CT" _popup.save_names() assert_bool(_popup.names_editor.visible).is_true() assert_bool(_popup.editor_status.visible).is_true() assert_bool(_popup.save_names_button.disabled).is_false() func test_search_popup_finds_soundtracks_by_alternative_names() -> void: _server.respond_json("/soundtracks", [ {"uuid": "st-1", "name": "Chrono Trigger", "alternative_names": ["CT"]}, {"uuid": "st-2", "name": "Terranigma", "alternative_names": []}, ]) var search: SearchPopup = _window.get_node("SearchPopup") search.get_list_of_soundtracks() search.open() search.search_field.text = "ct" search.search() assert_array(search.shown_names()).contains(["Chrono Trigger"]) var row: PanelContainer = search.search_results.get_child(0) assert_str((row.get_child(0) as RichTextLabel).get_parsed_text()).contains("CT") func _open_cover_menu() -> void: await _open_statistics() _popup.show_soundtrack(_card("Chrono Trigger").soundtrack) _popup.change_cover() # The server's answer for the soundtrack once its cover changed func _cover_time_becomes(modified: int) -> void: _server.respond_json("/soundtracks/st-1", {"uuid": "st-1", "name": "Chrono Trigger", "cover_modified": modified, "songs": []}) func _picture_file(bytes: PackedByteArray) -> String: var path: String = create_temp_dir("pictures_%d" % randi()).path_join("cover.jpg") var file: FileAccess = FileAccess.open(path, FileAccess.WRITE) file.store_buffer(bytes) file.close() return path func _downloads(path: String) -> int: var count: int = 0 for i: int in _server.requested_paths.size(): if _server.requested_paths[i] == path \ && _server.requested_methods[i] == HTTPClient.METHOD_GET: count += 1 return count func _now_playing() -> NowPlaying: return _quiz.get_node("NowPlaying") func test_cover_menu_opens_in_place_of_the_songs() -> void: await _open_cover_menu() assert_bool(_popup.cover_editor.visible).is_true() assert_bool(_popup.song_scroll.visible).is_false() assert_bool(_popup.edit_names_button.disabled).is_true() assert_bool(_popup.remove_cover_button.disabled).is_false() _popup.stop_editing() assert_bool(_popup.cover_editor.visible).is_false() assert_bool(_popup.song_scroll.visible).is_true() func test_soundtrack_without_a_cover_has_nothing_to_remove() -> void: await _open_statistics() _popup.show_soundtrack(_card("Terranigma").soundtrack) _popup.change_cover() assert_bool(_popup.remove_cover_button.disabled).is_true() func test_uploaded_picture_becomes_the_cover() -> void: var bytes: PackedByteArray = FileAccess.get_file_as_bytes( "res://tests/data/covers/cover_0.jpg") _cover_time_becomes(200) await _open_cover_menu() _popup.upload_picture(_picture_file(bytes)) var index: int = _server.requested_methods.find(HTTPClient.METHOD_PUT) assert_str(_server.requested_paths[index]).is_equal("/soundtracks/st-1/cover") assert_array(_server.requested_raw_bodies[index]).is_equal(bytes) assert_array(_server.requested_headers[index]).contains(["Content-Type: image/jpeg"]) # The new cover is downloaded again for the popup and the card assert_int(_card("Chrono Trigger").soundtrack.cover_modified).is_equal(200) await _runner.simulate_frames(2) # Downloaded once more, for the popup and the card together assert_int(_downloads("/soundtracks/st-1/cover")).is_equal(2) assert_object(_card("Chrono Trigger").cover_picture.texture).is_not_null() assert_str(_popup.cover_status.text).is_equal("The new cover is saved.") func test_file_that_is_not_a_picture_is_not_uploaded() -> void: await _open_cover_menu() _popup.upload_picture(_picture_file("not a picture".to_utf8_buffer())) assert_int(_server.requested_methods.count(HTTPClient.METHOD_PUT)).is_equal(0) assert_str(_popup.cover_status.text).contains("JPEG, PNG and WebP") func test_cover_picked_by_id() -> void: _server.respond_bytes("/soundtracks/st-1/cover/source", PackedByteArray(), 204) _cover_time_becomes(300) await _open_cover_menu() _popup.cover_source_button.select(1) _popup.cover_id_field.text = " 21 " _popup.use_cover_source() var index: int = _server.requested_paths.find("/soundtracks/st-1/cover/source") assert_int(_server.requested_methods[index]).is_equal(HTTPClient.METHOD_PUT) var body: Dictionary = JSON.parse_string(_server.requested_bodies[index]) assert_str(body.source).is_equal("anilist") assert_str(body.id).is_equal("21") assert_int(_popup.soundtrack.cover_modified).is_equal(300) func test_no_id_asks_nothing() -> void: await _open_cover_menu() _popup.use_cover_source() assert_int(_server.count("/soundtracks/st-1/cover/source")).is_equal(0) func test_removed_cover_shows_the_initials() -> void: _cover_time_becomes(0) await _open_cover_menu() _popup.remove_cover() var index: int = _server.requested_methods.find(HTTPClient.METHOD_DELETE) assert_str(_server.requested_paths[index]).is_equal("/soundtracks/st-1/cover") assert_bool(_popup.cover_placeholder.visible).is_true() assert_bool(_card("Chrono Trigger").cover_placeholder.visible).is_true() assert_bool(FileAccess.file_exists(CoverCache.folder().path_join("st-1"))).is_false() assert_bool(_popup.remove_cover_button.disabled).is_true() func test_refused_change_shows_the_servers_reason() -> void: _server.respond_json("/soundtracks/st-1/cover/source", {"error": "covers are not enabled on this server"}, 503) await _open_cover_menu() _popup.cover_id_field.text = "21" _popup.use_cover_source() assert_str(_popup.cover_status.text).is_equal( "Could not change the cover: covers are not enabled on this server.") assert_bool(_popup.use_source_button.disabled).is_false() func test_answer_shows_the_soundtracks_cover() -> void: await _play_first_song() assert_bool(_now_playing().has_cover()).is_false() _press("SongControls/ShowAnswerButton") assert_bool(_now_playing().has_cover()).is_true() await _runner.simulate_frames(1) # The cover sits just left of the titles, and the two are centered together var area: Rect2 = _now_playing().get_global_rect() var cover: Rect2 = _now_playing().cover_frame.get_global_rect() var titles: Rect2 = _now_playing().titles.get_global_rect() assert_float(titles.position.x - cover.end.x).is_equal(32.0) assert_float(absf((cover.position.x - area.position.x) - (area.end.x - titles.end.x))) \ .is_less_equal(1.0) # Up and down, the cover is centered between the top bar and the song list, and the # titles on the cover assert_float(absf((cover.position.y - area.position.y) - (area.end.y - cover.end.y))) \ .is_less_equal(1.0) var text_middle: float = (_now_playing().soundtrack_label.get_global_rect().position.y + _now_playing().song_label.get_global_rect().end.y) / 2.0 assert_float(absf(text_middle - cover.get_center().y)).is_less_equal(1.0) func test_answer_without_a_cover_shows_only_the_titles() -> void: _view.covers.set_cover_times({"st-1": 0}) await _play_first_song() _press("SongControls/ShowAnswerButton") assert_bool(_now_playing().has_cover()).is_false() assert_int(_server.count("/soundtracks/st-1/cover")).is_equal(0) func test_next_song_hides_the_cover_until_its_answer() -> void: await _play_first_song() _press("SongControls/ShowAnswerButton") _press("SongControls/NextButton") # The playing song is stopped first, which takes half a second await await_millis(700) assert_int(Playlist.currently_playing_song).is_equal(1) assert_bool(_now_playing().has_cover()).is_false() func test_changed_cover_shows_in_the_quiz_too() -> void: await _play_first_song() _press("SongControls/ShowAnswerButton") _cover_time_becomes(0) _popup.show_soundtrack(SoundtrackWithSongs.from_dict(STATISTICS[0])) _popup.change_cover() _popup.remove_cover() assert_bool(_now_playing().has_cover()).is_false()