c94b9e5e58
Co-Authored-By: Claude Code <noreply@anthropic.com>
175 lines
5.9 KiB
Dart
175 lines
5.9 KiB
Dart
/// UI-01 主题一致性测试(Task 3)。
|
||
///
|
||
/// 覆盖:浅色 ColorScheme 方案 §3.1 精确值、深色语义对应值、字阶
|
||
/// (22/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(0xFF5E6878));
|
||
});
|
||
});
|
||
|
||
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('22 页面标题 / 17 分组 / 16-14 正文 / 13-12 辅助', () {
|
||
expect(textTheme.titleLarge!.fontSize, 22);
|
||
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, 22);
|
||
});
|
||
});
|
||
|
||
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('输入框圆角 12', () {
|
||
final border = theme.inputDecorationTheme.border as OutlineInputBorder;
|
||
expect(border.borderRadius, BorderRadius.circular(kRadiusInput));
|
||
expect(kRadiusInput, 12);
|
||
});
|
||
|
||
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('底部导航指示器色已配置', () {
|
||
expect(
|
||
theme.navigationBarTheme.indicatorColor,
|
||
theme.colorScheme.secondaryContainer,
|
||
);
|
||
});
|
||
});
|
||
|
||
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);
|
||
});
|
||
});
|
||
}
|