import 'package:flame/components.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/behaviors/ball_spawning_behavior.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/select_character/select_character.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' as theme; class _TestGame extends Forge2DGame { @override Future onLoad() async { images.prefix = ''; await images.load(theme.Assets.images.dash.ball.keyName); } Future pump( List children, { GameBloc? gameBloc, }) async { await ensureAdd( FlameMultiBlocProvider( providers: [ FlameBlocProvider.value( value: gameBloc ?? GameBloc(), ), FlameBlocProvider.value( value: CharacterThemeCubit(), ), ], children: children, ), ); } } class _MockGameState extends Mock implements GameState {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); group( 'BallSpawningBehavior', () { final flameTester = FlameTester(_TestGame.new); test('can be instantiated', () { expect( BallSpawningBehavior(), isA(), ); }); flameTester.testGameWidget( 'loads', setUp: (game, _) async { final behavior = BallSpawningBehavior(); await game.pump([behavior]); }, verify: (game, _) async { expect( game.descendants().whereType().length, equals(1), ); }, ); group('listenWhen', () { test( 'never listens when new state not playing', () { final waiting = const GameState.initial() ..copyWith(status: GameStatus.waiting); final gameOver = const GameState.initial() ..copyWith(status: GameStatus.gameOver); final behavior = BallSpawningBehavior(); expect(behavior.listenWhen(_MockGameState(), waiting), isFalse); expect(behavior.listenWhen(_MockGameState(), gameOver), isFalse); }, ); test( 'listens when started playing', () { final waiting = const GameState.initial().copyWith(status: GameStatus.waiting); final playing = const GameState.initial().copyWith(status: GameStatus.playing); final behavior = BallSpawningBehavior(); expect(behavior.listenWhen(waiting, playing), isTrue); }, ); test( 'listens when lost rounds', () { final playing1 = const GameState.initial().copyWith( status: GameStatus.playing, rounds: 2, ); final playing2 = const GameState.initial().copyWith( status: GameStatus.playing, rounds: 1, ); final behavior = BallSpawningBehavior(); expect(behavior.listenWhen(playing1, playing2), isTrue); }, ); test( "doesn't listen when didn't lose any rounds", () { final playing = const GameState.initial().copyWith( status: GameStatus.playing, rounds: 2, ); final behavior = BallSpawningBehavior(); expect(behavior.listenWhen(playing, playing), isFalse); }, ); }); flameTester.testGameWidget( 'onNewState adds a ball', setUp: (game, _) async { await game.onLoad(); final behavior = BallSpawningBehavior(); await game.pump([ behavior, ZCanvasComponent(), Plunger.test(), ]); await game.ready(); }, verify: (game, tester) async { expect(game.descendants().whereType(), isEmpty); game .descendants() .whereType() .single .onNewState(_MockGameState()); game.update(0); await tester.pump(); expect(game.descendants().whereType(), isNotEmpty); }, ); }, ); }