import 'package:fancake/features/home/home_screen.dart'; import 'package:fancake/features/home/home_data.dart'; import 'package:fancake/features/quest/quest_data.dart'; import 'package:fancake/features/planet/hero_participation_sheet.dart'; import 'package:fancake/features/galaxy/widgets/zap_button.dart'; import 'package:fancake/features/galaxy/galaxy_screen.dart'; import 'package:fancake/features/galaxy/galaxy_data.dart'; import 'package:fancake/features/planet/planet_data.dart'; import 'package:fancake/features/planet/planet_detail_screen.dart'; import 'package:fancake/core/theme/app_theme.dart'; import 'eclipse_stubs.dart'; import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:fancake/core/data/planet_visual_state.dart'; import 'package:fancake/core/ui/hero_time_status.dart'; class _NoSpendGalaxy extends SampleGalaxyRepository { int spends = 0; @override Future zap({required String fandomId, required int count, required String clientSeq}) { spends++; return super.zap(fandomId: fandomId, count: count, clientSeq: clientSeq); } } void main() { testWidgets('HOME HERO 참여는 현재 팬덤의 서버 저장소로 진입한다', (tester) async { tester.view.physicalSize = const Size(1080, 2400); tester.view.devicePixelRatio = 2; addTearDown(tester.view.reset); String? opened; final home = await const SampleHomeRepository().load(); await tester.pumpWidget(MaterialApp(theme: buildAppTheme(), home: HomeScreen( repository: const SampleHomeRepository(), questRepository: const SampleQuestRepository(), eclipse: const NoEclipseRepository(), planetRepository: (id) { opened = id; return SamplePlanetRepository(fandomId: id); }, watchPlanet: (_) => Stream.value(PlanetVisualState(stage: 1, isHeroTime: true, heroEventIds: const {'gift'}, heroEndsAt: DateTime.now().add(const Duration(minutes: 5)))), ))); await tester.pump(); await tester.pump(const Duration(seconds: 1)); await tester.tap(find.text('ZAP+ · 선물 참여')); await tester.pump(); await tester.pump(const Duration(milliseconds: 400)); expect(opened, home.fandomId); expect(tester.widget(find.byType(HeroParticipationSheet)).fandomId, home.fandomId); expect(tester.takeException(), isNull); await tester.pumpWidget(const SizedBox()); }); testWidgets('HERO ZAP+ 진입은 POP을 차감하지 않고 종료시 일반 ZAP으로 돌아간다', (tester) async { final galaxy = _NoSpendGalaxy(); final states = StreamController.broadcast(); addTearDown(states.close); await tester.pumpWidget(MaterialApp(theme: buildAppTheme(), home: GalaxyScreen( repository: galaxy, planetRepository: samplePlanetRepository, eclipse: const NoEclipseRepository(), watchPlanet: (_) => states.stream, ))); await tester.pump(); await tester.pump(const Duration(seconds: 1)); expect(tester.widget(find.byType(ZapButton)).isHeroTime, isFalse); states.add(PlanetVisualState(stage: 1, isHeroTime: true, heroEventIds: const {'gift'}, heroEndsAt: DateTime.now().add(const Duration(minutes: 1)))); await tester.pump(); await tester.pump(); await tester.tap(find.text('ZAP+ · HERO TIME')); await tester.pump(); await tester.pump(const Duration(milliseconds: 400)); expect(find.byType(HeroParticipationSheet), findsOneWidget); expect(galaxy.spends, 0); Navigator.of(tester.element(find.byType(HeroParticipationSheet))).pop(); await tester.pump(); await tester.pump(const Duration(milliseconds: 400)); states.add(const PlanetVisualState(stage: 1)); await tester.pump(); await tester.pump(); expect(tester.widget(find.byType(ZapButton)).isHeroTime, isFalse); await tester.pumpWidget(const SizedBox()); }); testWidgets('HERO 참여는 개인 POP 잔액 0이어도 열 수 있다', (tester) async { var opened = 0; await tester.pumpWidget(MaterialApp(home: Scaffold(body: ZapButton( zapPop: 10, myPop: 0, isHeroTime: true, onZap: () => opened++, )))); await tester.tap(find.text('ZAP+ · HERO TIME')); expect(opened, 1); }); testWidgets('Galaxy 전광판은 해당 팬덤의 진행 중 행성 상세로 진입한다', (tester) async { tester.view.physicalSize = const Size(1080, 2400); tester.view.devicePixelRatio = 2; addTearDown(tester.view.reset); String? opened; await tester.pumpWidget(MaterialApp(theme: buildAppTheme(), home: GalaxyScreen( repository: const SampleGalaxyRepository(), eclipse: const NoEclipseRepository(), planetRepository: (id) { opened = id; return SamplePlanetRepository(fandomId: id); }, watchPlanet: (id) => id != 'bts' ? const Stream.empty() : Stream.value( PlanetVisualState(stage: 4, isHeroTime: true, heroNicknames: const ['공개팬'], heroEndsAt: DateTime.now().add(const Duration(minutes: 5))), ), ))); await tester.pump(); await tester.pump(const Duration(seconds: 1)); await tester.pump(); expect(find.text('BTS · HERO TIME 진행 중 →'), findsOneWidget); await tester.tap(find.text('BTS · HERO TIME 진행 중 →')); await tester.pump(); await tester.pump(const Duration(seconds: 1)); expect(opened, 'bts'); expect(find.byType(PlanetDetailScreen), findsOneWidget); expect(tester.takeException(), isNull); await tester.pumpWidget(const SizedBox()); await tester.pump(); }); testWidgets('서버 종료시각을 표시하고 만료·종료는 숨기며 구독을 해제한다', (tester) async { final states = StreamController.broadcast(); addTearDown(states.close); await tester.pumpWidget(MaterialApp(home: Scaffold(body: HeroTimeStatus( fandomId: 'bts', watch: (_) => states.stream, )))); expect(find.textContaining('HERO TIME'), findsNothing); states.add(PlanetVisualState(isHeroTime: true, heroNicknames: const ['공개팬'], heroClaimCount: 3, heroEndsAt: DateTime.now().add(const Duration(minutes: 5)))); await tester.pumpAndSettle(); expect(find.text('HERO TIME · 5:00 남음'), findsOneWidget); expect(find.text('by 공개팬'), findsOneWidget); expect(find.text('팬들의 선물 수령 3건'), findsOneWidget); states.add(PlanetVisualState(isHeroTime: true, heroEndsAt: DateTime.now().subtract(const Duration(seconds: 1)))); await tester.pumpAndSettle(); expect(find.textContaining('HERO TIME'), findsNothing); states.add(const PlanetVisualState()); await tester.pumpAndSettle(); expect(find.textContaining('HERO TIME'), findsNothing); await tester.pumpWidget(const SizedBox()); await tester.pumpAndSettle(); expect(states.hasListener, isFalse); }); }