// Author: fruit.com.cn import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fruitshorts/core/data/models/category_ranking.dart'; import 'package:fruitshorts/core/data/models/content_category.dart'; import 'package:fruitshorts/core/data/models/drama.dart'; import 'package:fruitshorts/core/data/models/page_result.dart'; import 'package:fruitshorts/core/data/services/category_controller.dart'; import 'package:fruitshorts/l10n/app_localizations.dart'; import 'package:fruitshorts/pages/recommend/recommend_discover_page.dart'; import 'package:provider/provider.dart'; import '../support/fake_catalog.dart'; import '../support/fake_category.dart'; void main() { testWidgets('should_keep_refresh_indicator_below_category_nav_when_pulling', ( tester, ) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final controller = CategoryController( FakeCategoryRepository(catalog: FakeCatalogRepository()), ); final dramas = [for (var index = 1; index <= 12; index++) _drama('$index')]; controller.categories = const [ContentCategory(id: '7', name: 'Romance')]; controller.dramaPageLoader = (categoryId, {pageIndex = 1, pageSize = 20}) async => PageResult(total: dramas.length, list: dramas); addTearDown(controller.dispose); await tester.pumpWidget( MaterialApp( locale: const Locale('en'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: Scaffold( body: ChangeNotifierProvider.value( value: controller, child: const RecommendDiscoverPage(active: true, onSearch: _noop), ), ), ), ); await tester.pumpAndSettle(); final nav = tester.getRect(find.byKey(const Key('recommend-tab-7'))); final scroll = find.byKey(const PageStorageKey('recommend-feed-7')); final scrollable = tester.state( find.descendant(of: scroll, matching: find.byType(Scrollable)).first, ); expect(scrollable.position.pixels, 0); final scrollRect = tester.getRect(scroll); final gesture = await tester.startGesture( Offset(scrollRect.center.dx, scrollRect.top + 120), ); await gesture.moveBy(const Offset(0, 160)); await tester.pump(); final indicator = find.byKey(const Key('pull-refresh-indicator')); expect(indicator, findsOneWidget); expect(tester.getRect(indicator).top, greaterThanOrEqualTo(nav.bottom)); await gesture.up(); await tester.pumpAndSettle(); }); testWidgets('should_center_empty_state_when_category_has_no_dramas', ( tester, ) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final controller = CategoryController( FakeCategoryRepository(catalog: FakeCatalogRepository()), ); controller.categories = const [ContentCategory(id: '7', name: 'Romance')]; controller.dramaPageLoader = (categoryId, {pageIndex = 1, pageSize = 20}) async => const PageResult(); addTearDown(controller.dispose); await tester.pumpWidget( MaterialApp( locale: const Locale('zh'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: ChangeNotifierProvider.value( value: controller, child: const RecommendDiscoverPage(active: true, onSearch: _noop), ), ), ); await tester.pump(); await tester.pump(); await tester.pump(const Duration(milliseconds: 1)); expect(find.text('暂无剧目'), findsOneWidget); expect(find.text('暂无剧目。'), findsNothing); final content = tester.getRect( find.byKey(const Key('empty-state-content')), ); final icon = tester.getRect(find.byKey(const Key('empty-state-icon'))); final title = tester.getRect(find.byKey(const Key('empty-state-title'))); final viewport = tester.view.physicalSize / tester.view.devicePixelRatio; expect(content.center.dx, closeTo(viewport.width / 2, 1.0)); expect(content.center.dy, closeTo(viewport.height / 2, 1.0)); expect(icon.center.dx, closeTo(title.center.dx, 0.1)); expect(title.top - icon.bottom, closeTo(12, 0.1)); }); testWidgets('should_render_ranking_layout_when_category_is_ranking', ( tester, ) async { final controller = CategoryController( FakeCategoryRepository(catalog: FakeCatalogRepository()), ); controller.categories = const [ ContentCategory( id: 'ranking', name: '推荐榜', layoutType: ContentCategory.rankingLayout, ), ]; var dramaLoads = 0; var rankingLoads = 0; controller.dramaPageLoader = (categoryId, {pageIndex = 1, pageSize = 20}) async { dramaLoads++; return const PageResult(); }; controller.rankingLoader = (categoryId) async { rankingLoads++; return CategoryRanking( id: categoryId, code: 'RECOMMEND_RANKING', revision: '2', entries: [ for (var rank = 1; rank <= 4; rank++) CategoryRankingEntry(rank: rank, drama: _drama('$rank')), ], ); }; addTearDown(controller.dispose); await tester.pumpWidget( MaterialApp( locale: const Locale('zh'), localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: ChangeNotifierProvider.value( value: controller, child: const RecommendDiscoverPage(active: true, onSearch: _noop), ), ), ); await tester.pumpAndSettle(); expect(rankingLoads, 1); expect(dramaLoads, 0); expect(find.byKey(const Key('ranking-top-1')), findsOneWidget); expect(find.byKey(const Key('ranking-row-3')), findsOneWidget); final navUnderline = tester.getRect( find.byKey(const Key('recommend-tab-ranking-underline')), ); final firstRow = tester.getRect(find.byKey(const Key('ranking-row-0'))); final gap = firstRow.top - navUnderline.bottom; expect(gap, greaterThan(10)); expect(gap, lessThan(26)); expect( find.byKey(const Key('recommend-category-grid-ranking')), findsNothing, ); }); } void _noop() {} Drama _drama(String id) => Drama( id: id, title: 'Rank $id', synopsis: '', posterUrl: '', viewCount: int.parse(id) * 10, );