mobile: normalize login account input

This commit is contained in:
2026-05-25 23:46:45 +08:00
parent 8c6b09d0d6
commit bf6f41adc9
4 changed files with 92 additions and 3 deletions
+3
View File
@@ -114,6 +114,9 @@ Created on 2026-04-18.
- artifact bundle: `mobile/qa-artifacts/real-device-smoke/2026-05-25T21-26-28-369/`
- current wireless-debug serial rotated again to `192.168.3.93:38491`; the first rerun hit `adb` `offline`, so the reconnect/smoke helpers were hardened to wait for the serial to reach `device` before continuing
- the latest `documents-workspace` retry on `192.168.3.93:38491` now exits cleanly with `SMOKE_BLOCKER=wireless_adb_unavailable` instead of an app-surface misfire, which makes the remaining issue explicit
- the next wireless-debug serial was `192.168.3.93:45167`; the app relaunched into the login form and surfaced that the Chinese IME entered the email domain separator as `。`, so mobile login now normalizes full-width account punctuation before calling the API
- after installing the rebuilt APK, `tools/real-device-smoke.ps1 -DeviceSerial 192.168.3.93:45167 -ConfigureDevice -SmokeDepth documents-workspace -LoginAccount <member-account> -LoginPassword <member-password>` returned `SMOKE_OK=True` with `DOCUMENTS_WORKSPACE_READY=True`, `DOCUMENTS_TYPE_LIST_OK=True`, and `DOCUMENTS_BUILDER_OK=True`
- artifact bundle: `mobile/qa-artifacts/real-device-smoke/2026-05-25T23-44-09-374/`
## Phase 0/1 mobile structure progress
@@ -15,11 +15,14 @@ class AuthController {
Future<void> login(
{required String account, required String password}) async {
final normalizedAccount = _normalizeLoginAccount(account);
isLoading.value = true;
errorMessage.value = null;
try {
final session =
await _apiClient.login(account: account, password: password);
final session = await _apiClient.login(
account: normalizedAccount,
password: password,
);
final currentUser =
await _apiClient.fetchCurrentUser(session.accessToken);
await SessionStore.save(_mergeSession(session, currentUser));
@@ -50,3 +53,17 @@ class AuthController {
);
}
}
String _normalizeLoginAccount(String value) {
final trimmed = value.trim();
if (trimmed.isEmpty) {
return trimmed;
}
return trimmed
.replaceAll('\uFF20', '@')
.replaceAll('\uFE6B', '@')
.replaceAll('\u3002', '.')
.replaceAll('\uFF0E', '.')
.replaceAll('\uFF61', '.');
}
@@ -0,0 +1,69 @@
import 'package:ai_contract_mobile/features/auth/application/auth_controller.dart';
import 'package:ai_contract_mobile/features/auth/domain/auth_session.dart';
import 'package:ai_contract_mobile/shared/network/api_client.dart';
import 'package:ai_contract_mobile/shared/storage/session_store.dart';
import 'package:flutter_test/flutter_test.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
SessionStore.useStorageDriverForTesting(InMemorySessionStorageDriver());
});
tearDown(() async {
await SessionStore.clear();
SessionStore.resetStorageDriverForTesting();
});
test('normalizes full-width email punctuation before login request',
() async {
final apiClient = _CapturingLoginApiClient();
final controller = AuthController(apiClient: apiClient);
await controller.login(
account: ' cyuqei@gmail。com ',
password: '816301',
);
expect(apiClient.account, 'cyuqei@gmail.com');
expect(apiClient.password, '816301');
expect(SessionStore.current?.username, 'cyuqei@gmail.com');
});
}
class _CapturingLoginApiClient extends ApiClient {
String? account;
String? password;
@override
Future<AuthSession> login({
required String account,
required String password,
}) async {
this.account = account;
this.password = password;
return AuthSession(
accessToken: 'token',
userId: 'user-1',
tenantId: 'tenant-1',
username: account,
userDisplayName: 'Member',
role: 'member',
status: 'active',
);
}
@override
Future<AuthSession> fetchCurrentUser(String accessToken) async {
return const AuthSession(
accessToken: 'token',
userId: 'user-1',
tenantId: 'tenant-1',
username: 'cyuqei@gmail.com',
userDisplayName: 'Member',
role: 'member',
status: 'active',
);
}
}
+1 -1
View File
@@ -204,7 +204,7 @@ function Invoke-AdbTextEntry {
& $script:adb -s $Serial shell input text (ConvertTo-AdbInputText -Value $domainParts[$i]) | Out-Null
}
if ($i -lt ($domainParts.Length - 1)) {
& $script:adb -s $Serial shell input keyevent KEYCODE_PERIOD | Out-Null
& $script:adb -s $Serial shell input text . | Out-Null
}
}
}