// Author: fruit.com.cn import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:provider/provider.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/config/app_config.dart'; import 'package:fruitshorts/core/data/models/drama.dart'; import 'package:fruitshorts/core/data/models/feed_page.dart'; import 'package:fruitshorts/core/data/models/home_feed.dart'; import 'package:fruitshorts/core/data/repositories/catalog_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/data/services/catalog_controller.dart'; import 'package:fruitshorts/core/theme/app_theme.dart'; import 'package:fruitshorts/l10n/app_localizations.dart'; import 'package:fruitshorts/pages/home/home_view.dart'; import '../support/fake_catalog.dart'; import '../support/fake_category.dart'; import '../support/fake_haptics.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(); AppConfig.clearRuntimeBaseUrl(); AppConfig.ready = Future.value(); }); tearDown(() { AppConfig.clearRuntimeBaseUrl(); AppConfig.ready = Future.value(); }); testWidgets('should_show_loading_without_flutter_splash_before_settles', ( tester, ) async { final controller = CatalogController(_EmptyCatalogRepository()) ..loading = true ..settled = false; addTearDown(controller.dispose); await _pumpHomePage(tester, controller); expect(find.byType(CircularProgressIndicator), findsOneWidget); expect(find.byKey(const Key('startup-splash')), findsNothing); }); testWidgets('should_show_spinner_when_retry_loads_after_catalog_settled', ( tester, ) async { final controller = CatalogController(_EmptyCatalogRepository()) ..loading = true ..settled = true; addTearDown(controller.dispose); await _pumpHomePage(tester, controller); expect(find.byType(CircularProgressIndicator), findsOneWidget); expect(find.byKey(const Key('startup-splash')), findsNothing); }); testWidgets('should_enter_home_after_loading_when_data_is_ready', ( tester, ) async { AppConfig.ready = Future.value(); final catalog = _PendingCatalogRepository(); await tester.pumpWidget( LumaDramaApp( playerFactory: createWidgetPlayer, catalogRepository: catalog, categoryRepository: FakeCategoryRepository(catalog: catalog), watchHistoryRepository: InMemoryWatchHistoryRepository(), favoriteRepository: InMemoryFavoriteRepository(cards: catalog.dramas), playbackRepository: FakePlaybackRepository(), commentRepository: FakeCommentRepository(), interactionRepository: FakeInteractionRepository(), noticeRepository: FakeNoticeRepository(), haptics: FakeHaptics(), ), ); await tester.pump(); expect(find.byKey(const Key('startup-splash')), findsNothing); expect(find.byType(CircularProgressIndicator), findsOneWidget); catalog.complete(_feed); await tester.pumpAndSettle(); expect(find.byKey(const Key('startup-splash')), findsNothing); expect(find.byKey(const Key('home-grid')), findsOneWidget); }); } Future _pumpHomePage( WidgetTester tester, CatalogController controller, ) async { await tester.pumpWidget( ChangeNotifierProvider.value( value: controller, child: MaterialApp( theme: AppTheme.light(), locale: const Locale('en'), supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: AppLocalizations.localizationsDelegates, home: const HomePage(), ), ), ); await tester.pump(); } final _feed = HomeFeed( sections: [ FeedSection(code: 'hot', title: 'Hot', dramas: [demoDramas.first]), ], ); class _EmptyCatalogRepository implements CatalogRepository { @override Future fetchHomeFeed() async => const HomeFeed(); @override Future fetchDiscoverFeed() async => const HomeFeed(); @override Future fetchFeed({ List types = const [], String? categoryId, String? cursor, int size = 10, }) async => const FeedPage(); @override Future fetchDrama(String dramaId) async => _feed.dramas.first; } class _PendingCatalogRepository implements CatalogRepository { final Completer _home = Completer(); List get dramas => _feed.dramas; void complete(HomeFeed feed) { if (!_home.isCompleted) _home.complete(feed); } @override Future fetchHomeFeed() => _home.future; @override Future fetchDiscoverFeed() async => _feed; @override Future fetchFeed({ List types = const [], String? categoryId, String? cursor, int size = 10, }) async => FeedPage(dramas: _feed.dramas); @override Future fetchDrama(String dramaId) async => _feed.dramas.first; }