aee9ba7b72
1. 记住邮箱:登录页预填上次邮箱(TokenStore.saveLastEmail,登录/注册成功时存, 退出登录不清除)。 2. 密码显隐:登录/注册密码框加眼睛按钮切换明文(PangolinIcons.eye/eyeOff)。 3. 7天免登陆:RootFlow 改为响应 authProvider——启动时有有效会话直接进主界面; AuthNotifier.refresh() 在服务端拒绝(refresh 过期/超 7 天)时 logout 回登录页, 网络错误则保留会话。会话恢复期间显示极简启动屏。 flutter analyze 0 error;114 tests passed。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
77 lines
3.4 KiB
Dart
77 lines
3.4 KiB
Dart
// desktop_pages_golden_test.dart — desktop 整页 golden(节点/统计/账户)
|
||
//
|
||
// 用 debugDefaultTargetPlatformOverride=macOS + 920×600 surface 让 context.formFactor
|
||
// 判定为 desktop,渲染 DesktopShell 各页,固化精还原成果作回归闸。
|
||
// 连接页含连接键无限旋转动画,单独由组件级 golden 覆盖,此处不截。
|
||
import 'package:flutter/foundation.dart';
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:pangolin_vpn/bridge/vpn_bridge_mock.dart';
|
||
import 'package:pangolin_vpn/bridge/vpn_bridge_provider.dart';
|
||
import 'package:pangolin_vpn/pangolin_theme.dart';
|
||
import 'package:pangolin_vpn/services/token_store.dart';
|
||
import 'package:pangolin_vpn/shell/desktop_shell.dart';
|
||
import 'package:pangolin_vpn/shell/home_shell.dart';
|
||
import 'package:pangolin_vpn/state/auth_provider.dart';
|
||
import 'package:pangolin_vpn/state/navigation_provider.dart';
|
||
|
||
class _NullTokenStore implements TokenStore {
|
||
const _NullTokenStore();
|
||
@override
|
||
Future<void> saveTokens({required String access, required String refresh}) async {}
|
||
@override
|
||
Future<String?> loadAccessToken() async => null;
|
||
@override
|
||
Future<String?> loadRefreshToken() async => null;
|
||
@override
|
||
Future<void> clear() async {}
|
||
@override
|
||
Future<void> markOnboarded() async {}
|
||
@override
|
||
Future<bool> isOnboarded() async => true;
|
||
@override
|
||
Future<void> saveLastEmail(String email) async {}
|
||
@override
|
||
Future<String?> loadLastEmail() async => null;
|
||
}
|
||
|
||
Future<void> _shoot(WidgetTester tester, NavView view, String name) async {
|
||
// 平台覆盖须在 test 体内设置并在结束前复位(否则触发 foundation 调试变量未复位断言)。
|
||
debugDefaultTargetPlatformOverride = TargetPlatform.macOS;
|
||
tester.view.physicalSize = const Size(920, 600);
|
||
tester.view.devicePixelRatio = 1.0;
|
||
addTearDown(tester.view.resetPhysicalSize);
|
||
addTearDown(tester.view.resetDevicePixelRatio);
|
||
try {
|
||
await tester.pumpWidget(ProviderScope(
|
||
overrides: [
|
||
tokenStoreProvider.overrideWithValue(const _NullTokenStore()),
|
||
vpnBridgeProvider.overrideWithValue(VpnBridgeMock()),
|
||
navViewProvider.overrideWith((ref) => view),
|
||
],
|
||
child: MaterialApp(
|
||
debugShowCheckedModeBanner: false,
|
||
theme: PangolinTheme.light,
|
||
home: const HomeShell(),
|
||
),
|
||
));
|
||
await tester.pumpAndSettle();
|
||
await expectLater(find.byType(DesktopShell), matchesGoldenFile('goldens/$name.png'));
|
||
} finally {
|
||
debugDefaultTargetPlatformOverride = null;
|
||
}
|
||
}
|
||
|
||
void main() {
|
||
testWidgets('desktop 节点页', (t) => _shoot(t, NavView.servers, 'desktop_servers'));
|
||
testWidgets('desktop 统计页', (t) => _shoot(t, NavView.stats, 'desktop_stats'));
|
||
testWidgets('desktop 账户页', (t) => _shoot(t, NavView.account, 'desktop_account'));
|
||
testWidgets('desktop 联系页', (t) => _shoot(t, NavView.contact, 'desktop_contact'));
|
||
testWidgets('desktop 设置页', (t) => _shoot(t, NavView.settings, 'desktop_settings'));
|
||
// 账户下钻子页(内容区 + 顶栏返回)
|
||
testWidgets('desktop 套餐选择', (t) => _shoot(t, NavView.plans, 'desktop_plans'));
|
||
testWidgets('desktop 兑换购买', (t) => _shoot(t, NavView.redeem, 'desktop_redeem'));
|
||
testWidgets('desktop 设备管理', (t) => _shoot(t, NavView.devices, 'desktop_devices'));
|
||
}
|