diff --git a/assets/images/components/plunger.png b/assets/images/components/plunger.png deleted file mode 100644 index f3cbdf0f..00000000 Binary files a/assets/images/components/plunger.png and /dev/null differ diff --git a/lib/game/components/components.dart b/lib/game/components/components.dart index 6a9d6a3d..058bfe20 100644 --- a/lib/game/components/components.dart +++ b/lib/game/components/components.dart @@ -8,6 +8,7 @@ export 'controlled_plunger.dart'; export 'controlled_sparky_computer.dart'; export 'flutter_forest.dart'; export 'game_flow_controller.dart'; +export 'launcher.dart'; export 'score_effect_controller.dart'; export 'score_points.dart'; export 'sparky_fire_zone.dart'; diff --git a/lib/game/components/controlled_ball.dart b/lib/game/components/controlled_ball.dart index 8354605c..67d75daf 100644 --- a/lib/game/components/controlled_ball.dart +++ b/lib/game/components/controlled_ball.dart @@ -19,6 +19,7 @@ class ControlledBall extends Ball with Controls { }) : super(baseColor: theme.characterTheme.ballColor) { controller = BallController(this); priority = Ball.launchRampPriority; + layer = Layer.launcher; } /// {@template bonus_ball} diff --git a/lib/game/components/launcher.dart b/lib/game/components/launcher.dart new file mode 100644 index 00000000..b9ec4a7f --- /dev/null +++ b/lib/game/components/launcher.dart @@ -0,0 +1,26 @@ +import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:pinball/game/components/components.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; + +/// {@template launcher} +/// A [Blueprint] which creates the [Plunger], [RocketSpriteComponent] and +/// [LaunchRamp]. +/// {@endtemplate} +class Launcher extends Forge2DBlueprint { + /// {@macro launcher} + Launcher(); + + /// [Plunger] to launch the [Ball] onto the board. + late final Plunger plunger; + + @override + void build(Forge2DGame gameRef) { + plunger = ControlledPlunger(compressionDistance: 12.3) + ..initialPosition = Vector2(40.1, -38); + + final _rocket = RocketSpriteComponent()..position = Vector2(43, 62); + + addAll([_rocket, plunger]); + addBlueprint(LaunchRamp()); + } +} diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 8f2fc171..4d06bd13 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -47,6 +47,7 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.chromeDino.mouth.keyName), images.load(components.Assets.images.chromeDino.head.keyName), images.load(components.Assets.images.plunger.plunger.keyName), + images.load(components.Assets.images.plunger.rocket.keyName), images.load(components.Assets.images.sparky.computer.base.keyName), images.load(components.Assets.images.sparky.computer.top.keyName), images.load(components.Assets.images.sparky.bumper.a.active.keyName), diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 0f15ace2..0dd1b52e 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -45,13 +45,10 @@ class PinballGame extends Forge2DGame await _addGameBoundaries(); unawaited(addFromBlueprint(Boundaries())); - unawaited(addFromBlueprint(LaunchRamp())); unawaited(addFromBlueprint(ControlledSparkyComputer())); - final plunger = ControlledPlunger(compressionDistance: 29) - ..initialPosition = Vector2(38, 19); - await add(plunger); - + final launcher = Launcher(); + unawaited(addFromBlueprint(launcher)); unawaited(add(Board())); unawaited(add(AlienZone())); unawaited(add(SparkyFireZone())); @@ -68,7 +65,7 @@ class PinballGame extends Forge2DGame ); unawaited(addFromBlueprint(SpaceshipRail())); - controller.attachTo(plunger); + controller.attachTo(launcher.plunger); await super.onLoad(); } @@ -168,7 +165,7 @@ class DebugPinballGame extends PinballGame with TapDetector { anchor: Anchor.center, ) ..position = Vector2(0, -7.8) - ..priority = -2; + ..priority = -4; await add(spriteComponent); } diff --git a/lib/gen/assets.gen.dart b/lib/gen/assets.gen.dart index 90013646..97be7f3e 100644 --- a/lib/gen/assets.gen.dart +++ b/lib/gen/assets.gen.dart @@ -20,10 +20,6 @@ class $AssetsImagesComponentsGen { /// File path: assets/images/components/background.png AssetGenImage get background => const AssetGenImage('assets/images/components/background.png'); - - /// File path: assets/images/components/plunger.png - AssetGenImage get plunger => - const AssetGenImage('assets/images/components/plunger.png'); } class Assets { diff --git a/lib/start_game/bloc/start_game_bloc.dart b/lib/start_game/bloc/start_game_bloc.dart new file mode 100644 index 00000000..ba44d88c --- /dev/null +++ b/lib/start_game/bloc/start_game_bloc.dart @@ -0,0 +1,58 @@ +import 'package:bloc/bloc.dart'; +import 'package:equatable/equatable.dart'; +import 'package:pinball/game/game.dart'; + +part 'start_game_event.dart'; +part 'start_game_state.dart'; + +/// {@template start_game_bloc} +/// Bloc that manages the app flow before the game starts. +/// {@endtemplate} +class StartGameBloc extends Bloc { + /// {@macro start_game_bloc} + StartGameBloc({ + required PinballGame game, + }) : _game = game, + super(const StartGameState.initial()) { + on(_onPlayTapped); + on(_onCharacterSelected); + on(_onHowToPlayFinished); + } + + final PinballGame _game; + + void _onPlayTapped( + PlayTapped event, + Emitter emit, + ) { + _game.gameFlowController.start(); + + emit( + state.copyWith( + status: StartGameStatus.selectCharacter, + ), + ); + } + + void _onCharacterSelected( + CharacterSelected event, + Emitter emit, + ) { + emit( + state.copyWith( + status: StartGameStatus.howToPlay, + ), + ); + } + + void _onHowToPlayFinished( + HowToPlayFinished event, + Emitter emit, + ) { + emit( + state.copyWith( + status: StartGameStatus.play, + ), + ); + } +} diff --git a/lib/start_game/bloc/start_game_event.dart b/lib/start_game/bloc/start_game_event.dart new file mode 100644 index 00000000..ce164e97 --- /dev/null +++ b/lib/start_game/bloc/start_game_event.dart @@ -0,0 +1,42 @@ +part of 'start_game_bloc.dart'; + +/// {@template start_game_event} +/// Event added during the start game flow. +/// {@endtemplate} +abstract class StartGameEvent extends Equatable { + /// {@macro start_game_event} + const StartGameEvent(); +} + +/// {@template play_tapped} +/// Play tapped event. +/// {@endtemplate} +class PlayTapped extends StartGameEvent { + /// {@macro play_tapped} + const PlayTapped(); + + @override + List get props => []; +} + +/// {@template character_selected} +/// Character selected event. +/// {@endtemplate} +class CharacterSelected extends StartGameEvent { + /// {@macro character_selected} + const CharacterSelected(); + + @override + List get props => []; +} + +/// {@template how_to_play_finished} +/// How to play finished event. +/// {@endtemplate} +class HowToPlayFinished extends StartGameEvent { + /// {@macro how_to_play_finished} + const HowToPlayFinished(); + + @override + List get props => []; +} diff --git a/lib/start_game/bloc/start_game_state.dart b/lib/start_game/bloc/start_game_state.dart new file mode 100644 index 00000000..ad7c7cbe --- /dev/null +++ b/lib/start_game/bloc/start_game_state.dart @@ -0,0 +1,44 @@ +part of 'start_game_bloc.dart'; + +/// Defines status of start game flow. +enum StartGameStatus { + /// Initial status. + initial, + + /// Selection characters status. + selectCharacter, + + /// How to play status. + howToPlay, + + /// Play status. + play, +} + +/// {@template start_game_state} +/// Represents the state of flow before the game starts. +/// {@endtemplate} +class StartGameState extends Equatable { + /// {@macro start_game_state} + const StartGameState({ + required this.status, + }); + + /// Initial [StartGameState]. + const StartGameState.initial() : this(status: StartGameStatus.initial); + + /// Status of [StartGameState]. + final StartGameStatus status; + + /// Creates a copy of [StartGameState]. + StartGameState copyWith({ + StartGameStatus? status, + }) { + return StartGameState( + status: status ?? this.status, + ); + } + + @override + List get props => [status]; +} diff --git a/lib/start_game/start_game.dart b/lib/start_game/start_game.dart new file mode 100644 index 00000000..7171c66d --- /dev/null +++ b/lib/start_game/start_game.dart @@ -0,0 +1 @@ +export 'bloc/start_game_bloc.dart'; diff --git a/packages/pinball_components/assets/images/plunger/rocket.png b/packages/pinball_components/assets/images/plunger/rocket.png new file mode 100644 index 00000000..ee5eef5b Binary files /dev/null and b/packages/pinball_components/assets/images/plunger/rocket.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index df66a9a8..35a998a4 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -187,6 +187,10 @@ class $AssetsImagesPlungerGen { /// File path: assets/images/plunger/plunger.png AssetGenImage get plunger => const AssetGenImage('assets/images/plunger/plunger.png'); + + /// File path: assets/images/plunger/rocket.png + AssetGenImage get rocket => + const AssetGenImage('assets/images/plunger/rocket.png'); } class $AssetsImagesSlingshotGen { diff --git a/packages/pinball_components/lib/src/components/ball.dart b/packages/pinball_components/lib/src/components/ball.dart index 21f44cba..9adefd3a 100644 --- a/packages/pinball_components/lib/src/components/ball.dart +++ b/packages/pinball_components/lib/src/components/ball.dart @@ -35,7 +35,7 @@ class Ball extends BodyComponent static const int spaceshipRailPriority = 2; /// Render priority for the [Ball] while it's on the [LaunchRamp]. - static const int launchRampPriority = 0; + static const int launchRampPriority = -2; /// The size of the [Ball]. static final Vector2 size = Vector2.all(4.13); diff --git a/packages/pinball_components/lib/src/components/boundaries.dart b/packages/pinball_components/lib/src/components/boundaries.dart index edbda8e1..38ce5f4c 100644 --- a/packages/pinball_components/lib/src/components/boundaries.dart +++ b/packages/pinball_components/lib/src/components/boundaries.dart @@ -13,7 +13,7 @@ class Boundaries extends Forge2DBlueprint { final bottomBoundary = _BottomBoundary(); final outerBoundary = _OuterBoundary(); - addAll([bottomBoundary, outerBoundary]); + addAll([outerBoundary, bottomBoundary]); } } @@ -23,7 +23,7 @@ class Boundaries extends Forge2DBlueprint { /// {@endtemplate bottom_boundary} class _BottomBoundary extends BodyComponent with InitialPosition { /// {@macro bottom_boundary} - _BottomBoundary() : super(priority: 2); + _BottomBoundary() : super(priority: 1); List _createFixtureDefs() { final fixturesDefs = []; @@ -88,7 +88,7 @@ class _BottomBoundarySpriteComponent extends SpriteComponent with HasGameRef { /// {@endtemplate outer_boundary} class _OuterBoundary extends BodyComponent with InitialPosition { /// {@macro outer_boundary} - _OuterBoundary() : super(priority: -1); + _OuterBoundary() : super(priority: Ball.launchRampPriority - 1); List _createFixtureDefs() { final fixturesDefs = []; diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index acb8a7c5..e719783f 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -21,6 +21,7 @@ export 'launch_ramp.dart'; export 'layer.dart'; export 'plunger.dart'; export 'ramp_opening.dart'; +export 'rocket.dart'; export 'score_text.dart'; export 'shapes/shapes.dart'; export 'slingshot.dart'; diff --git a/packages/pinball_components/lib/src/components/dino_walls.dart b/packages/pinball_components/lib/src/components/dino_walls.dart index 96f5a56c..53c67861 100644 --- a/packages/pinball_components/lib/src/components/dino_walls.dart +++ b/packages/pinball_components/lib/src/components/dino_walls.dart @@ -124,7 +124,7 @@ class _DinoTopWallSpriteComponent extends SpriteComponent with HasGameRef { /// {@endtemplate} class _DinoBottomWall extends BodyComponent with InitialPosition { ///{@macro dino_top_wall} - _DinoBottomWall() : super(priority: 1); + _DinoBottomWall(); List _createFixtureDefs() { final fixturesDef = []; diff --git a/packages/pinball_components/lib/src/components/launch_ramp.dart b/packages/pinball_components/lib/src/components/launch_ramp.dart index bcafd114..8d60b62e 100644 --- a/packages/pinball_components/lib/src/components/launch_ramp.dart +++ b/packages/pinball_components/lib/src/components/launch_ramp.dart @@ -17,20 +17,21 @@ class LaunchRamp extends Forge2DBlueprint { RampOpeningBallContactCallback<_LaunchRampExit>(), ]); - final launchRampBase = _LaunchRampBase()..layer = Layer.launcher; + final launchRampBase = _LaunchRampBase(); - final launchRampForegroundRailing = _LaunchRampForegroundRailing() - ..layer = Layer.launcher; + final launchRampForegroundRailing = _LaunchRampForegroundRailing(); final launchRampExit = _LaunchRampExit(rotation: math.pi / 2) - ..initialPosition = Vector2(1.8, -34.2) - ..layer = Layer.opening - ..renderBody = false; + ..initialPosition = Vector2(0.6, -34); + + final launchRampCloseWall = _LaunchRampCloseWall() + ..initialPosition = Vector2(4, -66.5); addAll([ launchRampBase, launchRampForegroundRailing, launchRampExit, + launchRampCloseWall, ]); } } @@ -40,10 +41,7 @@ class LaunchRamp extends Forge2DBlueprint { /// {@endtemplate} class _LaunchRampBase extends BodyComponent with InitialPosition, Layered { /// {@macro launch_ramp_base} - _LaunchRampBase() - : super( - priority: Ball.launchRampPriority - 1, - ) { + _LaunchRampBase() : super(priority: Ball.launchRampPriority - 1) { layer = Layer.launcher; } @@ -143,15 +141,9 @@ class _LaunchRampBaseSpriteComponent extends SpriteComponent with HasGameRef { /// Foreground railing for the [_LaunchRampBase] to render in front of the /// [Ball]. /// {@endtemplate} -class _LaunchRampForegroundRailing extends BodyComponent - with InitialPosition, Layered { +class _LaunchRampForegroundRailing extends BodyComponent with InitialPosition { /// {@macro launch_ramp_foreground_railing} - _LaunchRampForegroundRailing() - : super( - priority: Ball.launchRampPriority + 1, - ) { - layer = Layer.launcher; - } + _LaunchRampForegroundRailing() : super(priority: Ball.launchRampPriority + 1); List _createFixtureDefs() { final fixturesDef = []; @@ -221,6 +213,26 @@ class _LaunchRampForegroundRailingSpriteComponent extends SpriteComponent } } +class _LaunchRampCloseWall extends BodyComponent with InitialPosition, Layered { + _LaunchRampCloseWall() { + layer = Layer.board; + // renderBody = false; + } + + @override + Body createBody() { + final shape = EdgeShape()..set(Vector2.zero(), Vector2(0, 4)); + + final fixtureDef = FixtureDef(shape); + + final bodyDef = BodyDef() + ..userData = this + ..position = initialPosition; + + return world.createBody(bodyDef)..createFixture(fixtureDef); + } +} + /// {@template launch_ramp_exit} /// [RampOpening] with [Layer.launcher] to filter [Ball]s exiting the /// [LaunchRamp]. @@ -232,9 +244,14 @@ class _LaunchRampExit extends RampOpening { }) : _rotation = rotation, super( insideLayer: Layer.launcher, + outsideLayer: Layer.board, orientation: RampOrientation.down, insidePriority: Ball.launchRampPriority, - ); + outsidePriority: 0, + ) { + layer = Layer.launcher; + renderBody = false; + } final double _rotation; diff --git a/packages/pinball_components/lib/src/components/layer.dart b/packages/pinball_components/lib/src/components/layer.dart index 6f027d4c..9b20ecf2 100644 --- a/packages/pinball_components/lib/src/components/layer.dart +++ b/packages/pinball_components/lib/src/components/layer.dart @@ -88,7 +88,7 @@ extension LayerMaskBits on Layer { case Layer.spaceshipEntranceRamp: return 0x0002; case Layer.launcher: - return 0x0005; + return 0x0008; case Layer.spaceship: return 0x000A; case Layer.spaceshipExitRail: diff --git a/packages/pinball_components/lib/src/components/plunger.dart b/packages/pinball_components/lib/src/components/plunger.dart index 60fbd3c4..6b218e67 100644 --- a/packages/pinball_components/lib/src/components/plunger.dart +++ b/packages/pinball_components/lib/src/components/plunger.dart @@ -8,13 +8,15 @@ import 'package:pinball_components/pinball_components.dart'; /// /// [Plunger] ignores gravity so the player controls its downward [pull]. /// {@endtemplate} -class Plunger extends BodyComponent with InitialPosition { +class Plunger extends BodyComponent with InitialPosition, Layered { /// {@macro plunger} Plunger({ required this.compressionDistance, // TODO(ruimiguel): set to priority +1 over LaunchRamp once all priorities // are fixed. - }) : super(priority: 0); + }) : super(priority: 0) { + layer = Layer.launcher; + } /// Distance the plunger can lower. final double compressionDistance; @@ -90,8 +92,8 @@ class _PlungerSpriteComponent extends SpriteComponent with HasGameRef { this.sprite = sprite; size = sprite.originalSize / 10; anchor = Anchor.center; - position = Vector2(2, 19); - angle = -0.033; + position = Vector2(1.5, 13.4); + angle = -0.008; } } diff --git a/packages/pinball_components/lib/src/components/rocket.dart b/packages/pinball_components/lib/src/components/rocket.dart new file mode 100644 index 00000000..ee22efca --- /dev/null +++ b/packages/pinball_components/lib/src/components/rocket.dart @@ -0,0 +1,24 @@ +import 'package:flame/components.dart'; +import 'package:pinball_components/gen/assets.gen.dart'; +import 'package:pinball_components/pinball_components.dart' hide Assets; + +/// {@template rocket_sprite_component} +/// A [SpriteComponent] for the rocket over [Plunger]. +/// {@endtemplate} +class RocketSpriteComponent extends SpriteComponent with HasGameRef { + // TODO(ruimiguel): change this priority to be over launcher ramp and bottom + // wall. + /// {@macro rocket_sprite_component} + RocketSpriteComponent() : super(priority: 5); + + @override + Future onLoad() async { + await super.onLoad(); + final sprite = await gameRef.loadSprite( + Assets.images.plunger.rocket.keyName, + ); + this.sprite = sprite; + size = sprite.originalSize / 10; + anchor = Anchor.center; + } +} diff --git a/packages/pinball_components/test/src/components/golden/plunger/plunger.png b/packages/pinball_components/test/src/components/golden/plunger/plunger.png new file mode 100644 index 00000000..a33405f1 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/plunger/plunger.png differ diff --git a/packages/pinball_components/test/src/components/golden/plunger/rocket.png b/packages/pinball_components/test/src/components/golden/plunger/rocket.png new file mode 100644 index 00000000..9511f3d5 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/plunger/rocket.png differ diff --git a/packages/pinball_components/test/src/components/plunger_test.dart b/packages/pinball_components/test/src/components/plunger_test.dart index f6d91984..fb70f754 100644 --- a/packages/pinball_components/test/src/components/plunger_test.dart +++ b/packages/pinball_components/test/src/components/plunger_test.dart @@ -26,13 +26,12 @@ void main() { game.camera.followVector2(Vector2.zero()); game.camera.zoom = 4.1; }, - // TODO(ruimiguel): enable test when workflows are fixed. - // verify: (game, tester) async { - // await expectLater( - // find.byGame(), - // matchesGoldenFile('golden/plunger.png'), - // ); - // }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/plunger/plunger.png'), + ); + }, ); flameTester.test( diff --git a/packages/pinball_components/test/src/components/rocket_test.dart b/packages/pinball_components/test/src/components/rocket_test.dart new file mode 100644 index 00000000..c6a92561 --- /dev/null +++ b/packages/pinball_components/test/src/components/rocket_test.dart @@ -0,0 +1,28 @@ +// ignore_for_file: cascade_invocations + +import 'package:flame/components.dart'; +import 'package:flame_test/flame_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + group('RocketSpriteComponent', () { + final tester = FlameTester(TestGame.new); + + tester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + game.camera.followVector2(Vector2.zero()); + await game.ensureAdd(RocketSpriteComponent()); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/plunger/rocket.png'), + ); + }, + ); + }); +} diff --git a/test/start_game/bloc/start_game_bloc_test.dart b/test/start_game/bloc/start_game_bloc_test.dart new file mode 100644 index 00000000..ec1b3ced --- /dev/null +++ b/test/start_game/bloc/start_game_bloc_test.dart @@ -0,0 +1,62 @@ +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/game.dart'; +import 'package:pinball/start_game/bloc/start_game_bloc.dart'; + +import '../../helpers/helpers.dart'; + +void main() { + late PinballGame pinballGame; + + setUp(() { + pinballGame = MockPinballGame(); + + when( + () => pinballGame.gameFlowController, + ).thenReturn( + MockGameFlowController(), + ); + }); + + group('StartGameBloc', () { + blocTest( + 'on PlayTapped changes status to selectCharacter', + build: () => StartGameBloc( + game: pinballGame, + ), + act: (bloc) => bloc.add(const PlayTapped()), + expect: () => [ + const StartGameState( + status: StartGameStatus.selectCharacter, + ) + ], + ); + + blocTest( + 'on CharacterSelected changes status to howToPlay', + build: () => StartGameBloc( + game: pinballGame, + ), + act: (bloc) => bloc.add(const CharacterSelected()), + expect: () => [ + const StartGameState( + status: StartGameStatus.howToPlay, + ) + ], + ); + + blocTest( + 'on HowToPlayFinished changes status to play', + build: () => StartGameBloc( + game: pinballGame, + ), + act: (bloc) => bloc.add(const HowToPlayFinished()), + expect: () => [ + const StartGameState( + status: StartGameStatus.play, + ) + ], + ); + }); +} diff --git a/test/start_game/bloc/start_game_event_test.dart b/test/start_game/bloc/start_game_event_test.dart new file mode 100644 index 00000000..cf481d9f --- /dev/null +++ b/test/start_game/bloc/start_game_event_test.dart @@ -0,0 +1,29 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/start_game/bloc/start_game_bloc.dart'; + +void main() { + group('StartGameEvent', () { + test('PlayTapped supports value equality', () { + expect( + PlayTapped(), + equals(PlayTapped()), + ); + }); + + test('CharacterSelected supports value equality', () { + expect( + CharacterSelected(), + equals(CharacterSelected()), + ); + }); + + test('HowToPlayFinished supports value equality', () { + expect( + HowToPlayFinished(), + equals(HowToPlayFinished()), + ); + }); + }); +} diff --git a/test/start_game/bloc/start_game_state_test.dart b/test/start_game/bloc/start_game_state_test.dart new file mode 100644 index 00000000..7ede696d --- /dev/null +++ b/test/start_game/bloc/start_game_state_test.dart @@ -0,0 +1,43 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/start_game/bloc/start_game_bloc.dart'; + +void main() { + group('StartGameState', () { + final testState = StartGameState( + status: StartGameStatus.selectCharacter, + ); + + test('initial state has correct values', () { + final state = StartGameState( + status: StartGameStatus.initial, + ); + + expect(state, StartGameState.initial()); + }); + + test('supports value equality', () { + final secondState = StartGameState( + status: StartGameStatus.selectCharacter, + ); + + expect(testState, secondState); + }); + + test('supports copyWith', () { + final secondState = testState.copyWith(); + + expect(testState, secondState); + }); + + test('has correct props', () { + expect( + testState.props, + equals([ + StartGameStatus.selectCharacter, + ]), + ); + }); + }); +}