From cf03178cc55c8fb7410ce1595a42c244fcd699a8 Mon Sep 17 00:00:00 2001 From: Zhou Weidong <502333560@qq.com> Date: Sat, 17 Oct 2026 17:24:45 +0800 Subject: [PATCH] =?UTF-8?q?feat(flutter):=20=E9=A6=96=E9=A1=B5=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E4=B8=A4=E5=88=97=E5=85=A8=E9=83=A8=E5=89=A7=E7=9B=AE?= =?UTF-8?q?=E7=BD=91=E6=A0=BC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/l10n/app_en.arb | 1 + lib/l10n/app_id.arb | 1 + lib/l10n/app_localizations.dart | 6 + lib/l10n/app_localizations_en.dart | 3 + lib/l10n/app_localizations_id.dart | 3 + lib/l10n/app_localizations_zh.dart | 3 + lib/l10n/app_zh.arb | 1 + lib/pages/discover/discover_view.dart | 276 ++++-------------- .../discover/widgets/home_drama_grid.dart | 167 +++++++++++ test/discover/home_all_dramas_grid_test.dart | 245 ++++++++++++++++ test/widget_test.dart | 22 +- 11 files changed, 498 insertions(+), 230 deletions(-) create mode 100644 lib/pages/discover/widgets/home_drama_grid.dart create mode 100644 test/discover/home_all_dramas_grid_test.dart diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 9a6edc7..e2dfb60 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -55,6 +55,7 @@ "animatedTab": "Animated", "recommendedTab": "Recommended", "noCategoryDramas": "No dramas in this category yet.", + "allDramas": "All Dramas", "trendingTab": "Trending", "liveActionTab": "Live Action", "search": "Search", diff --git a/lib/l10n/app_id.arb b/lib/l10n/app_id.arb index cd96703..55e5409 100644 --- a/lib/l10n/app_id.arb +++ b/lib/l10n/app_id.arb @@ -55,6 +55,7 @@ "animatedTab": "Animasi", "recommendedTab": "Rekomendasi", "noCategoryDramas": "Belum ada drama di kategori ini.", + "allDramas": "Semua Drama", "trendingTab": "Sedang Tren", "liveActionTab": "Live Action", "search": "Cari", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 6f86913..5b57f1c 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -388,6 +388,12 @@ abstract class AppLocalizations { /// **'No dramas in this category yet.'** String get noCategoryDramas; + /// No description provided for @allDramas. + /// + /// In en, this message translates to: + /// **'All Dramas'** + String get allDramas; + /// No description provided for @trendingTab. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index d8ec4d3..42cf09b 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -157,6 +157,9 @@ class AppLocalizationsEn extends AppLocalizations { @override String get noCategoryDramas => 'No dramas in this category yet.'; + @override + String get allDramas => 'All Dramas'; + @override String get trendingTab => 'Trending'; diff --git a/lib/l10n/app_localizations_id.dart b/lib/l10n/app_localizations_id.dart index 3d53a5e..7e05221 100644 --- a/lib/l10n/app_localizations_id.dart +++ b/lib/l10n/app_localizations_id.dart @@ -157,6 +157,9 @@ class AppLocalizationsId extends AppLocalizations { @override String get noCategoryDramas => 'Belum ada drama di kategori ini.'; + @override + String get allDramas => 'Semua Drama'; + @override String get trendingTab => 'Sedang Tren'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index ca93d04..f0a608a 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -154,6 +154,9 @@ class AppLocalizationsZh extends AppLocalizations { @override String get noCategoryDramas => '这个分类暂时没有剧集。'; + @override + String get allDramas => '全部剧目'; + @override String get trendingTab => '热门'; diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index e942dac..71a6cb8 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -55,6 +55,7 @@ "animatedTab": "漫剧", "recommendedTab": "推荐", "noCategoryDramas": "这个分类暂时没有剧集。", + "allDramas": "全部剧目", "trendingTab": "热门", "liveActionTab": "真人剧", "search": "搜索", diff --git a/lib/pages/discover/discover_view.dart b/lib/pages/discover/discover_view.dart index ccb7005..5a1a97a 100644 --- a/lib/pages/discover/discover_view.dart +++ b/lib/pages/discover/discover_view.dart @@ -17,6 +17,8 @@ import 'discover_binding.dart'; import 'discover_logic.dart'; import 'widgets/cover_carousel.dart'; +part 'widgets/home_drama_grid.dart'; + class DiscoverPage extends StatefulWidget { const DiscoverPage({ super.key, @@ -101,40 +103,12 @@ class _DiscoverPageState extends State { final categories = context.watch(); final homeBanners = context.watch().feed.banners; final allDramas = [...?widget.dramas, ...categories.visibleDramas]; - final cards = _cardsFor(categories.visibleDramas, ''); final content = SafeArea( bottom: !widget.embedded, child: Column( children: [ _header(l10n), - Expanded( - child: ListView( - controller: logic.scroll, - padding: const EdgeInsets.only(bottom: 24), - children: [ - Padding( - padding: const EdgeInsets.fromLTRB(16, 4, 16, 0), - child: ClipRRect( - borderRadius: BorderRadius.circular(18), - child: _carousel(l10n, homeBanners, allDramas), - ), - ), - Padding( - padding: const EdgeInsets.fromLTRB(16, 18, 16, 0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - _categories(l10n, categories), - const SizedBox(height: 18), - _continueHeader(l10n), - const SizedBox(height: 12), - _dramaContent(l10n, categories, cards), - ], - ), - ), - ], - ), - ), + Expanded(child: _feed(l10n, categories, homeBanners, allDramas)), if (!widget.embedded) _bottomNav(l10n), ], ), @@ -150,6 +124,60 @@ class _DiscoverPageState extends State { ); } + Widget _feed( + AppLocalizations l10n, + CategoryController categories, + List banners, + List allDramas, + ) => CustomScrollView( + controller: logic.scroll, + slivers: [ + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 4, 16, 0), + child: ClipRRect( + borderRadius: BorderRadius.circular(18), + child: _carousel(l10n, banners, allDramas), + ), + ), + ), + SliverToBoxAdapter( + child: Padding( + padding: const EdgeInsets.fromLTRB(16, 18, 16, 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + _categories(l10n, categories), + const SizedBox(height: 18), + Text( + l10n.allDramas, + key: const Key('home-all-dramas-title'), + style: const TextStyle( + color: AppColors.onPrimary, + fontSize: 18, + fontWeight: FontWeight.w700, + height: 1.2, + ), + ), + ], + ), + ), + ), + _HomeDramaGrid( + l10n: l10n, + categories: categories, + onOpen: (drama) => logic.openCard( + context, + drama, + embedded: widget.embedded, + onDramaSelected: widget.onDramaSelected, + onAction: widget.onAction, + ), + ), + const SliverToBoxAdapter(child: SizedBox(height: 24)), + ], + ); + Widget _header(AppLocalizations l10n) { return Padding( padding: const EdgeInsets.fromLTRB(16, 6, 12, 8), @@ -239,164 +267,6 @@ class _DiscoverPageState extends State { } } - Widget _continueHeader(AppLocalizations l10n) => Row( - children: [ - Expanded( - child: Text( - l10n.continueWatching, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - fontSize: 18, - fontWeight: FontWeight.w600, - letterSpacing: 0.2, - height: 1.2, - ), - ), - ), - const SizedBox(width: 8), - Text( - l10n.seeAll, - style: const TextStyle( - color: AppColors.blueSoft, - fontSize: 13, - fontWeight: FontWeight.w500, - ), - ), - ], - ); - - Widget _dramaContent( - AppLocalizations l10n, - CategoryController categories, - List<_ShowcaseCard> cards, - ) { - if (categories.loading && categories.feed.sections.isEmpty) { - return const Padding( - padding: EdgeInsets.symmetric(vertical: 24), - child: Center(child: CircularProgressIndicator(strokeWidth: 2)), - ); - } - if (categories.error != null) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 18), - child: Row( - children: [ - Expanded( - child: Text( - l10n.categoriesError, - style: const TextStyle(color: Color(0xFFE6E1E3)), - ), - ), - TextButton(onPressed: categories.load, child: Text(l10n.retry)), - ], - ), - ); - } - if (cards.isEmpty) { - return Padding( - padding: const EdgeInsets.symmetric(vertical: 24), - child: Text( - categories.selectedSectionMissing - ? l10n.noCategoryDramas - : l10n.noSearchResults, - style: const TextStyle(color: Color(0xFFE6E1E3)), - ), - ); - } - return SizedBox( - height: 196, - child: ListView( - scrollDirection: Axis.horizontal, - children: [ - for (final card in cards) ...[ - _continueTile(l10n, card, categories.visibleDramas), - const SizedBox(width: 12), - ], - ], - ), - ); - } - - Widget _continueTile( - AppLocalizations l10n, - _ShowcaseCard card, - List source, - ) { - final drama = logic.dramaFor(card.dramaId, source); - return SizedBox( - width: 132, - child: InkWell( - onTap: () => logic.openCard( - context, - drama, - embedded: widget.embedded, - onDramaSelected: widget.onDramaSelected, - onAction: widget.onAction, - ), - child: ClipRRect( - borderRadius: BorderRadius.circular(14), - child: Stack( - fit: StackFit.expand, - children: [ - RemoteImage(card.image), - const DecoratedBox( - decoration: BoxDecoration( - gradient: LinearGradient( - begin: Alignment.topCenter, - end: Alignment.bottomCenter, - colors: [Color(0x00000000), Color(0xE6000000)], - stops: [0.45, 1], - ), - ), - ), - if (drama == null) - const ColoredBox( - color: Color(0x99000000), - child: Center( - child: Icon(Icons.image_outlined, color: Colors.white), - ), - ), - Positioned( - left: 10, - right: 10, - bottom: 10, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - card.title, - maxLines: 2, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - color: Colors.white, - fontSize: 13, - fontWeight: FontWeight.w600, - height: 1.2, - ), - ), - const SizedBox(height: 3), - Text( - drama == null ? l10n.videoPending : card.genre, - maxLines: 1, - overflow: TextOverflow.ellipsis, - style: const TextStyle( - color: Color(0xE6FFFFFF), - fontSize: 11, - fontWeight: FontWeight.w400, - height: 1.2, - ), - ), - ], - ), - ), - ], - ), - ), - ), - ); - } - Widget _categories(AppLocalizations l10n, CategoryController categories) { final choices = <({String code, String label})>[ (code: CategoryController.allCode, label: l10n.forYou), @@ -523,38 +393,6 @@ class _DiscoverNavItem extends StatelessWidget { ); } -class _ShowcaseCard { - const _ShowcaseCard({ - required this.title, - required this.genre, - required this.image, - this.dramaId, - }); - - final String title; - final String genre; - final String image; - final String? dramaId; -} - -/// 分区由服务端下发:0 为全部,其余按分区顺序对应筛选菜单(热门 / 最新 / 各分类)。 -List<_ShowcaseCard> _cardsFor(List dramas, String query) { - final normalized = query.trim().toLowerCase(); - return [ - for (final drama in dramas) - if (normalized.isEmpty || - drama.title.toLowerCase().contains(normalized) || - drama.genre.toLowerCase().contains(normalized) || - drama.category.toLowerCase().contains(normalized)) - _ShowcaseCard( - title: drama.title, - genre: drama.genre.isEmpty ? drama.category : drama.genre, - image: drama.posterUrl, - dramaId: drama.id, - ), - ]; -} - const _accent = AppColors.blueSoft; const _coin = Color(0xFFF5C451); diff --git a/lib/pages/discover/widgets/home_drama_grid.dart b/lib/pages/discover/widgets/home_drama_grid.dart new file mode 100644 index 0000000..de34d95 --- /dev/null +++ b/lib/pages/discover/widgets/home_drama_grid.dart @@ -0,0 +1,167 @@ +// Author: fruit.com.cn + +part of '../discover_view.dart'; + +class _HomeDramaGrid extends StatelessWidget { + const _HomeDramaGrid({ + required this.l10n, + required this.categories, + required this.onOpen, + }); + + final AppLocalizations l10n; + final CategoryController categories; + final ValueChanged onOpen; + + @override + Widget build(BuildContext context) { + if (categories.loading && categories.feed.sections.isEmpty) { + return const SliverPadding( + padding: EdgeInsets.symmetric(vertical: 24), + sliver: SliverToBoxAdapter( + child: Center(child: CircularProgressIndicator(strokeWidth: 2)), + ), + ); + } + if (categories.error != null) { + return SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 6, 16, 18), + sliver: SliverToBoxAdapter( + child: Row( + children: [ + Expanded( + child: Text( + l10n.categoriesError, + style: const TextStyle(color: AppColors.muted), + ), + ), + TextButton(onPressed: categories.load, child: Text(l10n.retry)), + ], + ), + ), + ); + } + + final dramas = categories.visibleDramas; + if (dramas.isEmpty) { + return SliverPadding( + padding: const EdgeInsets.fromLTRB(16, 6, 16, 24), + sliver: SliverToBoxAdapter( + child: Text( + categories.selectedSectionMissing + ? l10n.noCategoryDramas + : l10n.noSearchResults, + style: const TextStyle(color: AppColors.muted), + ), + ), + ); + } + + return SliverPadding( + key: const Key('home-all-dramas-section'), + padding: const EdgeInsets.symmetric(horizontal: 16), + sliver: SliverGrid( + key: const Key('home-all-dramas-grid'), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + mainAxisSpacing: 16, + crossAxisSpacing: 12, + childAspectRatio: 2 / 3, + ), + delegate: SliverChildBuilderDelegate( + (context, index) => _HomeDramaCard( + l10n: l10n, + drama: dramas[index], + onTap: () => onOpen(dramas[index]), + ), + childCount: dramas.length, + ), + ), + ); + } +} + +class _HomeDramaCard extends StatelessWidget { + const _HomeDramaCard({ + required this.l10n, + required this.drama, + required this.onTap, + }); + + final AppLocalizations l10n; + final Drama drama; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + final genre = drama.genre.isEmpty ? drama.category : drama.genre; + return InkWell( + key: Key('home-drama-card-${drama.id}'), + onTap: onTap, + borderRadius: BorderRadius.circular(14), + child: AspectRatio( + key: Key('home-drama-cover-${drama.id}'), + aspectRatio: 2 / 3, + child: ClipRRect( + borderRadius: BorderRadius.circular(14), + child: Stack( + fit: StackFit.expand, + children: [ + RemoteImage( + drama.posterUrl, + fit: BoxFit.cover, + showFallbackIcon: true, + ), + const DecoratedBox( + decoration: BoxDecoration( + gradient: LinearGradient( + begin: Alignment.topCenter, + end: Alignment.bottomCenter, + colors: [Color(0x00000000), Color(0xE6000000)], + stops: [0.4, 1], + ), + ), + ), + Positioned( + left: 10, + right: 10, + bottom: 10, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Text( + drama.title, + key: Key('home-drama-title-${drama.id}'), + maxLines: 2, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Colors.white, + fontSize: 14, + fontWeight: FontWeight.w700, + height: 1.2, + ), + ), + const SizedBox(height: 4), + Text( + genre.isEmpty ? l10n.videoPending : genre, + key: Key('home-drama-genre-${drama.id}'), + maxLines: 1, + overflow: TextOverflow.ellipsis, + style: const TextStyle( + color: Color(0xE6FFFFFF), + fontSize: 11, + fontWeight: FontWeight.w400, + height: 1.2, + ), + ), + ], + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/test/discover/home_all_dramas_grid_test.dart b/test/discover/home_all_dramas_grid_test.dart new file mode 100644 index 0000000..e724439 --- /dev/null +++ b/test/discover/home_all_dramas_grid_test.dart @@ -0,0 +1,245 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:shared_preferences_platform_interface/in_memory_shared_preferences_async.dart'; +import 'package:shared_preferences_platform_interface/shared_preferences_async_platform_interface.dart'; + +import 'package:fruitshorts/app.dart'; +import 'package:fruitshorts/core/data/models/content_category.dart'; +import 'package:fruitshorts/core/data/models/drama.dart'; +import 'package:fruitshorts/core/data/models/home_feed.dart'; +import 'package:fruitshorts/core/data/repositories/category_repository.dart'; +import 'package:fruitshorts/core/data/repositories/favorite_repository.dart'; +import 'package:fruitshorts/core/data/repositories/playback_repository.dart'; +import 'package:fruitshorts/core/data/repositories/watch_history_repository.dart'; +import 'package:fruitshorts/core/widgets/remote_image.dart'; + +import '../support/fake_catalog.dart'; +import '../support/fake_interaction_repositories.dart'; +import '../support/fake_notice_repository.dart'; +import '../support/widget_player.dart'; + +void main() { + setUp(() { + SharedPreferencesAsyncPlatform.instance = + InMemorySharedPreferencesAsync.empty(); + }); + + testWidgets( + 'should_show_all_dramas_in_two_column_sliver_grid_without_continue_watching', + (tester) async { + tester.view.physicalSize = const Size(390, 844); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.resetPhysicalSize); + addTearDown(tester.view.resetDevicePixelRatio); + + await _pumpApp(tester, categoryRepository: _GridCategoryRepository()); + await tester.tap(find.byKey(const Key('tab-discover'))); + await tester.pumpAndSettle(); + + expect(find.text('Continue Watching'), findsNothing); + expect(find.text('All Dramas'), findsOneWidget); + + final grid = tester.widget( + find.byKey(const Key('home-all-dramas-grid')), + ); + final delegate = + grid.gridDelegate as SliverGridDelegateWithFixedCrossAxisCount; + expect(delegate.crossAxisCount, 2); + expect(delegate.crossAxisSpacing, 12); + expect(delegate.mainAxisSpacing, 16); + expect(delegate.childAspectRatio, closeTo(2 / 3, 0.001)); + expect( + tester + .widget( + find.byKey(const Key('home-all-dramas-section')), + ) + .padding, + const EdgeInsets.symmetric(horizontal: 16), + ); + expect(find.byType(GridView), findsNothing); + expect(find.byType(CustomScrollView), findsOneWidget); + expect(find.byType(RemoteImage), findsWidgets); + + final cover = tester.widget( + find.byKey(const Key('home-drama-cover-city-lights-demo')), + ); + expect(cover.aspectRatio, closeTo(2 / 3, 0.001)); + expect( + tester + .widget( + find.byKey(const Key('home-drama-title-city-lights-demo')), + ) + .maxLines, + 2, + ); + expect( + tester + .widget( + find.byKey(const Key('home-drama-genre-city-lights-demo')), + ) + .maxLines, + 1, + ); + expect(tester.takeException(), isNull); + }, + ); + + testWidgets('should_update_grid_when_category_is_selected', (tester) async { + tester.view.physicalSize = const Size(390, 844); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.resetPhysicalSize); + addTearDown(tester.view.resetDevicePixelRatio); + + await _pumpApp(tester, categoryRepository: _GridCategoryRepository()); + await tester.tap(find.byKey(const Key('tab-discover'))); + await tester.pumpAndSettle(); + + expect(find.text('The Signal'), findsOneWidget); + expect(find.text('City Lights & Velvet Nights'), findsOneWidget); + + await tester.tap(find.text('Romance')); + await tester.pumpAndSettle(); + + expect(find.text('The Signal'), findsNothing); + expect(find.text('City Lights & Velvet Nights'), findsOneWidget); + expect( + find.byKey(const Key('home-drama-card-the-signal-demo')), + findsNothing, + ); + expect( + find.byKey(const Key('home-drama-card-city-lights-demo')), + findsOneWidget, + ); + }); + + testWidgets('should_keep_empty_messages_when_feed_has_no_dramas', ( + tester, + ) async { + tester.view.physicalSize = const Size(390, 844); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.resetPhysicalSize); + addTearDown(tester.view.resetDevicePixelRatio); + + await _pumpApp( + tester, + categoryRepository: _GridCategoryRepository( + discoverFeed: const HomeFeed(), + ), + ); + await tester.tap(find.byKey(const Key('tab-discover'))); + await tester.pumpAndSettle(); + + expect(find.text('All Dramas'), findsOneWidget); + expect(find.text('No matching dramas.'), findsOneWidget); + + await tester.tap(find.text('Romance')); + await tester.pumpAndSettle(); + + expect(find.text('No dramas in this category yet.'), findsOneWidget); + }); + + testWidgets('should_show_retry_when_category_feed_fails', (tester) async { + tester.view.physicalSize = const Size(390, 844); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.resetPhysicalSize); + addTearDown(tester.view.resetDevicePixelRatio); + + await _pumpApp(tester, categoryRepository: _FailingCategoryRepository()); + await tester.tap(find.byKey(const Key('tab-discover'))); + await tester.pumpAndSettle(); + + expect(find.text('All Dramas'), findsOneWidget); + expect(find.text('Categories could not be loaded.'), findsOneWidget); + expect(find.text('Retry'), findsOneWidget); + expect(find.byKey(const Key('home-all-dramas-grid')), findsNothing); + }); + + testWidgets('should_fit_two_column_grid_on_compact_screen', (tester) async { + tester.view.physicalSize = const Size(320, 568); + tester.view.devicePixelRatio = 1; + addTearDown(tester.view.resetPhysicalSize); + addTearDown(tester.view.resetDevicePixelRatio); + + await _pumpApp(tester, categoryRepository: _GridCategoryRepository()); + await tester.tap(find.byKey(const Key('tab-discover'))); + await tester.pumpAndSettle(); + await tester.scrollUntilVisible( + find.byKey(const Key('home-drama-card-city-lights-demo')), + 180, + scrollable: find.byType(Scrollable).last, + ); + await tester.pumpAndSettle(); + + expect(find.text('All Dramas'), findsOneWidget); + expect(find.text('City Lights & Velvet Nights'), findsOneWidget); + expect(tester.takeException(), isNull); + }); +} + +Future _pumpApp( + WidgetTester tester, { + required CategoryRepository categoryRepository, +}) async { + final catalog = FakeCatalogRepository(); + await tester.pumpWidget( + LumaDramaApp( + playerFactory: createWidgetPlayer, + catalogRepository: catalog, + categoryRepository: categoryRepository, + watchHistoryRepository: InMemoryWatchHistoryRepository(), + favoriteRepository: InMemoryFavoriteRepository(), + playbackRepository: FakePlaybackRepository(), + commentRepository: FakeCommentRepository(), + interactionRepository: FakeInteractionRepository(), + noticeRepository: FakeNoticeRepository(), + ), + ); + await tester.pumpAndSettle(); +} + +class _GridCategoryRepository implements CategoryRepository { + _GridCategoryRepository({HomeFeed? discoverFeed}) + : discoverFeed = discoverFeed ?? _defaultDiscoverFeed; + + final HomeFeed discoverFeed; + + @override + Future> fetchCategories() async => const [ + ContentCategory(id: '7', name: 'Romance'), + ]; + + @override + Future fetchDiscoverFeed() async => discoverFeed; +} + +class _FailingCategoryRepository implements CategoryRepository { + @override + Future> fetchCategories() async => const [ + ContentCategory(id: '7', name: 'Romance'), + ]; + + @override + Future fetchDiscoverFeed() async { + throw StateError('discover feed unavailable'); + } +} + +final _defaultDiscoverFeed = HomeFeed( + sections: [ + FeedSection(code: 'hot', title: 'Hot', dramas: demoDramas), + FeedSection( + code: 'category-7', + title: 'Romance', + dramas: [ + Drama( + id: 'city-lights-demo', + title: 'City Lights & Velvet Nights', + synopsis: 'City Lights & Velvet Nights synopsis', + posterUrl: '/files/city-lights-demo.png', + genre: 'Urban Romance', + detailLoaded: true, + ), + ], + ), + ], +); diff --git a/test/widget_test.dart b/test/widget_test.dart index c610e38..6f86ddf 100644 --- a/test/widget_test.dart +++ b/test/widget_test.dart @@ -155,7 +155,7 @@ void main() { await tester.pumpWidget(appUnderTest()); await tester.pumpAndSettle(); - expect(find.text('Continue Watching'), findsOneWidget); + expect(find.text('All Dramas'), findsOneWidget); await tester.binding.handlePopRoute(); await tester.pumpAndSettle(); expect( @@ -186,15 +186,15 @@ void main() { .active, isFalse, ); - expect(find.text('Continue Watching'), findsOneWidget); + expect(find.text('All Dramas'), findsOneWidget); await tester.binding.handlePopRoute(); await tester.pumpAndSettle(); await tester.tap(find.byKey(const Key('drawer-discover'))); await tester.pumpAndSettle(); - expect(find.text('Continue Watching'), findsOneWidget); + expect(find.text('All Dramas'), findsOneWidget); expect( - Navigator.of(tester.element(find.text('Continue Watching'))).canPop(), + Navigator.of(tester.element(find.text('All Dramas'))).canPop(), isFalse, ); }, @@ -313,7 +313,7 @@ void main() { await tester.pumpAndSettle(); expect(find.text('For You'), findsOneWidget); - expect(find.text('Continue Watching'), findsOneWidget); + expect(find.text('All Dramas'), findsOneWidget); expect(find.text('Rewards'), findsOneWidget); expect(find.text(r'$ 0'), findsOneWidget); expect(find.text('Urban Romance', skipOffstage: false), findsOneWidget); @@ -345,10 +345,10 @@ void main() { await tester.pumpAndSettle(); await tester.tap(find.text('Home')); await tester.pumpAndSettle(); - expect(find.text('Continue Watching'), findsOneWidget); + expect(find.text('All Dramas'), findsOneWidget); expect(find.byTooltip('Search'), findsNothing); expect( - Navigator.of(tester.element(find.text('Continue Watching'))).canPop(), + Navigator.of(tester.element(find.text('All Dramas'))).canPop(), isFalse, ); @@ -365,10 +365,10 @@ void main() { await tester.tap(find.byKey(const Key('tab-discover'))); await tester.pumpAndSettle(); - expect(find.text('Continue Watching'), findsOneWidget); + expect(find.text('All Dramas'), findsOneWidget); expect(find.byTooltip('Search'), findsNothing); expect( - Navigator.of(tester.element(find.text('Continue Watching'))).canPop(), + Navigator.of(tester.element(find.text('All Dramas'))).canPop(), isFalse, ); await tester.tap(find.byKey(const Key('tab-my-list'))); @@ -564,7 +564,7 @@ void main() { await tester.pumpWidget(appUnderTest()); await tester.pumpAndSettle(); - expect(find.text('Continue Watching'), findsOneWidget); + expect(find.text('All Dramas'), findsOneWidget); await tester.binding.handlePopRoute(); await tester.pumpAndSettle(); expect(find.text('Quick access'), findsOneWidget); @@ -572,7 +572,7 @@ void main() { await tester.binding.handlePopRoute(); await tester.pumpAndSettle(); expect(find.text('Quick access'), findsNothing); - expect(find.text('Continue Watching'), findsOneWidget); + expect(find.text('All Dramas'), findsOneWidget); expect( Navigator.of( tester.element(find.byKey(const Key('home-tabs-bar'))),