// Author: fruit.com.cn import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fruitshorts/app.dart'; import 'package:fruitshorts/core/data/models/watch_progress.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/theme/app_colors.dart'; import 'package:fruitshorts/core/theme/app_dimens.dart'; import 'package:fruitshorts/core/theme/app_theme.dart'; import 'package:fruitshorts/l10n/app_localizations.dart'; import 'package:fruitshorts/pages/auth/auth_view.dart'; import 'package:fruitshorts/pages/mine/mine_types.dart'; import 'package:fruitshorts/pages/mine/widgets/mine_menu.dart'; import 'package:fruitshorts/pages/store/vip_page.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 '../support/fake_catalog.dart'; import '../support/fake_category.dart'; import '../support/fake_interaction_repositories.dart'; import '../support/fake_notice_repository.dart'; import '../support/widget_player.dart'; void main() { testWidgets('should_render_visitor_user_vip_and_seven_menu_rows', ( tester, ) async { await _pumpApp(tester, signedIn: false); await _openMine(tester); expect(find.byKey(const Key('mine-user-row')), findsOneWidget); expect(find.byKey(const Key('mine-avatar')), findsOneWidget); expect(find.text('Visitor'), findsOneWidget); expect(find.text('UID: --'), findsOneWidget); expect(find.byKey(const Key('mine-uid-copy')), findsOneWidget); expect(find.byKey(const Key('mine-login')), findsOneWidget); expect(find.byKey(const Key('mine-edit')), findsNothing); expect(find.byKey(const Key('mine-vip')), findsOneWidget); expect(find.byKey(const Key('mine-vip-open')), findsOneWidget); expect(find.byKey(const Key('mine-menu')), findsOneWidget); final title = tester.widget( find.descendant( of: find.byKey(const Key('mine-page-surface')), matching: find.text('My'), ), ); expect(title.style?.fontSize, AppDimens.minePageTitle); expect(title.style?.fontWeight, FontWeight.w700); expect(find.byKey(const Key('mine-mini-player')), findsNothing); for (final oldShortcut in const [ Key('mine-watchlist'), Key('mine-history-shortcut'), Key('mine-downloads-shortcut'), ]) { expect(find.byKey(oldShortcut), findsNothing); } final menuRows = tester.widgetList( find.descendant( of: find.byKey(const Key('mine-menu')), matching: find.byType(ListTile), ), ); expect(menuRows.map((row) => row.key), const [ Key('mine-wallet'), Key('mine-downloads'), Key('mine-history'), Key('mine-watch-preferences'), Key('mine-help'), Key('mine-language'), Key('mine-settings'), ]); expect(menuRows, hasLength(7)); expect( menuRows.every((row) => row.minTileHeight == AppDimens.mineRowHeight), isTrue, ); final dividers = tester.widgetList( find.descendant( of: find.byKey(const Key('mine-menu')), matching: find.byType(Divider), ), ); expect(dividers, hasLength(6)); expect( dividers.every((divider) => divider.color == AppColors.contentSurface), isTrue, ); final menu = tester.widget(find.byKey(const Key('mine-menu'))); final menuDecoration = menu.decoration! as BoxDecoration; expect(menuDecoration.color, AppColors.mineCard); expect( menuDecoration.borderRadius, BorderRadius.circular(AppDimens.mineCardRadius), ); expect(menuDecoration.boxShadow, isNull); final vip = tester.widget(find.byKey(const Key('mine-vip'))); final vipDecoration = vip.decoration! as BoxDecoration; expect((vipDecoration.gradient! as LinearGradient).colors, const [ AppColors.vipGoldStart, AppColors.vipGoldEnd, ]); expect(vipDecoration.boxShadow, isNull); expect(tester.takeException(), isNull); await tester.tap(find.byKey(const Key('mine-login'))); await tester.pumpAndSettle(); expect(find.byType(AuthPage), findsOneWidget); }); testWidgets('should_render_signed_in_user_without_floating_mini_player', ( tester, ) async { await _pumpApp( tester, signedIn: true, history: InMemoryWatchHistoryRepository(seed: [_recent]), ); await _openMine(tester); expect(find.text('Viewer'), findsOneWidget); expect(find.text('UID: U-100'), findsOneWidget); expect(find.byKey(const Key('mine-edit')), findsOneWidget); expect(find.byKey(const Key('mine-login')), findsNothing); expect(find.byKey(const Key('mine-mini-player')), findsNothing); expect(find.byType(FloatingActionButton), findsNothing); await tester.tap(find.byKey(const Key('mine-edit'))); await tester.pumpAndSettle(); expect(find.byKey(const Key('mine-edit-profile')), findsOneWidget); expect(find.byKey(const Key('mine-edit-works')), findsOneWidget); expect(find.byKey(const Key('mine-edit-creator')), findsOneWidget); expect(find.byKey(const Key('mine-edit-notices')), findsOneWidget); await tester.tap(find.byKey(const Key('mine-edit-works'))); await tester.pumpAndSettle(); expect(find.byKey(const Key('mine-placeholder-page')), findsOneWidget); expect(tester.takeException(), isNull); }); testWidgets('should_open_vip_from_mine_without_floating_player', ( tester, ) async { await _pumpApp( tester, signedIn: true, history: InMemoryWatchHistoryRepository(seed: [_recent]), ); await _openMine(tester); await tester.tap(find.byKey(const Key('mine-vip-open'))); await tester.pumpAndSettle(); expect(find.byType(VipPage), findsOneWidget); await tester.binding.handlePopRoute(); await tester.pumpAndSettle(); expect(find.byKey(const Key('mine-mini-player')), findsNothing); expect(find.byType(FloatingActionButton), findsNothing); }); testWidgets('should_expose_seven_menu_destinations_in_order', (tester) async { tester.view.physicalSize = const Size(390, 900); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final opened = []; var languageOpened = 0; await tester.pumpWidget( MaterialApp( theme: AppTheme.dark(), locale: const Locale('en'), supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: AppLocalizations.localizationsDelegates, home: Scaffold( body: SingleChildScrollView( padding: const EdgeInsets.all(AppDimens.pageHorizontal), child: MineMenuList( onOpen: opened.add, onLanguage: () => languageOpened++, languageLabel: 'English', ), ), ), ), ); const rows = [ (Key('mine-wallet'), MineDestination.wallet), (Key('mine-downloads'), MineDestination.downloads), (Key('mine-history'), MineDestination.history), (Key('mine-watch-preferences'), MineDestination.preferences), (Key('mine-help'), MineDestination.help), ]; for (final row in rows) { await tester.tap(find.byKey(row.$1)); await tester.pump(); expect(opened.last, row.$2); } await tester.tap(find.byKey(const Key('mine-language'))); await tester.pump(); expect(languageOpened, 1); await tester.tap(find.byKey(const Key('mine-settings'))); await tester.pump(); expect(opened.last, MineDestination.settings); }); } const _recent = WatchProgress( dramaId: 'city-lights-demo', episodeId: 'city-lights-demo-3', dramaTitle: 'Recent drama', dramaCover: '/files/recent.jpg', episodeNumber: 3, totalEpisodes: 8, position: Duration(seconds: 30), duration: Duration(minutes: 2), ); Future _openMine(WidgetTester tester) async { await tester.tap(find.byKey(const Key('tab-mine'))); await tester.pumpAndSettle(); } Future _pumpApp( WidgetTester tester, { required bool signedIn, WatchHistoryRepository? history, }) async { tester.view.physicalSize = const Size(390, 1000); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); SharedPreferencesAsyncPlatform.instance = signedIn ? InMemorySharedPreferencesAsync.withData({ 'auth.accessToken': 'access', 'auth.refreshToken': 'refresh', 'auth.user': '{"uid":"U-100","nickname":"Viewer","avatarUrl":""}', }) : InMemorySharedPreferencesAsync.empty(); final catalog = FakeCatalogRepository(); await tester.pumpWidget( LumaDramaApp( playerFactory: createWidgetPlayer, catalogRepository: catalog, categoryRepository: FakeCategoryRepository(catalog: catalog), watchHistoryRepository: history ?? InMemoryWatchHistoryRepository(), favoriteRepository: InMemoryFavoriteRepository(cards: catalog.dramas), playbackRepository: FakePlaybackRepository(), commentRepository: FakeCommentRepository(), interactionRepository: FakeInteractionRepository(), noticeRepository: FakeNoticeRepository(), ), ); await tester.pumpAndSettle(); }