diff --git a/lib/start_game/widgets/start_game_listener.dart b/lib/start_game/widgets/start_game_listener.dart new file mode 100644 index 00000000..466c3c39 --- /dev/null +++ b/lib/start_game/widgets/start_game_listener.dart @@ -0,0 +1,85 @@ +// ignore_for_file: public_member_api_docs + +import 'package:flutter/material.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball/select_character/select_character.dart'; +import 'package:pinball/start_game/start_game.dart'; +import 'package:pinball/theme/theme.dart'; + +class StartGameListener extends StatelessWidget { + const StartGameListener({ + Key? key, + required Widget child, + required PinballGame game, + }) : _child = child, + _game = game, + super(key: key); + + final Widget _child; + final PinballGame _game; + + @override + Widget build(BuildContext context) { + return BlocListener( + listener: (context, state) { + switch (state.status) { + case StartGameStatus.selectCharacter: + _onSelectCharacter(context); + break; + case StartGameStatus.howToPlay: + _handleHowToPlay(context); + break; + case StartGameStatus.play: + _game.gameFlowController.start(); + break; + case StartGameStatus.initial: + break; + } + }, + child: _child, + ); + } + + void _onSelectCharacter( + BuildContext context, + ) { + showDialog( + context: context, + builder: (_) { + // TODO(arturplaczek): remove that when PR with PinballLayout will be + // merged + final height = MediaQuery.of(context).size.height * 0.5; + + return Center( + child: SizedBox( + height: height, + width: height * 1.2, + child: const CharacterSelectionDialog(), + ), + ); + }, + barrierDismissible: false, + ); + } +} + +Future _handleHowToPlay( + BuildContext context, +) async { + final startGameBloc = context.read(); + + await showDialog( + context: context, + barrierColor: AppColors.transparent, + builder: (_) { + return Center( + child: HowToPlayDialog( + onDismissCallback: () { + startGameBloc.add(const HowToPlayFinished()); + }, + ), + ); + }, + ); +} diff --git a/test/start_game/widgets/start_game_listener_test.dart b/test/start_game/widgets/start_game_listener_test.dart new file mode 100644 index 00000000..e35cef78 --- /dev/null +++ b/test/start_game/widgets/start_game_listener_test.dart @@ -0,0 +1,178 @@ +import 'package:bloc_test/bloc_test.dart'; +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/select_character/select_character.dart'; +import 'package:pinball/start_game/start_game.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + late StartGameBloc startGameBloc; + late PinballGame pinballGame; + + group('StartGameListener', () { + setUp(() { + startGameBloc = MockStartGameBloc(); + pinballGame = MockPinballGame(); + }); + + testWidgets( + 'on selectCharacter status shows SelectCharacter dialog', + (tester) async { + whenListen( + startGameBloc, + Stream.value( + const StartGameState(status: StartGameStatus.selectCharacter), + ), + initialState: const StartGameState.initial(), + ); + + await tester.pumpApp( + StartGameListener( + game: pinballGame, + child: const SizedBox.shrink(), + ), + startGameBloc: startGameBloc, + ); + + await tester.pumpAndSettle(); + + expect( + find.byType(CharacterSelectionDialog), + findsOneWidget, + ); + }, + ); + + testWidgets( + 'on howToPlay status shows HowToPlay dialog', + (tester) async { + whenListen( + startGameBloc, + Stream.value( + const StartGameState(status: StartGameStatus.howToPlay), + ), + initialState: const StartGameState.initial(), + ); + + await tester.pumpApp( + StartGameListener( + game: pinballGame, + child: const SizedBox.shrink(), + ), + startGameBloc: startGameBloc, + ); + + await tester.pumpAndSettle(); + + expect( + find.byType(HowToPlayDialog), + findsOneWidget, + ); + }, + ); + + testWidgets( + 'on play status call start on game controller', + (tester) async { + whenListen( + startGameBloc, + Stream.value( + const StartGameState(status: StartGameStatus.play), + ), + initialState: const StartGameState.initial(), + ); + + final gameController = MockGameFlowController(); + when(() => pinballGame.gameFlowController) + .thenAnswer((invocation) => gameController); + + await tester.pumpApp( + StartGameListener( + game: pinballGame, + child: const SizedBox.shrink(), + ), + startGameBloc: startGameBloc, + ); + + await tester.pumpAndSettle(kThemeAnimationDuration); + await tester.pumpAndSettle(kThemeAnimationDuration); + + verify(gameController.start).called(1); + }, + ); + + testWidgets( + 'do nothing on initial status', + (tester) async { + whenListen( + startGameBloc, + Stream.value( + const StartGameState(status: StartGameStatus.initial), + ), + initialState: const StartGameState.initial(), + ); + + await tester.pumpApp( + StartGameListener( + game: pinballGame, + child: const SizedBox.shrink(), + ), + startGameBloc: startGameBloc, + ); + + await tester.pumpAndSettle(); + + expect( + find.byType(HowToPlayDialog), + findsNothing, + ); + expect( + find.byType(CharacterSelectionDialog), + findsNothing, + ); + }, + ); + + testWidgets( + 'calls HowToPlayFinished event after HowToPlayDialog is closed', + (tester) async { + whenListen( + startGameBloc, + Stream.value( + const StartGameState(status: StartGameStatus.howToPlay), + ), + initialState: const StartGameState.initial(), + ); + + await tester.pumpApp( + StartGameListener( + game: pinballGame, + child: const SizedBox.shrink(), + ), + startGameBloc: startGameBloc, + ); + await tester.pumpAndSettle(); + + expect( + find.byType(HowToPlayDialog), + findsOneWidget, + ); + await tester.tapAt(const Offset(1, 1)); + await tester.pumpAndSettle(); + + expect( + find.byType(HowToPlayDialog), + findsNothing, + ); + await tester.pumpAndSettle(); + + verify( + () => startGameBloc.add(const HowToPlayFinished()), + ).called(1); + }, + ); + }); +}