feat(auth): add sendCodeSilently with isSending state

This commit is contained in:
qzl
2026-02-26 10:35:46 +08:00
parent c9e91e7849
commit 175da2a8b7
2 changed files with 73 additions and 0 deletions
@@ -16,6 +16,7 @@ class RegisterState extends Equatable {
final String? errorMessage;
final String? pendingEmail;
final bool codeSent;
final bool isSending;
const RegisterState({
this.username = const Username.pure(),
@@ -26,6 +27,7 @@ class RegisterState extends Equatable {
this.errorMessage,
this.pendingEmail,
this.codeSent = false,
this.isSending = false,
});
bool get isStep1Valid =>
@@ -41,6 +43,7 @@ class RegisterState extends Equatable {
String? errorMessage,
String? pendingEmail,
bool? codeSent,
bool? isSending,
}) {
return RegisterState(
username: username ?? this.username,
@@ -51,6 +54,7 @@ class RegisterState extends Equatable {
errorMessage: errorMessage,
pendingEmail: pendingEmail ?? this.pendingEmail,
codeSent: codeSent ?? this.codeSent,
isSending: isSending ?? this.isSending,
);
}
@@ -64,6 +68,7 @@ class RegisterState extends Equatable {
errorMessage,
pendingEmail,
codeSent,
isSending,
];
}
@@ -160,4 +165,31 @@ class RegisterCubit extends Cubit<RegisterState> {
emit(state.copyWith(errorMessage: message));
}
}
Future<void> sendCodeSilently() async {
if (!state.isStep1Valid) return;
emit(state.copyWith(isSending: true));
try {
final response = await _repository.signupStart(
SignupStartRequest(
username: state.username.value,
email: state.email.value,
password: state.password.value,
),
);
emit(
state.copyWith(
isSending: false,
pendingEmail: response.email,
codeSent: true,
errorMessage: null,
),
);
} catch (e) {
final message = e is ApiException ? e.message : '验证码发送失败,请重试';
emit(state.copyWith(isSending: false, errorMessage: message));
}
}
}
@@ -2,7 +2,10 @@ 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';
class MockAuthRepository extends Mock implements AuthRepository {}
@@ -14,6 +17,9 @@ void main() {
setUp(() {
mockRepository = MockAuthRepository();
cubit = RegisterCubit(mockRepository);
registerFallbackValue(
SignupStartRequest(username: '', email: '', password: ''),
);
});
tearDown(() {
@@ -46,4 +52,39 @@ void main() {
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.signupStart(any())).thenAnswer(
(_) async => SignupStartResponse(
status: 'ok',
email: 'test@example.com',
message: 'Code sent',
),
);
},
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.signupStart(any())).called(1);
},
);
});
}