6074fc8ed3
- tokens:次文字 #69778E、soft/card-border/seg 色组、输入圆角 10(kRadiusField) - theme:导航 indicatorColor 透明+显式选中蓝;卡片去阴影改 1px #EDF0F5 边框;输入边框同步 - 新组件 SegmentTabs(#EDF1F6 轨道/选中实蓝/light 浅蓝变体/null 无高亮)替换 M3 SegmentedButton - 上传中心:摘要去白卡直排+主数字 32/40;completed 次级筛选无高亮语义保留 - 相册卡 scrim 30% 起 #0C1728E8;导航角标红底白字白边;登录主标题 27/700 - theme_test 三处旧规格断言随 UI06 值更新;397/397 全绿、analyze 0 issues - 真机证据:MuMu + 测试服务器 API,登录/相册/详情/上传 4 页截图确认达标 - 登记偏差:语义色保持对比度合规深色版;视口矩阵随 G2;详见验收记录 §6 Co-Authored-By: Claude Code <noreply@anthropic.com>
172 lines
6.0 KiB
Dart
172 lines
6.0 KiB
Dart
/// UI-01 主题一致性测试(Task 3)。
|
||
///
|
||
/// 覆盖:浅色 ColorScheme 方案 §3.1 精确值、深色语义对应值、字阶
|
||
/// (UI-03 §3 校准:24/17/16-14/13-12)、组件主题(按钮高 48/圆角 12、
|
||
/// 输入圆角 12、Dialog 圆角 16、BottomSheet 顶部圆角 16、AppBar
|
||
/// titleLarge)、动效令牌(含「减少动态效果」归零)。
|
||
library;
|
||
|
||
import 'package:flutter/material.dart';
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:photo_live/app/theme.dart';
|
||
import 'package:photo_live/ui/tokens.dart';
|
||
|
||
void main() {
|
||
group('浅色 ColorScheme(方案 §3.1 精确值)', () {
|
||
final scheme = AppTheme.light.colorScheme;
|
||
|
||
test('基础色', () {
|
||
expect(scheme.primary, const Color(0xFF1A73E8));
|
||
expect(scheme.onPrimary, const Color(0xFFFFFFFF));
|
||
expect(scheme.surface, const Color(0xFFFFFFFF));
|
||
expect(scheme.onSurface, const Color(0xFF172033));
|
||
expect(scheme.secondary, const Color(0xFF996300), reason: '等待色');
|
||
expect(scheme.error, const Color(0xFFB42318));
|
||
// 成功色不进 ColorScheme(独立令牌)。
|
||
expect(kColorSuccess, const Color(0xFF16794B));
|
||
});
|
||
|
||
test('页面背景与分隔线/次文字', () {
|
||
expect(AppTheme.light.scaffoldBackgroundColor, const Color(0xFFF6F8FB));
|
||
expect(AppTheme.light.dividerTheme.color, kColorDivider);
|
||
expect(kColorDivider, const Color(0xFFE5EAF1));
|
||
expect(scheme.onSurfaceVariant, kColorTextSecondary);
|
||
expect(kColorTextSecondary, const Color(0xFF69778E)); // UI06: app-preview --muted
|
||
});
|
||
});
|
||
|
||
group('深色 ColorScheme(语义对应,不反转图片)', () {
|
||
final theme = AppTheme.dark;
|
||
final scheme = theme.colorScheme;
|
||
|
||
test('深表面 + 浅文字 + 提亮主色', () {
|
||
expect(scheme.brightness, Brightness.dark);
|
||
expect(scheme.surface, const Color(0xFF1F2530));
|
||
expect(scheme.onSurface, const Color(0xFFE6EAF2));
|
||
expect(scheme.primary, kColorInfoDark);
|
||
expect(scheme.onPrimary, const Color(0xFF0B2E6F));
|
||
expect(theme.scaffoldBackgroundColor, const Color(0xFF12161D));
|
||
});
|
||
|
||
test('等待/错误为深色提亮变体', () {
|
||
expect(scheme.secondary, kColorWaitingDark);
|
||
expect(scheme.error, kColorErrorDark);
|
||
});
|
||
});
|
||
|
||
group('字阶(随系统缩放:fontSize 走 TextScaler)', () {
|
||
final textTheme = AppTheme.light.textTheme;
|
||
|
||
test('24 页面标题 / 17 分组 / 16-14 正文 / 13-12 辅助', () {
|
||
expect(textTheme.titleLarge!.fontSize, 24);
|
||
expect(textTheme.titleMedium!.fontSize, 17);
|
||
expect(textTheme.bodyLarge!.fontSize, 16);
|
||
expect(textTheme.bodyMedium!.fontSize, 14);
|
||
expect(textTheme.bodySmall!.fontSize, 13);
|
||
expect(textTheme.labelSmall!.fontSize, 12);
|
||
});
|
||
|
||
test('AppBar 标题用 titleLarge', () {
|
||
expect(AppTheme.light.appBarTheme.titleTextStyle!.fontSize, 24);
|
||
});
|
||
});
|
||
|
||
group('组件主题', () {
|
||
final theme = AppTheme.light;
|
||
|
||
test('按钮高 48、圆角 12', () {
|
||
final filled = theme.filledButtonTheme.style;
|
||
expect(
|
||
filled!.minimumSize!.resolve(const <WidgetState>{}),
|
||
const Size(64, 48),
|
||
);
|
||
expect(
|
||
theme.elevatedButtonTheme.style!.minimumSize!.resolve(
|
||
const <WidgetState>{},
|
||
),
|
||
const Size(64, 48),
|
||
);
|
||
final shape =
|
||
filled.shape!.resolve(const <WidgetState>{})
|
||
as RoundedRectangleBorder;
|
||
expect(shape.borderRadius, BorderRadius.circular(kRadiusInput));
|
||
expect(
|
||
theme.outlinedButtonTheme.style!.minimumSize!.resolve(
|
||
const <WidgetState>{},
|
||
),
|
||
const Size(48, 48),
|
||
);
|
||
expect(
|
||
theme.textButtonTheme.style!.minimumSize!.resolve(
|
||
const <WidgetState>{},
|
||
),
|
||
const Size(48, 48),
|
||
);
|
||
});
|
||
|
||
test('输入框圆角 10(UI06: app-preview .field)', () {
|
||
final border = theme.inputDecorationTheme.border as OutlineInputBorder;
|
||
expect(border.borderRadius, BorderRadius.circular(kRadiusField));
|
||
expect(kRadiusField, 10);
|
||
});
|
||
|
||
test('Dialog 圆角 16、BottomSheet 顶部圆角 16', () {
|
||
final dialog = theme.dialogTheme.shape as RoundedRectangleBorder;
|
||
expect(dialog.borderRadius, BorderRadius.circular(kRadiusCard));
|
||
final sheet = theme.bottomSheetTheme.shape as RoundedRectangleBorder;
|
||
expect(
|
||
(sheet.borderRadius as BorderRadius).topLeft,
|
||
const Radius.circular(kRadiusCard),
|
||
);
|
||
expect(
|
||
(sheet.borderRadius as BorderRadius).topRight,
|
||
const Radius.circular(kRadiusCard),
|
||
);
|
||
expect(kRadiusCard, 16);
|
||
});
|
||
|
||
test('底部导航无胶囊指示器(UI06: app-preview .nav)', () {
|
||
expect(theme.navigationBarTheme.indicatorColor, Colors.transparent);
|
||
});
|
||
});
|
||
|
||
group('动效令牌', () {
|
||
testWidgets('默认轻量 180ms / 轮播 300ms / 减少动态效果归零', (tester) async {
|
||
expect(kMotionFast, const Duration(milliseconds: 180));
|
||
expect(kMotionCarousel, const Duration(milliseconds: 300));
|
||
expect(kMotionReduce, Duration.zero);
|
||
|
||
late BuildContext captured;
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
home: Builder(
|
||
builder: (context) {
|
||
captured = context;
|
||
return const SizedBox.shrink();
|
||
},
|
||
),
|
||
),
|
||
);
|
||
expect(motion(captured), kMotionFast);
|
||
expect(motion(captured, carousel: true), kMotionCarousel);
|
||
|
||
// 「减少动态效果」开启 → 一律归零。
|
||
await tester.pumpWidget(
|
||
MaterialApp(
|
||
home: MediaQuery(
|
||
data: const MediaQueryData(disableAnimations: true),
|
||
child: Builder(
|
||
builder: (context) {
|
||
captured = context;
|
||
return const SizedBox.shrink();
|
||
},
|
||
),
|
||
),
|
||
),
|
||
);
|
||
expect(motion(captured), kMotionReduce);
|
||
expect(motion(captured, carousel: true), kMotionReduce);
|
||
});
|
||
});
|
||
}
|