// Author: fruit.com.cn import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fruitshorts/core/data/services/interaction_controller.dart'; import 'package:fruitshorts/core/theme/app_theme.dart'; import 'package:fruitshorts/l10n/app_localizations.dart'; import 'package:fruitshorts/pages/player/widgets/home_feed_overlay.dart'; import '../support/fake_catalog.dart'; import '../support/fake_haptics.dart'; import '../support/fake_interaction_repositories.dart'; void main() { testWidgets('quick double tap likes without pausing', (tester) async { final repository = FakeInteractionRepository(); final haptics = FakeHaptics(); final key = GlobalKey<_HarnessState>(); await _pumpHarness(tester, repository, haptics, key); final gesture = find.byKey(const Key('home-feed-gesture')); final center = tester.getCenter(gesture); await tester.tapAt(center); await tester.pump(const Duration(milliseconds: 100)); await tester.tapAt(center); await tester.pumpAndSettle(); expect(key.currentState!.pauseToggles, 0); expect(repository.likeTargets, hasLength(1)); expect(haptics.lightImpacts, 1); expect( find.descendant( of: find.byKey(const Key('rail-like')), matching: find.byIcon(Icons.favorite_rounded), ), findsOneWidget, ); }); testWidgets('single tap toggles playback after 280ms', (tester) async { final key = GlobalKey<_HarnessState>(); await _pumpHarness(tester, FakeInteractionRepository(), FakeHaptics(), key); await tester.tapAt( tester.getCenter(find.byKey(const Key('home-feed-gesture'))), ); await tester.pump(const Duration(milliseconds: 279)); expect(key.currentState!.pauseToggles, 0); await tester.pump(const Duration(milliseconds: 2)); expect(key.currentState!.pauseToggles, 1); }); } Future _pumpHarness( WidgetTester tester, FakeInteractionRepository repository, FakeHaptics haptics, GlobalKey<_HarnessState> key, ) async { await tester.pumpWidget( MaterialApp( theme: AppTheme.dark(), locale: const Locale('en'), supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: AppLocalizations.localizationsDelegates, home: _Harness(key: key, repository: repository, haptics: haptics), ), ); await tester.pumpAndSettle(); } class _Harness extends StatefulWidget { const _Harness({super.key, required this.repository, required this.haptics}); final FakeInteractionRepository repository; final FakeHaptics haptics; @override State<_Harness> createState() => _HarnessState(); } class _HarnessState extends State<_Harness> { late final InteractionController interactions = InteractionController( widget.repository, ); int pauseToggles = 0; Future _like({bool likeOnly = false}) async { await widget.haptics.lightImpact(); final episodeId = demoDramas.first.episodes.first.id; if (interactions.isEpisodeBusy(episodeId) || (likeOnly && interactions.isEpisodeLiked(episodeId))) { return; } await interactions.toggleEpisodeLike(episodeId); } @override void dispose() { interactions.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final l10n = AppLocalizations.of(context)!; return Scaffold( backgroundColor: Colors.black, body: ListenableBuilder( listenable: interactions, builder: (context, _) { final episode = demoDramas.first.episodes.first; return HomeFeedOverlay( l10n: l10n, drama: demoDramas.first, episode: episode, controller: null, locked: false, liked: interactions.isEpisodeLiked(episode.id), likeCount: interactions.episodeLikeCount(episode.id), saved: false, visible: true, boosting: false, onLike: () => _like(), onDoubleTapLike: (_) => _like(likeOnly: true), onSave: () {}, onComments: () {}, onShare: () {}, onDetail: () {}, onOpenFullPlayer: () {}, onTogglePlay: () => setState(() => pauseToggles++), onBoostStart: () {}, onBoostEnd: () {}, onMore: () {}, ); }, ), ); } }