229 lines
12 KiB
Dart
229 lines
12 KiB
Dart
// 화면 점검용 캡처 (일반 시험이 아니다). 폰 크기(360×800)로 주요 화면을 그려 test_ux/shots/*.png 로 남긴다.
|
||
// flutter test test_ux/shots_test.dart --update-goldens
|
||
import 'dart:io';
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter/services.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:friendly_map/modules/app/app.dart';
|
||
import 'package:friendly_map/modules/core/core.dart';
|
||
import 'package:friendly_map/modules/kinship/kinship.dart';
|
||
import 'package:friendly_map/modules/platform/platform.dart';
|
||
import 'package:friendly_map/modules/storage/storage.dart';
|
||
import 'package:friendly_map/modules/transport/transport.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/chat_room_screen.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/contact_import_screen.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/circle_group_screen.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/circle_hub_screen.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/family_tree_screen.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/group_detail_screen.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/person_screen.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/requests_screen.dart';
|
||
import 'package:friendly_map/modules/ui/src/screens/settings_screen.dart';
|
||
import 'package:friendly_map/modules/ui/ui.dart';
|
||
|
||
import '../test/support/app_device.dart';
|
||
import '../test/support/fake_cipher.dart';
|
||
import '../test/support/storage_contract.dart' show FakeProbe;
|
||
import '../test/widget_test.dart' show FakeBootstrap;
|
||
|
||
class _Contacts implements ContactsProvider {
|
||
@override
|
||
Future<bool> requestPermission() async => true;
|
||
@override
|
||
Future<List<DeviceContact>> all() async => const [
|
||
DeviceContact(id: '1', name: '김민수', phones: ['010-3333-4444'], labels: ['가족']),
|
||
DeviceContact(id: '2', name: '이영희', phones: ['010-5555-6666'], labels: ['고등학교 동창']),
|
||
DeviceContact(id: '3', name: '박철수', phones: ['010-7777-8888'], labels: ['회사']),
|
||
DeviceContact(id: '4', name: '최지은', phones: ['010-1212-3434']),
|
||
DeviceContact(id: '5', name: '정우성', phones: ['02-555-1234']),
|
||
];
|
||
}
|
||
|
||
Future<void> _font(String family, String path) async {
|
||
final bytes = File(path).readAsBytesSync();
|
||
await (FontLoader(family)..addFont(Future.value(ByteData.view(Uint8List.fromList(bytes).buffer)))).load();
|
||
}
|
||
|
||
void main() {
|
||
late AppDevice me, minsu;
|
||
late FriendlyApp app;
|
||
|
||
setUpAll(() async {
|
||
await _font('Roboto', 'build/ux/kr.ttf');
|
||
await _font('MaterialIcons', '/sdks/flutter/bin/cache/artifacts/material_fonts/MaterialIcons-Regular.otf');
|
||
});
|
||
|
||
Future<void> seed(WidgetTester tester) async {
|
||
await tester.runAsync(() async {
|
||
final hub = MemoryHub();
|
||
me = await AppDevice.create(hub, '010-1111-2222', '+821011112222', '이미영', contacts: _Contacts());
|
||
minsu = await AppDevice.create(hub, '010-3333-4444', '+821033334444', '김민수');
|
||
app = me.app;
|
||
await app.friends.add(minsu.phone, minsu.name);
|
||
await AppDevice.settleAll();
|
||
await minsu.app.friends.accept(me.phone);
|
||
await AppDevice.settleAll();
|
||
for (final (p, n) in [('+821055556666', '이영희'), ('+821077778888', '박철수'), ('+821012123434', '최지은')]) {
|
||
await app.friends.add(p, n);
|
||
}
|
||
final people = {for (final p in await app.friends.people()) p.name: p.id};
|
||
await app.relate.into(people['이영희']!, 'school.high');
|
||
final hb = (await app.groups.add('한빛전자', CardKinds.company, parent: CardKinds.company))!;
|
||
final team = (await app.groups.add('결제팀', CardKinds.company, parent: hb.id))!;
|
||
await app.relate.into(people['박철수']!, team.id);
|
||
await app.relate.into(people['김민수']!, team.id);
|
||
final room = (await app.chats.openDirect(people['김민수']!, CardKinds.personal))!;
|
||
await app.chats.send(room.id, '안녕하세요, 내일 회의 몇 시죠?');
|
||
await app.work.assign(room.id, '결제 API 정리', assigneeId: people['김민수'], due: DateTime(2026, 10, 30));
|
||
await app.work.urgent(room.id, '결제 서버가 멈췄어요');
|
||
// 가족: 나(남) · 아내 · 아들 · 부모 · 할아버지 · 장인
|
||
await app.kin.setMe(gender: Gender.male, birthYear: 1990);
|
||
Future<String> rel(String n, Gender g) async => (await app.kin.addRelative(n, gender: g))!.id;
|
||
final dad = await rel('이정훈', Gender.male);
|
||
await app.relate.asFamily(dad, link: KinLink.parent, gender: Gender.male);
|
||
final mom = await rel('박순자', Gender.female);
|
||
await app.relate.asFamily(mom, link: KinLink.parent, gender: Gender.female);
|
||
await app.kin.setSpouse(dad, mom);
|
||
await app.relate.asFamily(await rel('이만복', Gender.male), link: KinLink.parent, to: dad, gender: Gender.male);
|
||
final wife = await rel('최유진', Gender.female);
|
||
await app.relate.asFamily(wife, link: KinLink.spouse, gender: Gender.female);
|
||
await app.relate.asFamily(await rel('최상철', Gender.male), link: KinLink.parent, to: wife, gender: Gender.male);
|
||
final son = await rel('이도윤', Gender.male);
|
||
await app.relate.asFamily(son, link: KinLink.child, gender: Gender.male);
|
||
await app.kin.setParent(son, wife);
|
||
await app.facts.save(son, (await app.facts.get(son)).copyWith(birthYear: 2020, birthMonth: 11, birthDay: 3));
|
||
// 학교: 고등학교 동문 여럿 (연도·반·대화)
|
||
final mineCards = await app.cards.mine();
|
||
await app.cards.edit(mineCards.firstWhere((c) => c.kind == CardKinds.school).id, fields: {'name': '이미영', 'school': '가람고', 'year': '2006'});
|
||
final cls = (await app.groups.add('27회 3반', CardKinds.school, parent: 'school.high'))!;
|
||
var seq = 0;
|
||
for (final (nm, yr) in [('정수빈', 2004), ('한지민', 2005), ('오세훈', 2006), ('윤아름', 2006), ('강동원', 2007), ('송혜교', 2007), ('유재석', 2008), ('조인성', 2003)]) {
|
||
final p = (await app.friends.add('+82107000${(1000 + seq++)}', nm))!;
|
||
await app.deps.storage.receivedCards.put(ReceivedCard(id: 'sc$seq', personId: p.id, kind: CardKinds.school, fieldsJson: encodeFields({'year': '$yr'}), receivedAt: 1, updatedAt: 1));
|
||
await app.groups.addMembers('school.high', [p.id]);
|
||
if (yr == 2006) await app.groups.addMembers(cls.id, [p.id]);
|
||
final room = (await app.chats.openDirect(p.id, CardKinds.personal))!;
|
||
for (var i = 0; i < (seq * 2) % 7; i++) {
|
||
await app.chats.send(room.id, '안녕 $i');
|
||
}
|
||
}
|
||
// 회사: 한빛전자 직원들 (직책·대화)
|
||
await app.cards.edit(mineCards.firstWhere((c) => c.kind == CardKinds.company).id, fields: {'name': '이미영', 'company': '한빛전자', 'position': '과장'});
|
||
final all = await app.groups.all();
|
||
final hb2 = all.firstWhere((v) => v.group.name == '한빛전자').group;
|
||
final pay = all.firstWhere((v) => v.group.name == '결제팀').group;
|
||
for (final (nm, pos, g2) in [('김부장', '부장', hb2.id), ('박차장', '차장', pay.id), ('최과장', '과장', pay.id), ('정대리', '대리', pay.id), ('한사원', '사원', pay.id), ('오사원', '사원', hb2.id)]) {
|
||
final p = (await app.friends.add('+82108000${(1000 + seq++)}', nm))!;
|
||
await app.deps.storage.receivedCards.put(ReceivedCard(id: 'cc$seq', personId: p.id, kind: CardKinds.company, fieldsJson: encodeFields({'position': pos}), receivedAt: 1, updatedAt: 1));
|
||
await app.groups.addMembers(g2, [p.id]);
|
||
final room = (await app.chats.openDirect(p.id, CardKinds.personal))!;
|
||
for (var i = 0; i < (seq * 3) % 6; i++) {
|
||
await app.chats.send(room.id, '안녕 $i');
|
||
}
|
||
}
|
||
// 김민수가 새 폰으로 다시 연결을 청함
|
||
final again = await AppDevice.create(hub, '010-3333-4444', '+821033334444', '김민수 새폰');
|
||
await again.app.friends.add(me.phone, '이미영');
|
||
await AppDevice.settleAll();
|
||
});
|
||
}
|
||
|
||
Future<void> open(WidgetTester tester) async {
|
||
tester.view.physicalSize = const Size(1080, 2400);
|
||
tester.view.devicePixelRatio = 3;
|
||
addTearDown(tester.view.reset);
|
||
await seed(tester);
|
||
await tester.pumpWidget(FriendlyMapApp(bootstrap: FakeBootstrap(keys: true, app: app)));
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
Future<void> shot(WidgetTester tester, String name) async {
|
||
await tester.runAsync(() => Future<void>.delayed(const Duration(milliseconds: 30)));
|
||
await tester.pumpAndSettle();
|
||
await expectLater(find.byType(MaterialApp), matchesGoldenFile('shots/$name.png'));
|
||
}
|
||
|
||
Future<void> push(WidgetTester tester, Widget page) async {
|
||
tester.state<NavigatorState>(find.byType(Navigator).first).push(MaterialPageRoute<void>(builder: (_) => page));
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
Future<void> tab(WidgetTester tester, String label) async {
|
||
await tester.tap(find.descendant(of: find.byType(NavigationBar), matching: find.text(label)));
|
||
await tester.pumpAndSettle();
|
||
}
|
||
|
||
testWidgets('처음 시작', (tester) async {
|
||
tester.view.physicalSize = const Size(1080, 2400);
|
||
tester.view.devicePixelRatio = 3;
|
||
addTearDown(tester.view.reset);
|
||
final fresh = FriendlyApp(AppDeps(storage: Storage.memory(), transport: MemoryTransport('+82100', MemoryHub()), cipher: FakeCipher('K'), identity: FakeIdentity('K'), probe: FakeProbe(10)));
|
||
await tester.pumpWidget(FriendlyMapApp(bootstrap: FakeBootstrap(app: fresh)));
|
||
await tester.pumpAndSettle();
|
||
await shot(tester, '00_onboarding');
|
||
});
|
||
|
||
testWidgets('아래 메뉴 다섯 칸', (tester) async {
|
||
await open(tester);
|
||
await shot(tester, '01_chats');
|
||
await tab(tester, '관계');
|
||
await shot(tester, '02_relations');
|
||
await tab(tester, '업무');
|
||
await shot(tester, '03_tasks');
|
||
await tab(tester, '명함');
|
||
await shot(tester, '04_cards');
|
||
await tab(tester, '나');
|
||
await shot(tester, '05_me');
|
||
});
|
||
|
||
testWidgets('요청 · 그룹 · 설정', (tester) async {
|
||
await open(tester);
|
||
await push(tester, const RequestsScreen());
|
||
await shot(tester, '10_requests');
|
||
await push(tester, const GroupDetailScreen(groupId: CardKinds.school));
|
||
await shot(tester, '11_group_school');
|
||
await push(tester, const GroupDetailScreen(groupId: CardKinds.company));
|
||
await shot(tester, '12_group_company');
|
||
await push(tester, const SettingsScreen());
|
||
await shot(tester, '13_settings');
|
||
await push(tester, const FamilyTreeScreen());
|
||
await shot(tester, '14_family_tree');
|
||
await push(tester, const CircleHubScreen(rootId: CardKinds.school));
|
||
await shot(tester, '15_school_hub');
|
||
await push(tester, const CircleGroupScreen(groupId: 'school.high'));
|
||
await shot(tester, '16_school_ring');
|
||
await tester.tap(find.text('목록'));
|
||
await tester.pumpAndSettle();
|
||
await shot(tester, '17_school_list');
|
||
await tester.tap(find.text('고르기'));
|
||
await tester.pumpAndSettle();
|
||
await tester.tap(find.textContaining('모두'));
|
||
await tester.pumpAndSettle();
|
||
await shot(tester, '18_school_pick');
|
||
await push(tester, const CircleHubScreen(rootId: CardKinds.company));
|
||
await shot(tester, '19_company_hub');
|
||
await push(tester, const CircleGroupScreen(groupId: 'g1'));
|
||
await tester.tap(find.text('목록'));
|
||
await tester.pumpAndSettle();
|
||
await shot(tester, '19b_company_list');
|
||
});
|
||
|
||
testWidgets('주소록 · 사람 · 대화', (tester) async {
|
||
await open(tester);
|
||
await push(tester, const ContactImportScreen());
|
||
await shot(tester, '20_contacts');
|
||
final people = {for (final p in (await tester.runAsync(() => app.friends.people()))!) p.name: p.id};
|
||
await push(tester, PersonScreen(personId: people['김민수']!));
|
||
await shot(tester, '21_person');
|
||
await tester.tap(find.text('관계'));
|
||
await tester.pumpAndSettle();
|
||
await shot(tester, '22_relate');
|
||
await tester.tapAt(const Offset(10, 10));
|
||
await tester.pumpAndSettle();
|
||
final rooms = (await tester.runAsync(() => app.chats.summaries()))!;
|
||
await push(tester, ChatRoomScreen(roomId: rooms.first.room.id));
|
||
await shot(tester, '23_chat_room');
|
||
});
|
||
}
|