앱 단위·화면·통합 테스트

This commit is contained in:
Sn-Kinos
2026-10-04 01:43:10 +09:00
parent de44739e63
commit 4f6e1eac61
63 changed files with 14700 additions and 230 deletions
+308
View File
@@ -0,0 +1,308 @@
@Tags(['design'])
library;
import 'package:fancake/core/data/repositories.dart';
import 'package:fancake/core/router/app_router.dart';
import 'package:fancake/core/theme/app_theme.dart';
import 'package:fancake/features/auth/auth_repository.dart';
import 'package:fancake/features/eclipse/eclipse_data.dart';
import 'package:fancake/features/galaxy/galaxy_search_screen.dart';
import 'package:fancake/features/planet/fpi_ranking_screen.dart';
import 'package:fancake/features/planet/lounge_post_screens.dart';
import 'package:fancake/features/planet/moderation.dart';
import 'package:fancake/features/planet/planet_data.dart';
import 'package:fancake/features/planet/planet_detail_screen.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../eclipse_stubs.dart';
import 'design_match_test.dart' as base;
/// 디자인 일치 2단계 **그룹 B**(F2 갤럭시 · F3 행성/성운 상세) 렌더.
///
/// 공유 하네스([base.main])를 그대로 돌리되 shots 만 이 그룹 것으로 바꾼다.
/// 표본은 Figma 사본 문구와 같게 여기서 만든다(공유 표본 저장소는 건드리지 않는다).
///
/// 실행: `DESIGN_MATCH_OUT=<폴더> fvm flutter test --tags design test/design_match/b_test.dart`
void main() {
base.shots
..clear()
..addAll(_shots);
base.main();
}
Future<void> _settle(WidgetTester t) async {
for (var i = 0; i < 6; i++) {
await t.pump(const Duration(milliseconds: 300));
}
}
Future<void> _tap(WidgetTester t, Finder f) async {
await t.tap(f.first, warnIfMissed: false);
await _settle(t);
}
Future<void> _overlay(
WidgetTester t,
void Function(BuildContext c) open,
) async {
open(t.element(find.byType(Scaffold).last));
await _settle(t);
}
final _now = DateTime.now();
const _photo = 'assets/figma/lounge-post-sample.png';
LoungePost _post(
String author,
Duration ago,
String text, {
int likes = 8,
int comments = 24,
List<String> images = const [],
String? photo,
Duration? video,
}) => LoungePost(
id: author,
authorName: author,
createdAt: _now.subtract(ago),
title: '',
body: text,
likes: likes,
comments: comments,
images: images,
authorPhoto: photo,
videoDuration: video,
);
/// F3-05 `244:22509` — 첫 댓글 전 글.
final _hongPost = _post(
'BTS_Hong',
Duration.zero,
'안녕하세요! BTS 팬이에요!',
likes: 0,
comments: 0,
images: const [_photo],
photo: 'assets/figma/avatar-hong.png',
video: const Duration(seconds: 36),
);
/// F3-07 `252:163306` — 사진 세 장 글.
final _ilaPost = _post(
'ILA_WITH',
const Duration(days: 3),
'여러분 지금 미국 컴백 무대 보셨나요?\n사진 공유해드릴게요 모두 온라인 직관하세요',
likes: 8,
comments: 5,
images: const [_photo, _photo, _photo],
);
FpiScore _score(FpiMetric m, int v, int rank, int trend) =>
FpiScore(metric: m, value: v, rank: rank, trend: trend, myTopPercent: 1);
/// 시안 수치 그대로의 행성 · 성운 상세.
class _FigmaPlanet extends SamplePlanetRepository {
const _FigmaPlanet();
@override
Future<PlanetDetail> load({required bool nebula}) async => nebula
? PlanetDetail(
fandomId: 'bts-rm',
name: 'BTS_RM',
isNebula: true,
stage: 0,
totalFpi: 0,
members: 1,
ownerName: 'BTS_Hong',
ownerPhoto: 'assets/figma/avatar-hong.png',
scores: const [],
posts: const [],
)
: PlanetDetail(
fandomId: 'bts',
name: 'BTS',
isNebula: false,
totalFpi: 1220,
members: 850,
ownerName: 'BTS_Maker',
ownerPhoto: 'assets/figma/avatar-maker.png',
scores: [
_score(FpiMetric.bpm, 34200, 2, 1),
_score(FpiMetric.health, 1180000, 4, -1),
_score(FpiMetric.referral, 34200, 3, 0),
_score(FpiMetric.contribution, 450100, 2, 2),
_score(FpiMetric.community, 1000, 4, -2),
],
posts: [
_post(
'arrrmy_',
const Duration(minutes: 3),
'I love BTS sooooo much 😭😭😭',
),
_post(
'AR_NNJO',
const Duration(hours: 3),
'NAMJOON so handsome isn’t it???',
images: const [_photo],
),
_ilaPost,
],
);
@override
Future<List<LoungePost>> morePosts({
required int offset,
int limit = 5,
}) async => const [];
@override
Future<FpiRanking> planetRanking({
required FpiMetric metric,
int limit = 30,
}) async => FpiRanking(
mine: const FpiRankRow(
rank: 24,
id: 'bts',
name: 'BTS',
value: 34200,
change: 12,
isMine: true,
),
rows: _rows(const [
(1, 'Bigbnag', 782200, 1),
(2, 'NewJeans', 703200, 0),
(3, 'Blackpink', 689700, 1),
(5, 'Twice', 531100, -1),
(6, 'ITZY', 527800, 2),
(7, 'Aespa', 422400, 3),
(8, 'Seventeen', 329600, 8),
(9, 'Eighteen', 311200, -1),
(10, 'Nineteen', 134500, 1),
]),
);
@override
Future<FpiRanking> userRanking({
required FpiMetric metric,
required String fandomId,
int limit = 30,
}) async => FpiRanking(
mine: const FpiRankRow(
rank: 82,
id: 'me',
name: 'BTS_Hong',
value: 3,
change: 1,
),
rows: _rows(const [
(1, 'GLAS_SMILE', 3000, 0),
(2, 'Suyom_mo', 2600, 0),
(3, 'Bool_huuu', 1400, 0),
(4, 'Wachooo', 1200, 1),
(5, 'Jeans_Bewn', 1200, 0),
(6, 'pinkyy', 1100, 1),
(7, 'T_T', 1000, -1),
(8, 'Yogi_ITZY', 98, 2),
]),
);
}
List<FpiRankRow> _rows(List<(int, String, int, int)> t) => [
for (final (rank, name, value, change) in t)
FpiRankRow(rank: rank, id: name, name: name, value: value, change: change),
];
Widget _detail({bool nebula = false}) => PlanetDetailScreen(
nebula: nebula,
repository: const _FigmaPlanet(),
eclipse: const NoEclipseRepository(),
);
const _report = ModerationTarget(
authorName: 'BTS_Hong',
title: '공방 신청 꿀팁 공유합니다.',
);
/// 이클립스가 없는 표본 저장소. 탭 화면은 공유 하네스가 저장소를 직접 만들어서
/// 바꿀 수 없으므로, 탭 셸을 통째로 한 번 더 띄운다(하단 탭까지 그려진다).
class _NoEclipseRepos extends Repositories {
_NoEclipseRepos() : super(live: false);
@override
EclipseRepository get eclipse => const NoEclipseRepository();
}
Widget _tab(String path) {
final router = buildRouter(
AuthRepository()..signInWithMockSession(),
repositories: _NoEclipseRepos(),
)..go(path);
return MaterialApp.router(
theme: buildAppTheme(),
debugShowCheckedModeBanner: false,
routerConfig: router,
);
}
final _shots = <base.Shot>[
// F2 갤럭시
base.Shot('F2-01', build: (_) async => _tab('/galaxy')),
base.Shot(
'F2-02',
build: (r) async => GalaxySearchScreen(repository: r.galaxy),
),
// F3 행성/성운 상세
base.Shot('F3-01', h: 1042, build: (_) async => _detail(nebula: true)),
base.Shot(
'F3-02',
build: (_) async => const ReportScreen(
target: ModerationTarget(authorName: 'BTS_Hong', title: 'BTS_RM'),
),
),
base.Shot(
'F3-03',
build: (_) async => _detail(nebula: true),
after: (t) => _overlay(
t,
(c) => confirmBlock(c, 'BTS_Hong', message: '이 성운을 차단할까요?'),
),
),
base.Shot('F3-04', h: 1243, build: (_) async => _detail()),
base.Shot('F3-05', build: (_) async => LoungePostScreen(post: _hongPost)),
base.Shot('F3-06', build: (_) async => const ReportScreen(target: _report)),
base.Shot(
'F3-07',
build: (_) async => LoungePostScreen(post: _ilaPost),
after: (t) => _overlay(t, (c) => confirmBlock(c, 'ILA_WITH')),
),
base.Shot('F3-09', build: (_) async => const LoungeComposeScreen()),
base.Shot(
'F3-10',
build: (_) async => const FpiRankingScreen(
metric: FpiMetric.bpm,
repository: _FigmaPlanet(),
),
),
base.Shot(
'F3-11',
build: (_) async => const FpiUserRankingScreen(
metric: FpiMetric.bpm,
fandomId: 'bts',
planetName: 'BTS',
repository: _FigmaPlanet(),
),
),
base.Shot(
'F3-12',
build: (_) async => _detail(),
after: (t) async {
await _tap(t, find.text('팬덤 라운지'));
await _tap(
t,
find.image(const AssetImage('assets/figma/icon-post-more.png')),
);
},
),
base.Shot('F3-13', build: (_) async => const ReportScreen(target: _report)),
];