From f88dbb7fb8693c5b7bf1ee07a664e79aec1668d8 Mon Sep 17 00:00:00 2001 From: Erick Zanardo Date: Fri, 6 May 2022 20:59:20 -0300 Subject: [PATCH] feat: tests --- lib/game/components/backbox/backbox.dart | 14 +- lib/game/pinball_game.dart | 11 -- lib/game/view/pinball_game_page.dart | 25 +--- lib/game/view/widgets/mobile_controls.dart | 47 +++---- lib/l10n/arb/app_en.arb | 4 + .../lib/src/keyboard_input_controller.dart | 16 +++ .../src/keyboard_input_controller_test.dart | 36 +++++ .../src/widgets/pinball_dpad_button_test.dart | 18 +-- .../lib/src/platform_helper.dart | 5 +- .../game/components/backbox/backbox_test.dart | 75 ++++++++-- test/game/view/pinball_game_page_test.dart | 14 ++ .../view/widgets/mobile_controls_test.dart | 131 ++++++++++++++++++ test/game/view/widgets/mobile_dpad_test.dart | 111 +++++++++++++++ 13 files changed, 423 insertions(+), 84 deletions(-) create mode 100644 test/game/view/widgets/mobile_controls_test.dart create mode 100644 test/game/view/widgets/mobile_dpad_test.dart diff --git a/lib/game/components/backbox/backbox.dart b/lib/game/components/backbox/backbox.dart index 2563bf21..8590db44 100644 --- a/lib/game/components/backbox/backbox.dart +++ b/lib/game/components/backbox/backbox.dart @@ -9,6 +9,7 @@ import 'package:pinball/game/game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' hide Assets; +import 'package:platform_helper/platform_helper.dart'; /// {@template backbox} /// The [Backbox] of the pinball machine. @@ -17,16 +18,20 @@ class Backbox extends PositionComponent with ZIndex, HasGameRef { /// {@macro backbox} Backbox({ required LeaderboardRepository leaderboardRepository, - }) : _bloc = BackboxBloc(leaderboardRepository: leaderboardRepository); + }) : _bloc = BackboxBloc(leaderboardRepository: leaderboardRepository), + _platformHelper = PlatformHelper(); /// {@macro backbox} @visibleForTesting Backbox.test({ required BackboxBloc bloc, - }) : _bloc = bloc; + required PlatformHelper platformHelper, + }) : _bloc = bloc, + _platformHelper = platformHelper; late final Component _display; final BackboxBloc _bloc; + final PlatformHelper _platformHelper; late StreamSubscription _subscription; @override @@ -59,8 +64,9 @@ class Backbox extends PositionComponent with ZIndex, HasGameRef { } else if (state is LeaderboardSuccessState) { _display.add(LeaderboardDisplay(entries: state.entries)); } else if (state is InitialsFormState) { - // TODO check - gameRef.overlays.add(PinballGame.mobileControlsOverlay); + if (_platformHelper.isMobile) { + gameRef.overlays.add(PinballGame.mobileControlsOverlay); + } _display.add( InitialsInputDisplay( score: state.score, diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 8da327e8..c37c02af 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -8,7 +8,6 @@ import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/game/behaviors/behaviors.dart'; import 'package:pinball/game/game.dart'; @@ -117,16 +116,6 @@ class PinballGame extends PinballForge2DGame final focusedBoardSide = {}; - void triggerVirtualKeyUp(LogicalKeyboardKey key) { - final keyControllers = descendants().whereType(); - - for (final controller in keyControllers) { - if (!controller.onVirtualKeyUp(key)) { - break; - } - } - } - @override void onTapDown(int pointerId, TapDownInfo info) { if (info.raw.kind == PointerDeviceKind.touch) { diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index 898c5d38..58e2702b 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -12,6 +12,7 @@ import 'package:pinball/l10n/l10n.dart'; import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_ui/pinball_ui.dart'; class PinballGamePage extends StatelessWidget { @@ -143,29 +144,7 @@ class PinballGameLoadedView extends StatelessWidget { bottom: 0, left: 0, right: 0, - child: MobileControls( - onTapUp: () { - game.triggerVirtualKeyUp(LogicalKeyboardKey.arrowUp); - }, - onTapDown: () { - game.triggerVirtualKeyUp( - LogicalKeyboardKey.arrowDown, - ); - }, - onTapLeft: () { - game.triggerVirtualKeyUp( - LogicalKeyboardKey.arrowLeft, - ); - }, - onTapRight: () { - game.triggerVirtualKeyUp( - LogicalKeyboardKey.arrowRight, - ); - }, - onTapEnter: () { - game.triggerVirtualKeyUp(LogicalKeyboardKey.enter); - }, - ), + child: MobileControls(game: game), ); }, }, diff --git a/lib/game/view/widgets/mobile_controls.dart b/lib/game/view/widgets/mobile_controls.dart index b69f4c85..c5761eb6 100644 --- a/lib/game/view/widgets/mobile_controls.dart +++ b/lib/game/view/widgets/mobile_controls.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:pinball/game/game.dart'; +import 'package:pinball/l10n/l10n.dart'; +import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_ui/pinball_ui.dart'; /// {@template mobile_controls} @@ -7,43 +10,35 @@ import 'package:pinball_ui/pinball_ui.dart'; /// {@endtemplate} class MobileControls extends StatelessWidget { /// {@macro mobile_controls} - const MobileControls({Key? key, - required this.onTapUp, - required this.onTapDown, - required this.onTapLeft, - required this.onTapRight, - required this.onTapEnter, + const MobileControls({ + Key? key, + required this.game, }) : super(key: key); - /// Called when dpad up is pressed - final VoidCallback onTapUp; - - /// Called when dpad down is pressed - final VoidCallback onTapDown; - - /// Called when dpad left is pressed - final VoidCallback onTapLeft; - - /// Called when dpad right is pressed - final VoidCallback onTapRight; - - /// Called when enter is pressed - final VoidCallback onTapEnter; + /// Game instance + final PinballGame game; @override Widget build(BuildContext context) { + final l10n = AppLocalizations.of(context); return Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ MobileDpad( - onTapUp: onTapUp, - onTapDown: onTapUp, - onTapLeft: onTapLeft, - onTapRight: onTapRight, + onTapUp: () => game.triggerVirtualKeyUp(LogicalKeyboardKey.arrowUp), + onTapDown: () => game.triggerVirtualKeyUp( + LogicalKeyboardKey.arrowDown, + ), + onTapLeft: () => game.triggerVirtualKeyUp( + LogicalKeyboardKey.arrowLeft, + ), + onTapRight: () => game.triggerVirtualKeyUp( + LogicalKeyboardKey.arrowRight, + ), ), PinballButton( - text: 'Enter', // TODO l10n - onTap: onTapEnter, + text: l10n.enter, + onTap: () => game.triggerVirtualKeyUp(LogicalKeyboardKey.enter), ), ], ); diff --git a/lib/l10n/arb/app_en.arb b/lib/l10n/arb/app_en.arb index 3d15d9fd..1b685f39 100644 --- a/lib/l10n/arb/app_en.arb +++ b/lib/l10n/arb/app_en.arb @@ -167,5 +167,9 @@ "ioPinball": "I/O Pinball", "@ioPinball": { "description": "I/O Pinball - Name of the game" + }, + "enter": "Enter", + "@enter": { + "description": "Enter show on the mobile controls button" } } diff --git a/packages/pinball_flame/lib/src/keyboard_input_controller.dart b/packages/pinball_flame/lib/src/keyboard_input_controller.dart index 4ce80804..e5806906 100644 --- a/packages/pinball_flame/lib/src/keyboard_input_controller.dart +++ b/packages/pinball_flame/lib/src/keyboard_input_controller.dart @@ -1,4 +1,5 @@ import 'package:flame/components.dart'; +import 'package:flame/game.dart'; import 'package:flutter/services.dart'; /// The signature for a key handle function @@ -44,3 +45,18 @@ class KeyboardInputController extends Component with KeyboardHandler { return true; } } + +/// Add the ability to virtually trigger key events to a [FlameGame]'s +/// [KeyboardInputController]. +extension VirtualKeyEvents on FlameGame { + /// Trigger a key up + void triggerVirtualKeyUp(LogicalKeyboardKey key) { + final keyControllers = descendants().whereType(); + + for (final controller in keyControllers) { + if (!controller.onVirtualKeyUp(key)) { + break; + } + } + } +} diff --git a/packages/pinball_flame/test/src/keyboard_input_controller_test.dart b/packages/pinball_flame/test/src/keyboard_input_controller_test.dart index 7b554e8c..f3c783ad 100644 --- a/packages/pinball_flame/test/src/keyboard_input_controller_test.dart +++ b/packages/pinball_flame/test/src/keyboard_input_controller_test.dart @@ -1,11 +1,36 @@ // ignore_for_file: cascade_invocations, one_member_abstracts +import 'package:flame/game.dart'; +import 'package:flame_test/flame_test.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball_flame/pinball_flame.dart'; +class _TestGame extends FlameGame { + bool pressed = false; + + @override + Future? onLoad() async { + await super.onLoad(); + + await add( + KeyboardInputController( + keyUp: { + LogicalKeyboardKey.enter: () { + pressed = true; + return true; + }, + LogicalKeyboardKey.escape: () { + return false; + }, + }, + ), + ); + } +} + abstract class _KeyCall { bool onCall(); } @@ -75,4 +100,15 @@ void main() { }, ); }); + group('VirtualKeyEvents', () { + final flameTester = FlameTester(_TestGame.new); + + group('onVirtualKeyUp', () { + flameTester.test('triggers the event', (game) async { + await game.ready(); + game.triggerVirtualKeyUp(LogicalKeyboardKey.enter); + expect(game.pressed, isTrue); + }); + }); + }); } diff --git a/packages/pinball_ui/test/src/widgets/pinball_dpad_button_test.dart b/packages/pinball_ui/test/src/widgets/pinball_dpad_button_test.dart index 56e72203..4be1f671 100644 --- a/packages/pinball_ui/test/src/widgets/pinball_dpad_button_test.dart +++ b/packages/pinball_ui/test/src/widgets/pinball_dpad_button_test.dart @@ -49,17 +49,17 @@ class _VoidCallbackStub extends Mock implements _VoidCallbackStubBase {} void main() { group('PinballDpadButton', () { - testWidgets('can be tapped', (tester) async { - final stub = _VoidCallbackStub(); - await tester.pumpButton( - direction: PinballDpadDirection.up, - onTap: stub.onCall, - ); + testWidgets('can be tapped', (tester) async { + final stub = _VoidCallbackStub(); + await tester.pumpButton( + direction: PinballDpadDirection.up, + onTap: stub.onCall, + ); - await tester.tap(find.byType(Image)); + await tester.tap(find.byType(Image)); - verify(stub.onCall).called(1); - }); + verify(stub.onCall).called(1); + }); group('when it is up', () { testWidgets('renders the correct image', (tester) async { diff --git a/packages/platform_helper/lib/src/platform_helper.dart b/packages/platform_helper/lib/src/platform_helper.dart index 53cf4491..638d1ab6 100644 --- a/packages/platform_helper/lib/src/platform_helper.dart +++ b/packages/platform_helper/lib/src/platform_helper.dart @@ -6,8 +6,7 @@ import 'package:flutter/foundation.dart'; class PlatformHelper { /// {@macro platform_helper} bool get isMobile { - return true; - //return defaultTargetPlatform == TargetPlatform.iOS || - // defaultTargetPlatform == TargetPlatform.android; + return defaultTargetPlatform == TargetPlatform.iOS || + defaultTargetPlatform == TargetPlatform.android; } } diff --git a/test/game/components/backbox/backbox_test.dart b/test/game/components/backbox/backbox_test.dart index d61bd83a..7a15bf25 100644 --- a/test/game/components/backbox/backbox_test.dart +++ b/test/game/components/backbox/backbox_test.dart @@ -19,6 +19,7 @@ import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' as theme; +import 'package:platform_helper/platform_helper.dart'; class _TestGame extends Forge2DGame with HasKeyboardHandlerComponents { final character = theme.DashTheme(); @@ -64,6 +65,8 @@ RawKeyUpEvent _mockKeyUp(LogicalKeyboardKey key) { return event; } +class _MockPlatformHelper extends Mock implements PlatformHelper {} + class _MockBackboxBloc extends Mock implements BackboxBloc {} class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { @@ -104,21 +107,27 @@ void main() { final flameTester = FlameTester(_TestGame.new); late BackboxBloc bloc; + late PlatformHelper platformHelper; setUp(() { bloc = _MockBackboxBloc(); + platformHelper = _MockPlatformHelper(); whenListen( bloc, Stream.empty(), initialState: LoadingState(), ); + when(() => platformHelper.isMobile).thenReturn(false); }); group('Backbox', () { flameTester.test( 'loads correctly', (game) async { - final backbox = Backbox.test(bloc: bloc); + final backbox = Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ); await game.pump(backbox); expect(game.descendants(), contains(backbox)); }, @@ -127,7 +136,10 @@ void main() { flameTester.test( 'adds LeaderboardRequested when loaded', (game) async { - final backbox = Backbox.test(bloc: bloc); + final backbox = Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ); await game.pump(backbox); verify(() => bloc.add(LeaderboardRequested())).called(1); @@ -142,7 +154,10 @@ void main() { ..followVector2(Vector2(0, -130)) ..zoom = 6; await game.pump( - Backbox.test(bloc: bloc), + Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ), ); await tester.pump(); }, @@ -161,6 +176,7 @@ void main() { bloc: BackboxBloc( leaderboardRepository: _MockLeaderboardRepository(), ), + platformHelper: platformHelper, ); await game.pump(backbox); backbox.requestInitials( @@ -189,7 +205,10 @@ void main() { Stream.empty(), initialState: state, ); - final backbox = Backbox.test(bloc: bloc); + final backbox = Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ); await game.pump(backbox); game.onKeyEvent(_mockKeyUp(LogicalKeyboardKey.enter), {}); @@ -205,6 +224,34 @@ void main() { }, ); + flameTester.test( + 'adds the mobile controls overlay when it is mobile', + (game) async { + final bloc = _MockBackboxBloc(); + final platformHelper = _MockPlatformHelper(); + final state = InitialsFormState( + score: 10, + character: game.character, + ); + whenListen( + bloc, + Stream.empty(), + initialState: state, + ); + when(() => platformHelper.isMobile).thenReturn(true); + final backbox = Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ); + await game.pump(backbox); + + expect( + game.overlays.value, + contains(PinballGame.mobileControlsOverlay), + ); + }, + ); + flameTester.test( 'adds InitialsSubmissionSuccessDisplay on InitialsSuccessState', (game) async { @@ -213,7 +260,10 @@ void main() { Stream.empty(), initialState: InitialsSuccessState(), ); - final backbox = Backbox.test(bloc: bloc); + final backbox = Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ); await game.pump(backbox); expect( @@ -234,7 +284,10 @@ void main() { Stream.empty(), initialState: InitialsFailureState(), ); - final backbox = Backbox.test(bloc: bloc); + final backbox = Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ); await game.pump(backbox); expect( @@ -256,7 +309,10 @@ void main() { initialState: LeaderboardSuccessState(entries: const []), ); - final backbox = Backbox.test(bloc: bloc); + final backbox = Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ); await game.pump(backbox); expect( @@ -276,7 +332,10 @@ void main() { initialState: LoadingState(), ); - final backbox = Backbox.test(bloc: bloc); + final backbox = Backbox.test( + bloc: bloc, + platformHelper: platformHelper, + ); await game.pump(backbox); backbox.removeFromParent(); diff --git a/test/game/view/pinball_game_page_test.dart b/test/game/view/pinball_game_page_test.dart index d1ecd72d..77ff832c 100644 --- a/test/game/view/pinball_game_page_test.dart +++ b/test/game/view/pinball_game_page_test.dart @@ -331,5 +331,19 @@ void main() { expect(game.focusNode.hasFocus, isTrue); }); + + testWidgets('mobile controls when the overlay is added', (tester) async { + await tester.pumpApp( + PinballGameView(game: game), + gameBloc: gameBloc, + startGameBloc: startGameBloc, + ); + + game.overlays.add(PinballGame.mobileControlsOverlay); + + await tester.pump(); + + expect(find.byType(MobileControls), findsOneWidget); + }); }); } diff --git a/test/game/view/widgets/mobile_controls_test.dart b/test/game/view/widgets/mobile_controls_test.dart new file mode 100644 index 00000000..ab9c0b76 --- /dev/null +++ b/test/game/view/widgets/mobile_controls_test.dart @@ -0,0 +1,131 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball/l10n/l10n.dart'; +import 'package:pinball_flame/pinball_flame.dart'; +import 'package:pinball_ui/pinball_ui.dart'; + +class _MockPinballGame extends Mock implements PinballGame {} + +extension _WidgetTesterX on WidgetTester { + Future pumpMobileControls(PinballGame game) async { + await pumpWidget( + MaterialApp( + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + ], + home: Scaffold( + body: MobileControls(game: game), + ), + ), + ); + } +} + +extension _CommonFindersX on CommonFinders { + Finder byPinballDpadDirection(PinballDpadDirection direction) { + return byWidgetPredicate((widget) { + return widget is PinballDpadButton && widget.direction == direction; + }); + } +} + +void main() { + group('MobileControls', () { + testWidgets('renders', (tester) async { + await tester.pumpMobileControls(_MockPinballGame()); + + expect(find.byType(PinballButton), findsOneWidget); + expect(find.byType(MobileDpad), findsOneWidget); + }); + + testWidgets('correctly triggers the arrow up', (tester) async { + var pressed = false; + final component = KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowUp: () => pressed = true, + }, + ); + final game = _MockPinballGame(); + when(game.descendants).thenReturn([component]); + + await tester.pumpMobileControls(game); + await tester.tap(find.byPinballDpadDirection(PinballDpadDirection.up)); + await tester.pump(); + + expect(pressed, isTrue); + }); + + testWidgets('correctly triggers the arrow down', (tester) async { + var pressed = false; + final component = KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowDown: () => pressed = true, + }, + ); + final game = _MockPinballGame(); + when(game.descendants).thenReturn([component]); + + await tester.pumpMobileControls(game); + await tester.tap(find.byPinballDpadDirection(PinballDpadDirection.down)); + await tester.pump(); + + expect(pressed, isTrue); + }); + + testWidgets('correctly triggers the arrow right', (tester) async { + var pressed = false; + final component = KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowRight: () => pressed = true, + }, + ); + final game = _MockPinballGame(); + when(game.descendants).thenReturn([component]); + + await tester.pumpMobileControls(game); + await tester.tap(find.byPinballDpadDirection(PinballDpadDirection.right)); + await tester.pump(); + + expect(pressed, isTrue); + }); + + testWidgets('correctly triggers the arrow left', (tester) async { + var pressed = false; + final component = KeyboardInputController( + keyUp: { + LogicalKeyboardKey.arrowLeft: () => pressed = true, + }, + ); + final game = _MockPinballGame(); + when(game.descendants).thenReturn([component]); + + await tester.pumpMobileControls(game); + await tester.tap(find.byPinballDpadDirection(PinballDpadDirection.left)); + await tester.pump(); + + expect(pressed, isTrue); + }); + + testWidgets('correctly triggers the enter', (tester) async { + var pressed = false; + final component = KeyboardInputController( + keyUp: { + LogicalKeyboardKey.enter: () => pressed = true, + }, + ); + final game = _MockPinballGame(); + when(game.descendants).thenReturn([component]); + + await tester.pumpMobileControls(game); + await tester.tap(find.byType(PinballButton)); + await tester.pump(); + + expect(pressed, isTrue); + }); + }); +} diff --git a/test/game/view/widgets/mobile_dpad_test.dart b/test/game/view/widgets/mobile_dpad_test.dart new file mode 100644 index 00000000..4aaa23ca --- /dev/null +++ b/test/game/view/widgets/mobile_dpad_test.dart @@ -0,0 +1,111 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball_ui/pinball_ui.dart'; + +extension _WidgetTesterX on WidgetTester { + Future pumpDpad({ + required VoidCallback onTapUp, + required VoidCallback onTapDown, + required VoidCallback onTapLeft, + required VoidCallback onTapRight, + }) async { + await pumpWidget( + MaterialApp( + home: Scaffold( + body: MobileDpad( + onTapUp: onTapUp, + onTapDown: onTapDown, + onTapLeft: onTapLeft, + onTapRight: onTapRight, + ), + ), + ), + ); + } +} + +extension _CommonFindersX on CommonFinders { + Finder byPinballDpadDirection(PinballDpadDirection direction) { + return byWidgetPredicate((widget) { + return widget is PinballDpadButton && widget.direction == direction; + }); + } +} + +abstract class _VoidCallbackStubBase { + void onCall(); +} + +class _VoidCallbackStub extends Mock implements _VoidCallbackStubBase {} + +void main() { + group('MobileDpad', () { + testWidgets('renders correctly', (tester) async { + await tester.pumpDpad( + onTapUp: () {}, + onTapDown: () {}, + onTapLeft: () {}, + onTapRight: () {}, + ); + + expect( + find.byType(PinballDpadButton), + findsNWidgets(4), + ); + }); + + testWidgets('can tap up', (tester) async { + final stub = _VoidCallbackStub(); + await tester.pumpDpad( + onTapUp: stub.onCall, + onTapDown: () {}, + onTapLeft: () {}, + onTapRight: () {}, + ); + + await tester.tap(find.byPinballDpadDirection(PinballDpadDirection.up)); + verify(stub.onCall).called(1); + }); + + testWidgets('can tap down', (tester) async { + final stub = _VoidCallbackStub(); + await tester.pumpDpad( + onTapUp: () {}, + onTapDown: stub.onCall, + onTapLeft: () {}, + onTapRight: () {}, + ); + + await tester.tap(find.byPinballDpadDirection(PinballDpadDirection.down)); + verify(stub.onCall).called(1); + }); + + testWidgets('can tap left', (tester) async { + final stub = _VoidCallbackStub(); + await tester.pumpDpad( + onTapUp: () {}, + onTapDown: () {}, + onTapLeft: stub.onCall, + onTapRight: () {}, + ); + + await tester.tap(find.byPinballDpadDirection(PinballDpadDirection.left)); + verify(stub.onCall).called(1); + }); + + testWidgets('can tap left', (tester) async { + final stub = _VoidCallbackStub(); + await tester.pumpDpad( + onTapUp: () {}, + onTapDown: () {}, + onTapLeft: () {}, + onTapRight: stub.onCall, + ); + + await tester.tap(find.byPinballDpadDirection(PinballDpadDirection.right)); + verify(stub.onCall).called(1); + }); + }); +}