fix(client): 联系/购买渠道卡点击外部打开(修空 onTap)+ 桌面账户子导航复现测试

- channel_launch: @→t.me / 邮箱→mailto / 域名→https,外部打开
- 联系页(桌面)/联系&兑换子页(移动)渠道卡接上 onTap
- account_desktop_nav_test: 证明桌面设置/兑换/联系/订单导航正常(231 全绿)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013nMthbVEmQquxBRKb9Fj8u
This commit is contained in:
wangjia
2026-07-12 06:54:18 +08:00
parent b30f472d90
commit c3d32a476e
5 changed files with 141 additions and 4 deletions
@@ -0,0 +1,106 @@
// account_desktop_nav_test.dart — 复现 Windows/桌面账户页子导航点击是否切 navView。
//
// 用 debugDefaultTargetPlatformOverride=windows + 宽 surface 让 context.formFactor==desktop
// 渲染 DesktopShell 账户页,点每个 _NavRow,断言 navViewProvider 切到对应子视图。
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;
}
/// 在桌面态渲染 DesktopShell(起始 account 视图),点账户卡里 [label] 那一行,
/// 返回点击后的 navViewProvider 值。
Future<NavView> _tapRow(WidgetTester tester, String label) 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);
// 侧栏与账户卡可能共享同一文案(设置/联系我们),故限定在 AccountPage 内。
final row = find.descendant(of: find.byType(AccountPage), matching: find.text(label));
expect(row, findsOneWidget, reason: '账户卡里应有且仅有一行「$label');
final container = ProviderScope.containerOf(tester.element(find.byType(HomeShell)));
expect(container.read(navViewProvider), NavView.account);
await tester.tap(row);
await tester.pump();
return container.read(navViewProvider);
} finally {
debugDefaultTargetPlatformOverride = null;
}
}
void main() {
setUpAll(disableGoogleFontsFetching);
testWidgets('desktop account -> settings', (tester) async {
expect(await _tapRow(tester, t.settingsTitle), NavView.settings);
});
testWidgets('desktop account -> redeem', (tester) async {
expect(await _tapRow(tester, t.redeemEntry), NavView.redeem);
});
testWidgets('desktop account -> contact', (tester) async {
expect(await _tapRow(tester, t.contactEntry), NavView.contact);
});
testWidgets('desktop account -> orders', (tester) async {
expect(await _tapRow(tester, t.ordersTitle), NavView.orders);
});
testWidgets('desktop account -> devices', (tester) async {
expect(await _tapRow(tester, t.myDevices), NavView.devices);
});
}