210 lines
6.6 KiB
Dart
210 lines
6.6 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:provider/provider.dart';
|
|
|
|
import 'package:lanebow/screens/club/event_details_screen.dart';
|
|
import 'package:lanebow/models/event_model.dart';
|
|
import 'package:lanebow/models/participant_model.dart';
|
|
import 'package:lanebow/models/score_model.dart';
|
|
import 'package:lanebow/models/member_model.dart';
|
|
import 'package:lanebow/models/club_model.dart';
|
|
import 'package:lanebow/models/team_model.dart';
|
|
import 'package:lanebow/services/event_service.dart';
|
|
import 'package:lanebow/services/club_service.dart';
|
|
import 'package:lanebow/services/member_service.dart';
|
|
import 'package:lanebow/widgets/loading_indicator.dart';
|
|
|
|
// 간단한 테스트 더블 구현 (ChangeNotifier 유지)
|
|
class FakeEventService extends EventService {
|
|
@override
|
|
List<Participant> participants = [];
|
|
@override
|
|
List<Score> scores = [];
|
|
|
|
@override
|
|
Future<List<Participant>> fetchEventParticipants(String eventId) async {
|
|
return participants;
|
|
}
|
|
|
|
@override
|
|
Future<List<Score>> fetchEventScores(String eventId) async {
|
|
return scores;
|
|
}
|
|
|
|
@override
|
|
Future<List<Team>> fetchEventTeams(String eventId) async {
|
|
// 팀 기능은 테스트에서 필수가 아니므로 빈 리스트를 즉시 반환하여 네트워크 의존성을 제거
|
|
return [];
|
|
}
|
|
}
|
|
|
|
class FakeClubService extends ClubService {
|
|
Club? club;
|
|
@override
|
|
Future<Club> fetchClub(String clubId) async {
|
|
return club ?? Club(name: '클럽', id: clubId);
|
|
}
|
|
}
|
|
|
|
class FakeMemberService extends MemberService {
|
|
List<Member> membersByIds = [];
|
|
@override
|
|
Future<List<Member>> fetchMembersByIds(String clubId, List<String> ids) async {
|
|
return membersByIds;
|
|
}
|
|
|
|
@override
|
|
Future<void> setClubId(String clubId) async {
|
|
// no-op: 테스트에서는 내부 상태 주입만 필요하며 네트워크 호출 불필요
|
|
return;
|
|
}
|
|
|
|
@override
|
|
Future<void> fetchClubMembers() async {
|
|
// no-op: 테스트에서는 네트워크 호출 없이 즉시 완료
|
|
return;
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
// ChangeNotifier 서브타입에 Provider.value 사용 허용(자동 업데이트 불필요한 테스트 시나리오)
|
|
Provider.debugCheckInvalidValueType = null;
|
|
|
|
group('EventDetailsScreen', () {
|
|
late FakeEventService eventService;
|
|
late FakeClubService clubService;
|
|
late FakeMemberService memberService;
|
|
|
|
final event = Event(
|
|
id: 'e1',
|
|
clubId: 'c1',
|
|
title: '테스트 이벤트',
|
|
description: '설명입니다',
|
|
startDate: DateTime(2025, 1, 2, 19, 30),
|
|
endDate: DateTime(2025, 1, 2, 22, 00),
|
|
location: '강남볼링장',
|
|
type: '정기전',
|
|
status: '진행중',
|
|
maxParticipants: 24,
|
|
currentParticipants: 2,
|
|
gameCount: 3,
|
|
participantFee: 10000,
|
|
registrationDeadline: DateTime(2024, 12, 31, 23, 59),
|
|
publicHash: 'hash123',
|
|
accessPassword: '1234',
|
|
isActive: true,
|
|
);
|
|
|
|
setUp(() {
|
|
eventService = FakeEventService();
|
|
clubService = FakeClubService();
|
|
memberService = FakeMemberService();
|
|
|
|
// Participants
|
|
eventService.participants = [
|
|
Participant(
|
|
id: 'p1',
|
|
eventId: 'e1',
|
|
memberId: 'm1',
|
|
registeredAt: DateTime(2025, 1, 1),
|
|
),
|
|
Participant(
|
|
id: 'p2',
|
|
eventId: 'e1',
|
|
memberId: 'm2',
|
|
registeredAt: DateTime(2025, 1, 1),
|
|
),
|
|
];
|
|
// Scores
|
|
eventService.scores = [
|
|
Score(
|
|
id: 's1',
|
|
eventId: 'e1',
|
|
memberId: 'm1',
|
|
clubId: 'c1',
|
|
frames: const [100, 100, 100, 100, 100],
|
|
totalScore: 500,
|
|
handicap: 0,
|
|
date: DateTime(2025, 1, 2),
|
|
),
|
|
];
|
|
// Club
|
|
clubService.club = Club(id: 'c1', name: '볼링클럽', ownerId: 'u1');
|
|
// Members
|
|
memberService.membersByIds = [
|
|
Member(
|
|
id: 'm1',
|
|
userId: 'u1',
|
|
clubId: 'c1',
|
|
name: '철수',
|
|
email: 'a@b.com',
|
|
isActive: true,
|
|
gender: 'M',
|
|
birthDate: DateTime(1990, 1, 1),
|
|
),
|
|
Member(
|
|
id: 'm2',
|
|
userId: 'u2',
|
|
clubId: 'c1',
|
|
name: '영희',
|
|
email: 'c@d.com',
|
|
isActive: true,
|
|
gender: 'F',
|
|
birthDate: DateTime(1992, 1, 1),
|
|
),
|
|
];
|
|
});
|
|
|
|
Widget wrapWithProviders(Widget child) {
|
|
return MultiProvider(
|
|
providers: [
|
|
Provider<EventService>.value(value: eventService),
|
|
Provider<ClubService>.value(value: clubService),
|
|
Provider<MemberService>.value(value: memberService),
|
|
],
|
|
child: MaterialApp(home: child),
|
|
);
|
|
}
|
|
|
|
testWidgets('로딩 후 기본 정보 탭 렌더링 및 핵심 정보 표시', (tester) async {
|
|
await tester.pumpWidget(wrapWithProviders(EventDetailsScreen(event: event)));
|
|
|
|
// 초기 로딩 표시
|
|
expect(find.byType(LoadingIndicator), findsOneWidget);
|
|
|
|
// async 로딩/초기 빌드가 끝나도록 약간 더 대기 (무한 대기 방지)
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
await tester.pump(const Duration(milliseconds: 200));
|
|
|
|
// AppBar 제목
|
|
expect(find.text('테스트 이벤트'), findsOneWidget);
|
|
|
|
// 탭 존재 확인
|
|
expect(find.text('기본 정보'), findsOneWidget);
|
|
expect(find.text('참가자'), findsOneWidget);
|
|
expect(find.text('점수'), findsOneWidget);
|
|
expect(find.text('팀'), findsOneWidget);
|
|
|
|
// 기본 정보 섹션이 렌더링되고 공유 버튼이 존재
|
|
expect(find.byIcon(Icons.share), findsOneWidget);
|
|
// 장소/날짜와 같은 텍스트 일부가 표시되는지(정확한 포맷 대신 포함 여부만 확인)
|
|
expect(find.textContaining('강남'), findsWidgets);
|
|
});
|
|
|
|
testWidgets('참가자 탭: 참가자 수와 리스트 표시', (tester) async {
|
|
await tester.pumpWidget(wrapWithProviders(EventDetailsScreen(event: event)));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
await tester.pump(const Duration(milliseconds: 50));
|
|
|
|
// 참가자 탭으로 이동: 탭 터치 대신 TabBarView를 드래그로 넘김(제스처 경로 단순화)
|
|
await tester.drag(find.byType(TabBarView), const Offset(-400, 0));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
await tester.pump(const Duration(milliseconds: 250));
|
|
|
|
// 리스트가 렌더링되는지(정확한 항목 텍스트 대신 리스트 존재 확인)
|
|
expect(find.byType(ListView), findsWidgets);
|
|
});
|
|
});
|
|
}
|