import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fruitshorts/core/data/services/category_controller.dart'; import 'package:fruitshorts/core/data/services/home_feed_controller.dart'; import 'package:fruitshorts/l10n/app_localizations.dart'; import 'package:fruitshorts/pages/home/home_feed_channel.dart'; import 'package:fruitshorts/pages/recommend/recommend_page.dart'; import 'package:provider/provider.dart'; import '../support/fake_catalog.dart'; import '../support/fake_category.dart'; void main() { testWidgets('should_refresh_once_when_parent_rebuilds_repeatedly', ( tester, ) async { final fixture = await _pumpRecommend(tester, active: true); expect(fixture.controller.refreshCalls, 1); for (var index = 0; index < 20; index++) { fixture.harnessKey.currentState!.rebuildSameInputs(); await tester.pump(); } expect(tester.takeException(), isNull); expect(fixture.controller.refreshCalls, 1); }); testWidgets('should_refresh_once_when_active_turns_on', (tester) async { final fixture = await _pumpRecommend(tester, active: false); expect(fixture.controller.refreshCalls, 0); fixture.harnessKey.currentState!.update(active: true); await tester.pump(); expect(fixture.controller.refreshCalls, 1); fixture.harnessKey.currentState!.rebuildSameInputs(); await tester.pump(); expect(fixture.controller.refreshCalls, 1); }); testWidgets('should_refresh_again_when_data_source_changes', (tester) async { final fixture = await _pumpRecommend(tester, active: true); expect(fixture.controller.refreshCalls, 1); fixture.harnessKey.currentState!.update(active: false); await tester.pump(); expect(fixture.controller.refreshCalls, 1); fixture.harnessKey.currentState!.update(active: true); await tester.pump(); expect(fixture.controller.refreshCalls, 2); }); testWidgets('should_not_refresh_after_dispose_when_channel_changes', ( tester, ) async { final fixture = await _pumpRecommend(tester, active: true); expect(fixture.controller.refreshCalls, 1); fixture.harnessKey.currentState!.update(active: true); final selection = fixture.controller.selectChannel(HomeFeedChannel.anime); await tester.pumpWidget(const MaterialApp(home: SizedBox.shrink())); await selection; await tester.pump(); expect(fixture.controller.refreshCalls, 1); }); } Future<_RecommendFixture> _pumpRecommend( WidgetTester tester, { required bool active, }) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final catalog = FakeCatalogRepository(); final controller = _RecordingHomeFeedController(catalog); final categories = CategoryController( FakeCategoryRepository(catalog: catalog), ); final harnessKey = GlobalKey<_RecommendHarnessState>(); final fixture = _RecommendFixture( controller: controller, categoryController: categories, harnessKey: harnessKey, ); addTearDown(controller.dispose); addTearDown(categories.dispose); await tester.pumpWidget(fixture.app(active: active)); await tester.pump(); return fixture; } class _RecommendFixture { const _RecommendFixture({ required this.controller, required this.categoryController, required this.harnessKey, }); final _RecordingHomeFeedController controller; final CategoryController categoryController; final GlobalKey<_RecommendHarnessState> harnessKey; Widget app({required bool active}) { return MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: ChangeNotifierProvider.value( value: categoryController, child: _RecommendHarness( key: harnessKey, controller: controller, active: active, ), ), ); } } class _RecommendHarness extends StatefulWidget { const _RecommendHarness({ super.key, required this.controller, required this.active, }); final HomeFeedController controller; final bool active; @override State<_RecommendHarness> createState() => _RecommendHarnessState(); } class _RecommendHarnessState extends State<_RecommendHarness> { late HomeFeedController _controller; late bool _active; @override void initState() { super.initState(); _controller = widget.controller; _active = widget.active; } void rebuildSameInputs() { setState(() {}); } void update({required bool active}) { setState(() { _active = active; }); } @override Widget build(BuildContext context) { return RecommendPage( feedController: _controller, active: _active, videoOnly: true, onSearch: () {}, ); } } class _RecordingHomeFeedController extends HomeFeedController { _RecordingHomeFeedController(super.repository); int refreshCalls = 0; @override Future refreshCurrent() async { refreshCalls++; notifyListeners(); return true; } }