diff --git a/lib/game/bloc/game_bloc.dart b/lib/game/bloc/game_bloc.dart index 49f40d1f..43d6005b 100644 --- a/lib/game/bloc/game_bloc.dart +++ b/lib/game/bloc/game_bloc.dart @@ -17,12 +17,13 @@ class GameBloc extends Bloc { } void _onRoundLost(RoundLost event, Emitter emit) { - final score = state.score * state.multiplier; + final score = state.totalScore + state.roundScore * state.multiplier; final roundsLeft = math.max(state.rounds - 1, 0); emit( state.copyWith( - score: score, + totalScore: score, + roundScore: 0, multiplier: 1, rounds: roundsLeft, ), @@ -32,7 +33,7 @@ class GameBloc extends Bloc { void _onScored(Scored event, Emitter emit) { if (!state.isGameOver) { emit( - state.copyWith(score: state.score + event.points), + state.copyWith(roundScore: state.roundScore + event.points), ); } } diff --git a/lib/game/bloc/game_state.dart b/lib/game/bloc/game_state.dart index 4ce9042d..2ccb4405 100644 --- a/lib/game/bloc/game_state.dart +++ b/lib/game/bloc/game_state.dart @@ -26,22 +26,32 @@ enum GameBonus { class GameState extends Equatable { /// {@macro game_state} const GameState({ - required this.score, + required this.totalScore, + required this.roundScore, required this.multiplier, required this.rounds, required this.bonusHistory, - }) : assert(score >= 0, "Score can't be negative"), + }) : 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() - : score = 0, + : totalScore = 0, + roundScore = 0, multiplier = 1, rounds = 3, bonusHistory = const []; - /// The current score of the game. - final int score; + /// The score for the current round of the game. + /// + /// Multipliers are only applied to the score for the current round once is + /// lost. Then the [roundScore] is added to the [totalScore] and reset to 0 + /// for the next round. + final int roundScore; + + /// The total score of the game. + final int totalScore; /// The current multiplier for the score. final int multiplier; @@ -58,20 +68,25 @@ class GameState extends Equatable { /// Determines when the game is over. bool get isGameOver => rounds == 0; + /// The score displayed at the game. + int get displayScore => roundScore + totalScore; + GameState copyWith({ - int? score, + int? totalScore, + int? roundScore, int? multiplier, int? balls, int? rounds, List? bonusHistory, }) { assert( - score == null || score >= this.score, - "Score can't be decreased", + totalScore == null || totalScore >= this.totalScore, + "Total score can't be decreased", ); return GameState( - score: score ?? this.score, + totalScore: totalScore ?? this.totalScore, + roundScore: roundScore ?? this.roundScore, multiplier: multiplier ?? this.multiplier, rounds: rounds ?? this.rounds, bonusHistory: bonusHistory ?? this.bonusHistory, @@ -80,7 +95,8 @@ class GameState extends Equatable { @override List get props => [ - score, + totalScore, + roundScore, multiplier, rounds, bonusHistory, diff --git a/lib/game/components/game_flow_controller.dart b/lib/game/components/game_flow_controller.dart index 4af93610..1299e6eb 100644 --- a/lib/game/components/game_flow_controller.dart +++ b/lib/game/components/game_flow_controller.dart @@ -30,7 +30,7 @@ class GameFlowController extends ComponentController // TODO(erickzanardo): implement score submission and "navigate" to the // next page component.descendants().whereType().first.initialsInput( - score: state?.score ?? 0, + score: state?.displayScore ?? 0, characterIconPath: component.characterTheme.leaderboardIcon.keyName, ); component.firstChild()!.focusOnGameOverBackbox(); diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index d49216fb..aa963a53 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -81,7 +81,7 @@ class PinballGame extends PinballForge2DGame await super.onLoad(); } - BoardSide? focusedBoardSide; + final focusedBoardSide = {}; @override void onTapDown(int pointerId, TapDownInfo info) { @@ -94,9 +94,10 @@ class PinballGame extends PinballForge2DGame descendants().whereType().single.pullFor(2); } else { final leftSide = info.eventPosition.widget.x < canvasSize.x / 2; - focusedBoardSide = leftSide ? BoardSide.left : BoardSide.right; + focusedBoardSide[pointerId] = + leftSide ? BoardSide.left : BoardSide.right; final flippers = descendants().whereType().where((flipper) { - return flipper.side == focusedBoardSide; + return flipper.side == focusedBoardSide[pointerId]; }); flippers.first.moveUp(); } @@ -107,23 +108,23 @@ class PinballGame extends PinballForge2DGame @override void onTapUp(int pointerId, TapUpInfo info) { - _moveFlippersDown(); + _moveFlippersDown(pointerId); super.onTapUp(pointerId, info); } @override void onTapCancel(int pointerId) { - _moveFlippersDown(); + _moveFlippersDown(pointerId); super.onTapCancel(pointerId); } - void _moveFlippersDown() { - if (focusedBoardSide != null) { + void _moveFlippersDown(int pointerId) { + if (focusedBoardSide[pointerId] != null) { final flippers = descendants().whereType().where((flipper) { - return flipper.side == focusedBoardSide; + return flipper.side == focusedBoardSide[pointerId]; }); flippers.first.moveDown(); - focusedBoardSide = null; + focusedBoardSide.remove(pointerId); } } } diff --git a/lib/game/view/widgets/game_hud.dart b/lib/game/view/widgets/game_hud.dart index 605bceb4..b40536aa 100644 --- a/lib/game/view/widgets/game_hud.dart +++ b/lib/game/view/widgets/game_hud.dart @@ -7,8 +7,8 @@ import 'package:pinball_ui/pinball_ui.dart'; /// {@template game_hud} /// Overlay on the [PinballGame]. /// -/// Displays the current [GameState.score], [GameState.rounds] and animates when -/// the player gets a [GameBonus]. +/// Displays the current [GameState.displayScore], [GameState.rounds] and +/// animates when the player gets a [GameBonus]. /// {@endtemplate} class GameHud extends StatefulWidget { /// {@macro game_hud} diff --git a/lib/game/view/widgets/score_view.dart b/lib/game/view/widgets/score_view.dart index 1fe57eb1..76ab9fa4 100644 --- a/lib/game/view/widgets/score_view.dart +++ b/lib/game/view/widgets/score_view.dart @@ -69,7 +69,7 @@ class _ScoreText extends StatelessWidget { @override Widget build(BuildContext context) { - final score = context.select((GameBloc bloc) => bloc.state.score); + final score = context.select((GameBloc bloc) => bloc.state.displayScore); return Text( score.formatScore(), diff --git a/test/game/behaviors/scoring_behavior_test.dart b/test/game/behaviors/scoring_behavior_test.dart index 3e710641..47903d8a 100644 --- a/test/game/behaviors/scoring_behavior_test.dart +++ b/test/game/behaviors/scoring_behavior_test.dart @@ -52,7 +52,8 @@ void main() { blocBuilder: () { bloc = _MockGameBloc(); const state = GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 3, bonusHistory: [], diff --git a/test/game/bloc/game_bloc_test.dart b/test/game/bloc/game_bloc_test.dart index 3711105e..5927291b 100644 --- a/test/game/bloc/game_bloc_test.dart +++ b/test/game/bloc/game_bloc_test.dart @@ -6,7 +6,7 @@ void main() { group('GameBloc', () { test('initial state has 3 rounds and empty score', () { final gameBloc = GameBloc(); - expect(gameBloc.state.score, equals(0)); + expect(gameBloc.state.roundScore, equals(0)); expect(gameBloc.state.rounds, equals(3)); }); @@ -19,21 +19,17 @@ void main() { bloc.add(const RoundLost()); }, expect: () => [ - const GameState( - score: 0, - multiplier: 1, - rounds: 2, - bonusHistory: [], - ), + isA()..having((state) => state.rounds, 'rounds', 2), ], ); blocTest( - 'apply multiplier to score ' + 'apply multiplier to roundScore and add it to totalScore ' 'when round is lost', build: GameBloc.new, seed: () => const GameState( - score: 5, + totalScore: 10, + roundScore: 5, multiplier: 3, rounds: 2, bonusHistory: [], @@ -43,8 +39,8 @@ void main() { }, expect: () => [ isA() - ..having((state) => state.score, 'score', 15) - ..having((state) => state.rounds, 'rounds', 1), + ..having((state) => state.totalScore, 'totalScore', 25) + ..having((state) => state.roundScore, 'roundScore', 0) ], ); @@ -53,7 +49,8 @@ void main() { 'when round is lost', build: GameBloc.new, seed: () => const GameState( - score: 5, + totalScore: 10, + roundScore: 5, multiplier: 3, rounds: 2, bonusHistory: [], @@ -62,9 +59,7 @@ void main() { bloc.add(const RoundLost()); }, expect: () => [ - isA() - ..having((state) => state.multiplier, 'multiplier', 1) - ..having((state) => state.rounds, 'rounds', 1), + isA()..having((state) => state.multiplier, 'multiplier', 1) ], ); }); @@ -79,10 +74,10 @@ void main() { ..add(const Scored(points: 3)), expect: () => [ isA() - ..having((state) => state.score, 'score', 2) + ..having((state) => state.roundScore, 'roundScore', 2) ..having((state) => state.isGameOver, 'isGameOver', false), isA() - ..having((state) => state.score, 'score', 5) + ..having((state) => state.roundScore, 'roundScore', 5) ..having((state) => state.isGameOver, 'isGameOver', false), ], ); @@ -99,15 +94,15 @@ void main() { }, expect: () => [ isA() - ..having((state) => state.score, 'score', 0) + ..having((state) => state.roundScore, 'roundScore', 0) ..having((state) => state.rounds, 'rounds', 2) ..having((state) => state.isGameOver, 'isGameOver', false), isA() - ..having((state) => state.score, 'score', 0) + ..having((state) => state.roundScore, 'roundScore', 0) ..having((state) => state.rounds, 'rounds', 1) ..having((state) => state.isGameOver, 'isGameOver', false), isA() - ..having((state) => state.score, 'score', 0) + ..having((state) => state.roundScore, 'roundScore', 0) ..having((state) => state.rounds, 'rounds', 0) ..having((state) => state.isGameOver, 'isGameOver', true), ], @@ -124,11 +119,9 @@ void main() { ..add(const MultiplierIncreased()), expect: () => [ isA() - ..having((state) => state.score, 'score', 0) ..having((state) => state.multiplier, 'multiplier', 2) ..having((state) => state.isGameOver, 'isGameOver', false), isA() - ..having((state) => state.score, 'score', 0) ..having((state) => state.multiplier, 'multiplier', 3) ..having((state) => state.isGameOver, 'isGameOver', false), ], @@ -139,7 +132,8 @@ void main() { 'when multiplier is 6 and game is not over', build: GameBloc.new, seed: () => const GameState( - score: 0, + totalScore: 10, + roundScore: 0, multiplier: 6, rounds: 3, bonusHistory: [], @@ -160,15 +154,12 @@ void main() { }, expect: () => [ isA() - ..having((state) => state.score, 'score', 0) ..having((state) => state.multiplier, 'multiplier', 1) ..having((state) => state.isGameOver, 'isGameOver', false), isA() - ..having((state) => state.score, 'score', 0) ..having((state) => state.multiplier, 'multiplier', 1) ..having((state) => state.isGameOver, 'isGameOver', false), isA() - ..having((state) => state.score, 'score', 0) ..having((state) => state.multiplier, 'multiplier', 1) ..having((state) => state.isGameOver, 'isGameOver', true), ], diff --git a/test/game/bloc/game_state_test.dart b/test/game/bloc/game_state_test.dart index add25e05..b59115a3 100644 --- a/test/game/bloc/game_state_test.dart +++ b/test/game/bloc/game_state_test.dart @@ -8,14 +8,16 @@ void main() { test('supports value equality', () { expect( GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 3, bonusHistory: const [], ), equals( const GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 3, bonusHistory: [], @@ -28,7 +30,8 @@ void main() { test('can be instantiated', () { expect( const GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 3, bonusHistory: [], @@ -40,11 +43,29 @@ void main() { test( 'throws AssertionError ' - 'when score is negative', + 'when totalScore is negative', () { expect( () => GameState( - score: -1, + totalScore: -1, + roundScore: 0, + multiplier: 1, + rounds: 3, + bonusHistory: const [], + ), + throwsAssertionError, + ); + }, + ); + + test( + 'throws AssertionError ' + 'when roundScore is negative', + () { + expect( + () => GameState( + totalScore: 0, + roundScore: -1, multiplier: 1, rounds: 3, bonusHistory: const [], @@ -60,7 +81,8 @@ void main() { () { expect( () => GameState( - score: 1, + totalScore: 0, + roundScore: 1, multiplier: 0, rounds: 3, bonusHistory: const [], @@ -76,7 +98,8 @@ void main() { () { expect( () => GameState( - score: 1, + totalScore: 0, + roundScore: 1, multiplier: 1, rounds: -1, bonusHistory: const [], @@ -91,7 +114,8 @@ void main() { 'is true ' 'when no rounds are left', () { const gameState = GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 0, bonusHistory: [], @@ -103,7 +127,8 @@ void main() { 'is false ' 'when one 1 round left', () { const gameState = GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 1, bonusHistory: [], @@ -115,16 +140,17 @@ void main() { group('copyWith', () { test( 'throws AssertionError ' - 'when scored is decreased', + 'when totalScore is decreased', () { const gameState = GameState( - score: 2, + totalScore: 2, + roundScore: 2, multiplier: 1, rounds: 3, bonusHistory: [], ); expect( - () => gameState.copyWith(score: gameState.score - 1), + () => gameState.copyWith(totalScore: gameState.totalScore - 1), throwsAssertionError, ); }, @@ -135,7 +161,8 @@ void main() { 'when no argument specified', () { const gameState = GameState( - score: 2, + totalScore: 0, + roundScore: 2, multiplier: 1, rounds: 3, bonusHistory: [], @@ -152,13 +179,15 @@ void main() { 'when all arguments specified', () { const gameState = GameState( - score: 2, + totalScore: 0, + roundScore: 2, multiplier: 1, rounds: 3, bonusHistory: [], ); final otherGameState = GameState( - score: gameState.score + 1, + totalScore: gameState.totalScore + 1, + roundScore: gameState.roundScore + 1, multiplier: gameState.multiplier + 1, rounds: gameState.rounds + 1, bonusHistory: const [GameBonus.googleWord], @@ -167,7 +196,8 @@ void main() { expect( gameState.copyWith( - score: otherGameState.score, + totalScore: otherGameState.totalScore, + roundScore: otherGameState.roundScore, multiplier: otherGameState.multiplier, rounds: otherGameState.rounds, bonusHistory: otherGameState.bonusHistory, diff --git a/test/game/components/controlled_flipper_test.dart b/test/game/components/controlled_flipper_test.dart index e8b7aaf3..f9561b60 100644 --- a/test/game/components/controlled_flipper_test.dart +++ b/test/game/components/controlled_flipper_test.dart @@ -27,7 +27,8 @@ void main() { blocBuilder: () { final bloc = _MockGameBloc(); const state = GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 0, bonusHistory: [], diff --git a/test/game/components/controlled_plunger_test.dart b/test/game/components/controlled_plunger_test.dart index c832e24a..4d9c9c74 100644 --- a/test/game/components/controlled_plunger_test.dart +++ b/test/game/components/controlled_plunger_test.dart @@ -22,7 +22,8 @@ void main() { blocBuilder: () { final bloc = _MockGameBloc(); const state = GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 0, bonusHistory: [], diff --git a/test/game/components/game_flow_controller_test.dart b/test/game/components/game_flow_controller_test.dart index c7196057..e403396d 100644 --- a/test/game/components/game_flow_controller_test.dart +++ b/test/game/components/game_flow_controller_test.dart @@ -23,7 +23,8 @@ void main() { group('listenWhen', () { test('is true when the game over state has changed', () { final state = GameState( - score: 10, + totalScore: 0, + roundScore: 10, multiplier: 1, rounds: 0, bonusHistory: const [], @@ -79,7 +80,8 @@ void main() { () { gameFlowController.onNewState( GameState( - score: 0, + totalScore: 0, + roundScore: 10, multiplier: 1, rounds: 0, bonusHistory: const [], diff --git a/test/game/components/multiballs/behaviors/multiballs_behavior_test.dart b/test/game/components/multiballs/behaviors/multiballs_behavior_test.dart index 5f8b1400..b294d350 100644 --- a/test/game/components/multiballs/behaviors/multiballs_behavior_test.dart +++ b/test/game/components/multiballs/behaviors/multiballs_behavior_test.dart @@ -74,7 +74,8 @@ void main() { test('is false when the bonusHistory state is the same', () { final previous = GameState.initial(); final state = GameState( - score: 10, + totalScore: 0, + roundScore: 10, multiplier: 1, rounds: 0, bonusHistory: const [], diff --git a/test/game/components/multipliers/behaviors/multipliers_behavior_test.dart b/test/game/components/multipliers/behaviors/multipliers_behavior_test.dart index 40a952f1..ca3c5921 100644 --- a/test/game/components/multipliers/behaviors/multipliers_behavior_test.dart +++ b/test/game/components/multipliers/behaviors/multipliers_behavior_test.dart @@ -56,7 +56,8 @@ void main() { group('listenWhen', () { test('is true when the multiplier has changed', () { final state = GameState( - score: 10, + totalScore: 0, + roundScore: 10, multiplier: 2, rounds: 0, bonusHistory: const [], @@ -71,7 +72,8 @@ void main() { test('is false when the multiplier state is the same', () { final state = GameState( - score: 10, + totalScore: 0, + roundScore: 10, multiplier: 1, rounds: 0, bonusHistory: const [], diff --git a/test/game/pinball_game_test.dart b/test/game/pinball_game_test.dart index eaa8c573..f942c47c 100644 --- a/test/game/pinball_game_test.dart +++ b/test/game/pinball_game_test.dart @@ -415,6 +415,51 @@ void main() { expect(flippers.first.body.linearVelocity.y, isPositive); }); + + flameTester.test( + 'multiple touches control both flippers', + (game) async { + await game.ready(); + + final raw = _MockTapDownDetails(); + when(() => raw.kind).thenReturn(PointerDeviceKind.touch); + + final leftEventPosition = _MockEventPosition(); + when(() => leftEventPosition.game).thenReturn(Vector2.zero()); + when(() => leftEventPosition.widget).thenReturn(Vector2.zero()); + + final rightEventPosition = _MockEventPosition(); + when(() => rightEventPosition.game).thenReturn(Vector2.zero()); + when(() => rightEventPosition.widget).thenReturn(game.canvasSize); + + final leftTapDownEvent = _MockTapDownInfo(); + when(() => leftTapDownEvent.eventPosition) + .thenReturn(leftEventPosition); + when(() => leftTapDownEvent.raw).thenReturn(raw); + + final rightTapDownEvent = _MockTapDownInfo(); + when(() => rightTapDownEvent.eventPosition) + .thenReturn(rightEventPosition); + when(() => rightTapDownEvent.raw).thenReturn(raw); + + final flippers = game.descendants().whereType(); + final rightFlipper = flippers.elementAt(0); + final leftFlipper = flippers.elementAt(1); + + game.onTapDown(0, leftTapDownEvent); + game.onTapDown(1, rightTapDownEvent); + + expect(leftFlipper.body.linearVelocity.y, isNegative); + expect(leftFlipper.side, equals(BoardSide.left)); + expect(rightFlipper.body.linearVelocity.y, isNegative); + expect(rightFlipper.side, equals(BoardSide.right)); + + expect( + game.focusedBoardSide, + equals({0: BoardSide.left, 1: BoardSide.right}), + ); + }, + ); }); group('plunger control', () { diff --git a/test/game/view/widgets/bonus_animation_test.dart b/test/game/view/widgets/bonus_animation_test.dart index 2284ca8d..52c1b3d8 100644 --- a/test/game/view/widgets/bonus_animation_test.dart +++ b/test/game/view/widgets/bonus_animation_test.dart @@ -1,9 +1,5 @@ // ignore_for_file: invalid_use_of_protected_member -import 'dart:typed_data'; - -import 'package:flame/assets.dart'; -import 'package:flame/flame.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -13,8 +9,6 @@ import 'package:pinball_flame/pinball_flame.dart'; import '../../../helpers/helpers.dart'; -class _MockImages extends Mock implements Images {} - class _MockCallback extends Mock { void call(); } @@ -24,13 +18,7 @@ void main() { const animationDuration = 6; setUp(() async { - // TODO(arturplaczek): need to find for a better solution for loading image - // or use original images from BonusAnimation.loadAssets() - final image = await decodeImageFromList(Uint8List.fromList(fakeImage)); - final images = _MockImages(); - when(() => images.fromCache(any())).thenReturn(image); - when(() => images.load(any())).thenAnswer((_) => Future.value(image)); - Flame.images = images; + await mockFlameImages(); }); group('loads SpriteAnimationWidget correctly for', () { diff --git a/test/game/view/widgets/game_hud_test.dart b/test/game/view/widgets/game_hud_test.dart index f8be70c2..19c860da 100644 --- a/test/game/view/widgets/game_hud_test.dart +++ b/test/game/view/widgets/game_hud_test.dart @@ -1,11 +1,8 @@ // ignore_for_file: prefer_const_constructors import 'dart:async'; -import 'dart:typed_data'; import 'package:bloc_test/bloc_test.dart'; -import 'package:flame/assets.dart'; -import 'package:flame/flame.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; @@ -18,8 +15,6 @@ import 'package:pinball_ui/pinball_ui.dart'; import '../../../helpers/helpers.dart'; -class _MockImages extends Mock implements Images {} - class _MockGameBloc extends Mock implements GameBloc {} void main() { @@ -27,22 +22,17 @@ void main() { late GameBloc gameBloc; const initialState = GameState( - score: 1000, + totalScore: 0, + roundScore: 1000, multiplier: 1, rounds: 1, bonusHistory: [], ); setUp(() async { - gameBloc = _MockGameBloc(); + await mockFlameImages(); - // TODO(arturplaczek): need to find for a better solution for loading - // image or use original images from BonusAnimation.loadAssets() - final image = await decodeImageFromList(Uint8List.fromList(fakeImage)); - final images = _MockImages(); - when(() => images.fromCache(any())).thenReturn(image); - when(() => images.load(any())).thenAnswer((_) => Future.value(image)); - Flame.images = images; + gameBloc = _MockGameBloc(); whenListen( gameBloc, @@ -81,7 +71,10 @@ void main() { gameBloc: gameBloc, ); - expect(find.text(initialState.score.formatScore()), findsOneWidget); + expect( + find.text(initialState.roundScore.formatScore()), + findsOneWidget, + ); }, ); diff --git a/test/game/view/widgets/play_button_overlay_test.dart b/test/game/view/widgets/play_button_overlay_test.dart index 1d7070e0..843592c3 100644 --- a/test/game/view/widgets/play_button_overlay_test.dart +++ b/test/game/view/widgets/play_button_overlay_test.dart @@ -1,10 +1,8 @@ import 'package:bloc_test/bloc_test.dart'; -import 'package:flame/flame.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_theme/pinball_theme.dart'; import '../../../helpers/helpers.dart'; @@ -21,14 +19,12 @@ void main() { late CharacterThemeCubit characterThemeCubit; setUp(() async { - Flame.images.prefix = ''; - await Flame.images.load(const DashTheme().animation.keyName); - await Flame.images.load(const AndroidTheme().animation.keyName); - await Flame.images.load(const DinoTheme().animation.keyName); - await Flame.images.load(const SparkyTheme().animation.keyName); + await mockFlameImages(); + game = _MockPinballGame(); gameFlowController = _MockGameFlowController(); characterThemeCubit = _MockCharacterThemeCubit(); + whenListen( characterThemeCubit, const Stream.empty(), diff --git a/test/game/view/widgets/round_count_display_test.dart b/test/game/view/widgets/round_count_display_test.dart index e3a4b887..049aba95 100644 --- a/test/game/view/widgets/round_count_display_test.dart +++ b/test/game/view/widgets/round_count_display_test.dart @@ -13,7 +13,8 @@ void main() { group('RoundCountDisplay renders', () { late GameBloc gameBloc; const initialState = GameState( - score: 0, + totalScore: 0, + roundScore: 0, multiplier: 1, rounds: 3, bonusHistory: [], diff --git a/test/game/view/widgets/score_view_test.dart b/test/game/view/widgets/score_view_test.dart index 695dc6e1..0e4acafc 100644 --- a/test/game/view/widgets/score_view_test.dart +++ b/test/game/view/widgets/score_view_test.dart @@ -15,9 +15,11 @@ class _MockGameBloc extends Mock implements GameBloc {} void main() { late GameBloc gameBloc; late StreamController stateController; - const score = 123456789; + const totalScore = 123456789; + const roundScore = 1234; const initialState = GameState( - score: score, + totalScore: totalScore, + roundScore: roundScore, multiplier: 1, rounds: 1, bonusHistory: [], @@ -42,7 +44,10 @@ void main() { ); await tester.pump(); - expect(find.text(score.formatScore()), findsOneWidget); + expect( + find.text(initialState.displayScore.formatScore()), + findsOneWidget, + ); }); testWidgets('renders game over', (tester) async { @@ -69,17 +74,23 @@ void main() { gameBloc: gameBloc, ); - expect(find.text(score.formatScore()), findsOneWidget); + expect( + find.text(initialState.displayScore.formatScore()), + findsOneWidget, + ); final newState = initialState.copyWith( - score: 987654321, + roundScore: 5678, ); stateController.add(newState); await tester.pump(); - expect(find.text(newState.score.formatScore()), findsOneWidget); + expect( + find.text(newState.displayScore.formatScore()), + findsOneWidget, + ); }); }); } diff --git a/test/helpers/fakes.dart b/test/helpers/fakes.dart index d782ede4..706733a1 100644 --- a/test/helpers/fakes.dart +++ b/test/helpers/fakes.dart @@ -5,70 +5,3 @@ import 'package:pinball/game/game.dart'; class FakeContact extends Fake implements Contact {} class FakeGameEvent extends Fake implements GameEvent {} - -const fakeImage = [ - 0x89, - 0x50, - 0x4E, - 0x47, - 0x0D, - 0x0A, - 0x1A, - 0x0A, - 0x00, - 0x00, - 0x00, - 0x0D, - 0x49, - 0x48, - 0x44, - 0x52, - 0x00, - 0x00, - 0x00, - 0x01, - 0x00, - 0x00, - 0x00, - 0x01, - 0x08, - 0x06, - 0x00, - 0x00, - 0x00, - 0x1F, - 0x15, - 0xC4, - 0x89, - 0x00, - 0x00, - 0x00, - 0x0A, - 0x49, - 0x44, - 0x41, - 0x54, - 0x78, - 0x9C, - 0x63, - 0x00, - 0x01, - 0x00, - 0x00, - 0x05, - 0x00, - 0x01, - 0x0D, - 0x0A, - 0x2D, - 0xB4, - 0x00, - 0x00, - 0x00, - 0x00, - 0x49, - 0x45, - 0x4E, - 0x44, - 0xAE, -]; diff --git a/test/helpers/helpers.dart b/test/helpers/helpers.dart index febf8d36..6621abcc 100644 --- a/test/helpers/helpers.dart +++ b/test/helpers/helpers.dart @@ -2,6 +2,7 @@ export 'builders.dart'; export 'fakes.dart'; export 'forge2d.dart'; export 'key_testers.dart'; +export 'mock_flame_images.dart'; export 'pump_app.dart'; export 'test_games.dart'; export 'text_span.dart'; diff --git a/test/helpers/mock_flame_images.dart b/test/helpers/mock_flame_images.dart new file mode 100644 index 00000000..48e4d40e --- /dev/null +++ b/test/helpers/mock_flame_images.dart @@ -0,0 +1,92 @@ +import 'dart:typed_data'; + +import 'package:flame/assets.dart'; +import 'package:flame/flame.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; + +class _MockImages extends Mock implements Images {} + +/// {@template mock_flame_images} +/// Mock for flame images instance. +/// +/// Using real images blocks the tests, for this reason we need fake image +/// everywhere we use [Images.fromCache] or [Images.load]. +/// {@endtemplate} +// TODO(arturplaczek): need to find for a better solution for loading image +// or use original images. +Future mockFlameImages() async { + final image = await decodeImageFromList(Uint8List.fromList(_fakeImage)); + final images = _MockImages(); + when(() => images.fromCache(any())).thenReturn(image); + when(() => images.load(any())).thenAnswer((_) => Future.value(image)); + Flame.images = images; +} + +const _fakeImage = [ + 0x89, + 0x50, + 0x4E, + 0x47, + 0x0D, + 0x0A, + 0x1A, + 0x0A, + 0x00, + 0x00, + 0x00, + 0x0D, + 0x49, + 0x48, + 0x44, + 0x52, + 0x00, + 0x00, + 0x00, + 0x01, + 0x00, + 0x00, + 0x00, + 0x01, + 0x08, + 0x06, + 0x00, + 0x00, + 0x00, + 0x1F, + 0x15, + 0xC4, + 0x89, + 0x00, + 0x00, + 0x00, + 0x0A, + 0x49, + 0x44, + 0x41, + 0x54, + 0x78, + 0x9C, + 0x63, + 0x00, + 0x01, + 0x00, + 0x00, + 0x05, + 0x00, + 0x01, + 0x0D, + 0x0A, + 0x2D, + 0xB4, + 0x00, + 0x00, + 0x00, + 0x00, + 0x49, + 0x45, + 0x4E, + 0x44, + 0xAE, +]; diff --git a/test/select_character/view/character_selection_page_test.dart b/test/select_character/view/character_selection_page_test.dart index 28033030..7d64dd39 100644 --- a/test/select_character/view/character_selection_page_test.dart +++ b/test/select_character/view/character_selection_page_test.dart @@ -1,5 +1,4 @@ import 'package:bloc_test/bloc_test.dart'; -import 'package:flame/flame.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; @@ -17,11 +16,8 @@ void main() { late CharacterThemeCubit characterThemeCubit; setUp(() async { - Flame.images.prefix = ''; - await Flame.images.load(const DashTheme().animation.keyName); - await Flame.images.load(const AndroidTheme().animation.keyName); - await Flame.images.load(const DinoTheme().animation.keyName); - await Flame.images.load(const SparkyTheme().animation.keyName); + await mockFlameImages(); + characterThemeCubit = _MockCharacterThemeCubit(); whenListen( characterThemeCubit,