Files
pangolin/client/test/widget/account_ia_test.dart
T
wangjia b839c77712 feat(account): IA 重排+邀请入口+删偏好卡+设置/联系 formFactor 门控
账户页重排为:账户信息 → 购买套餐/我的订单/兑换码(三行合一) → 邀请好友(新增,
gift 图标+t.inviteEntry/inviteEntrySub,接 NavView.invite) → 设备(常显)/
设置/联系(仅 mobile,桌面侧栏已有同入口,formFactor 门控避免重复) → 退出。
删语言/主题/协议卡及仅供其用的 _LangSwitch/_PangolinSwitch(l10n getter 保留,
Settings 页仍用)。_NavRow 加可选 subtitle 用于邀请卡副标题。

新增 InviteScreen(client/lib/screens/invite_page.dart)占位屏:邀请码/链接卡
(可复制,值写死 PANGOLIN-DEMO)+ 奖励规则三条 + 我的奖励占位卡,顶部
TODO(spec-2) 标注接口点(邀请码/链接由后端签发,奖励进度来自 /v1/invite)。

顺带修复 desktop_shell.dart 非穷尽 switch 编译错误(NavView 加 invite 后未接线,
整个 client 目前无法编译);notifications 分支占位回落账户页,留给 Task 6 接线。

test/widget/account_ia_test.dart 新增桌面态 IA 结构断言(邀请入口存在/语言不在/
兑换在);account_desktop_nav_test.dart 同步去掉桌面态设置/联系两个失效断言
(改走桌面侧栏,非账户卡),加邀请入口断言。desktop/tablet 账户页 golden 重录。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-12 17:44:51 +08:00

85 lines
3.3 KiB
Dart

// account_ia_test.dart — 账户页 IA 结构断言:邀请入口在/语言不在/兑换在。
//
// 桌面态(windows 平台 + 宽 surface),经 HomeShell 渲染真实 DesktopShell → AccountPage,
// 与 account_desktop_nav_test.dart 同一套 harness。
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/l10n/app_text.dart';
import 'package:pangolin_vpn/l10n/strings_zh.dart';
import 'package:pangolin_vpn/pangolin_theme.dart';
import 'package:pangolin_vpn/screens/account_page.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/app_providers.dart';
import 'package:pangolin_vpn/state/auth_provider.dart';
import 'package:pangolin_vpn/state/navigation_provider.dart';
import '../helpers/harness.dart';
final AppText t = StringsZh();
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;
}
void main() {
setUpAll(disableGoogleFontsFetching);
testWidgets('account page IA: invite entry present / language moved out / redeem present', (tester) async {
debugDefaultTargetPlatformOverride = TargetPlatform.windows;
tester.view.physicalSize = const Size(920, 1400);
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()),
appTextProvider.overrideWithValue(t),
navViewProvider.overrideWith((ref) => NavView.account),
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
theme: PangolinTheme.light,
home: const HomeShell(),
),
));
await tester.pumpAndSettle();
expect(find.byType(DesktopShell), findsOneWidget);
expect(find.byType(AccountPage), findsOneWidget);
// 邀请入口存在(账户页内)。
expect(find.descendant(of: find.byType(AccountPage), matching: find.text(t.inviteEntry)), findsOneWidget);
// 语言/主题不在账户页(移到 Settings)。
expect(find.descendant(of: find.byType(AccountPage), matching: find.text(t.language)), findsNothing);
// 兑换在账户页(购买&订单&兑换块)。
expect(find.descendant(of: find.byType(AccountPage), matching: find.text(t.redeemEntry)), findsOneWidget);
} finally {
debugDefaultTargetPlatformOverride = null;
}
});
}