import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:timezone/data/latest.dart' as tzdata; import 'package:hepan_everyday/widgets/glass_dock.dart'; import 'package:hepan_everyday/widgets/glass_refraction.dart'; import 'package:hepan_everyday/app/hepan_app.dart'; import 'navigation_test.dart' show uiController; void main() { setUpAll(tzdata.initializeTimeZones); testWidgets('inactive icon press leaves the cursor at its resting size', ( tester, ) async { await tester.pumpWidget(_dock()); await tester.pumpAndSettle(); final lens = find.byType(GlassRefraction).last; final resting = tester.getSize(lens); final gesture = await tester.startGesture( tester.getCenter(find.byIcon(Icons.assignment_rounded)), ); await tester.pump(const Duration(milliseconds: 110)); await tester.pump(const Duration(milliseconds: 200)); expect(tester.getSize(lens), resting); await gesture.cancel(); await tester.pumpAndSettle(); }); testWidgets('tap cursor travels monotonically with no size jitter', ( tester, ) async { await tester.pumpWidget(_dock()); await tester.pumpAndSettle(); final lens = find.byType(GlassRefraction).last; final resting = tester.getRect(lens); final target = tester.getCenter(find.byIcon(Icons.assignment_rounded)).dx; await tester.tap(find.byIcon(Icons.assignment_rounded)); await tester.pump(); var previous = resting.center.dx; for (var frame = 0; frame < 50; frame++) { await tester.pump(const Duration(milliseconds: 16)); final rect = tester.getRect(lens); expect(rect.width, closeTo(resting.width, 0.05)); expect(rect.height, closeTo(resting.height, 0.05)); expect(rect.center.dx, greaterThanOrEqualTo(previous - 0.05)); expect(rect.center.dx, lessThanOrEqualTo(target + 0.05)); previous = rect.center.dx; } expect(previous, closeTo(target, 0.05)); }); testWidgets('base and selected cursor both refract the live backdrop', ( tester, ) async { await tester.pumpWidget(_dock()); await tester.pumpAndSettle(); final glass = find.byType(GlassRefraction); expect(glass, findsNWidgets(2)); expect(tester.getSize(glass.first), const Size(264, 58)); expect( tester.widget(glass.first).strength, greaterThan(0), ); }); testWidgets('reduced motion snaps the cursor without oscillation', ( tester, ) async { await tester.pumpWidget(_dock(reduceMotion: true)); await tester.pumpAndSettle(); final lens = find.byType(GlassRefraction).last; final resting = tester.getSize(lens); await tester.tap(find.byIcon(Icons.person_rounded)); await tester.pump(); final target = tester.getCenter(find.byIcon(Icons.person_rounded)).dx; expect(tester.getCenter(lens).dx, closeTo(target, 0.05)); await tester.pump(const Duration(milliseconds: 200)); expect(tester.getCenter(lens).dx, closeTo(target, 0.05)); expect(tester.getSize(lens), resting); }); testWidgets('held cursor enlarges before dragging and returns on release', ( tester, ) async { await tester.pumpWidget( MaterialApp( home: Scaffold( body: Center(child: GlassDock(selectedIndex: 0, onSelected: (_) {})), ), ), ); await tester.pumpAndSettle(); final lens = find.byType(GlassRefraction).last; final resting = tester.getSize(lens); final gesture = await tester.startGesture( tester.getCenter(find.byKey(const Key('dock-cursor'))), ); await tester.pump(const Duration(milliseconds: 110)); await tester.pump(const Duration(milliseconds: 200)); final held = tester.getSize(lens); expect(held.width, greaterThan(resting.width * 1.25)); expect(held.height, greaterThan(resting.height * 1.25)); await gesture.cancel(); await tester.pumpAndSettle(); expect(tester.getSize(lens).width, closeTo(resting.width, 0.5)); expect(tester.getSize(lens).height, closeTo(resting.height, 0.5)); }); testWidgets('Dock stays stationary and tappable while scrolling timetable', ( tester, ) async { tester.view.physicalSize = const Size(390, 844); tester.view.devicePixelRatio = 1; addTearDown(tester.view.resetPhysicalSize); addTearDown(tester.view.resetDevicePixelRatio); final c = uiController(); await c.initialize(); await c.showDemo(); await tester.pumpWidget(HepanApp(controller: c)); await tester.pumpAndSettle(); final dock = find.byType(GlassDock); final before = tester.getRect(dock); final gesture = await tester.startGesture( tester.getCenter(find.text('周三')), ); await gesture.moveBy(const Offset(0, -80)); await tester.pump(); await tester.pump(const Duration(milliseconds: 120)); final fading = find.ancestor( of: dock, matching: find.byType(FadeTransition), ); final opacity = fading.evaluate().isEmpty ? 1.0 : tester.widget(fading.first).opacity.value; expect(opacity, greaterThan(0.95)); expect(tester.getRect(dock), before); expect(tester.getRect(dock).top, lessThan(844)); await gesture.up(); await tester.pumpAndSettle(); expect(dock.hitTestable(), findsOneWidget); c.dispose(); }); } Widget _dock({bool reduceMotion = false}) { var selected = 0; return MaterialApp( home: MediaQuery( data: MediaQueryData(disableAnimations: reduceMotion), child: StatefulBuilder( builder: (context, setState) => Scaffold( body: Center( child: GlassDock( selectedIndex: selected, onSelected: (index) => setState(() => selected = index), ), ), ), ), ), ); }