app scaffold
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
// feature_auth 的高价值断言:
|
||||
// 1. 登录失败不能变成未捕获异常("密码错了"不是崩溃),要留在 state 里展示。
|
||||
// 2. 提交中按钮必须置灰——门店网络慢,用户会反复点。
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:core_auth/core_auth.dart';
|
||||
import 'package:core_foundation/core_foundation.dart';
|
||||
import 'package:feature_auth/feature_auth.dart';
|
||||
// LoginPage 不对外导出(别的包没有直接构造它的正当理由),测试走 src。
|
||||
import 'package:feature_auth/src/presentation/login_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
class _FailingRepo implements AuthRepository {
|
||||
/// 非 null 时 login 挂在这个 completer 上,用来观察"请求在途"这一帧。
|
||||
Completer<LoginResult>? gate;
|
||||
int calls = 0;
|
||||
String? lastUsername;
|
||||
|
||||
@override
|
||||
Future<LoginResult> login({required String username, required String password}) {
|
||||
calls++;
|
||||
lastUsername = username;
|
||||
return gate?.future ?? Future<LoginResult>.error(const BusinessException(40101, '账号或密码错误'));
|
||||
}
|
||||
|
||||
@override
|
||||
Future<UserContext> fetchCurrentUser() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<List<StoreContext>> fetchAccessibleStores() => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<StoreContext> switchStore(int storeId) => throw UnimplementedError();
|
||||
|
||||
@override
|
||||
Future<void> revokeSession() => throw UnimplementedError();
|
||||
}
|
||||
|
||||
void main() {
|
||||
testWidgets('登录失败展示业务文案,不抛出未捕获异常', (WidgetTester tester) async {
|
||||
final _FailingRepo repo = _FailingRepo();
|
||||
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [authRepositoryProvider.overrideWithValue(repo)],
|
||||
child: const MaterialApp(home: LoginPage()),
|
||||
),
|
||||
);
|
||||
|
||||
// 前后空格是扫码枪/手动输入的常见污染,必须在提交前 trim。
|
||||
await tester.enterText(find.byKey(const Key('login_username')), ' clerk01 ');
|
||||
await tester.enterText(find.byKey(const Key('login_password')), 'pwd');
|
||||
await tester.tap(find.byKey(const Key('login_submit')));
|
||||
await tester.pump();
|
||||
|
||||
expect(repo.lastUsername, 'clerk01');
|
||||
|
||||
await tester.pumpAndSettle();
|
||||
expect(find.text('账号或密码错误'), findsOneWidget);
|
||||
expect(tester.takeException(), isNull);
|
||||
});
|
||||
|
||||
testWidgets('提交中按钮置灰,重复点击不会重复发请求', (WidgetTester tester) async {
|
||||
final _FailingRepo repo = _FailingRepo()..gate = Completer<LoginResult>();
|
||||
|
||||
await tester.pumpWidget(
|
||||
ProviderScope(
|
||||
overrides: [authRepositoryProvider.overrideWithValue(repo)],
|
||||
child: const MaterialApp(home: LoginPage()),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.tap(find.byKey(const Key('login_submit')));
|
||||
await tester.pump(); // 请求还挂在 gate 上,这一帧就是"提交中"
|
||||
|
||||
final FilledButton button = tester.widget(find.byKey(const Key('login_submit')));
|
||||
expect(button.onPressed, isNull, reason: '门店网络慢,用户会反复点');
|
||||
|
||||
await tester.tap(find.byKey(const Key('login_submit')), warnIfMissed: false);
|
||||
await tester.pump();
|
||||
expect(repo.calls, 1);
|
||||
|
||||
repo.gate!.completeError(const BusinessException(40101, '账号或密码错误'));
|
||||
await tester.pumpAndSettle();
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user