feat(client): desktop 其他页精还原 + 整页 golden 闸

- stats_page 对照 dapp.jsx DStats 精对齐: padding 32/8、指标卡 padding 16/18·
  gap 14·值 mono 24、柱状图卡 padding 20/18·标题 13、柱 height 140(容 bar 86)·宽 34
- nodes_page 对照 DServers 微调: padding top 4、网格 gap 10(智能卡置顶保留, 符 §5)
- 新增 test/golden/desktop_pages_golden_test.dart: 用 macOS 平台覆盖 + 920×600 surface
  让 formFactor=desktop, 截 节点/统计/账户 三页整页 golden 作回归闸(连接页含旋转动画
  由组件级 golden 覆盖, 不在此截)

测试: flutter test 79 通过(+3 desktop 整页 golden), analyze 0 error

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-06-16 12:02:24 +08:00
parent 1340b477a7
commit 22e4eb1ead
6 changed files with 90 additions and 22 deletions
@@ -0,0 +1,62 @@
// 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 {}
}
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'));
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB