import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:patbond_flutter/core/network/api_exception.dart'; import 'package:patbond_flutter/core/theme/app_theme.dart'; import 'package:patbond_flutter/core/widgets/inline_error_banner.dart'; import 'package:patbond_flutter/features/auth/auth_repository.dart'; import 'package:patbond_flutter/features/auth/login_page.dart'; import '../../helpers/auth_test_helpers.dart'; void main() { Future pumpLogin(WidgetTester tester, AuthRepository repository) { return tester.pumpWidget( MaterialApp( theme: buildAppTheme(), home: LoginPage(authRepository: repository), ), ); } Future fillValidInput(WidgetTester tester) async { await tester.enterText(find.byType(TextFormField).at(0), 'gunnar'); await tester.enterText(find.byType(TextFormField).at(1), 'passw0rd'); } testWidgets('初始态:品牌区、两个输入框、登录按钮与注册链接,无错误无 loading', (tester) async { await pumpLogin(tester, FakeAuthRepository()); expect(find.text('Patbond'), findsOneWidget); expect(find.text('用户名 / 手机号'), findsOneWidget); expect(find.text('密码'), findsOneWidget); expect(find.text('登录'), findsOneWidget); expect(find.text('立即注册'), findsOneWidget); expect(find.byType(InlineErrorBanner), findsNothing); expect(find.byType(CircularProgressIndicator), findsNothing); }); testWidgets('loading 态:按钮转圈、字段禁用、注册链接不可点', (tester) async { final completer = Completer(); final repository = FakeAuthRepository(loginHandler: () => completer.future); await pumpLogin(tester, repository); await fillValidInput(tester); await tester.tap(find.text('登录')); await tester.pump(); expect(find.byType(CircularProgressIndicator), findsOneWidget); final fields = tester.widgetList(find.byType(TextFormField)); expect(fields.every((field) => field.enabled == false), isTrue); final registerLink = tester.widget( find.widgetWithText(TextButton, '立即注册'), ); expect(registerLink.onPressed, isNull); completer.complete(); await tester.pumpAndSettle(); expect(find.byType(CircularProgressIndicator), findsNothing); }); testWidgets('字段错误态:空表单提交显示两条字段级错误,输入即清除', (tester) async { final repository = FakeAuthRepository(); await pumpLogin(tester, repository); await tester.tap(find.text('登录')); await tester.pump(); expect(find.text('请输入用户名或手机号'), findsOneWidget); expect(find.text('请输入密码'), findsOneWidget); expect(repository.loginCalls, 0); await tester.enterText(find.byType(TextFormField).at(0), 'g'); await tester.pump(); expect(find.text('请输入用户名或手机号'), findsNothing); expect(find.text('请输入密码'), findsOneWidget); }); testWidgets('横幅错误态:40100 显示「用户名或密码错误」,任一字段输入即清除', (tester) async { final repository = FakeAuthRepository( loginHandler: () => Future.error( const ApiBusinessException(code: ApiCodes.badCredentials, message: ''), ), ); await pumpLogin(tester, repository); await fillValidInput(tester); await tester.tap(find.text('登录')); await tester.pumpAndSettle(); expect(find.byType(InlineErrorBanner), findsOneWidget); expect(find.text('用户名或密码错误'), findsOneWidget); expect(repository.loginCalls, 1); await tester.enterText(find.byType(TextFormField).at(1), 'changed1'); await tester.pump(); expect(find.byType(InlineErrorBanner), findsNothing); }); }