소셜 웹 로그인 중단·실패를 이메일 오류 대신 소셜 로그인 문구로 안내 · 네트워크 오류 안내 · Android 중단된 웹 로그인 정리 후 재시도
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import 'package:fancake/features/auth/auth.dart';
|
||||
import 'package:fancake/features/auth/login_screen.dart';
|
||||
import 'package:fancake/features/auth/social_providers.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart' as fb;
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
@@ -61,7 +63,10 @@ void main() {
|
||||
test('에러 카탈로그 ID 는 경로와 무관하게 E-AUTH-08 이다', () {
|
||||
// 문구만 갈랐다. 카탈로그를 손대면 Admin·QA 의 코드 대조가 어긋난다.
|
||||
expect(
|
||||
authCatalogCode(_exception('account-exists-with-different-credential')),
|
||||
authCatalogCode(
|
||||
_exception('account-exists-with-different-credential'),
|
||||
flow: AuthFlow.signIn,
|
||||
),
|
||||
'E-AUTH-08',
|
||||
);
|
||||
});
|
||||
@@ -85,13 +90,54 @@ void main() {
|
||||
// 「등록되지 않은 이메일」과 「비밀번호 오류」가 같은 문구로 나가야 한다.
|
||||
for (final code in ['user-not-found', 'wrong-password']) {
|
||||
expect(
|
||||
authMessage(_exception(code), flow: AuthFlow.signIn),
|
||||
authMessage(_exception(code), flow: AuthFlow.email),
|
||||
'이메일 또는 비밀번호를 확인해 주세요.',
|
||||
reason: code,
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
group('소셜 웹 로그인 실패 · 중단 — 이메일 문구를 쓰지 않는다', () {
|
||||
const webCodes = [
|
||||
'web-context-already-presented',
|
||||
'web-network-request-failed',
|
||||
'web-internal-error',
|
||||
'internal-error',
|
||||
'invalid-credential',
|
||||
];
|
||||
|
||||
test('소셜 로그인 · 연결의 알 수 없는 실패는 E-AUTH-03 · 소셜 문구', () {
|
||||
for (final code in webCodes) {
|
||||
for (final flow in [AuthFlow.signIn, AuthFlow.link]) {
|
||||
expect(authCatalogCode(_exception(code), flow: flow), 'E-AUTH-03', reason: '$code $flow');
|
||||
expect(
|
||||
authMessage(_exception(code), flow: flow),
|
||||
isNot('이메일 또는 비밀번호를 확인해 주세요.'),
|
||||
reason: '$code $flow',
|
||||
);
|
||||
}
|
||||
}
|
||||
expect(
|
||||
authMessage(_exception('web-context-already-presented'), flow: AuthFlow.signIn),
|
||||
'로그인이 완료되지 않았습니다. 다시 시도해 주세요.',
|
||||
);
|
||||
expect(
|
||||
authMessage(_exception('web-network-request-failed'), flow: AuthFlow.signIn),
|
||||
'네트워크 연결을 확인한 뒤 다시 시도해 주세요.',
|
||||
);
|
||||
expect(authMessage(_exception('internal-error'), flow: AuthFlow.signIn), '소셜 로그인에 실패했습니다.');
|
||||
});
|
||||
|
||||
test('이메일 폼만 E-AUTH-01 · 「이메일 또는 비밀번호」', () {
|
||||
expect(authCatalogCode(_exception('internal-error'), flow: AuthFlow.email), 'E-AUTH-01');
|
||||
expect(authMessage(_exception('internal-error'), flow: AuthFlow.email), '이메일 또는 비밀번호를 확인해 주세요.');
|
||||
});
|
||||
|
||||
test('취소는 성공이 아니고 다시 시도하라고 안내한다', () {
|
||||
expect(authNotice(const AuthCancelled()), '로그인이 완료되지 않았습니다. 다시 시도해 주세요.');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/// `FirebaseAuthException` 의 생성자가 `@protected` 라 밖에서 직접 부르면
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:fancake/features/auth/social_providers.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart' as fb;
|
||||
import 'package:firebase_auth_mocks/firebase_auth_mocks.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
@@ -112,6 +113,52 @@ void main() {
|
||||
expect(outcome, isA<AuthCancelled>());
|
||||
});
|
||||
|
||||
group('웹 흐름 중단 뒤 재시도', () {
|
||||
late List<String> cleared;
|
||||
setUp(() {
|
||||
cleared = [];
|
||||
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(const MethodChannel('kr.co.fanuzu.app/auth'), (call) async {
|
||||
cleared.add(call.method);
|
||||
return null;
|
||||
});
|
||||
});
|
||||
tearDown(() => TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger
|
||||
.setMockMethodCallHandler(const MethodChannel('kr.co.fanuzu.app/auth'), null));
|
||||
|
||||
test('앞선 흐름이 남아 막히면(web-context-already-presented) 정리하고 한 번 다시 열어 로그인된다', () async {
|
||||
final auth = _StaleThenOkAuth();
|
||||
final outcome = await line(auth).signIn();
|
||||
expect(outcome, isA<AuthSucceeded>());
|
||||
expect(auth.calls, 2);
|
||||
expect(cleared, ['cancelPendingWebSignIn']);
|
||||
});
|
||||
|
||||
test('정리 뒤에도 막히면 소셜 문구로 실패 — 「이메일 또는 비밀번호」가 아니다', () async {
|
||||
final outcome = await line(_ThrowingAuth('web-context-already-presented')).signIn();
|
||||
expect((outcome as AuthFailed).code, 'E-AUTH-03');
|
||||
expect(outcome.message, '로그인이 완료되지 않았습니다. 다시 시도해 주세요.');
|
||||
});
|
||||
|
||||
test('망 오류도 소셜 경로 문구', () async {
|
||||
final outcome = await line(_ThrowingAuth('web-network-request-failed')).signIn();
|
||||
expect((outcome as AuthFailed).code, 'E-AUTH-03');
|
||||
expect(outcome.message, '네트워크 연결을 확인한 뒤 다시 시도해 주세요.');
|
||||
});
|
||||
|
||||
testWidgets('Android: 결과 없이 돌아와 취소로 풀릴 때 Firebase 대기도 정리한다', (tester) async {
|
||||
late AuthOutcome outcome;
|
||||
line(_HangingAuth()).signIn().then((o) => outcome = o);
|
||||
for (final s in [AppLifecycleState.inactive, AppLifecycleState.hidden, AppLifecycleState.paused,
|
||||
AppLifecycleState.hidden, AppLifecycleState.inactive, AppLifecycleState.resumed]) {
|
||||
tester.binding.handleAppLifecycleStateChanged(s);
|
||||
}
|
||||
await tester.pump(const Duration(seconds: 9));
|
||||
expect(outcome, isA<AuthCancelled>());
|
||||
expect(cleared, ['cancelPendingWebSignIn']);
|
||||
});
|
||||
});
|
||||
|
||||
group('계정 연결', () {
|
||||
test('같은 UID 에 oidc.line 을 붙인다', () async {
|
||||
final user = _LinkingUser(linkedEmail: 'fan@fanuzu.co.kr');
|
||||
@@ -219,6 +266,19 @@ class _ThrowingAuth extends MockFirebaseAuth {
|
||||
throw fb.FirebaseAuthException(code: code);
|
||||
}
|
||||
|
||||
/// 앞선 웹 흐름이 남아 첫 시도가 막히는 Firebase(Android `web-context-already-presented`).
|
||||
class _StaleThenOkAuth extends MockFirebaseAuth {
|
||||
_StaleThenOkAuth() : super(mockUser: MockUser(uid: 'line-1'));
|
||||
var calls = 0;
|
||||
|
||||
@override
|
||||
Future<fb.UserCredential> signInWithProvider(fb.AuthProvider provider) {
|
||||
calls += 1;
|
||||
if (calls == 1) throw fb.FirebaseAuthException(code: 'web-context-already-presented');
|
||||
return super.signInWithProvider(provider);
|
||||
}
|
||||
}
|
||||
|
||||
/// 콜백을 잃어 영영 끝나지 않는 Firebase 대기 Task.
|
||||
class _HangingAuth extends MockFirebaseAuth {
|
||||
@override
|
||||
|
||||
Reference in New Issue
Block a user