// Author: fruit.com.cn import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fruitshorts/core/data/models/drama.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/home/widgets/home_feed_card.dart'; void main() { testWidgets('should_render_grid_card_cover_badge_views_title_and_tags', ( tester, ) async { var taps = 0; await _pumpCard( tester, drama: _drama( id: 'anime-card', title: 'A very long home grid drama title that must wrap', dramaType: 'ANIME', viewCount: 444500, ), onTap: () => taps++, ); final card = find.byKey(const Key('home-feed-card-anime-card')); expect(tester.widget(card).color, AppColors.transparent); expect( tester .widgetList( find.descendant(of: card, matching: find.byType(Container)), ) .map((container) => container.decoration) .whereType() .every((decoration) => decoration.border == null), isTrue, ); final cover = find.byKey(const Key('home-feed-cover-anime-card')); final coverSize = tester.getSize(cover); expect(coverSize.height / coverSize.width, closeTo(6 / 5, 0.02)); final coverClip = tester.widget( find.byKey(const Key('home-feed-cover-clip-anime-card')), ); expect( coverClip.borderRadius, BorderRadius.circular(AppDimens.cardCoverRadius), ); final badge = find.byKey( const Key('drama-cover-badge-ribbon-anime-card-anime'), ); expect(badge, findsOneWidget); expect( tester .widget(find.descendant(of: badge, matching: find.byType(Text))) .style ?.fontSize, AppDimens.cardBadgeFont, ); expect( tester.getTopRight(badge).dx, closeTo(tester.getTopRight(cover).dx, 1), ); expect( tester.getTopRight(badge).dy, closeTo(tester.getTopRight(cover).dy, 1), ); expect(find.text('Anime'), findsOneWidget); expect(find.byKey(const Key('home-feed-views-anime-card')), findsOneWidget); final views = find.byKey(const Key('home-feed-views-anime-card')); final viewsText = tester.widget( find.descendant(of: views, matching: find.byType(Text)), ); expect(viewsText.data, '44.5w'); expect(viewsText.style?.fontSize, AppDimens.cardPlayCountFont); expect(viewsText.style?.fontWeight, FontWeight.w400); expect(viewsText.style?.color, AppColors.navSelected); expect( find.descendant(of: cover, matching: find.byKey(views)), findsNothing, ); expect( find.descendant( of: views, matching: find.byIcon(Icons.auto_awesome_rounded), ), findsOneWidget, ); final title = tester.widget( find.byKey(const Key('home-feed-title-anime-card')), ); expect(title.maxLines, 2); expect(title.style?.fontSize, AppDimens.cardTitleFont); expect(title.style?.fontWeight, FontWeight.w500); expect(title.style?.height, AppDimens.cardTitleLineHeight); final tags = tester.widget( find.byKey(const Key('home-feed-tags-anime-card')), ); expect(tags.data, 'Romance | Urban'); expect(tags.style?.fontSize, AppDimens.cardTagFont); expect(tags.style?.color, AppColors.textTertiary); expect(tester.getTopLeft(title).dx, tester.getTopLeft(cover).dx); expect(tester.getTopLeft(tags).dx, tester.getTopLeft(cover).dx); await tester.tap(card); expect(taps, 1); }); testWidgets('should_choose_anime_new_and_hot_badges_from_real_data', ( tester, ) async { final now = DateTime(2026, 10, 22, 12); await tester.pumpWidget( MaterialApp( theme: AppTheme.dark(), locale: const Locale('en'), supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: AppLocalizations.localizationsDelegates, home: Scaffold( body: Row( children: [ for (final drama in [ _drama(id: 'anime', title: 'Anime', dramaType: 'ANIME'), _drama( id: 'new', title: 'New', createdAt: now.subtract(const Duration(days: 2)), ), _drama(id: 'hot', title: 'Hot', isHot: true), ]) SizedBox( width: 130, height: 240, child: HomeFeedCard(drama: drama, onTap: () {}, now: now), ), ], ), ), ), ); await tester.pumpAndSettle(); expect( find.byKey(const Key('drama-cover-badge-ribbon-anime-anime')), findsOneWidget, ); expect( find.byKey(const Key('drama-cover-badge-ribbon-new-newRelease')), findsOneWidget, ); expect( find.byKey(const Key('drama-cover-badge-ribbon-hot-hot')), findsOneWidget, ); }); } Drama _drama({ required String id, required String title, String dramaType = 'LIVE', int viewCount = 0, bool isHot = false, DateTime? createdAt, }) { return Drama( id: id, title: title, synopsis: 'synopsis', posterUrl: '/files/$id.jpg', genre: 'Romance', category: 'Urban', viewCount: viewCount, isHot: isHot, dramaType: dramaType, createdAt: createdAt, ); } Future _pumpCard( WidgetTester tester, { required Drama drama, required VoidCallback onTap, }) { return tester.pumpWidget( MaterialApp( theme: AppTheme.dark(), locale: const Locale('en'), supportedLocales: AppLocalizations.supportedLocales, localizationsDelegates: AppLocalizations.localizationsDelegates, home: Scaffold( body: Center( child: SizedBox( width: 130, height: 240, child: HomeFeedCard( drama: drama, onTap: onTap, now: DateTime(2026, 10, 22, 12), ), ), ), ), ), ); }