/// 设置页 widget 测试(P3 计划 Task 3)。 /// /// 覆盖:修改密码入口跳 /password-change;退出登录 → 调 api.logout /// (尽力而为)→ 清空本地会话 → 清栈回 /login。 library; import 'package:dio/dio.dart'; import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:photo_live/core/api_client.dart'; import 'package:photo_live/core/session_store.dart'; import 'package:photo_live/l10n/app_localizations.dart'; import 'package:photo_live/pages/home_page.dart'; import 'package:photo_live/pages/login_page.dart'; import 'package:photo_live/pages/password_change_page.dart'; import '../helpers/fakes.dart'; void main() { late FakeSessionStore store; setUp(() { store = FakeSessionStore(); store.session = Session( token: 'tok-live-1', expiresAt: DateTime.utc(2099, 1, 1), account: 'staff01', ); }); ApiClient clientOf(FakeAdapter adapter) => ApiClient( sessionStore: store, dio: Dio(BaseOptions(baseUrl: 'http://test.local')) ..httpClientAdapter = adapter, ); Future pumpHome(WidgetTester tester, ApiClient client) async { await tester.pumpWidget( ProviderScope( overrides: [apiClientProvider.overrideWith((ref) => client)], child: MaterialApp( localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, home: const HomePage(), routes: { '/login': (context) => const LoginPage(), '/password-change': (context) => const PasswordChangePage(), }, ), ), ); await tester.pumpAndSettle(); // 切到设置 tab(底部导航第三项)。 await tester.tap(find.byIcon(Icons.settings_outlined)); await tester.pumpAndSettle(); } testWidgets('修改密码入口 → 跳 /password-change', (tester) async { final adapter = FakeAdapter((_) => throw StateError('不应发起请求')); await pumpHome(tester, clientOf(adapter)); await tester.tap(find.text('Change password')); await tester.pumpAndSettle(); expect(find.byType(PasswordChangePage), findsOneWidget); }); testWidgets('退出登录 → api.logout + 清会话 + 清栈回 /login(服务端失败亦然)', (tester) async { // 服务端撤销失败(500)也照样本地登出——尽力而为语义。 final adapter = FakeAdapter( (o) => o.path == '/v1/auth/logout' ? (500, errorBody('INTERNAL_ERROR')) : throw StateError('unexpected ${o.path}'), ); await pumpHome(tester, clientOf(adapter)); await tester.tap(find.text('Sign out')); await tester.pumpAndSettle(); expect(adapter.requests.single.path, '/v1/auth/logout'); expect(adapter.requests.single.method, 'POST'); expect(store.session, isNull, reason: '本地会话必须清除'); expect(find.byType(LoginPage), findsOneWidget); expect(find.byType(HomePage), findsNothing, reason: '主壳已出栈'); }); }