import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:lanebow/models/event_model.dart'; import 'package:lanebow/models/participant_model.dart'; import 'package:lanebow/models/team_model.dart'; import 'package:lanebow/services/event_service.dart'; import '../../utils/event_bus_test_utils.dart'; import 'package:lanebow/utils/test_utils.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; import 'test_event_details_screen.dart'; // 이벤트 서비스 모킹을 위한 어노테이션 @GenerateMocks([EventService]) import 'team_generation_test.mocks.dart'; void main() { late MockEventService mockEventService; late Event testEvent; // 테스트 설정 setUp(() async { // EventBus 테스트 환경 초기화 (플래키 방지) TestUtils.setTestMode(true); await EventBusTestUtils.setUp('team_generation_test'); mockEventService = MockEventService(); // 테스트용 이벤트 데이터 생성 testEvent = Event( id: '1', clubId: '1', title: '테스트 이벤트', description: '테스트 설명', type: '팀전', status: '예정', startDate: DateTime.now(), endDate: DateTime.now().add(const Duration(hours: 3)), location: '테스트 볼링장', maxParticipants: 20, gameCount: 3, participantFee: 10000, registrationDeadline: DateTime.now().add(const Duration(days: 1)), publicHash: 'test123', accessPassword: 'pass123', isActive: true, ); // 참가자 데이터 모킹 final testParticipants = [ Participant( id: '1', eventId: '1', memberId: '1', name: '참가자 1', status: '확정', isPaid: true, registeredAt: DateTime.now(), ), Participant( id: '2', eventId: '1', memberId: '2', name: '참가자 2', status: '확정', isPaid: true, registeredAt: DateTime.now(), ), Participant( id: '3', eventId: '1', memberId: '3', name: '참가자 3', status: '확정', isPaid: true, registeredAt: DateTime.now(), ), Participant( id: '4', eventId: '1', memberId: '4', name: '참가자 4', status: '확정', isPaid: true, registeredAt: DateTime.now(), ), Participant( id: '5', eventId: '1', memberId: '5', name: '참가자 5', status: '확정', isPaid: true, registeredAt: DateTime.now(), ), Participant( id: '6', eventId: '1', memberId: '6', name: '참가자 6', status: '확정', isPaid: true, registeredAt: DateTime.now(), ), ]; when(mockEventService.fetchEventParticipants(any)) .thenAnswer((_) async => testParticipants); when(mockEventService.fetchEventTeams(any)) .thenAnswer((_) async => []); }); tearDown(() async { // 테스트 종료 후 EventBus 자원 정리 await EventBusTestUtils.tearDown(); }); Widget createEventDetailsScreen() { return MaterialApp( home: ChangeNotifierProvider.value( value: mockEventService, child: TestEventDetailsScreen(event: testEvent), ), ); } group('팀 생성 옵션 UI 테스트', () { testWidgets('팀 생성 옵션이 올바르게 표시되어야 함', (WidgetTester tester) async { // given await tester.pumpWidget(createEventDetailsScreen()); await tester.pumpAndSettle(); // 팀 탭으로 이동 await tester.tap(find.text('팀').first); await tester.pumpAndSettle(); // then // 팀 생성 옵션 확인 expect(find.text('팀 생성 옵션'), findsOneWidget); expect(find.text('팀 인원수로 나누기'), findsOneWidget); expect(find.text('팀 개수로 나누기'), findsOneWidget); // 라디오 버튼 확인 expect(find.byType(Radio), findsNWidgets(2)); // 팀 생성 버튼 확인 expect(find.text('팀 생성하기'), findsOneWidget); }); testWidgets('팀 인원수 옵션 선택 시 인원수 입력 필드가 표시되어야 함', (WidgetTester tester) async { // given await tester.pumpWidget(createEventDetailsScreen()); await tester.pumpAndSettle(); // 팀 탭으로 이동 await tester.tap(find.text('팀').first); await tester.pumpAndSettle(); // when // 팀 인원수로 나누기 라디오 버튼 선택 await tester.tap(find.text('팀 인원수로 나누기')); await tester.pumpAndSettle(); // then // 인원수 입력 필드 확인 expect(find.text('인원수:'), findsOneWidget); expect(find.textContaining('총'), findsOneWidget); }); testWidgets('팀 개수 옵션 선택 시 팀 수 입력 필드가 표시되어야 함', (WidgetTester tester) async { // given await tester.pumpWidget(createEventDetailsScreen()); await tester.pumpAndSettle(); // 팀 탭으로 이동 await tester.tap(find.text('팀').first); await tester.pumpAndSettle(); // when // 팀 개수로 나누기 라디오 버튼 선택 // 라디오 버튼을 찾아 탭 await tester.tap(find.byType(Radio).at(1)); await tester.pumpAndSettle(); // then // 팀 수 입력 필드 확인 // TextField 위젯이 있는지 확인 expect(find.byType(TextField), findsWidgets); // 팀 수 라벨 확인 expect(find.text('팀 수:'), findsOneWidget); // 참가자 수 확인 expect(find.textContaining('총'), findsOneWidget); }); testWidgets('팀 생성 버튼 클릭 시 팀이 생성되어야 함', (WidgetTester tester) async { // given await tester.pumpWidget(createEventDetailsScreen()); await tester.pumpAndSettle(); // 팀 탭으로 이동 await tester.tap(find.text('팀').first); await tester.pumpAndSettle(); // when // 팀 생성 버튼 클릭 await tester.tap(find.text('팀 생성하기')); await tester.pumpAndSettle(); // then // 팀 생성 확인 메시지 확인 expect(find.byType(SnackBar), findsOneWidget); expect(find.text('팀이 생성되었습니다'), findsOneWidget); }); }); group('팀 에버리지/핸디캡 계산 및 표시 테스트', () { testWidgets('팀 에버리지가 올바르게 계산되고 표시되어야 함', (WidgetTester tester) async { // given // 팀 데이터 모킹 final testTeams = [ Team( id: '1', eventId: '1', name: 'A', memberIds: ['1', '2', '3'], ), ]; when(mockEventService.fetchEventTeams(any)) .thenAnswer((_) async => testTeams); await tester.pumpWidget(createEventDetailsScreen()); await tester.pumpAndSettle(); // 팀 탭으로 이동 await tester.tap(find.text('팀').first); await tester.pumpAndSettle(); // then // 팀 에버리지 표시 확인 expect(find.textContaining('팀 에버리지:'), findsOneWidget); }); }); group('미배정 인원 표시 테스트', () { testWidgets('미배정 참가자가 올바르게 표시되어야 함', (WidgetTester tester) async { // given // 팀 데이터 모킹 (일부 참가자만 배정) final testTeams = [ Team( id: '1', eventId: '1', name: 'A', memberIds: ['1', '2', '3'], ), ]; when(mockEventService.fetchEventTeams(any)) .thenAnswer((_) async => testTeams); await tester.pumpWidget(createEventDetailsScreen()); await tester.pumpAndSettle(); // 팀 탭으로 이동 await tester.tap(find.text('팀').first); await tester.pumpAndSettle(); // then // 미배정 참가자 섹션 확인 // 미배정 참가자 텍스트 확인 - Card 내부의 Text 위젯으로 표시됨 // 텍스트 스타일이 적용된 경우 정확한 텍스트 매칭이 어려울 수 있으므로 참가자 이름만 확인 // 미배정 참가자 섹션이 존재하는지 확인 expect(find.byType(Card), findsWidgets); // 실제 구현에서 미배정 참가자 텍스트가 어떻게 표시되는지 확인 // 텍스트 스타일이 적용된 경우 정확한 텍스트 매칭이 어려울 수 있으므로 텍스트 타입으로 확인 expect(find.byType(Text), findsWidgets); }); }); }