Files
fanuzu/app/test/signup_terms_test.dart
T

213 lines
7.5 KiB
Dart

import 'dart:async';
import 'package:fancake/app.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/signup_screens.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
/// 가입 약관 — 동의 기록이 없는 계정은 HOME 대신 약관으로 가고, 동의를 기록한 뒤에만 HOME 에 들어간다.
/// 소셜 로그인 진행 중에는 화면 전체를 막고 진행 표시를 띄운다.
void main() {
setUp(() {
FirebaseBoot.debugSetMode(FirebaseMode.mock, reason: '가입 약관 위젯 테스트');
});
const newUser = AuthAccount(
uid: 'new-user',
provider: AuthProviderKind.kakao,
);
testWidgets('로그인 진행 중 로딩 · 중복 입력 막음 → 동의 없는 새 계정은 약관 → 필수 동의 → HOME', (
tester,
) async {
final signIn = Completer<AuthOutcome>();
final provider = _Provider(AuthProviderKind.kakao, () => signIn.future);
Set<String>? recorded;
final auth = AuthRepository(
providers: [provider],
releaseMode: false,
needsTerms: (_) async => recorded == null,
recordTerms: (agreed) async => recorded = agreed,
);
await tester.pumpWidget(FancakeApp(auth: auth));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(FilledButton, '로그인'));
await tester.pumpAndSettle();
final kakao = find.text(providerButtonLabel(AuthProviderKind.kakao));
await tester.tap(kakao);
await tester.pump();
expect(find.byType(BlockingProgress), findsOneWidget);
expect(find.byType(CircularProgressIndicator), findsOneWidget);
await tester.tap(kakao, warnIfMissed: false);
await tester.pump();
expect(provider.calls, 1);
signIn.complete(const AuthSucceeded(newUser));
await tester.pumpAndSettle();
expect(find.byType(NavigationBar), findsNothing);
expect(find.text('이용 약관'), findsOneWidget);
expect(find.text('위치기반 서비스 이용약관 (필수)'), findsNothing);
final done = find.widgetWithText(FilledButton, '완료');
expect(tester.widget<FilledButton>(done).onPressed, isNull);
for (final label in [
'서비스 이용약관 (필수)',
'개인정보 수집·이용 동의 (필수)',
'만 14세 이상입니다. (필수)',
]) {
await tester.scrollUntilVisible(find.text(label), 100);
await tester.tap(find.text(label));
await tester.pump();
}
// 개인정보처리방침은 체크 칸 없는 고지 · 보기 링크.
final policy = find.byKey(const ValueKey('privacy-policy-link'));
await tester.tap(policy);
await tester.pumpAndSettle();
expect(find.text('개인정보처리방침'), findsOneWidget);
await tester.tap(find.text('확인'));
await tester.pumpAndSettle();
expect(tester.widget<FilledButton>(done).onPressed, isNotNull);
await tester.tap(done);
await tester.pumpAndSettle();
expect(recorded, {'service', 'privacy', 'age'});
expect(find.byType(NavigationBar), findsOneWidget);
expect(find.byType(BlockingProgress), findsNothing);
});
testWidgets('개인정보처리방침 고지는 스크롤 없이 하단 버튼 위에 보이고 체크 칸이 없다', (tester) async {
// iPhone 11 — 414x896pt · 상단 48 · 하단 34.
tester.view.physicalSize = const Size(828, 1792);
tester.view.devicePixelRatio = 2;
tester.view.padding = const FakeViewPadding(top: 96, bottom: 68);
addTearDown(tester.view.reset);
await tester.pumpWidget(const MaterialApp(home: TermsScreen()));
await tester.pumpAndSettle();
final notice = find.byKey(const ValueKey('privacy-policy-notice'));
expect(
find.descendant(
of: notice,
matching: find.text('개인정보 처리 내용은 개인정보처리방침에서 확인할 수 있습니다.'),
),
findsOneWidget,
);
final link = find.descendant(of: notice, matching: find.text('보기'));
expect(link, findsOneWidget);
expect(
find.descendant(of: notice, matching: find.byType(ImageIcon)),
findsNothing,
);
// 화면 안 · 「이전」 버튼 위.
expect(tester.getRect(link).bottom, lessThanOrEqualTo(896 - 34));
expect(
tester.getRect(notice).bottom,
lessThanOrEqualTo(
tester.getRect(find.widgetWithText(FilledButton, '이전')).top,
),
);
await tester.tap(link);
await tester.pumpAndSettle();
expect(find.text('개인정보처리방침'), findsOneWidget);
});
testWidgets('개인정보처리방침 보기는 약관 문구가 아닌 처리방침 문구를 보여 준다', (tester) async {
await tester.pumpWidget(const MaterialApp(home: TermsScreen()));
await tester.pumpAndSettle();
await tester.tap(find.byKey(const ValueKey('privacy-policy-link')));
await tester.pumpAndSettle();
final body = tester
.widget<Text>(
find.descendant(
of: find.byType(AlertDialog),
matching: find.textContaining('임시 문구'),
),
)
.data!;
expect(body, contains('개인정보처리방침'));
expect(body, isNot(contains('서비스 이용약관')));
});
testWidgets('이미 동의한 계정은 약관 없이 HOME', (tester) async {
final auth = AuthRepository(
providers: [
_Provider(
AuthProviderKind.google,
() async => const AuthSucceeded(newUser),
),
],
releaseMode: false,
needsTerms: (_) async => false,
recordTerms: (_) async => fail('기록하지 않는다'),
);
await tester.pumpWidget(FancakeApp(auth: auth));
await tester.pumpAndSettle();
await tester.tap(find.widgetWithText(FilledButton, '로그인'));
await tester.pumpAndSettle();
await tester.tap(find.text(providerButtonLabel(AuthProviderKind.google)));
await tester.pumpAndSettle();
expect(find.byType(NavigationBar), findsOneWidget);
});
testWidgets('가입 모드 — 인증 전에 받은 동의를 기록하고 약관을 다시 묻지 않는다', (tester) async {
Set<String>? recorded;
final auth = AuthRepository(
providers: [
_Provider(
AuthProviderKind.line,
() async => const AuthSucceeded(newUser),
),
],
releaseMode: false,
needsTerms: (_) async => recorded == null,
recordTerms: (agreed) async => recorded = agreed,
);
await tester.pumpWidget(FancakeApp(auth: auth));
await tester.pumpAndSettle();
await tester.tap(find.text('회원가입'));
await tester.pumpAndSettle();
await tester.tap(
find.text(providerButtonLabel(AuthProviderKind.line, signUp: true)),
);
await tester.pumpAndSettle();
await tester.tap(find.text('전체 동의'));
await tester.pump();
await tester.tap(find.widgetWithText(FilledButton, '완료'));
await tester.pumpAndSettle();
expect(recorded, {'service', 'privacy', 'age', 'marketing'});
expect(find.byType(NavigationBar), findsOneWidget);
});
}
final class _Provider implements SocialAuthProvider {
_Provider(this.provider, this._signIn);
@override
final AuthProviderKind provider;
final Future<AuthOutcome> Function() _signIn;
int calls = 0;
@override
bool get isLive => true;
@override
List<String> get missingConfig => const [];
@override
Future<AuthOutcome> signIn() {
calls += 1;
return _signIn();
}
@override
Future<void> signOut() async {}
}