已完成用户APP首期功能开发
交付用户端首期页面、配套接口、后台资源及测试文档。用户APP构建、静态分析和三个管理后台构建通过;完整测试仍有2项失败,后端模型注释检查未通过,详见交付记录。
This commit is contained in:
@@ -14,6 +14,14 @@ class _RecordingUserSession extends UserSession {
|
||||
int loginCallCount = 0;
|
||||
String? lastPhone;
|
||||
String? lastPassword;
|
||||
String? lastCode;
|
||||
int codeCalls = 0;
|
||||
|
||||
@override
|
||||
Future<String> sendCode(String phone, String purpose) async {
|
||||
codeCalls++;
|
||||
return 'request-1';
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> login({
|
||||
@@ -25,30 +33,84 @@ class _RecordingUserSession extends UserSession {
|
||||
loginCallCount += 1;
|
||||
lastPhone = phone;
|
||||
lastPassword = password;
|
||||
lastCode = verificationCode;
|
||||
}
|
||||
}
|
||||
|
||||
/// 模拟服务端确认短信未实际发送,验证页面不会显示虚假倒计时。
|
||||
class _UnavailableCodeSession extends _RecordingUserSession {
|
||||
@override
|
||||
Future<String> sendCode(String phone, String purpose) async {
|
||||
throw const ApiException(2410, '短信验证码暂未开放');
|
||||
}
|
||||
}
|
||||
|
||||
/// 验证用户端登录界面的关键交互。
|
||||
void main() {
|
||||
testWidgets('显隐保留密码和焦点,重复选择当前登录方式不清空输入', (tester) async {
|
||||
await tester.pumpWidget(MaterialApp(home: LoginPage(session: _RecordingUserSession())));
|
||||
await tester.tap(find.text('密码登录'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byType(TextField).at(1), 'Input-test-123');
|
||||
await tester.tap(find.byTooltip('显示密码'));
|
||||
await tester.pumpAndSettle();
|
||||
var field = tester.widget<TextField>(find.byType(TextField).at(1));
|
||||
expect(field.controller!.text, 'Input-test-123');
|
||||
expect(field.obscureText, isFalse);
|
||||
expect(field.focusNode!.hasFocus, isTrue);
|
||||
await tester.tap(find.text('密码登录'));
|
||||
await tester.pumpAndSettle();
|
||||
expect(
|
||||
tester.widget<TextField>(find.byType(TextField).at(1)).controller!.text,
|
||||
'Input-test-123',
|
||||
);
|
||||
await tester.tap(find.byTooltip('隐藏密码'));
|
||||
await tester.pumpAndSettle();
|
||||
field = tester.widget<TextField>(find.byType(TextField).at(1));
|
||||
expect(field.obscureText, isTrue);
|
||||
expect(field.controller!.text, 'Input-test-123');
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('有焦点时切换验证码建立新连接并清除上一种凭据', (tester) async {
|
||||
await tester.pumpWidget(MaterialApp(home: LoginPage(session: _RecordingUserSession())));
|
||||
await tester.tap(find.text('密码登录'));
|
||||
await tester.pumpAndSettle();
|
||||
await tester.enterText(find.byType(TextField).at(1), 'Input-test-123');
|
||||
await tester.tap(find.text('验证码登录'));
|
||||
await tester.pumpAndSettle();
|
||||
final field = tester.widget<TextField>(find.byType(TextField).at(1));
|
||||
expect(field.controller!.text, isEmpty);
|
||||
expect(field.keyboardType, TextInputType.number);
|
||||
expect(field.obscureText, isFalse);
|
||||
expect(field.focusNode!.hasFocus, isTrue);
|
||||
await tester.enterText(find.byType(TextField).at(1), '314159');
|
||||
expect(field.controller!.text, '314159');
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('login page exposes phone and password fields', (tester) async {
|
||||
final session = UserSession(SecureSessionStore());
|
||||
await tester.pumpWidget(MaterialApp(home: LoginPage(session: session)));
|
||||
|
||||
expect(find.text('瓶安芯'), findsOneWidget);
|
||||
expect(find.bySemanticsLabel('瓶安芯'), findsOneWidget);
|
||||
expect(find.byType(TextField), findsNWidgets(2));
|
||||
expect(find.text('安全登录'), findsOneWidget);
|
||||
expect(find.text('登录'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('手机号错误且密码为空时同时提示并阻止登录调用', (tester) async {
|
||||
final session = _RecordingUserSession();
|
||||
await tester.pumpWidget(MaterialApp(home: LoginPage(session: session)));
|
||||
await tester.tap(find.text('密码登录'));
|
||||
await tester.pump();
|
||||
|
||||
await tester.enterText(find.byType(TextField).first, '+8613800138000');
|
||||
await tester.tap(find.text('安全登录'));
|
||||
await tester.ensureVisible(find.text('登录'));
|
||||
await tester.tap(find.text('登录'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('请输入正确的11位手机号'), findsOneWidget);
|
||||
expect(find.text('请输入密码'), findsOneWidget);
|
||||
expect(tester.widget<TextField>(find.byType(TextField).at(1)).decoration?.errorText, '请输入密码');
|
||||
expect(find.text('Invalid Argument'), findsNothing);
|
||||
expect(session.loginCallCount, 0);
|
||||
expect(tester.widget<TextField>(find.byType(TextField).first).focusNode?.hasFocus, isTrue);
|
||||
@@ -57,13 +119,16 @@ void main() {
|
||||
testWidgets('合法手机号配空密码时提示密码必填并阻止登录调用', (tester) async {
|
||||
final session = _RecordingUserSession();
|
||||
await tester.pumpWidget(MaterialApp(home: LoginPage(session: session)));
|
||||
await tester.tap(find.text('密码登录'));
|
||||
await tester.pump();
|
||||
|
||||
await tester.enterText(find.byType(TextField).first, '13800138000');
|
||||
await tester.tap(find.text('安全登录'));
|
||||
await tester.ensureVisible(find.text('登录'));
|
||||
await tester.tap(find.text('登录'));
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('请输入正确的11位手机号'), findsNothing);
|
||||
expect(find.text('请输入密码'), findsOneWidget);
|
||||
expect(tester.widget<TextField>(find.byType(TextField).at(1)).decoration?.errorText, '请输入密码');
|
||||
expect(session.loginCallCount, 0);
|
||||
expect(tester.widget<TextField>(find.byType(TextField).at(1)).focusNode?.hasFocus, isTrue);
|
||||
});
|
||||
@@ -71,18 +136,21 @@ void main() {
|
||||
testWidgets('修改字段清除对应错误并由回车原样提交密码', (tester) async {
|
||||
final session = _RecordingUserSession();
|
||||
await tester.pumpWidget(MaterialApp(home: LoginPage(session: session)));
|
||||
await tester.tap(find.text('密码登录'));
|
||||
await tester.pump();
|
||||
|
||||
await tester.tap(find.text('安全登录'));
|
||||
await tester.ensureVisible(find.text('登录'));
|
||||
await tester.tap(find.text('登录'));
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byType(TextField).first, '13800138000');
|
||||
await tester.pump();
|
||||
|
||||
expect(find.text('请输入正确的11位手机号'), findsNothing);
|
||||
expect(find.text('请输入密码'), findsOneWidget);
|
||||
expect(tester.widget<TextField>(find.byType(TextField).at(1)).decoration?.errorText, '请输入密码');
|
||||
|
||||
await tester.enterText(find.byType(TextField).at(1), ' 密码123 ');
|
||||
await tester.pump();
|
||||
expect(find.text('请输入密码'), findsNothing);
|
||||
expect(tester.widget<TextField>(find.byType(TextField).at(1)).decoration?.errorText, isNull);
|
||||
|
||||
await tester.testTextInput.receiveAction(TextInputAction.done);
|
||||
await tester.pump();
|
||||
@@ -95,7 +163,33 @@ void main() {
|
||||
test('接口错误优先按错误码中文化并屏蔽未知英文', () {
|
||||
expect(localizeApiErrorMessage(1704, 'Invalid Argument'), '请求参数错误');
|
||||
expect(localizeApiErrorMessage(1108, 'Password Incorrect'), '密码错误');
|
||||
expect(localizeApiErrorMessage(2410, 'service unavailable'), '短信验证码暂未开放');
|
||||
expect(localizeApiErrorMessage(9999, '业务处理失败'), '业务处理失败');
|
||||
expect(localizeApiErrorMessage(9999, 'Unexpected Error'), '操作失败,请稍后重试');
|
||||
});
|
||||
|
||||
testWidgets('验证码发送后修改手机号不得复用旧请求', (tester) async {
|
||||
final session = _RecordingUserSession();
|
||||
await tester.pumpWidget(MaterialApp(home: LoginPage(session: session)));
|
||||
await tester.enterText(find.byType(TextField).first, '13800138000');
|
||||
await tester.tap(find.text('获取验证码'));
|
||||
await tester.pump();
|
||||
await tester.enterText(find.byType(TextField).first, '13800138001');
|
||||
await tester.enterText(find.byType(TextField).at(1), '123456');
|
||||
await tester.ensureVisible(find.text('登录'));
|
||||
await tester.tap(find.text('登录'));
|
||||
await tester.pump();
|
||||
expect(find.text('请先获取当前手机号的验证码'), findsOneWidget);
|
||||
expect(session.loginCallCount, 0);
|
||||
await tester.pumpWidget(const SizedBox());
|
||||
});
|
||||
|
||||
testWidgets('短信未实际发送时明确提示暂未开放且不启动倒计时', (tester) async {
|
||||
await tester.pumpWidget(MaterialApp(home: LoginPage(session: _UnavailableCodeSession())));
|
||||
await tester.enterText(find.byType(TextField).first, '13800138000');
|
||||
await tester.tap(find.text('获取验证码'));
|
||||
await tester.pump();
|
||||
expect(find.text('短信验证码暂未开放'), findsOneWidget);
|
||||
expect(find.text('60s'), findsNothing);
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user