가입 약관 동의 기록 · 서비스 탈퇴 · 온보딩 6단계 · Coach Mark
This commit is contained in:
@@ -180,10 +180,7 @@ void main() {
|
||||
testWidgets('행성/성운 칸은 가입 목록을 팬덤 stage 그림으로 그린다', (tester) async {
|
||||
await pumpWide(
|
||||
tester,
|
||||
MyScreen(
|
||||
repository: SampleMyRepository(),
|
||||
galaxy: _OneNebula(),
|
||||
),
|
||||
MyScreen(repository: SampleMyRepository(), galaxy: _OneNebula()),
|
||||
);
|
||||
await tester.pump();
|
||||
final images = tester
|
||||
@@ -191,7 +188,10 @@ void main() {
|
||||
.map((i) => (i.image as AssetImage).assetName)
|
||||
.toList();
|
||||
expect(images, contains('assets/figma/nebula-card.png'));
|
||||
expect(images.where((a) => a.startsWith('assets/figma/my-planet-')), isEmpty);
|
||||
expect(
|
||||
images.where((a) => a.startsWith('assets/figma/my-planet-')),
|
||||
isEmpty,
|
||||
);
|
||||
expect(find.bySemanticsLabel('새 성운'), findsOneWidget);
|
||||
});
|
||||
|
||||
@@ -233,8 +233,12 @@ void main() {
|
||||
return null;
|
||||
},
|
||||
);
|
||||
addTearDown(() => tester.binding.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(SystemChannels.platform, null));
|
||||
addTearDown(
|
||||
() => tester.binding.defaultBinaryMessenger.setMockMethodCallHandler(
|
||||
SystemChannels.platform,
|
||||
null,
|
||||
),
|
||||
);
|
||||
expect(await shareProfile(profile, '문구 복사'), isTrue);
|
||||
expect(clip, profileShareText(profile));
|
||||
});
|
||||
@@ -471,6 +475,41 @@ void main() {
|
||||
expect(find.text('취소'), findsOneWidget);
|
||||
});
|
||||
|
||||
Future<void> confirmWithdraw(WidgetTester tester) async {
|
||||
await tester.scrollUntilVisible(find.text('서비스 탈퇴'), 200);
|
||||
await tester.ensureVisible(find.text('서비스 탈퇴'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('서비스 탈퇴'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.tap(find.text('서비스 탈퇴').last);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
testWidgets('SNS 연동이 남아 있으면 비정상 탈퇴 안내만 (554:24908)', (tester) async {
|
||||
final repo = _WithdrawRepo();
|
||||
await pumpWide(tester, SettingsScreen(repository: repo));
|
||||
await tester.pump();
|
||||
await confirmWithdraw(tester);
|
||||
expect(find.text('연동된 계정 해제 후 계정 탈퇴를 진행해주세요.'), findsOneWidget);
|
||||
expect(repo.withdrawn, isFalse);
|
||||
});
|
||||
|
||||
testWidgets('연동이 하나뿐이면 서버 탈퇴를 부른다 (554:24885)', (tester) async {
|
||||
final repo = _WithdrawRepo();
|
||||
await pumpWide(
|
||||
tester,
|
||||
SettingsScreen(
|
||||
repository: repo,
|
||||
linker: SampleAccountLinker(
|
||||
seed: const {AuthProviderKind.google: 'fan@gmail.com'},
|
||||
),
|
||||
),
|
||||
);
|
||||
await tester.pump();
|
||||
await confirmWithdraw(tester);
|
||||
expect(repo.withdrawn, isTrue);
|
||||
});
|
||||
|
||||
testWidgets('SNS 연동 라벨은 제공자 이름만 쓴다 — 「LINE으」 잘림 없음', (tester) async {
|
||||
await pumpWide(tester, SettingsScreen(repository: SampleMyRepository()));
|
||||
await tester.pump();
|
||||
@@ -1112,9 +1151,22 @@ class _OneNebula extends SampleGalaxyRepository {
|
||||
myPop: 0,
|
||||
memberships: [
|
||||
PlanetNode(
|
||||
id: 'n1', name: '새 성운', rank: 0, fpi: 0, members: 1, online: 0,
|
||||
isMine: true, stage: 0,
|
||||
id: 'n1',
|
||||
name: '새 성운',
|
||||
rank: 0,
|
||||
fpi: 0,
|
||||
members: 1,
|
||||
online: 0,
|
||||
isMine: true,
|
||||
stage: 0,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class _WithdrawRepo extends SampleMyRepository {
|
||||
bool withdrawn = false;
|
||||
|
||||
@override
|
||||
Future<void> withdraw() async => withdrawn = true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,151 @@
|
||||
import 'package:fancake/app.dart';
|
||||
import 'package:fancake/core/data/repositories.dart';
|
||||
import 'package:fancake/core/firebase/firebase_boot.dart';
|
||||
import 'package:fancake/features/auth/auth.dart';
|
||||
import 'package:fancake/features/auth/auth_repository.dart';
|
||||
import 'package:fancake/features/auth/login_screen.dart';
|
||||
import 'package:fancake/features/auth/onboarding_data.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
/// 온보딩 6단계(10/3 통합회신 1항) · 「로그인」 버튼 신규 계정의 약관 우회 차단(E-05).
|
||||
void main() {
|
||||
setUp(() {
|
||||
FirebaseBoot.debugSetMode(FirebaseMode.mock, reason: '온보딩 테스트');
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
|
||||
Future<(AuthRepository, SampleOnboardingRepository)> boot(
|
||||
WidgetTester tester, {
|
||||
required bool needs,
|
||||
bool intro = false,
|
||||
}) async {
|
||||
tester.view.physicalSize = const Size(1080, 2400);
|
||||
tester.view.devicePixelRatio = 2;
|
||||
addTearDown(tester.view.reset);
|
||||
final repos = Repositories(live: false);
|
||||
final auth = AuthRepository(
|
||||
providers: [_Provider()],
|
||||
releaseMode: true,
|
||||
needsOnboarding: (_) async => needs,
|
||||
introPending: intro,
|
||||
);
|
||||
addTearDown(auth.dispose);
|
||||
await tester.pumpWidget(FancakeApp(auth: auth, repositories: repos));
|
||||
await tester.pumpAndSettle();
|
||||
return (auth, repos.onboarding as SampleOnboardingRepository);
|
||||
}
|
||||
|
||||
testWidgets('「로그인」으로 들어온 신규 계정도 약관부터 — 셸로 가지 않는다', (tester) async {
|
||||
final (auth, _) = await boot(tester, needs: true);
|
||||
await auth.signIn(SocialProvider.google);
|
||||
await tester.pumpAndSettle();
|
||||
expect(auth.onboarding, OnboardingGate.required);
|
||||
expect(find.byType(NavigationBar), findsNothing);
|
||||
expect(find.text('개인정보 수집·이용 및 처리방침 동의 (필수)'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('약관 동의가 있는 계정은 바로 셸', (tester) async {
|
||||
final (auth, _) = await boot(tester, needs: false);
|
||||
await auth.signIn(SocialProvider.google);
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(NavigationBar), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('약관 → 프로필 → 관심 아티스트 → 행성 가입 → 권한 → 가입 완료 → 셸', (tester) async {
|
||||
final (auth, repo) = await boot(tester, needs: true);
|
||||
await auth.signIn(SocialProvider.google);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// ② 약관 — 필수만 동의(마케팅 미동의).
|
||||
for (final label in [
|
||||
'서비스 이용약관 (필수)',
|
||||
'개인정보 수집·이용 및 처리방침 동의 (필수)',
|
||||
'위치기반 서비스 이용약관 (필수)',
|
||||
'만 14세 이상입니다. (필수)',
|
||||
]) {
|
||||
await tester.tap(find.text(label));
|
||||
}
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('완료'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(repo.agreed, {'service', 'privacy', 'location', 'age'});
|
||||
|
||||
// ③ 프로필
|
||||
await tester.enterText(find.byType(TextField).first, '우주팬');
|
||||
await tester.enterText(find.byType(TextField).last, 'ABCDEFGHJK');
|
||||
await tester.pump();
|
||||
await tester.tap(find.text('다음'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(repo.profile?.nickname, '우주팬');
|
||||
expect(repo.profile?.country, isNotEmpty);
|
||||
expect(repo.referral, 'ABCDEFGHJK');
|
||||
|
||||
// ④ 관심 아티스트 — 최소 1개 전에는 다음이 잠겨 있다.
|
||||
final next = find.widgetWithText(FilledButton, '다음');
|
||||
expect(tester.widget<FilledButton>(next).onPressed, isNull);
|
||||
await tester.tap(find.text('NewJeans'));
|
||||
await tester.tap(find.text('BTS'));
|
||||
await tester.pump();
|
||||
await tester.tap(next);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// ⑤ 행성 가입 — 고른 순서(첫 번째가 대표).
|
||||
await tester.tap(find.text('선택한 행성 가입하기'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(repo.joined, ['newjeans', 'bts']);
|
||||
|
||||
// ⑥ 권한 안내 → 가입 완료(182:29344) → 셸
|
||||
expect(find.text('앱 권한 안내'), findsOneWidget);
|
||||
await tester.tap(find.text('계속'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('회원가입이 완료되었습니다'), findsOneWidget);
|
||||
await tester.tap(find.text('시작'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.byType(NavigationBar), findsOneWidget);
|
||||
expect(
|
||||
(await SharedPreferences.getInstance()).getBool('coach.pending'),
|
||||
isNotNull,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('가입 모드에서 받은 약관은 다시 묻지 않고 기록만 한다', (tester) async {
|
||||
final (auth, repo) = await boot(tester, needs: true);
|
||||
auth.pendingConsent = {
|
||||
'service',
|
||||
'privacy',
|
||||
'location',
|
||||
'age',
|
||||
'marketing',
|
||||
};
|
||||
await auth.signIn(SocialProvider.google, signUp: true);
|
||||
await tester.pumpAndSettle();
|
||||
expect(repo.agreed, contains('marketing'));
|
||||
expect(find.text('프로필을 설정해 주세요'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('첫 실행은 소개 3장 — 건너뛰기를 눌러도 로그인 화면', (tester) async {
|
||||
final (auth, _) = await boot(tester, needs: false, intro: true);
|
||||
expect(find.text('우리 팬덤의 행성을 키워요'), findsOneWidget);
|
||||
await tester.tap(find.text('건너뛰기'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(auth.introPending, isFalse);
|
||||
expect(find.byKey(loginLogoKey), findsOneWidget);
|
||||
});
|
||||
}
|
||||
|
||||
final class _Provider implements SocialAuthProvider {
|
||||
@override
|
||||
AuthProviderKind get provider => SocialProvider.google;
|
||||
@override
|
||||
bool get isLive => true;
|
||||
@override
|
||||
List<String> get missingConfig => const [];
|
||||
@override
|
||||
Future<AuthOutcome> signIn() async => const AuthSucceeded(
|
||||
AuthAccount(uid: 'onb-user', provider: SocialProvider.google),
|
||||
);
|
||||
@override
|
||||
Future<void> signOut() async {}
|
||||
}
|
||||
Reference in New Issue
Block a user