Files
social-app/apps/test/features/auth/presentation/cubits/register_cubit_test.dart
T

216 lines
6.8 KiB
Dart

import 'package:bloc_test/bloc_test.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:formz/formz.dart';
import 'package:mocktail/mocktail.dart';
import 'package:social_app/core/form_inputs/form_inputs.dart';
import 'package:social_app/features/auth/data/auth_repository.dart';
import 'package:social_app/features/auth/data/models/auth_response.dart';
import 'package:social_app/features/auth/data/models/signup_request.dart';
import 'package:social_app/features/auth/presentation/cubits/register_cubit.dart';
import 'package:social_app/core/api/api_exception.dart';
class MockAuthRepository extends Mock implements AuthRepository {}
void main() {
late RegisterCubit cubit;
late MockAuthRepository mockRepository;
setUp(() {
mockRepository = MockAuthRepository();
cubit = RegisterCubit(mockRepository);
registerFallbackValue(
SignupStartRequest(username: '', email: '', password: ''),
);
registerFallbackValue(SignupResendRequest(email: ''));
});
tearDown(() {
cubit.close();
});
group('RegisterCubit', () {
test('initial state has pure status', () {
expect(cubit.state.status, FormzSubmissionStatus.initial);
});
blocTest<RegisterCubit, RegisterState>(
'usernameChanged updates username',
build: () => cubit,
act: (c) => c.usernameChanged('testuser'),
expect: () => [isA<RegisterState>()],
);
blocTest<RegisterCubit, RegisterState>(
'emailChanged updates email',
build: () => cubit,
act: (c) => c.emailChanged('test@example.com'),
expect: () => [isA<RegisterState>()],
);
blocTest<RegisterCubit, RegisterState>(
'passwordChanged updates password',
build: () => cubit,
act: (c) => c.passwordChanged('password123'),
expect: () => [isA<RegisterState>()],
);
});
group('sendCodeSilently', () {
blocTest<RegisterCubit, RegisterState>(
'sets isSending to true then false on success',
build: () => cubit,
seed: () => RegisterState(
username: const Username.dirty('testuser'),
email: const Email.dirty('test@example.com'),
password: const Password.dirty('password123'),
),
setUp: () {
when(() => mockRepository.createVerification(any())).thenAnswer(
(_) async => VerificationCreateResponse(email: 'test@example.com'),
);
},
act: (c) => c.sendCodeSilently(),
expect: () => [
predicate<RegisterState>((state) => state.isSending == true),
predicate<RegisterState>(
(state) =>
state.isSending == false &&
state.codeSent == true &&
state.pendingEmail == 'test@example.com' &&
state.errorMessage == null,
),
],
verify: (_) {
verify(() => mockRepository.createVerification(any())).called(1);
},
);
blocTest<RegisterCubit, RegisterState>(
'restores isSending to false and sets errorMessage and failure status on error',
build: () => cubit,
seed: () => RegisterState(
username: const Username.dirty('testuser'),
email: const Email.dirty('test@example.com'),
password: const Password.dirty('password123'),
),
setUp: () {
when(
() => mockRepository.createVerification(any()),
).thenThrow(ServerException('Network error'));
},
act: (c) => c.sendCodeSilently(),
expect: () => [
predicate<RegisterState>((state) => state.isSending == true),
predicate<RegisterState>(
(state) =>
state.isSending == false &&
state.errorMessage == 'Network error' &&
state.status == FormzSubmissionStatus.failure,
),
],
verify: (_) {
verify(() => mockRepository.createVerification(any())).called(1);
},
);
blocTest<RegisterCubit, RegisterState>(
'does not call createVerification when input is invalid',
build: () => cubit,
seed: () => RegisterState(
username: const Username.dirty(''),
email: const Email.dirty('invalid'),
password: const Password.dirty(''),
),
act: (c) => c.sendCodeSilently(),
expect: () => [],
verify: (_) {
verifyNever(() => mockRepository.createVerification(any()));
},
);
blocTest<RegisterCubit, RegisterState>(
'returns early when isSending is true',
build: () => cubit,
seed: () => RegisterState(
username: const Username.dirty('testuser'),
email: const Email.dirty('test@example.com'),
password: const Password.dirty('password123'),
isSending: true,
),
act: (c) => c.sendCodeSilently(),
expect: () => [],
verify: (_) {
verifyNever(() => mockRepository.createVerification(any()));
},
);
});
group('resendCode', () {
blocTest<RegisterCubit, RegisterState>(
'returns false and sets failure status when pendingEmail is null',
build: () => cubit,
seed: () => RegisterState(pendingEmail: null),
act: (c) => c.resendCode(),
expect: () => [
isA<RegisterState>()
.having((s) => s.status, 'status', FormzSubmissionStatus.failure)
.having((s) => s.errorMessage, 'errorMessage', '验证码发送失败,请返回上一步重试'),
],
verify: (_) {
verifyNever(() => mockRepository.resendVerification(any()));
},
);
blocTest<RegisterCubit, RegisterState>(
'returns true and sets status on success',
build: () => cubit,
seed: () => RegisterState(pendingEmail: 'test@example.com'),
setUp: () {
when(
() => mockRepository.resendVerification(any()),
).thenAnswer((_) async {});
},
act: (c) => c.resendCode(),
expect: () => [
isA<RegisterState>().having(
(s) => s.status,
'status',
FormzSubmissionStatus.inProgress,
),
isA<RegisterState>().having(
(s) => s.status,
'status',
FormzSubmissionStatus.success,
),
],
verify: (_) {
verify(() => mockRepository.resendVerification(any())).called(1);
},
);
blocTest<RegisterCubit, RegisterState>(
'returns false on error',
build: () => cubit,
seed: () => RegisterState(pendingEmail: 'test@example.com'),
setUp: () {
when(
() => mockRepository.resendVerification(any()),
).thenThrow(ServerException('Network error'));
},
act: (c) => c.resendCode(),
expect: () => [
isA<RegisterState>().having(
(s) => s.status,
'status',
FormzSubmissionStatus.inProgress,
),
isA<RegisterState>().having(
(s) => s.status,
'status',
FormzSubmissionStatus.failure,
),
],
);
});
}