Files
photo-live/apps/mobile/test/pages/settings_tab_test.dart
T
2026-09-19 05:00:11 +08:00

137 lines
4.9 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/// 设置页 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/app/locale_controller.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<void> pumpHome(
WidgetTester tester,
ApiClient client, {
List<dynamic> extraOverrides = const <dynamic>[],
}) async {
await tester.pumpWidget(
ProviderScope(
// Task 9 起主壳含队列页(触达 sessionAccountProvider/TaskStore),
// 真实平台通道在测试环境挂起,必须注入替身。
overrides: [
...pageTestOverrides(session: store, client: client),
...extraOverrides,
],
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)也照样本地登出——尽力而为语义。
// 首页「相册」页签(Task 4)进入即自动拉列表,需一并兜底路由。
final adapter = FakeAdapter((o) {
if (o.path == '/v1/auth/logout') return (500, errorBody('INTERNAL_ERROR'));
if (o.path == '/v1/albums') return (500, errorBody('INTERNAL_ERROR'));
throw StateError('unexpected ${o.path}');
});
await pumpHome(tester, clientOf(adapter));
await tester.tap(find.text('Sign out'));
await tester.pumpAndSettle();
final logout = adapter.requests.singleWhere(
(r) => r.path == '/v1/auth/logout',
);
expect(logout.method, 'POST');
expect(store.session, isNull, reason: '本地会话必须清除');
expect(find.byType(LoginPage), findsOneWidget);
expect(find.byType(HomePage), findsNothing, reason: '主壳已出栈');
});
testWidgets('语言切换:底部弹层三选一 → 更新档位 + 持久化(P3 Task 10)',
(tester) async {
final adapter = FakeAdapter((_) => throw StateError('不应发起请求'));
final localeStore = FakeLocaleStore();
await pumpHome(
tester,
clientOf(adapter),
extraOverrides: [
localeSettingsProvider.overrideWith(
() => LocaleSettings(store: localeStore),
),
],
);
// 本 harness 的 MaterialApp 固定 en(live 全 App 重建在
// app/locale_test.dart 的 PhotoLiveApp 用例覆盖)。
expect(find.text('Language'), findsOneWidget);
expect(find.text('Follow system'), findsOneWidget, reason: '默认档位副标题');
await tester.tap(find.text('Language'));
await tester.pumpAndSettle();
// 弹层三选项(语言名以自身语言书写,跨 locale 不变)。
// 「Follow system」出现两次:主档 tile 副标题 + 弹层选项。
expect(find.text('Follow system'), findsNWidgets(2));
expect(find.text('中文'), findsOneWidget);
expect(find.text('English'), findsOneWidget);
await tester.tap(find.text('中文'));
await tester.pumpAndSettle();
expect(find.text('中文'), findsOneWidget, reason: '副标题更新为当前档位');
expect(localeStore.writes, ['zh'], reason: '选择写入 app.locale');
});
}