refactor: include GameStatus

pull/345/head
alestiago 3 years ago
parent 0595e82649
commit 38bf7603b7

@ -14,6 +14,16 @@ class GameBloc extends Bloc<GameEvent, GameState> {
on<MultiplierIncreased>(_onIncreasedMultiplier);
on<BonusActivated>(_onBonusActivated);
on<SparkyTurboChargeActivated>(_onSparkyTurboChargeActivated);
on<GameOver>(_onGameOver);
on<GameStarted>(_onGameStarted);
}
void _onGameStarted(GameStarted _, Emitter emit) {
emit(state.copyWith(status: GameStatus.playing));
}
void _onGameOver(GameOver _, Emitter emit) {
emit(state.copyWith(status: GameStatus.gameOver));
}
void _onRoundLost(RoundLost event, Emitter emit) {
@ -26,12 +36,13 @@ class GameBloc extends Bloc<GameEvent, GameState> {
roundScore: 0,
multiplier: 1,
rounds: roundsLeft,
status: roundsLeft == 0 ? GameStatus.gameOver : state.status,
),
);
}
void _onScored(Scored event, Emitter emit) {
if (!state.isGameOver) {
if (state.status.isPlaying) {
emit(
state.copyWith(roundScore: state.roundScore + event.points),
);
@ -39,7 +50,7 @@ class GameBloc extends Bloc<GameEvent, GameState> {
}
void _onIncreasedMultiplier(MultiplierIncreased event, Emitter emit) {
if (!state.isGameOver) {
if (state.status.isPlaying) {
emit(
state.copyWith(
multiplier: math.min(state.multiplier + 1, 6),

@ -59,3 +59,17 @@ class MultiplierIncreased extends GameEvent {
@override
List<Object?> get props => [];
}
class GameStarted extends GameEvent {
const GameStarted();
@override
List<Object?> get props => [];
}
class GameOver extends GameEvent {
const GameOver();
@override
List<Object?> get props => [];
}

@ -20,6 +20,18 @@ enum GameBonus {
androidSpaceship,
}
enum GameStatus {
waiting,
playing,
gameOver,
}
extension GameStatusX on GameStatus {
bool get isWaiting => this == GameStatus.waiting;
bool get isPlaying => this == GameStatus.playing;
bool get isGameOver => this == GameStatus.gameOver;
}
/// {@template game_state}
/// Represents the state of the pinball game.
/// {@endtemplate}
@ -31,13 +43,15 @@ class GameState extends Equatable {
required this.multiplier,
required this.rounds,
required this.bonusHistory,
required this.status,
}) : assert(totalScore >= 0, "TotalScore can't be negative"),
assert(roundScore >= 0, "Round score can't be negative"),
assert(multiplier > 0, 'Multiplier must be greater than zero'),
assert(rounds >= 0, "Number of rounds can't be negative");
const GameState.initial()
: totalScore = 0,
: status = GameStatus.waiting,
totalScore = 0,
roundScore = 0,
multiplier = 1,
rounds = 3,
@ -65,8 +79,7 @@ class GameState extends Equatable {
/// PinballGame.
final List<GameBonus> bonusHistory;
/// Determines when the game is over.
bool get isGameOver => rounds == 0;
final GameStatus status;
/// The score displayed at the game.
int get displayScore => roundScore + totalScore;
@ -78,6 +91,7 @@ class GameState extends Equatable {
int? balls,
int? rounds,
List<GameBonus>? bonusHistory,
GameStatus? status,
}) {
assert(
totalScore == null || totalScore >= this.totalScore,
@ -90,6 +104,7 @@ class GameState extends Equatable {
multiplier: multiplier ?? this.multiplier,
rounds: rounds ?? this.rounds,
bonusHistory: bonusHistory ?? this.bonusHistory,
status: status ?? this.status,
);
}
@ -100,5 +115,6 @@ class GameState extends Equatable {
multiplier,
rounds,
bonusHistory,
status,
];
}

@ -8,7 +8,7 @@ export 'controlled_plunger.dart';
export 'dino_desert/dino_desert.dart';
export 'drain.dart';
export 'flutter_forest/flutter_forest.dart';
export 'game_flow_controller.dart';
export 'game_bloc_status_listener.dart';
export 'google_word/google_word.dart';
export 'launcher.dart';
export 'multiballs/multiballs.dart';

@ -37,7 +37,7 @@ class FlipperController extends ComponentController<Flipper>
RawKeyEvent event,
Set<LogicalKeyboardKey> keysPressed,
) {
if (state?.isGameOver ?? false) return true;
if (state?.status.isGameOver ?? false) return true;
if (!_keys.contains(event.logicalKey)) return true;
if (event is RawKeyDownEvent) {

@ -38,7 +38,7 @@ class PlungerController extends ComponentController<Plunger>
RawKeyEvent event,
Set<LogicalKeyboardKey> keysPressed,
) {
if (state?.isGameOver ?? false) return true;
if (state?.status.isGameOver ?? false) return true;
if (!_keys.contains(event.logicalKey)) return true;
if (event is RawKeyDownEvent) {

@ -0,0 +1,32 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/game/game.dart';
/// Listns to the [GameBloc] and updates the game accoringly.
class GameBlocStatusListener extends Component
with BlocComponent<GameBloc, GameState>, HasGameRef<PinballGame> {
@override
bool listenWhen(GameState? previousState, GameState newState) {
return previousState?.status != newState.status;
}
@override
void onNewState(GameState state) {
switch (state.status) {
case GameStatus.waiting:
break;
case GameStatus.playing:
gameRef.audio.backgroundMusic();
gameRef.firstChild<CameraController>()?.focusOnGame();
gameRef.overlays.remove(PinballGame.playButtonOverlay);
break;
case GameStatus.gameOver:
gameRef.descendants().whereType<Backbox>().first.initialsInput(
score: state.displayScore,
characterIconPath: gameRef.characterTheme.leaderboardIcon.keyName,
);
gameRef.firstChild<CameraController>()!.focusOnGameOverBackbox();
break;
}
}
}

@ -1,45 +0,0 @@
import 'package:flame/components.dart';
import 'package:flame_bloc/flame_bloc.dart';
import 'package:pinball/game/game.dart';
import 'package:pinball_flame/pinball_flame.dart';
/// {@template game_flow_controller}
/// A [Component] that controls the game over and game restart logic
/// {@endtemplate}
class GameFlowController extends ComponentController<PinballGame>
with BlocComponent<GameBloc, GameState> {
/// {@macro game_flow_controller}
GameFlowController(PinballGame component) : super(component);
@override
bool listenWhen(GameState? previousState, GameState newState) {
return previousState?.isGameOver != newState.isGameOver;
}
@override
void onNewState(GameState state) {
if (state.isGameOver) {
_initialsInput();
} else {
start();
}
}
/// Puts the game in the initials input state.
void _initialsInput() {
// TODO(erickzanardo): implement score submission and "navigate" to the
// next page
component.descendants().whereType<Backbox>().first.initialsInput(
score: state?.displayScore ?? 0,
characterIconPath: component.characterTheme.leaderboardIcon.keyName,
);
component.firstChild<CameraController>()!.focusOnGameOverBackbox();
}
/// Puts the game in the playing state.
void start() {
component.audio.backgroundMusic();
component.firstChild<CameraController>()?.focusOnGame();
component.overlays.remove(PinballGame.playButtonOverlay);
}
}

@ -41,11 +41,8 @@ class PinballGame extends PinballForge2DGame
final AppLocalizations l10n;
late final GameFlowController gameFlowController;
@override
Future<void> onLoad() async {
await add(gameFlowController = GameFlowController(this));
await add(CameraController(this));
final machine = [
@ -65,7 +62,9 @@ class PinballGame extends PinballForge2DGame
SparkyScorch(),
];
await add(
await addAll(
[
GameBlocStatusListener(),
ZCanvasComponent(
children: [
...machine,
@ -76,6 +75,7 @@ class PinballGame extends PinballForge2DGame
Launcher(),
],
),
],
);
await super.onLoad();
@ -136,9 +136,7 @@ class _GameBallsController extends ComponentController<PinballGame>
@override
bool listenWhen(GameState? previousState, GameState newState) {
final noBallsLeft = component.descendants().whereType<Ball>().isEmpty;
final notGameOver = !newState.isGameOver;
return noBallsLeft && notGameOver;
return noBallsLeft && newState.status.isPlaying;
}
@override

@ -112,7 +112,6 @@ class PinballGameLoadedView extends StatelessWidget {
final leftMargin = (screenWidth / 2) - (gameWidgetWidth / 1.8);
return StartGameListener(
game: game,
child: Stack(
children: [
Positioned.fill(

@ -27,7 +27,8 @@ class _GameHudState extends State<GameHud> {
@override
Widget build(BuildContext context) {
final isGameOver = context.select((GameBloc bloc) => bloc.state.isGameOver);
final isGameOver =
context.select((GameBloc bloc) => bloc.state.status.isGameOver);
return _ScoreViewDecoration(
child: SizedBox(

@ -13,7 +13,8 @@ class ScoreView extends StatelessWidget {
@override
Widget build(BuildContext context) {
final isGameOver = context.select((GameBloc bloc) => bloc.state.isGameOver);
final isGameOver =
context.select((GameBloc bloc) => bloc.state.status.isGameOver);
return Padding(
padding: const EdgeInsets.symmetric(

@ -18,13 +18,10 @@ 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) {
@ -35,7 +32,7 @@ class StartGameListener extends StatelessWidget {
break;
case StartGameStatus.selectCharacter:
_onSelectCharacter(context);
_game.gameFlowController.start();
context.read<GameBloc>().add(const GameStarted());
break;
case StartGameStatus.howToPlay:
_onHowToPlay(context);

@ -57,6 +57,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: [],
status: GameStatus.playing,
);
whenListen(bloc, Stream.value(state), initialState: state);
return bloc;

@ -10,6 +10,20 @@ void main() {
expect(gameBloc.state.rounds, equals(3));
});
blocTest<GameBloc, GameState>(
'GameStarted starts the game',
build: GameBloc.new,
act: (bloc) => bloc.add(const GameStarted()),
expect: () => [
isA<GameState>()
..having(
(state) => state.status,
'status',
GameStatus.playing,
),
],
);
group('RoundLost', () {
blocTest<GameBloc, GameState>(
'decreases number of rounds '
@ -23,6 +37,24 @@ void main() {
],
);
blocTest<GameBloc, GameState>(
'sets game over when there are no more rounds',
build: GameBloc.new,
act: (bloc) {
bloc
..add(const RoundLost())
..add(const RoundLost())
..add(const RoundLost());
},
expect: () => [
isA<GameState>()..having((state) => state.rounds, 'rounds', 2),
isA<GameState>()..having((state) => state.rounds, 'rounds', 1),
isA<GameState>()
..having((state) => state.rounds, 'rounds', 0)
..having((state) => state.status, 'status', GameStatus.gameOver),
],
);
blocTest<GameBloc, GameState>(
'apply multiplier to roundScore and add it to totalScore '
'when round is lost',
@ -33,6 +65,7 @@ void main() {
multiplier: 3,
rounds: 2,
bonusHistory: [],
status: GameStatus.playing,
),
act: (bloc) {
bloc.add(const RoundLost());
@ -45,8 +78,7 @@ void main() {
);
blocTest<GameBloc, GameState>(
'resets multiplier '
'when round is lost',
'resets multiplier when round is lost',
build: GameBloc.new,
seed: () => const GameState(
totalScore: 10,
@ -54,6 +86,7 @@ void main() {
multiplier: 3,
rounds: 2,
bonusHistory: [],
status: GameStatus.playing,
),
act: (bloc) {
bloc.add(const RoundLost());
@ -66,25 +99,26 @@ void main() {
group('Scored', () {
blocTest<GameBloc, GameState>(
'increases score '
'when game is not over',
'increases score when playing',
build: GameBloc.new,
act: (bloc) => bloc
..add(const GameStarted())
..add(const Scored(points: 2))
..add(const Scored(points: 3)),
expect: () => [
isA<GameState>()
..having((state) => state.status, 'status', GameStatus.playing),
isA<GameState>()
..having((state) => state.roundScore, 'roundScore', 2)
..having((state) => state.isGameOver, 'isGameOver', false),
..having((state) => state.status, 'status', GameStatus.playing),
isA<GameState>()
..having((state) => state.roundScore, 'roundScore', 5)
..having((state) => state.isGameOver, 'isGameOver', false),
..having((state) => state.status, 'status', GameStatus.playing),
],
);
blocTest<GameBloc, GameState>(
"doesn't increase score "
'when game is over',
"doesn't increase score when game is over",
build: GameBloc.new,
act: (bloc) {
for (var i = 0; i < bloc.state.rounds; i++) {
@ -96,15 +130,27 @@ void main() {
isA<GameState>()
..having((state) => state.roundScore, 'roundScore', 0)
..having((state) => state.rounds, 'rounds', 2)
..having((state) => state.isGameOver, 'isGameOver', false),
..having(
(state) => state.status,
'status',
GameStatus.gameOver,
),
isA<GameState>()
..having((state) => state.roundScore, 'roundScore', 0)
..having((state) => state.rounds, 'rounds', 1)
..having((state) => state.isGameOver, 'isGameOver', false),
..having(
(state) => state.status,
'status',
GameStatus.gameOver,
),
isA<GameState>()
..having((state) => state.roundScore, 'roundScore', 0)
..having((state) => state.rounds, 'rounds', 0)
..having((state) => state.isGameOver, 'isGameOver', true),
..having(
(state) => state.status,
'status',
GameStatus.gameOver,
),
],
);
});
@ -115,15 +161,26 @@ void main() {
'when multiplier is below 6 and game is not over',
build: GameBloc.new,
act: (bloc) => bloc
..add(const GameStarted())
..add(const MultiplierIncreased())
..add(const MultiplierIncreased()),
expect: () => [
isA<GameState>()
..having((state) => state.status, 'status', GameStatus.playing),
isA<GameState>()
..having((state) => state.multiplier, 'multiplier', 2)
..having((state) => state.isGameOver, 'isGameOver', false),
..having(
(state) => state.status,
'status',
GameStatus.gameOver,
),
isA<GameState>()
..having((state) => state.multiplier, 'multiplier', 3)
..having((state) => state.isGameOver, 'isGameOver', false),
..having(
(state) => state.status,
'status',
GameStatus.gameOver,
),
],
);
@ -137,6 +194,7 @@ void main() {
multiplier: 6,
rounds: 3,
bonusHistory: [],
status: GameStatus.playing,
),
act: (bloc) => bloc..add(const MultiplierIncreased()),
expect: () => const <GameState>[],
@ -147,21 +205,36 @@ void main() {
'when game is over',
build: GameBloc.new,
act: (bloc) {
bloc.add(const GameStarted());
for (var i = 0; i < bloc.state.rounds; i++) {
bloc.add(const RoundLost());
}
bloc.add(const MultiplierIncreased());
},
expect: () => [
isA<GameState>()
..having((state) => state.status, 'status', GameStatus.playing),
isA<GameState>()
..having((state) => state.multiplier, 'multiplier', 1)
..having((state) => state.isGameOver, 'isGameOver', false),
..having(
(state) => state.status,
'status',
GameStatus.gameOver,
),
isA<GameState>()
..having((state) => state.multiplier, 'multiplier', 1)
..having((state) => state.isGameOver, 'isGameOver', false),
..having(
(state) => state.status,
'status',
GameStatus.gameOver,
),
isA<GameState>()
..having((state) => state.multiplier, 'multiplier', 1)
..having((state) => state.isGameOver, 'isGameOver', true),
..having(
(state) => state.status,
'status',
GameStatus.gameOver,
),
],
);
});

@ -13,6 +13,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: const [],
status: GameStatus.waiting,
),
equals(
const GameState(
@ -21,6 +22,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: [],
status: GameStatus.waiting,
),
),
);
@ -35,6 +37,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: [],
status: GameStatus.waiting,
),
isNotNull,
);
@ -52,6 +55,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: const [],
status: GameStatus.waiting,
),
throwsAssertionError,
);
@ -69,6 +73,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: const [],
status: GameStatus.waiting,
),
throwsAssertionError,
);
@ -86,6 +91,7 @@ void main() {
multiplier: 0,
rounds: 3,
bonusHistory: const [],
status: GameStatus.waiting,
),
throwsAssertionError,
);
@ -103,40 +109,13 @@ void main() {
multiplier: 1,
rounds: -1,
bonusHistory: const [],
status: GameStatus.waiting,
),
throwsAssertionError,
);
},
);
group('isGameOver', () {
test(
'is true '
'when no rounds are left', () {
const gameState = GameState(
totalScore: 0,
roundScore: 0,
multiplier: 1,
rounds: 0,
bonusHistory: [],
);
expect(gameState.isGameOver, isTrue);
});
test(
'is false '
'when one 1 round left', () {
const gameState = GameState(
totalScore: 0,
roundScore: 0,
multiplier: 1,
rounds: 1,
bonusHistory: [],
);
expect(gameState.isGameOver, isFalse);
});
});
group('copyWith', () {
test(
'throws AssertionError '
@ -148,6 +127,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: [],
status: GameStatus.waiting,
);
expect(
() => gameState.copyWith(totalScore: gameState.totalScore - 1),
@ -166,6 +146,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: [],
status: GameStatus.waiting,
);
expect(
gameState.copyWith(),
@ -184,6 +165,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: [],
status: GameStatus.waiting,
);
final otherGameState = GameState(
totalScore: gameState.totalScore + 1,
@ -191,6 +173,7 @@ void main() {
multiplier: gameState.multiplier + 1,
rounds: gameState.rounds + 1,
bonusHistory: const [GameBonus.googleWord],
status: GameStatus.playing,
);
expect(gameState, isNot(equals(otherGameState)));
@ -201,6 +184,7 @@ void main() {
multiplier: otherGameState.multiplier,
rounds: otherGameState.rounds,
bonusHistory: otherGameState.bonusHistory,
status: otherGameState.status,
),
equals(otherGameState),
);

@ -32,6 +32,7 @@ void main() {
multiplier: 1,
rounds: 0,
bonusHistory: [],
status: GameStatus.playing,
);
whenListen(bloc, Stream.value(state), initialState: state);
return bloc;

@ -27,6 +27,7 @@ void main() {
multiplier: 1,
rounds: 0,
bonusHistory: [],
status: GameStatus.playing,
);
whenListen(bloc, Stream.value(state), initialState: state);
return bloc;

@ -19,7 +19,7 @@ class _MockActiveOverlaysNotifier extends Mock
class _MockPinballAudio extends Mock implements PinballAudio {}
void main() {
group('GameFlowController', () {
group('GameBlocStatusListener', () {
group('listenWhen', () {
test('is true when the game over state has changed', () {
final state = GameState(
@ -28,11 +28,12 @@ void main() {
multiplier: 1,
rounds: 0,
bonusHistory: const [],
status: GameStatus.playing,
);
final previous = GameState.initial();
expect(
GameFlowController(_MockPinballGame()).listenWhen(previous, state),
GameBlocStatusListener().listenWhen(previous, state),
isTrue,
);
});
@ -42,7 +43,7 @@ void main() {
late PinballGame game;
late Backbox backbox;
late CameraController cameraController;
late GameFlowController gameFlowController;
late GameBlocStatusListener gameFlowController;
late PinballAudio pinballAudio;
late ActiveOverlaysNotifier overlays;
@ -50,7 +51,7 @@ void main() {
game = _MockPinballGame();
backbox = _MockBackbox();
cameraController = _MockCameraController();
gameFlowController = GameFlowController(game);
gameFlowController = GameBlocStatusListener();
overlays = _MockActiveOverlaysNotifier();
pinballAudio = _MockPinballAudio();
@ -85,6 +86,7 @@ void main() {
multiplier: 1,
rounds: 0,
bonusHistory: const [],
status: GameStatus.gameOver,
),
);
@ -103,7 +105,6 @@ void main() {
'changes the backbox and camera correctly when it is not a game over',
() {
gameFlowController.onNewState(GameState.initial());
verify(cameraController.focusOnGame).called(1);
verify(() => overlays.remove(PinballGame.playButtonOverlay))
.called(1);
@ -114,7 +115,6 @@ void main() {
'plays the background music on start',
() {
gameFlowController.onNewState(GameState.initial());
verify(pinballAudio.backgroundMusic).called(1);
},
);

@ -79,6 +79,7 @@ void main() {
multiplier: 1,
rounds: 0,
bonusHistory: const [],
status: GameStatus.playing,
);
expect(

@ -60,6 +60,7 @@ void main() {
roundScore: 10,
multiplier: 2,
rounds: 0,
status: GameStatus.playing,
bonusHistory: const [],
);
@ -76,6 +77,7 @@ void main() {
roundScore: 10,
multiplier: 1,
rounds: 0,
status: GameStatus.playing,
bonusHistory: const [],
);

@ -234,7 +234,7 @@ void main() {
// TODO(ruimiguel): check why testGameWidget doesn't add any ball
// to the game. Test needs to have no balls, so fortunately works.
final newState = _MockGameState();
when(() => newState.isGameOver).thenReturn(false);
when(() => newState.status).thenReturn(GameStatus.playing);
game.descendants().whereType<ControlledBall>().forEach(
(ball) => ball.controller.lost(),
);
@ -251,7 +251,7 @@ void main() {
"doesn't listen when some balls are left",
(game) async {
final newState = _MockGameState();
when(() => newState.isGameOver).thenReturn(false);
when(() => newState.status).thenReturn(GameStatus.playing);
await game.ready();
@ -272,7 +272,7 @@ void main() {
// TODO(ruimiguel): check why testGameWidget doesn't add any ball
// to the game. Test needs to have no balls, so fortunately works.
final newState = _MockGameState();
when(() => newState.isGameOver).thenReturn(true);
when(() => newState.status).thenReturn(GameStatus.gameOver);
game.descendants().whereType<ControlledBall>().forEach(
(ball) => ball.controller.lost(),
);

@ -27,6 +27,7 @@ void main() {
multiplier: 1,
rounds: 1,
bonusHistory: [],
status: GameStatus.playing,
);
setUp(() async {

@ -18,6 +18,7 @@ void main() {
multiplier: 1,
rounds: 3,
bonusHistory: [],
status: GameStatus.playing,
);
setUp(() {

@ -23,6 +23,7 @@ void main() {
multiplier: 1,
rounds: 1,
bonusHistory: [],
status: GameStatus.playing,
);
setUp(() {

@ -1,26 +1,8 @@
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';
class _MockPinballGame extends Mock implements PinballGame {}
class _MockGameFlowController extends Mock implements GameFlowController {}
void main() {
late PinballGame pinballGame;
setUp(() {
pinballGame = _MockPinballGame();
when(
() => pinballGame.gameFlowController,
).thenReturn(
_MockGameFlowController(),
);
});
group('StartGameBloc', () {
blocTest<StartGameBloc, StartGameState>(
'on PlayTapped changes status to selectCharacter',

@ -12,17 +12,14 @@ import '../../helpers/helpers.dart';
class _MockStartGameBloc extends Mock implements StartGameBloc {}
class _MockCharacterThemeCubit extends Mock implements CharacterThemeCubit {}
class _MockPinballGame extends Mock implements PinballGame {}
class _MockGameBloc extends Mock implements GameBloc {}
class _MockGameFlowController extends Mock implements GameFlowController {}
class _MockCharacterThemeCubit extends Mock implements CharacterThemeCubit {}
class _MockPinballAudio extends Mock implements PinballAudio {}
void main() {
late StartGameBloc startGameBloc;
late PinballGame pinballGame;
late PinballAudio pinballAudio;
late CharacterThemeCubit characterThemeCubit;
@ -31,14 +28,25 @@ void main() {
await mockFlameImages();
startGameBloc = _MockStartGameBloc();
pinballGame = _MockPinballGame();
pinballAudio = _MockPinballAudio();
characterThemeCubit = _MockCharacterThemeCubit();
});
group('on selectCharacter status', () {
late GameBloc gameBloc;
setUp(() {
gameBloc = _MockGameBloc();
whenListen(
gameBloc,
const Stream<GameState>.empty(),
initialState: const GameState.initial(),
);
});
testWidgets(
'calls start on the game controller',
'calls onGameStarted event',
(tester) async {
whenListen(
startGameBloc,
@ -47,19 +55,16 @@ void main() {
),
initialState: const StartGameState.initial(),
);
final gameController = _MockGameFlowController();
when(() => pinballGame.gameFlowController)
.thenAnswer((_) => gameController);
await tester.pumpApp(
StartGameListener(
game: pinballGame,
child: const SizedBox.shrink(),
const StartGameListener(
child: SizedBox.shrink(),
),
gameBloc: gameBloc,
startGameBloc: startGameBloc,
);
verify(gameController.start).called(1);
verify(() => gameBloc.add(const GameStarted())).called(1);
},
);
@ -78,15 +83,12 @@ void main() {
Stream.value(const CharacterThemeState.initial()),
initialState: const CharacterThemeState.initial(),
);
final gameController = _MockGameFlowController();
when(() => pinballGame.gameFlowController)
.thenAnswer((_) => gameController);
await tester.pumpApp(
StartGameListener(
game: pinballGame,
child: const SizedBox.shrink(),
const StartGameListener(
child: SizedBox.shrink(),
),
gameBloc: gameBloc,
startGameBloc: startGameBloc,
characterThemeCubit: characterThemeCubit,
);
@ -113,9 +115,8 @@ void main() {
);
await tester.pumpApp(
StartGameListener(
game: pinballGame,
child: const SizedBox.shrink(),
const StartGameListener(
child: SizedBox.shrink(),
),
startGameBloc: startGameBloc,
);
@ -141,9 +142,8 @@ void main() {
);
await tester.pumpApp(
StartGameListener(
game: pinballGame,
child: const SizedBox.shrink(),
const StartGameListener(
child: SizedBox.shrink(),
),
startGameBloc: startGameBloc,
);
@ -173,9 +173,8 @@ void main() {
);
await tester.pumpApp(
StartGameListener(
game: pinballGame,
child: const SizedBox.shrink(),
const StartGameListener(
child: SizedBox.shrink(),
),
startGameBloc: startGameBloc,
);
@ -208,9 +207,8 @@ void main() {
'adds HowToPlayFinished event',
(tester) async {
await tester.pumpApp(
StartGameListener(
game: pinballGame,
child: const SizedBox.shrink(),
const StartGameListener(
child: SizedBox.shrink(),
),
startGameBloc: startGameBloc,
);
@ -239,9 +237,8 @@ void main() {
'plays the I/O Pinball voice over audio',
(tester) async {
await tester.pumpApp(
StartGameListener(
game: pinballGame,
child: const SizedBox.shrink(),
const StartGameListener(
child: SizedBox.shrink(),
),
startGameBloc: startGameBloc,
pinballAudio: pinballAudio,

Loading…
Cancel
Save