import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:health_app/core/app_colors.dart'; import 'package:health_app/core/app_design_tokens.dart'; import 'package:health_app/core/app_theme.dart'; import 'package:health_app/widgets/common_widgets.dart'; import 'package:health_app/widgets/app_empty_state.dart'; void main() { testWidgets('secondary page separates neutral canvas from white app bar', ( tester, ) async { await tester.pumpWidget( MaterialApp( theme: AppTheme.lightTheme, home: GradientScaffold( appBar: AppBar(title: const Text('用药管理')), body: const SizedBox.expand(), ), ), ); final scaffold = tester.widget(find.byType(Scaffold)); expect(scaffold.backgroundColor, AppColors.background); expect(AppTheme.lightTheme.appBarTheme.backgroundColor, Colors.white); }); testWidgets('create fab uses one white gradient-outline visual', ( tester, ) async { await tester.pumpWidget( MaterialApp( home: Scaffold( floatingActionButton: AppCreateFab(tooltip: '添加用药', onPressed: () {}), ), ), ); final border = tester.widget( find.byKey(const ValueKey('app-create-fab-border')), ); final decoration = border.decoration as BoxDecoration; expect(decoration.gradient, AppColors.actionOutlineGradient); expect(decoration.borderRadius, AppRadius.pillBorder); expect(find.byIcon(Icons.add_rounded), findsOneWidget); }); testWidgets('empty state uses a flat restrained icon surface', ( tester, ) async { await tester.pumpWidget( const MaterialApp( home: Scaffold( body: AppEmptyState( icon: Icons.event_available_outlined, title: '今日没有安排', iconColor: AppColors.success, ), ), ), ); final surface = tester.widget( find.byKey(const ValueKey('empty-state-icon-surface')), ); final decoration = surface.decoration as BoxDecoration; expect(decoration.gradient, isNull); expect(decoration.boxShadow, isNull); }); test('health metrics and notification preferences use lucide icons', () { final trend = File('lib/pages/chart/trend_page.dart').readAsStringSync(); final notifications = File( 'lib/pages/settings/notification_prefs_page.dart', ).readAsStringSync(); expect(trend, contains('LucideIcons.gauge')); expect(trend, contains('LucideIcons.heartPulse')); expect(notifications, contains('AppModuleVisuals.medication.icon')); expect(notifications, contains('LucideIcons.slidersHorizontal')); }); test( 'login and registration keep the image background with neutral forms', () { final login = File( 'lib/pages/auth/login_page.dart', ).readAsStringSync().replaceAll('\r\n', '\n'); expect(login, contains("Image.asset(\n _loginBg")); expect(login, contains('Colors.white.withValues(alpha: 0.88)')); expect(login, contains('fillColor: AppColors.cardInner')); expect(login, contains('showDragHandle: true')); }, ); test('today health special states do not use a filled warning row', () { final homeCard = File( 'lib/pages/home/widgets/chat_messages_view.dart', ).readAsStringSync(); expect(homeCard, contains("'overdue': LucideIcons.circleAlert")); expect(homeCard, contains('color: Colors.transparent')); expect(homeCard, isNot(contains('isOverdue ? const Color(0xFFFFEBEE)'))); expect(homeCard, contains('EdgeInsets.fromLTRB(14, 5, 14, 7)')); expect(homeCard, contains('EdgeInsets.symmetric(vertical: 1)')); }); test('active create and report states follow the shared visual system', () { final remaining = File('lib/pages/remaining_pages.dart').readAsStringSync(); final reports = File( 'lib/pages/report/report_pages.dart', ).readAsStringSync(); expect(remaining, contains("AppGradientOutlineButton(label: '保存计划'")); expect(reports, contains('icon: LucideIcons.camera')); expect(reports, contains('icon: LucideIcons.images')); final pdfState = reports.substring( reports.indexOf('Widget _buildPdf'), reports.indexOf('String _absoluteUrl'), ); expect(pdfState, contains('return AppEmptyState(')); expect(pdfState, contains('icon: AppModuleVisuals.report.icon')); }); test('secondary module colors do not keep the old mixed palettes', () { final diet = File( 'lib/pages/diet/diet_nutrition_widgets.dart', ).readAsStringSync(); final remaining = File('lib/pages/remaining_pages.dart').readAsStringSync(); final reports = File( 'lib/pages/report/report_pages.dart', ).readAsStringSync(); final medication = File( 'lib/pages/medication/medication_list_page.dart', ).readAsStringSync(); final checkin = File( 'lib/pages/medication/medication_checkin_page.dart', ).readAsStringSync(); final exercise = File( 'lib/pages/exercise/exercise_plan_page.dart', ).readAsStringSync(); expect(diet, contains('static const primary = Color(0xFF6476E8)')); expect(remaining, isNot(contains('AppModuleVisuals.diet.color'))); expect(reports, contains('const _reportPageSoft = Color(0xFFF0F0FF)')); expect(reports, isNot(contains('Color(0xFF60A5FA)'))); expect(medication, contains('AppColors.blueMeasure')); expect(checkin, contains('AppColors.blueMeasure')); expect(exercise, contains('AppColors.blueMeasure')); }); test('trend colors and status text colors stay visually distinct', () { final colors = File('lib/core/app_colors.dart').readAsStringSync(); final trend = File('lib/pages/chart/trend_page.dart').readAsStringSync(); expect(trend, contains('class _TrendColors')); expect(trend, contains("'color': _TrendColors.bloodPressure")); expect(trend, contains("'color': _TrendColors.heartRate")); expect(trend, isNot(contains("'color': Color(0xFFEF4444)"))); expect(trend, isNot(contains("'color': Color(0xFFF59E0B)"))); expect(colors, contains('successText = Color(0xFF15803D)')); expect(colors, contains('errorText = Color(0xFFDC2626)')); expect(colors, contains('warningText = Color(0xFFB45309)')); }); test('exercise rows use the shared leading icon list pattern', () { final exercise = File( 'lib/pages/exercise/exercise_plan_page.dart', ).readAsStringSync(); final row = exercise.substring( exercise.indexOf('class _ExercisePlanRow'), exercise.indexOf('class _CompactActionButton'), ); expect(row, contains('AppModuleVisuals.exercise.icon')); expect(row, contains('color: AppColors.exerciseLight')); expect(row, contains('left: 68')); expect(row, isNot(contains('width: 16'))); final modules = File('lib/core/app_module_visuals.dart').readAsStringSync(); expect(modules, contains('icon: Icons.directions_run_outlined')); }); test( 'report utility action and notification rows stay neutral and compact', () { final analysis = File( 'lib/pages/report/ai_analysis_page.dart', ).readAsStringSync(); final notifications = File( 'lib/pages/notifications/notification_center_page.dart', ).readAsStringSync(); final originalReportAction = analysis.substring( analysis.indexOf("label: const Text('查看原始报告')"), analysis.indexOf("label: const Text('查看原始报告')") + 500, ); expect( originalReportAction, contains('foregroundColor: AppColors.textPrimary'), ); expect(originalReportAction, contains('color: AppColors.border')); expect(notifications, contains('BoxConstraints(minHeight: 82)')); expect(notifications, contains('width: 68')); expect(notifications, contains("const TextSpan(text: ' 条未读消息')")); expect( notifications, isNot(contains('color: AppColors.notificationLight')), ); }, ); }