import 'package:flutter/gestures.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:lucide_icons_flutter/lucide_icons.dart'; import 'package:material_ui/material_ui.dart'; void main() { late List tapped; setUp(() => tapped = []); /// Shows a tile beside something else to move the pointer or focus to. Future show(WidgetTester tester, {bool? expanded}) async { await tester.pumpWidget( MaterialApp( home: Scaffold( body: Column( children: [ TextButton(onPressed: () {}, child: const Text('Elsewhere')), InstanceTile( icon: const Icon(LucideIcons.globe), label: 'learn.halkyon.eu', tagline: 'Comes with the app', expanded: expanded, onTap: () => tapped.add('tile'), ), ], ), ), ), ); } double ring(WidgetTester tester) => tester.widget(find.byKey(InstanceTile.ringKey)).opacity; double tagline(WidgetTester tester) => tester .widget( find.ancestor( of: find.text('Comes with the app'), matching: find.byType(Opacity), ), ) .opacity; testWidgets('without a mouse, the tagline shows from the start', ( tester, ) async { await show(tester); expect(tagline(tester), 1); expect(ring(tester), 0); }); testWidgets('with a mouse, the ring and the tagline come in under the ' 'pointer and go once it leaves', (tester) async { await show(tester); final mouse = await tester.createGesture(kind: PointerDeviceKind.mouse); addTearDown(mouse.removePointer); await mouse.addPointer(location: tester.getCenter(find.text('Elsewhere'))); await tester.pumpAndSettle(); expect(tagline(tester), 0); expect(ring(tester), 0); await mouse.moveTo(tester.getCenter(find.byType(Icon))); // The frame that starts it, then halfway. await tester.pump(); await tester.pump(InstanceTile.duration ~/ 2); expect(ring(tester), inExclusiveRange(0, 1), reason: 'it fades in'); await tester.pumpAndSettle(); expect(tagline(tester), 1); expect(ring(tester), 1); await mouse.moveTo(tester.getCenter(find.text('Elsewhere'))); await tester.pumpAndSettle(); expect(tagline(tester), 0); expect(ring(tester), 0); }); testWidgets("the keyboard's focus rings it, and Enter chooses it", ( tester, ) async { await show(tester); // From Elsewhere to the tile. await tester.sendKeyEvent(LogicalKeyboardKey.tab); await tester.sendKeyEvent(LogicalKeyboardKey.tab); await tester.pumpAndSettle(); expect(ring(tester), 1); await tester.sendKeyEvent(LogicalKeyboardKey.enter); expect(tapped, ['tile']); }); testWidgets('it rings while pressed', (tester) async { await show(tester); final finger = await tester.startGesture( tester.getCenter(find.byType(Icon)), ); await tester.pumpAndSettle(); expect(ring(tester), 1); await finger.up(); await tester.pumpAndSettle(); expect(ring(tester), 0); expect(tapped, ['tile']); }); testWidgets('it stays ringed while what it opens is open', (tester) async { await show(tester, expanded: true); expect(ring(tester), 1); await show(tester, expanded: false); await tester.pumpAndSettle(); expect(ring(tester), 0); }); testWidgets('a screen reader hears one button, its tagline included', ( tester, ) async { await show(tester, expanded: false); expect( // The label's node is merged into the tile's. tester.getSemantics(find.text('learn.halkyon.eu')), matchesSemantics( label: 'learn.halkyon.eu\nComes with the app', isButton: true, hasTapAction: true, hasFocusAction: true, isFocusable: true, hasExpandedState: true, ), ); }); testWidgets('with animations off, it rings at once', (tester) async { await tester.pumpWidget( MaterialApp( // The app's own MediaQuery would hide one above it. builder: (context, child) => MediaQuery( data: MediaQuery.of(context).copyWith(disableAnimations: true), child: child!, ), home: Scaffold( body: InstanceTile( icon: const Icon(LucideIcons.globe), label: 'learn.halkyon.eu', tagline: 'Comes with the app', expanded: false, onTap: () {}, ), ), ), ); await tester.startGesture(tester.getCenter(find.byType(Icon))); await tester.pump(); expect(ring(tester), 1); }); }