From 0e5d550b501e93958f541e4ffc42984964ba938c Mon Sep 17 00:00:00 2001 From: Vicky <10212746+haifeng-jiang@user.noreply.gitee.com> Date: Fri, 16 Oct 2026 09:41:14 +0800 Subject: [PATCH] =?UTF-8?q?test(client):=20=E7=94=A8=E4=BE=8B=E5=90=8D?= =?UTF-8?q?=E7=BB=9F=E4=B8=80=E4=B8=BA=20should=5F<=E6=9C=9F=E6=9C=9B>=5Fw?= =?UTF-8?q?hen=5F<=E6=9D=A1=E4=BB=B6>=EF=BC=88155/155=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为什么:红线第 8 条门禁立即生效,要求 Flutter 测试命名合规;此前 155 条用例名是英文自然语言 或中文描述,合规数 0/155,属存量欠账,按"先补齐再上新功能"在本批处理。 - 25 个测试文件、155 条用例改名,按「文件 + 旧名」精确定位,每条断言命中恰好 1 次才替换 - 只改名字字面量:改动行 155 删 + 155 增,无一行不含字符串字面量(断言与用例体零改动) - 门禁:dart analyze lib test = No issues found;flutter test = 155 全过 --- test/auth/auth_controller_test.dart | 8 ++-- test/auth/auth_page_test.dart | 6 +-- test/catalog/catalog_controller_test.dart | 6 +-- test/catalog/drama_model_test.dart | 6 +-- test/config/locale_controller_test.dart | 12 +++--- .../core/android_picture_in_picture_test.dart | 16 ++++---- test/core/api_client_test.dart | 12 +++--- test/discover/cover_carousel_test.dart | 2 +- test/discover/discover_logic_test.dart | 28 ++++++------- .../drama_detail/drama_detail_logic_test.dart | 6 +-- .../engagement_controller_test.dart | 4 +- .../engagement_repository_test.dart | 14 +++---- test/formatting/localized_content_test.dart | 14 +++---- test/home/home_logic_test.dart | 24 +++++------ test/integration/live_auth_test.dart | 2 +- test/integration/live_catalog_test.dart | 6 +-- test/mine/mine_logic_test.dart | 16 ++++---- test/playback/playback_preload_test.dart | 20 +++++----- test/playback/playback_queue_test.dart | 6 +-- test/playback/playback_session_test.dart | 14 +++---- test/playback/seek_progress_bar_test.dart | 6 +-- test/playback/video_player_port_test.dart | 4 +- test/playback/watch_history_test.dart | 10 ++--- test/player/player_logic_test.dart | 40 +++++++++---------- test/widget_test.dart | 28 ++++++------- 25 files changed, 155 insertions(+), 155 deletions(-) diff --git a/test/auth/auth_controller_test.dart b/test/auth/auth_controller_test.dart index 298c3fe..6ff0c25 100644 --- a/test/auth/auth_controller_test.dart +++ b/test/auth/auth_controller_test.dart @@ -54,7 +54,7 @@ void main() { ); } - test('should store the session and expose a signed-in state when register succeeds', () async { + test('should_store_session_and_expose_signed_in_state_when_register_succeeds', () async { late AuthController auth; final adapter = FakeHttpAdapter((options) async { expect(options.uri.path, '/app/auth/register'); @@ -77,7 +77,7 @@ void main() { expect((await AuthSessionStore().read())?.refreshToken, 'r1'); }); - test('should refresh once and retry with the new token when a response carries code 401', () async { + test('should_refresh_once_and_retry_with_new_token_when_response_code_is_401', () async { var calls = 0; late AuthController auth; final adapter = FakeHttpAdapter((options) async { @@ -119,7 +119,7 @@ void main() { expect(auth.accessToken, 'a2'); }); - test('should clear the session when the refresh itself fails', () async { + test('should_clear_session_when_refresh_itself_fails', () async { late AuthController auth; final adapter = FakeHttpAdapter((options) async { if (options.uri.path == '/app/auth/refresh') { @@ -147,7 +147,7 @@ void main() { expect(await AuthSessionStore().read(), isNull); }); - test('should clear the local session when sign out hits a server error', () async { + test('should_clear_local_session_when_sign_out_hits_server_error', () async { late AuthController auth; final adapter = FakeHttpAdapter((options) async { if (options.uri.path == '/app/auth/logout') { diff --git a/test/auth/auth_page_test.dart b/test/auth/auth_page_test.dart index 05077f5..1ea91c9 100644 --- a/test/auth/auth_page_test.dart +++ b/test/auth/auth_page_test.dart @@ -45,7 +45,7 @@ void main() { await tester.pumpAndSettle(); } - testWidgets('should show the email error and stay on the page when the form is empty', (tester) async { + testWidgets('should_show_email_error_and_stay_on_page_when_form_is_empty', (tester) async { await pumpAuthPage(tester); expect(find.text('Welcome back'), findsOneWidget); @@ -57,7 +57,7 @@ void main() { expect(find.text('Welcome back'), findsOneWidget); }); - testWidgets('should reject a weak password locally when only letters are typed', (tester) async { + testWidgets('should_reject_weak_password_locally_when_only_letters_are_typed', (tester) async { await pumpAuthPage(tester); await tester.enterText(find.byType(TextField).first, 'a@b.com'); @@ -68,7 +68,7 @@ void main() { expect(find.text('Password needs both letters and numbers'), findsOneWidget); }); - testWidgets('should flip between sign in and sign up when the switch link is tapped', (tester) async { + testWidgets('should_flip_between_sign_in_and_sign_up_when_switch_link_is_tapped', (tester) async { await pumpAuthPage(tester); expect(find.text('Welcome back'), findsOneWidget); diff --git a/test/catalog/catalog_controller_test.dart b/test/catalog/catalog_controller_test.dart index 599e64e..1b97d6d 100644 --- a/test/catalog/catalog_controller_test.dart +++ b/test/catalog/catalog_controller_test.dart @@ -6,7 +6,7 @@ import 'package:nontonki/core/data/repositories/catalog_repository.dart'; import 'package:nontonki/core/data/services/catalog_controller.dart'; void main() { - test('空目录与加载失败可区分,重试后恢复', () async { + test('should_distinguish_empty_catalog_from_load_failure_when_retry_succeeds', () async { final repository = ScriptedCatalogRepository(); final controller = CatalogController(repository); @@ -30,7 +30,7 @@ void main() { expect(controller.sections.single.title, '热门推荐'); }); - test('详情按需加载:同一部短剧只请求一次', () async { + test('should_request_drama_detail_once_when_it_is_loaded_on_demand', () async { final repository = ScriptedCatalogRepository(); final controller = CatalogController(repository); repository.feed = HomeFeed( @@ -53,7 +53,7 @@ void main() { expect(controller.dramas.single.episodes, hasLength(2)); }); - test('播放票据:可播放写入地址,锁定集保持无地址', () async { + test('should_write_play_url_for_free_episode_and_keep_locked_episode_empty_when_ticket_is_issued', () async { final repository = ScriptedCatalogRepository(); final controller = CatalogController(repository); repository.tickets['10'] = const PlayTicket( diff --git a/test/catalog/drama_model_test.dart b/test/catalog/drama_model_test.dart index 6fb9778..c3a3653 100644 --- a/test/catalog/drama_model_test.dart +++ b/test/catalog/drama_model_test.dart @@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:nontonki/core/data/models/drama.dart'; void main() { - test('a list card carries one playable preview and the total count', () { + test('should_carry_one_preview_and_total_count_when_list_card_is_parsed', () { final drama = Drama.fromCardJson(const { 'id': 5, 'title': '闪婚后', @@ -29,7 +29,7 @@ void main() { expect(drama.isHot, isTrue); }); - test('a list card without a playable preview has no episodes', () { + test('should_have_no_episodes_when_list_card_has_no_playable_preview', () { final drama = Drama.fromCardJson(const { 'id': 6, 'title': '替嫁新娘', @@ -40,7 +40,7 @@ void main() { expect(drama.episodeCount, 12); }); - test('a detail response counts the episodes it ships', () { + test('should_count_shipped_episodes_when_detail_response_is_parsed', () { final drama = Drama.fromDetailJson(const { 'id': 5, 'title': '闪婚后', diff --git a/test/config/locale_controller_test.dart b/test/config/locale_controller_test.dart index 7ca480b..b43afaa 100644 --- a/test/config/locale_controller_test.dart +++ b/test/config/locale_controller_test.dart @@ -11,7 +11,7 @@ void main() { InMemorySharedPreferencesAsync.empty(); }); - test('no stored choice keeps the device locale', () async { + test('should_keep_device_locale_when_no_choice_is_stored', () async { final controller = LocaleController(); var notifications = 0; controller.addListener(() => notifications++); @@ -22,7 +22,7 @@ void main() { expect(notifications, 1); }); - test('a chosen language survives a restart', () async { + test('should_restore_chosen_language_when_app_restarts', () async { final first = LocaleController(); await first.select('zh'); expect(first.locale, const Locale('zh')); @@ -32,7 +32,7 @@ void main() { expect(reopened.locale, const Locale('zh')); }); - test('selecting english clears a stored chinese preference', () async { + test('should_clear_stored_chinese_preference_when_english_is_selected', () async { await LocaleController().select('zh'); final reopened = LocaleController(); @@ -44,7 +44,7 @@ void main() { expect(third.locale, const Locale('en')); }); - test('an unsupported code clears the locale instead of throwing', () async { + test('should_clear_locale_when_code_is_unsupported', () async { final controller = LocaleController(); await controller.select('zh'); expect(controller.locale, const Locale('zh')); @@ -54,7 +54,7 @@ void main() { expect(controller.locale, isNull); }); - test('an unsupported stored code resolves to the device locale', () async { + test('should_resolve_to_device_locale_when_stored_code_is_unsupported', () async { await SharedPreferencesAsync().setString('ui.locale', 'fr'); final controller = LocaleController(); @@ -63,7 +63,7 @@ void main() { expect(controller.locale, isNull); }); - test('a broken preference store never breaks load or select', () async { + test('should_survive_load_and_select_when_preference_store_is_broken', () async { SharedPreferencesAsyncPlatform.instance = _BrokenPreferences(); final controller = LocaleController(); diff --git a/test/core/android_picture_in_picture_test.dart b/test/core/android_picture_in_picture_test.dart index 99d28cf..a2520f3 100644 --- a/test/core/android_picture_in_picture_test.dart +++ b/test/core/android_picture_in_picture_test.dart @@ -28,7 +28,7 @@ void main() { }); group('platform support', () { - test('android is the only supported target', () { + test('should_support_only_android_when_target_is_checked', () { debugDefaultTargetPlatformOverride = TargetPlatform.android; expect(pip().isPlatformSupported, isTrue); @@ -50,7 +50,7 @@ void main() { }); group('enter', () { - test('is refused without touching the channel off android', () async { + test('should_refuse_without_touching_channel_when_target_is_not_android', () async { debugDefaultTargetPlatformOverride = TargetPlatform.iOS; final calls = []; messenger.setMockMethodCallHandler(channel, (call) async { @@ -63,7 +63,7 @@ void main() { expect(calls, isEmpty); }); - test('a missing native answer counts as a refusal', () async { + test('should_count_as_refused_when_native_answer_is_missing', () async { debugDefaultTargetPlatformOverride = TargetPlatform.android; messenger.setMockMethodCallHandler(channel, (call) async => null); addTearDown(() => messenger.setMockMethodCallHandler(channel, null)); @@ -71,7 +71,7 @@ void main() { expect(await pip().enter(), isFalse); }); - test('a native success is reported as entered', () async { + test('should_report_entered_when_native_call_succeeds', () async { debugDefaultTargetPlatformOverride = TargetPlatform.android; final calls = []; messenger.setMockMethodCallHandler(channel, (call) async { @@ -87,7 +87,7 @@ void main() { }); group('state listener', () { - test('pip state changes reach the listener in order', () async { + test('should_deliver_pip_state_changes_in_order_when_native_events_arrive', () async { final received = []; pip().setStateListener(received.add); @@ -97,7 +97,7 @@ void main() { expect(received, [true, false]); }); - test('only a literal true is read as enabled', () async { + test('should_treat_only_literal_true_as_enabled_when_native_value_arrives', () async { final received = []; pip().setStateListener(received.add); @@ -108,7 +108,7 @@ void main() { expect(received, [false, false, false]); }); - test('unrelated native methods are ignored', () async { + test('should_ignore_unrelated_native_methods_when_events_arrive', () async { final received = []; pip().setStateListener(received.add); @@ -118,7 +118,7 @@ void main() { expect(received, [true]); }); - test('clearing the listener silences later callbacks', () async { + test('should_silence_later_callbacks_when_listener_is_cleared', () async { final received = []; final observer = pip()..setStateListener(received.add); await sendToDart('onPipChanged', true); diff --git a/test/core/api_client_test.dart b/test/core/api_client_test.dart index 7abf6ca..903e231 100644 --- a/test/core/api_client_test.dart +++ b/test/core/api_client_test.dart @@ -14,7 +14,7 @@ void main() { return jsonEncode({'code': code, 'message': message, 'data': data}); } - test('should return the data field when the envelope code is zero', () async { + test('should_return_data_field_when_envelope_code_is_zero', () async { final adapter = FakeHttpAdapter( (_) async => FakeHttpResponse(envelope({'id': 7})), ); @@ -24,7 +24,7 @@ void main() { expect((data as Map)['id'], 7); }); - test('should throw an ApiException carrying the server message when the envelope code is not zero', () async { + test('should_throw_api_exception_with_server_message_when_envelope_code_is_not_zero', () async { final adapter = FakeHttpAdapter( (_) async => FakeHttpResponse( envelope(null, code: 40001, message: '邮箱格式不正确'), @@ -42,7 +42,7 @@ void main() { ); }); - test('should map a body without envelope to an ApiException carrying the http status', () async { + test('should_map_to_api_exception_with_http_status_when_body_has_no_envelope', () async { final adapter = FakeHttpAdapter((_) async => FakeHttpResponse('oops', 502)); final client = ApiClient(baseUrl: 'http://127.0.0.1:8081', adapter: adapter); @@ -54,7 +54,7 @@ void main() { ); }); - test('should send the bearer token and the device header when both are available', () async { + test('should_send_bearer_token_and_device_header_when_both_are_available', () async { final adapter = FakeHttpAdapter((_) async => FakeHttpResponse(envelope(null))); final client = ApiClient( baseUrl: 'http://127.0.0.1:8081', @@ -71,7 +71,7 @@ void main() { expect(request.headers['Accept'], 'application/json'); }); - test('should put the query map into the request uri', () async { + test('should_put_query_map_into_request_uri_when_query_is_given', () async { final adapter = FakeHttpAdapter((_) async => FakeHttpResponse(envelope(null))); final client = ApiClient(baseUrl: 'http://127.0.0.1:8081', adapter: adapter); @@ -83,7 +83,7 @@ void main() { expect(uri.queryParameters['page'], '2'); }); - test('should not refresh the token when the call opts out of auth retry', () async { + test('should_skip_refresh_when_call_opts_out_of_auth_retry', () async { var refreshed = false; final adapter = FakeHttpAdapter( (_) async => FakeHttpResponse(envelope(null, code: 401, message: '登录已过期')), diff --git a/test/discover/cover_carousel_test.dart b/test/discover/cover_carousel_test.dart index 28e60e8..2f52609 100644 --- a/test/discover/cover_carousel_test.dart +++ b/test/discover/cover_carousel_test.dart @@ -5,7 +5,7 @@ import 'package:nontonki/pages/discover/widgets/cover_carousel.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); - testWidgets('the cube turns and keeps the title on the poster', (tester) async { + testWidgets('should_turn_cube_and_keep_title_on_poster_when_carousel_pages', (tester) async { await tester.pumpWidget( MaterialApp( home: Scaffold( diff --git a/test/discover/discover_logic_test.dart b/test/discover/discover_logic_test.dart index 671f76c..cc89799 100644 --- a/test/discover/discover_logic_test.dart +++ b/test/discover/discover_logic_test.dart @@ -28,7 +28,7 @@ Drama _drama(String id, {List numbers = const [1, 2, 3]}) => Drama( void main() { TestWidgetsFlutterBinding.ensureInitialized(); - test('the constructor resolves the resume position of episode one', () async { + test('should_resolve_resume_position_of_episode_one_when_constructed', () async { final history = FakeWatchHistory()..saved = const Duration(seconds: 4); final logic = DiscoverLogic(history: history, drama: _drama('signal')); @@ -39,7 +39,7 @@ void main() { logic.dispose(); }); - test('refreshPosition re-reads after the drama is swapped', () async { + test('should_reread_resume_position_when_drama_is_swapped', () async { final history = FakeWatchHistory(); final logic = DiscoverLogic(history: history, drama: _drama('signal')); @@ -54,7 +54,7 @@ void main() { logic.dispose(); }); - test('a list card without episodes resumes from the last watch', () async { + test('should_resume_from_last_watch_when_list_card_has_no_episodes', () async { final history = FakeWatchHistory() ..saved = const Duration(seconds: 9) ..lastEpisode = 'signal-2'; @@ -68,7 +68,7 @@ void main() { logic.dispose(); }); - test('a list card without history has no resume position', () async { + test('should_have_no_resume_position_when_list_card_has_no_history', () async { final history = FakeWatchHistory(); final logic = DiscoverLogic( history: history, @@ -80,7 +80,7 @@ void main() { logic.dispose(); }); - test('selectCategory stores the index and notifies once', () { + test('should_store_index_and_notify_once_when_category_is_selected', () { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); var notifications = 0; logic.addListener(() => notifications++); @@ -92,7 +92,7 @@ void main() { logic.dispose(); }); - test('search visibility follows the flag or the typed text', () { + test('should_show_search_when_flag_is_on_or_text_is_typed', () { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); expect(logic.showSearch, isFalse); @@ -109,7 +109,7 @@ void main() { logic.dispose(); }); - test('dramaFor matches by id and tolerates a missing id', () { + test('should_match_by_id_and_tolerate_missing_id_when_looking_up_drama', () { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); final catalog = [_drama('signal'), _drama('city')]; @@ -120,7 +120,7 @@ void main() { logic.dispose(); }); - test('syncDrama only adopts a drama while the page is active', () { + test('should_adopt_drama_only_when_page_is_active', () { final history = FakeWatchHistory(); final logic = DiscoverLogic(history: history, drama: _drama('signal')); @@ -163,7 +163,7 @@ void main() { logic.dispose(); }); - testWidgets('a search request opens and focuses the field when active', ( + testWidgets('should_open_and_focus_field_when_search_is_requested_and_page_is_active', ( tester, ) async { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); @@ -196,7 +196,7 @@ void main() { logic.dispose(); }); - testWidgets('a hidden page never opens or focuses the search field', ( + testWidgets('should_keep_search_field_closed_when_page_is_hidden', ( tester, ) async { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); @@ -231,7 +231,7 @@ void main() { logic.dispose(); }); - testWidgets('act returns the choice for a page and forwards it when embedded', ( + testWidgets('should_return_choice_and_forward_it_when_act_runs_embedded', ( tester, ) async { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); @@ -278,7 +278,7 @@ void main() { logic.dispose(); }); - testWidgets('a card without playable media shows the artwork notice', ( + testWidgets('should_show_artwork_notice_when_card_has_no_playable_media', ( tester, ) async { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); @@ -306,7 +306,7 @@ void main() { logic.dispose(); }); - testWidgets('an embedded card forwards the chosen drama', (tester) async { + testWidgets('should_forward_chosen_drama_when_card_is_embedded', (tester) async { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); late BuildContext context; Drama? selected; @@ -333,7 +333,7 @@ void main() { logic.dispose(); }); - test('notify after dispose is a no-op', () { + test('should_do_nothing_when_notifying_after_dispose', () { final logic = DiscoverLogic(history: FakeWatchHistory(), drama: _drama('s')); var notifications = 0; logic.addListener(() => notifications++); diff --git a/test/drama_detail/drama_detail_logic_test.dart b/test/drama_detail/drama_detail_logic_test.dart index e6e5b53..a2a25c3 100644 --- a/test/drama_detail/drama_detail_logic_test.dart +++ b/test/drama_detail/drama_detail_logic_test.dart @@ -42,7 +42,7 @@ Drama _detail(String id) => Drama( ); void main() { - test('a list card is upgraded to the full episode list', () async { + test('should_upgrade_to_full_episode_list_when_detail_loads_for_list_card', () async { var calls = 0; final logic = DramaDetailLogic( drama: _card('signal'), @@ -61,7 +61,7 @@ void main() { logic.dispose(); }); - test('a failed load keeps the card and reports the reason', () async { + test('should_keep_card_and_report_reason_when_load_fails', () async { final logic = DramaDetailLogic( drama: _card('signal'), history: FakeWatchHistory(), @@ -79,7 +79,7 @@ void main() { logic.dispose(); }); - test('an already loaded detail skips the request', () async { + test('should_skip_request_when_detail_is_already_loaded', () async { var calls = 0; final logic = DramaDetailLogic( drama: _detail('signal'), diff --git a/test/engagement/engagement_controller_test.dart b/test/engagement/engagement_controller_test.dart index fddd9b2..1e6a9fe 100644 --- a/test/engagement/engagement_controller_test.dart +++ b/test/engagement/engagement_controller_test.dart @@ -5,7 +5,7 @@ import 'package:shared_preferences_platform_interface/in_memory_shared_preferenc import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; void main() { - test('saved, liked and comments are kept by drama id', () async { + test('should_keep_saved_liked_and_comments_by_drama_id_when_toggled', () async { final repository = InMemoryEngagementRepository(); final first = EngagementController(repository); await first.load(); @@ -28,7 +28,7 @@ void main() { expect(reopened.isSaved('signal'), isFalse); }); - test('local engagement survives a new repository instance', () async { + test('should_survive_new_repository_instance_when_engagement_is_local', () async { SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty(); final first = EngagementController(SharedPreferencesEngagementRepository()); diff --git a/test/engagement/engagement_repository_test.dart b/test/engagement/engagement_repository_test.dart index c2701be..5f6f8c3 100644 --- a/test/engagement/engagement_repository_test.dart +++ b/test/engagement/engagement_repository_test.dart @@ -27,7 +27,7 @@ void main() { return repository().load(); } - test('a first launch reads an empty snapshot', () async { + test('should_read_empty_snapshot_when_first_launch', () async { final snapshot = await repository().load(); expect(snapshot.savedDramaIds, isEmpty); @@ -35,7 +35,7 @@ void main() { expect(snapshot.commentsByDrama, isEmpty); }); - test('a snapshot survives a save and reload round trip', () async { + test('should_survive_save_and_reload_round_trip_when_snapshot_is_stored', () async { await repository().save( const EngagementSnapshot( savedDramaIds: {'signal', 'city'}, @@ -55,7 +55,7 @@ void main() { }); }); - test('an empty snapshot round trips as empty collections', () async { + test('should_round_trip_as_empty_collections_when_snapshot_is_empty', () async { await repository().save(const EngagementSnapshot()); final loaded = await repository().load(); @@ -65,7 +65,7 @@ void main() { expect(loaded.commentsByDrama, isEmpty); }); - test('corrupt or non-object payloads fall back to an empty snapshot', () async { + test('should_fall_back_to_empty_snapshot_when_payload_is_corrupt_or_not_an_object', () async { for (final raw in [ '', 'not json at all', @@ -82,7 +82,7 @@ void main() { } }); - test('wrongly typed fields are dropped instead of throwing', () async { + test('should_drop_mistyped_fields_when_payload_has_wrong_types', () async { final snapshot = await loadRaw( jsonEncode({ 'saved': 'signal', @@ -103,7 +103,7 @@ void main() { }); }); - test('a payload missing keys falls back per field', () async { + test('should_fall_back_per_field_when_payload_keys_are_missing', () async { final snapshot = await loadRaw(jsonEncode({'saved': ['signal']})); expect(snapshot.savedDramaIds, {'signal'}); @@ -111,7 +111,7 @@ void main() { expect(snapshot.commentsByDrama, isEmpty); }); - test('save writes only json primitives', () async { + test('should_write_only_json_primitives_when_saving', () async { await repository().save( const EngagementSnapshot( savedDramaIds: {'signal'}, diff --git a/test/formatting/localized_content_test.dart b/test/formatting/localized_content_test.dart index 6c79427..e097924 100644 --- a/test/formatting/localized_content_test.dart +++ b/test/formatting/localized_content_test.dart @@ -17,7 +17,7 @@ void main() { final en = AppLocalizationsEn(); final zh = AppLocalizationsZh(); - test('each seeded drama maps onto its own localized copy', () { + test('should_map_each_seeded_drama_to_its_own_localized_copy_when_looked_up', () { final signal = _drama(id: 'the-signal-demo'); final city = _drama(id: 'city-lights-demo'); final vengeance = _drama(id: 'vengeance-demo'); @@ -31,7 +31,7 @@ void main() { expect(localizedDramaSynopsis(en, vengeance), en.dramaVengeanceSynopsis); }); - test('the mapping does not collapse two dramas onto one entry', () { + test('should_keep_two_dramas_apart_when_localized_copy_is_mapped', () { final titles = { localizedDramaTitle(en, _drama(id: 'the-signal-demo')), localizedDramaTitle(en, _drama(id: 'city-lights-demo')), @@ -47,7 +47,7 @@ void main() { expect(synopses, hasLength(3)); }); - test('localized copy is translated rather than reused verbatim', () { + test('should_translate_localized_copy_when_content_is_looked_up', () { final signal = _drama(id: 'the-signal-demo'); expect(localizedDramaTitle(zh, signal), zh.dramaSignalTitle); @@ -55,7 +55,7 @@ void main() { expect(localizedDramaTitle(zh, signal), isNot(en.dramaSignalTitle)); }); - test('an unknown drama keeps the catalog text in every locale', () { + test('should_keep_catalog_text_in_every_locale_when_drama_is_unknown', () { final custom = _drama( id: 'custom-demo', title: 'Custom Title', @@ -68,7 +68,7 @@ void main() { expect(localizedDramaSynopsis(zh, custom), 'Custom synopsis'); }); - test('the raw catalog text is ignored for a seeded drama', () { + test('should_ignore_raw_catalog_text_when_drama_is_seeded', () { final signal = _drama( id: 'the-signal-demo', title: 'stale title', @@ -79,14 +79,14 @@ void main() { expect(localizedDramaSynopsis(en, signal), isNot('stale synopsis')); }); - test('episodes one to four use named titles', () { + test('should_use_named_titles_when_episode_number_is_one_to_four', () { expect(localizedEpisodeTitle(en, 1), en.episodeOneTitle); expect(localizedEpisodeTitle(en, 2), en.episodeTwoTitle); expect(localizedEpisodeTitle(en, 3), en.episodeThreeTitle); expect(localizedEpisodeTitle(en, 4), en.episodeFourTitle); }); - test('later episodes fall back to a numbered label', () { + test('should_fall_back_to_numbered_label_when_episode_number_is_later', () { for (final number in [5, 12, 100]) { expect(localizedEpisodeTitle(en, number), en.episodeLabel(number)); expect(localizedEpisodeTitle(en, number), contains('$number')); diff --git a/test/home/home_logic_test.dart b/test/home/home_logic_test.dart index c56b869..4ac5323 100644 --- a/test/home/home_logic_test.dart +++ b/test/home/home_logic_test.dart @@ -18,7 +18,7 @@ void main() { final dramas = [_drama('signal'), _drama('city')]; - test('the first catalog entry becomes the current drama', () { + test('should_make_first_catalog_entry_the_current_drama_when_catalog_loads', () { final logic = HomeLogic(dramas); expect(logic.currentDrama.id, 'signal'); @@ -30,11 +30,11 @@ void main() { logic.dispose(); }); - test('an empty catalog cannot build the shell', () { + test('should_not_build_shell_when_catalog_is_empty', () { expect(() => HomeLogic(const []), throwsStateError); }); - test('a plain tab switch raises no request counter', () { + test('should_not_raise_request_counter_when_only_tab_switches', () { final logic = HomeLogic(dramas); var notifications = 0; logic.addListener(() => notifications++); @@ -50,7 +50,7 @@ void main() { logic.dispose(); }); - test('each select flag raises exactly its own counter', () { + test('should_raise_only_its_own_counter_when_each_select_flag_is_used', () { final logic = HomeLogic(dramas); logic.select(1, search: true); @@ -71,7 +71,7 @@ void main() { logic.dispose(); }); - test('the watchlist shortcut defaults to the second section', () { + test('should_default_to_second_section_when_watchlist_shortcut_is_used', () { final logic = HomeLogic(dramas); logic.select(2, watchlist: true); @@ -82,7 +82,7 @@ void main() { logic.dispose(); }); - test('openProfile and openLibrary disagree about the profile flag', () { + test('should_differ_on_profile_flag_when_open_profile_and_open_library_are_used', () { final logic = HomeLogic(dramas); logic.openProfile(); @@ -103,7 +103,7 @@ void main() { logic.dispose(); }); - test('system back returns home, and home itself opens the drawer', () { + test('should_return_home_and_open_drawer_when_system_back_is_pressed', () { final logic = HomeLogic(dramas); logic.onSystemBack(); @@ -134,7 +134,7 @@ void main() { logic.dispose(); }); - test('drawer state only notifies on an actual change', () { + test('should_notify_only_on_actual_change_when_drawer_state_is_set', () { final logic = HomeLogic(dramas); var notifications = 0; logic.addListener(() => notifications++); @@ -151,7 +151,7 @@ void main() { logic.dispose(); }); - test('drama and immersive callbacks update state', () { + test('should_update_state_when_drama_and_immersive_callbacks_fire', () { final logic = HomeLogic(dramas); logic.onDramaChanged(dramas[1]); @@ -162,7 +162,7 @@ void main() { logic.dispose(); }); - test('my page actions map onto root tabs', () { + test('should_map_onto_root_tabs_when_my_page_actions_fire', () { final logic = HomeLogic(dramas); logic.myAction(MyPageAction.discover); @@ -179,7 +179,7 @@ void main() { logic.dispose(); }); - test('notifying after dispose is a no-op', () { + test('should_do_nothing_when_notifying_after_dispose', () { final logic = HomeLogic(dramas); var notifications = 0; logic.addListener(() => notifications++); @@ -190,7 +190,7 @@ void main() { expect(notifications, 0); }); - testWidgets('discover actions map onto root tabs', (tester) async { + testWidgets('should_map_onto_root_tabs_when_discover_actions_fire', (tester) async { final logic = HomeLogic(dramas); late BuildContext context; await tester.pumpWidget( diff --git a/test/integration/live_auth_test.dart b/test/integration/live_auth_test.dart index 844b820..21e0ed7 100644 --- a/test/integration/live_auth_test.dart +++ b/test/integration/live_auth_test.dart @@ -15,7 +15,7 @@ void main() { tearDownAll(client.close); - test('should surface a business error when signing in with an unknown email', () async { + test('should_surface_business_error_when_sign_in_uses_unknown_email', () async { try { await api.login( email: 'no-such-user-for-dio-check@example.com', diff --git a/test/integration/live_catalog_test.dart b/test/integration/live_catalog_test.dart index a3698de..0b79238 100644 --- a/test/integration/live_catalog_test.dart +++ b/test/integration/live_catalog_test.dart @@ -19,7 +19,7 @@ void main() { } } - test('首页聚合:能解析出轮播与分区', () async { + test('should_parse_banner_and_sections_when_home_feed_is_fetched', () async { if (!await serverUp()) { markTestSkipped('客户端接口服务未启动,已跳过联调测试'); return; @@ -33,7 +33,7 @@ void main() { expect(card.posterUrl, startsWith('/files/')); }); - test('详情:剧集按下发顺序排列,可播放集带地址', () async { + test('should_order_episodes_by_number_and_attach_url_when_detail_is_fetched', () async { if (!await serverUp()) { markTestSkipped('客户端接口服务未启动,已跳过联调测试'); return; @@ -55,7 +55,7 @@ void main() { } }); - test('播放票据:可播放时返回地址', () async { + test('should_return_play_url_when_episode_is_playable', () async { if (!await serverUp()) { markTestSkipped('客户端接口服务未启动,已跳过联调测试'); return; diff --git a/test/mine/mine_logic_test.dart b/test/mine/mine_logic_test.dart index c94dba8..9fb07c8 100644 --- a/test/mine/mine_logic_test.dart +++ b/test/mine/mine_logic_test.dart @@ -32,7 +32,7 @@ Drama _drama(String id, {List numbers = const [1, 2, 3]}) => Drama( void main() { TestWidgetsFlutterBinding.ensureInitialized(); - test('the constructor keeps the requested section and reads episode one', () async { + test('should_keep_requested_section_and_read_episode_one_when_constructed', () async { final history = FakeWatchHistory()..saved = const Duration(seconds: 7); final logic = MineLogic( history: history, @@ -47,7 +47,7 @@ void main() { logic.dispose(); }); - test('a list card without episodes resumes from the last watch', () async { + test('should_resume_from_last_watch_when_list_card_has_no_episodes', () async { final history = FakeWatchHistory() ..saved = const Duration(seconds: 7) ..lastEpisode = 'signal-3'; @@ -62,7 +62,7 @@ void main() { logic.dispose(); }); - test('section and filter selections notify', () { + test('should_notify_when_section_and_filter_are_selected', () { final logic = MineLogic( history: FakeWatchHistory(), drama: _drama('s'), @@ -81,7 +81,7 @@ void main() { logic.dispose(); }); - test('syncPage only adopts a drama while the page is active', () { + test('should_adopt_drama_only_when_page_is_active', () { final history = FakeWatchHistory(); final logic = MineLogic( history: history, @@ -125,7 +125,7 @@ void main() { logic.dispose(); }); - test('a section request resets the section even while the page is inactive', () { + test('should_reset_section_when_section_request_arrives_while_page_is_inactive', () { final logic = MineLogic( history: FakeWatchHistory(), drama: _drama('s'), @@ -146,7 +146,7 @@ void main() { logic.dispose(); }); - testWidgets('act returns the choice for a page and forwards it when embedded', ( + testWidgets('should_return_choice_and_forward_it_when_act_runs_embedded', ( tester, ) async { final logic = MineLogic( @@ -197,7 +197,7 @@ void main() { logic.dispose(); }); - testWidgets('the language sheet switches the app locale', (tester) async { + testWidgets('should_switch_app_locale_when_language_sheet_is_used', (tester) async { SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty(); final locale = LocaleController(); @@ -239,7 +239,7 @@ void main() { logic.dispose(); }); - test('notify after dispose is a no-op', () { + test('should_do_nothing_when_notifying_after_dispose', () { final logic = MineLogic( history: FakeWatchHistory(), drama: _drama('s'), diff --git a/test/playback/playback_preload_test.dart b/test/playback/playback_preload_test.dart index 4dc671b..a4f09a8 100644 --- a/test/playback/playback_preload_test.dart +++ b/test/playback/playback_preload_test.dart @@ -17,7 +17,7 @@ void main() { ), ); - test('warm playback starts before old native disposal finishes', () async { + test('should_start_warm_playback_when_old_native_disposal_is_still_running', () async { final release = Completer(); final factory = _Factory(disposeGates: {'ep-0': release}); final session = PlaybackSession( @@ -41,7 +41,7 @@ void main() { await session.close(); }); - test('failed native release blocks another route from allocating', () async { + test('should_block_another_route_from_allocating_when_native_release_fails', () async { final factory = _Factory(disposeFailures: {'ep-0'}); final coordinator = PlaybackCoordinator(); PlaybackSession createSession() => PlaybackSession( @@ -68,7 +68,7 @@ void main() { }); test( - 'next is prepared silently and promoted without another initialize', + 'should_prepare_next_silently_and_promote_it_when_swiped', () async { final factory = _Factory(); final prepared = []; @@ -103,7 +103,7 @@ void main() { }, ); - test('in-flight preload is reused when swiped before ready', () async { + test('should_reuse_in_flight_preload_when_swiped_before_ready', () async { final gate = Completer(); final factory = _Factory(gates: {'ep-1': gate}); final session = PlaybackSession( @@ -127,7 +127,7 @@ void main() { await session.close(); }); - test('rapid reverse and skipped selection discard stale warm work', () async { + test('should_discard_stale_warm_work_when_reversing_rapidly_or_skipping_selection', () async { final gate = Completer(); final factory = _Factory(gates: {'ep-1': gate}); final session = PlaybackSession( @@ -153,7 +153,7 @@ void main() { }); test( - 'category adjacency and locked next never preload the wrong episode', + 'should_never_preload_wrong_episode_when_next_is_locked_or_category_based', () async { final factory = _Factory(); final session = PlaybackSession( @@ -172,7 +172,7 @@ void main() { }, ); - test('preload failure is isolated and selecting it retries once', () async { + test('should_isolate_preload_failure_and_retry_once_when_selected', () async { final factory = _Factory(failOnce: {'ep-1'}); final session = PlaybackSession( episodes: episodes, @@ -190,7 +190,7 @@ void main() { await session.close(); }); - test('completion in background never starts either player', () async { + test('should_start_no_player_when_completion_happens_in_background', () async { final gate = Completer(); final factory = _Factory(gates: {'ep-1': gate}); final session = PlaybackSession( @@ -215,7 +215,7 @@ void main() { await session.close(); }); - test('closing during preloading disposes pending resources', () async { + test('should_dispose_pending_resources_when_closing_during_preload', () async { final gate = Completer(); final factory = _Factory(gates: {'ep-1': gate}); final session = PlaybackSession( @@ -233,7 +233,7 @@ void main() { }); test( - 'home and series share a two-player budget across route handoff', + 'should_share_two_player_budget_when_handing_off_between_home_and_series', () async { final factory = _Factory(); final coordinator = PlaybackCoordinator(); diff --git a/test/playback/playback_queue_test.dart b/test/playback/playback_queue_test.dart index 179f4a2..1117367 100644 --- a/test/playback/playback_queue_test.dart +++ b/test/playback/playback_queue_test.dart @@ -3,7 +3,7 @@ import 'package:nontonki/core/data/models/drama.dart'; import 'package:nontonki/core/data/services/playback_queue.dart'; void main() { - test('home feed advances between dramas, series player between episodes', () { + test('should_advance_between_dramas_on_home_and_between_episodes_in_series_when_queue_is_built', () { final dramas = [_drama('a', 3), _drama('b', 2), _drama('c', 4)]; final home = PlaybackQueue.home(dramas); @@ -22,7 +22,7 @@ void main() { expect(series.nextIndex(2), isNull); }); - test('dramas without episodes are left out of the home queue', () { + test('should_leave_dramas_out_when_they_have_no_episodes', () { final home = PlaybackQueue.home([ _drama('a', 2), Drama( @@ -36,7 +36,7 @@ void main() { expect(home.entryAt(0).drama.id, 'a'); }); - test('a list card with a preview episode plays on the home feed', () { + test('should_play_preview_episode_when_list_card_has_one', () { final home = PlaybackQueue.home([ Drama.fromCardJson(const { 'id': 5, diff --git a/test/playback/playback_session_test.dart b/test/playback/playback_session_test.dart index 99e17ab..de700d1 100644 --- a/test/playback/playback_session_test.dart +++ b/test/playback/playback_session_test.dart @@ -17,7 +17,7 @@ void main() { ), ); - test('locked episode never creates or initializes a player', () async { + test('should_create_no_player_when_episode_is_locked', () async { final factory = FakeFactory(); final session = PlaybackSession( episodes: episodes, @@ -30,7 +30,7 @@ void main() { await session.close(); }); - test('rapid selection drops stale initialize and keeps one player', () async { + test('should_drop_stale_initialize_and_keep_one_player_when_selection_is_rapid', () async { final factory = FakeFactory(holdFirstInitialize: true); final session = PlaybackSession( episodes: episodes, @@ -52,7 +52,7 @@ void main() { }); test( - 'background and overlay pause without forgetting playback intent', + 'should_pause_without_forgetting_intent_when_backgrounding_or_overlay_shows', () async { final factory = FakeFactory(); final session = PlaybackSession( @@ -80,7 +80,7 @@ void main() { }, ); - test('resume preparation finishes before video starts', () async { + test('should_finish_resume_preparation_before_video_starts_when_resuming', () async { final factory = FakeFactory(); final prepared = Completer(); final session = PlaybackSession( @@ -99,7 +99,7 @@ void main() { await session.close(); }); - test('pausing then swiping to the next episode resumes playback', () async { + test('should_resume_playback_when_swiping_after_pause', () async { final factory = FakeFactory(); final session = PlaybackSession( episodes: episodes, @@ -121,7 +121,7 @@ void main() { await session.close(); }); - test('re-selecting the same episode keeps the user pause', () async { + test('should_keep_user_pause_when_reselecting_the_same_episode', () async { final factory = FakeFactory(); final session = PlaybackSession( episodes: episodes, @@ -140,7 +140,7 @@ void main() { await session.close(); }); - test('automatic advance respects visibility and playback intent', () async { + test('should_respect_visibility_and_intent_when_advancing_automatically', () async { final factory = FakeFactory(); final session = PlaybackSession( episodes: episodes, diff --git a/test/playback/seek_progress_bar_test.dart b/test/playback/seek_progress_bar_test.dart index 01f1114..137e7db 100644 --- a/test/playback/seek_progress_bar_test.dart +++ b/test/playback/seek_progress_bar_test.dart @@ -4,7 +4,7 @@ import 'package:nontonki/pages/player/widgets/seek_progress_bar.dart'; import 'package:video_player/video_player.dart'; void main() { - test('seek target stays inside playable duration', () { + test('should_keep_seek_target_inside_playable_duration_when_dragging', () { const duration = Duration(seconds: 12); expect(seekPositionForFraction(0, duration), Duration.zero); expect( @@ -14,7 +14,7 @@ void main() { expect(seekPositionForFraction(1, duration), lessThan(duration)); }); - testWidgets('horizontal slider drag previews and seeks on release', ( + testWidgets('should_preview_while_dragging_and_seek_on_release_when_slider_is_horizontal', ( tester, ) async { final video = ValueNotifier( @@ -61,7 +61,7 @@ void main() { expect(target!, lessThan(const Duration(seconds: 12))); }); - testWidgets('compact seek bar shows time only while dragging', ( + testWidgets('should_show_time_only_while_dragging_when_seek_bar_is_compact', ( tester, ) async { final video = ValueNotifier( diff --git a/test/playback/video_player_port_test.dart b/test/playback/video_player_port_test.dart index 0c7553c..5a35138 100644 --- a/test/playback/video_player_port_test.dart +++ b/test/playback/video_player_port_test.dart @@ -12,7 +12,7 @@ Episode _episode(String videoUrl) => Episode( ); void main() { - test('相对播放地址补上服务地址', () { + test('should_prefix_service_base_when_play_url_is_relative', () { final uri = VideoPlayerPort.resolveUri( _episode('/files/episode/202610/clip.mp4'), ); @@ -27,7 +27,7 @@ void main() { ); }); - test('已经是绝对地址的不再拼接', () { + test('should_keep_url_untouched_when_play_url_is_absolute', () { final uri = VideoPlayerPort.resolveUri( _episode('https://cdn.example.test/clip.mp4'), ); diff --git a/test/playback/watch_history_test.dart b/test/playback/watch_history_test.dart index 8e3c081..d814deb 100644 --- a/test/playback/watch_history_test.dart +++ b/test/playback/watch_history_test.dart @@ -5,7 +5,7 @@ import 'package:shared_preferences_platform_interface/in_memory_shared_preferenc import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; void main() { - test('resume position clamps invalid and completed values', () { + test('should_clamp_invalid_and_completed_values_when_resolving_resume_position', () { const duration = Duration(seconds: 8); expect( clampResumePosition(const Duration(seconds: -1), duration), @@ -25,7 +25,7 @@ void main() { ); }); - test('watch history is scoped by profile, drama and episode', () async { + test('should_scope_history_by_profile_drama_and_episode_when_saving', () async { SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty(); final history = SharedPreferencesWatchHistoryRepository(); @@ -53,7 +53,7 @@ void main() { expect(await history.lastEpisodeId('profile-a', 'drama-b'), isNull); }); - test('resume prefers the first loaded episode', () async { + test('should_prefer_first_loaded_episode_when_resuming', () async { SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty(); final history = SharedPreferencesWatchHistoryRepository(); @@ -87,7 +87,7 @@ void main() { ); }); - test('resume falls back to the last watched episode for a list card', () async { + test('should_fall_back_to_last_watched_episode_when_resuming_a_list_card', () async { SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty(); final history = SharedPreferencesWatchHistoryRepository(); @@ -112,7 +112,7 @@ void main() { ); }); - test('a list card with no history has no resume position', () async { + test('should_have_no_resume_position_when_list_card_has_no_history', () async { SharedPreferencesAsyncPlatform.instance = InMemorySharedPreferencesAsync.empty(); final history = SharedPreferencesWatchHistoryRepository(); diff --git a/test/player/player_logic_test.dart b/test/player/player_logic_test.dart index 1dedbf4..6a71cbe 100644 --- a/test/player/player_logic_test.dart +++ b/test/player/player_logic_test.dart @@ -55,7 +55,7 @@ void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('displayEpisode', () { - test('falls back until a number is picked for that drama', () { + test('should_fall_back_until_episode_number_is_picked_for_that_drama_when_resolving', () { final logic = _logic(FakeWatchHistory()); final drama = _drama('signal'); final fallback = drama.episodes.first; @@ -70,7 +70,7 @@ void main() { expect(logic.displayEpisode(city, city.episodes.first).number, 1); }); - test('an unknown picked number stops overriding the fallback', () { + test('should_stop_overriding_fallback_when_picked_number_is_unknown', () { final logic = _logic(FakeWatchHistory()); final drama = _drama('signal'); final fallback = drama.episodes.first; @@ -82,7 +82,7 @@ void main() { }); group('nextVisibleIndex', () { - test('walks the visible list and stops at the end', () { + test('should_walk_visible_list_and_stop_at_the_end_when_advancing', () { final logic = _logic(FakeWatchHistory()); logic.visibleIndices = [0, 2, 4]; @@ -91,7 +91,7 @@ void main() { expect(logic.nextVisibleIndex(4), isNull); }); - test('an index outside the visible list has no successor', () { + test('should_have_no_successor_when_index_is_outside_visible_list', () { final logic = _logic(FakeWatchHistory()); logic.visibleIndices = [0, 2, 4]; @@ -100,7 +100,7 @@ void main() { expect(logic.nextVisibleIndex(-1), isNull); }); - test('an empty visible list has no successor', () { + test('should_have_no_successor_when_visible_list_is_empty', () { final logic = _logic(FakeWatchHistory()); logic.visibleIndices = []; @@ -109,7 +109,7 @@ void main() { }); group('surface flags', () { - test('clearing the screen hides the chrome until it is restored', () { + test('should_hide_chrome_until_restored_when_screen_is_cleared', () { final logic = _logic(FakeWatchHistory()); final immersive = []; logic.onImmersiveChanged = immersive.add; @@ -135,7 +135,7 @@ void main() { expect(immersive, [true, false]); }); - test('a picture in picture callback outranks the cleared screen flag', () async { + test('should_outrank_cleared_screen_flag_when_picture_in_picture_callback_fires', () async { final factory = _RecordingFactory(); final drama = _drama('signal'); final logic = _logic(FakeWatchHistory()); @@ -164,7 +164,7 @@ void main() { await _drain(logic); }); - test('a picture in picture callback is ignored once unmounted', () { + test('should_ignore_picture_in_picture_callback_when_unmounted', () { final logic = _logic(FakeWatchHistory()); logic.isMounted = () => false; @@ -175,7 +175,7 @@ void main() { }); group('history writes', () { - test('progress writes are serialized in call order', () async { + test('should_serialize_progress_writes_in_call_order_when_saving', () async { final history = FakeWatchHistory(); final logic = _logic(history); final drama = _drama('signal'); @@ -197,7 +197,7 @@ void main() { ]); }); - test('a failed write does not drop the writes after it', () async { + test('should_keep_later_writes_when_an_earlier_write_fails', () async { final history = FakeWatchHistory(); final logic = _logic(history); final drama = _drama('signal'); @@ -219,7 +219,7 @@ void main() { expect(history.writes, ['demo-local-v1|signal|signal-2|2000']); }); - test('saving with no tracked player writes nothing', () async { + test('should_write_nothing_when_no_player_is_tracked', () async { final history = FakeWatchHistory(); final logic = _logic(history); @@ -232,7 +232,7 @@ void main() { }); group('start', () { - test('a detail route plays a single series', () async { + test('should_play_single_series_when_route_is_detail', () async { final factory = _RecordingFactory(); final drama = _drama('signal'); final logic = _logic(FakeWatchHistory()); @@ -255,7 +255,7 @@ void main() { expect(factory.live, 0); }); - test('home mode plays one episode per feed drama', () async { + test('should_play_one_episode_per_feed_drama_when_in_home_mode', () async { final factory = _RecordingFactory(); final feed = [_drama('signal'), _drama('city'), _drama('vengeance')]; final logic = _logic(FakeWatchHistory()); @@ -281,7 +281,7 @@ void main() { await _drain(logic); }); - test('a locked episode never reaches the player factory', () async { + test('should_never_reach_player_factory_when_episode_is_locked', () async { final factory = _RecordingFactory(); final drama = _drama('locked', isFree: false); final logic = _logic(FakeWatchHistory()); @@ -300,7 +300,7 @@ void main() { await _drain(logic); }); - test('starting from a later index only preloads what follows it', () async { + test('should_preload_only_what_follows_when_starting_from_a_later_index', () async { final factory = _RecordingFactory(); final drama = _drama('signal'); final logic = _logic(FakeWatchHistory()); @@ -320,7 +320,7 @@ void main() { await _drain(logic); }); - test('an inactive route holds no players until it becomes visible', () async { + test('should_hold_no_players_when_route_is_inactive', () async { final factory = _RecordingFactory(); final drama = _drama('signal'); final logic = _logic(FakeWatchHistory()); @@ -341,7 +341,7 @@ void main() { }); group('lifecycle', () { - test('backgrounding pauses the active player and resuming plays it', () async { + test('should_pause_active_player_when_backgrounded_and_play_it_when_resumed', () async { final factory = _RecordingFactory(); final drama = _drama('signal'); final logic = _logic(FakeWatchHistory()); @@ -368,7 +368,7 @@ void main() { await _drain(logic); }); - test('an inactive route stops playing without losing the intent', () async { + test('should_stop_playing_without_losing_intent_when_route_becomes_inactive', () async { final factory = _RecordingFactory(); final drama = _drama('signal'); final logic = _logic(FakeWatchHistory()); @@ -398,7 +398,7 @@ void main() { }); group('dispose', () { - test('is safe to repeat and releases every player', () async { + test('should_release_every_player_when_dispose_runs_twice', () async { final factory = _RecordingFactory(); final drama = _drama('signal'); final logic = _logic(FakeWatchHistory()); @@ -417,7 +417,7 @@ void main() { expect(() => logic.dispose(), returnsNormally); }); - test('disables further notifications', () async { + test('should_disable_further_notifications_when_disposed', () async { final factory = _RecordingFactory(); final drama = _drama('signal'); final logic = _logic(FakeWatchHistory()); diff --git a/test/widget_test.dart b/test/widget_test.dart index 6a63e43..28f02e2 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -28,7 +28,7 @@ void main() { }); testWidgets( - 'home swipe promotes next without recreating it or showing loading', + 'should_promote_next_without_recreating_it_or_showing_loading_when_home_is_swiped', (tester) async { final created = []; await tester.pumpWidget( @@ -57,7 +57,7 @@ void main() { }, ); - testWidgets('immersive home opens the series detail', (tester) async { + testWidgets('should_open_series_detail_when_immersive_home_is_tapped', (tester) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); @@ -82,7 +82,7 @@ void main() { expect(find.byTooltip('Explore series'), findsNothing); }); - testWidgets('clear screen hides tabs and restores them on tap', ( + testWidgets('should_hide_tabs_and_restore_them_when_clear_screen_is_tapped', ( tester, ) async { tester.view.physicalSize = const Size(390, 844); @@ -107,7 +107,7 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('home quick drawer opens current series and switches root tabs', ( + testWidgets('should_open_current_series_and_switch_root_tabs_when_quick_drawer_is_used', ( tester, ) async { tester.view.physicalSize = const Size(360, 720); @@ -162,7 +162,7 @@ void main() { ); }); - testWidgets('interface language changes without a caption control', ( + testWidgets('should_change_interface_language_when_locale_is_picked', ( tester, ) async { tester.view.physicalSize = const Size(390, 844); @@ -193,7 +193,7 @@ void main() { expect(find.text('热门'), findsOneWidget); }); - testWidgets('home controls fit a compact Android viewport', (tester) async { + testWidgets('should_fit_compact_android_viewport_when_home_controls_are_shown', (tester) async { tester.view.physicalSize = const Size(360, 720); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); @@ -217,7 +217,7 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('My page fits a compact Android viewport and returns home', ( + testWidgets('should_fit_compact_android_viewport_and_return_home_when_my_page_is_shown', ( tester, ) async { tester.view.physicalSize = const Size(360, 720); @@ -241,7 +241,7 @@ void main() { }); testWidgets( - 'Discover shows three mock playable dramas and one pending poster', + 'should_show_three_playable_dramas_and_one_pending_poster_when_discover_is_opened', (tester) async { tester.view.physicalSize = const Size(360, 720); tester.view.devicePixelRatio = 1; @@ -276,7 +276,7 @@ void main() { }, ); - testWidgets('bottom tabs stay on one route', (tester) async { + testWidgets('should_stay_on_one_route_when_bottom_tabs_are_used', (tester) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); @@ -327,7 +327,7 @@ void main() { }); testWidgets( - 'home swipe changes drama, entered player swipe changes episode', + 'should_change_drama_on_home_swipe_and_episode_on_player_swipe_when_swiping', (tester) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; @@ -359,7 +359,7 @@ void main() { }, ); - testWidgets('shorts page has no category navigation', (tester) async { + testWidgets('should_hide_category_navigation_when_shorts_page_is_shown', (tester) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); @@ -381,7 +381,7 @@ void main() { expect(find.text('City Lights & Velvet Nights'), findsOneWidget); expect(tester.takeException(), isNull); }); - testWidgets('right rail saves, comments, likes and invokes share in order', ( + testWidgets('should_save_comment_like_and_share_in_order_when_right_rail_is_used', ( tester, ) async { tester.view.physicalSize = const Size(390, 844); @@ -439,7 +439,7 @@ void main() { expect(tester.takeException(), isNull); }); - testWidgets('system back opens the drawer and stays on home', (tester) async { + testWidgets('should_open_drawer_and_stay_on_home_when_system_back_is_pressed', (tester) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); @@ -466,7 +466,7 @@ void main() { ); }); - testWidgets('My watchlist lists saved dramas across the home feed', ( + testWidgets('should_list_saved_dramas_across_home_feed_when_watchlist_is_opened', ( tester, ) async { tester.view.physicalSize = const Size(390, 844);