diff --git a/lib/game/components/backbox/backbox.dart b/lib/game/components/backbox/backbox.dart index f435ebdf..dc5041f0 100644 --- a/lib/game/components/backbox/backbox.dart +++ b/lib/game/components/backbox/backbox.dart @@ -1,10 +1,9 @@ import 'dart:async'; import 'package:flame/components.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; import 'package:pinball/game/components/backbox/displays/displays.dart'; +import 'package:pinball/game/pinball_game.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' hide Assets; @@ -12,11 +11,11 @@ import 'package:pinball_theme/pinball_theme.dart' hide Assets; /// {@template backbox} /// The [Backbox] of the pinball machine. /// {@endtemplate} -class Backbox extends PositionComponent with HasGameRef, ZIndex { +class Backbox extends PositionComponent with HasGameRef, ZIndex { /// {@macro backbox} Backbox({ - LeaderboardRepository? leaderboardRepository, - }) : _leaderboardRepository = leaderboardRepository, + required BackboxBloc bloc, + }) : _bloc = bloc, super( position: Vector2(0, -87), anchor: Anchor.bottomCenter, @@ -29,16 +28,12 @@ class Backbox extends PositionComponent with HasGameRef, ZIndex { } late final Component _display; - late final LeaderboardRepository? _leaderboardRepository; - late BackboxBloc _bloc; + final BackboxBloc _bloc; late StreamSubscription _subscription; @override Future onLoad() async { - final repository = _leaderboardRepository ?? - gameRef.buildContext!.read(); - _bloc = BackboxBloc(leaderboardRepository: repository); - _bloc.stream.listen((state) { + _subscription = _bloc.stream.listen((state) { _display.children.removeWhere((_) => true); _build(state); }); diff --git a/lib/game/components/backbox/bloc/backbox_event.dart b/lib/game/components/backbox/bloc/backbox_event.dart index bbee0be3..977521c2 100644 --- a/lib/game/components/backbox/bloc/backbox_event.dart +++ b/lib/game/components/backbox/bloc/backbox_event.dart @@ -24,7 +24,7 @@ class PlayerInitialsRequested extends BackboxEvent { final CharacterTheme character; @override - List get props => [props, character]; + List get props => [score, character]; } /// {@template player_initials_submited} @@ -46,5 +46,5 @@ class PlayerInitialsSubmited extends BackboxEvent { final CharacterTheme character; @override - List get props => [props, initials, character]; + List get props => [score, initials, character]; } diff --git a/lib/game/components/backbox/displays/loading_display.dart b/lib/game/components/backbox/displays/loading_display.dart index 8cf2f9a4..3d569230 100644 --- a/lib/game/components/backbox/displays/loading_display.dart +++ b/lib/game/components/backbox/displays/loading_display.dart @@ -1,6 +1,7 @@ import 'package:flame/components.dart'; import 'package:flutter/material.dart'; import 'package:pinball/game/game.dart'; +import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_ui/pinball_ui.dart'; @@ -13,9 +14,14 @@ final _bodyTextPaint = TextPaint( ); /// {@template loading_display} -/// +/// Display used to show the loading animation /// {@endtemplate} class LoadingDisplay extends TextComponent with HasGameRef { + /// {@template loading_display + LoadingDisplay({ AppLocalizations? l10n }) : _l10n = l10n; + + final AppLocalizations? _l10n; + late final String _label; @override @@ -24,7 +30,7 @@ class LoadingDisplay extends TextComponent with HasGameRef { position = Vector2(0, -10); anchor = Anchor.center; - text = _label = gameRef.l10n.loading; + text = _label = (_l10n ?? gameRef.l10n).loading; textRenderer = _bodyTextPaint; await add( diff --git a/lib/game/pinball_game.dart b/lib/game/pinball_game.dart index 817e9d28..e5edc63e 100644 --- a/lib/game/pinball_game.dart +++ b/lib/game/pinball_game.dart @@ -7,8 +7,9 @@ import 'package:flame/input.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; +import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/game/behaviors/behaviors.dart'; +import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_audio/pinball_audio.dart'; @@ -24,6 +25,7 @@ class PinballGame extends PinballForge2DGame MultiTouchTapDetector { PinballGame({ required this.characterTheme, + required this.leaderboardRepository, required this.l10n, required this.player, }) : super(gravity: Vector2(0, 30)) { @@ -41,6 +43,8 @@ class PinballGame extends PinballForge2DGame final PinballPlayer player; + final LeaderboardRepository leaderboardRepository; + final AppLocalizations l10n; @override @@ -50,7 +54,7 @@ class PinballGame extends PinballForge2DGame final machine = [ BoardBackgroundSpriteComponent(), Boundaries(), - Backbox(), + Backbox(bloc: BackboxBloc(leaderboardRepository: leaderboardRepository)), ]; final decals = [ GoogleWord(position: Vector2(-4.25, 1.8)), @@ -185,11 +189,13 @@ class _GameBallsController extends ComponentController class DebugPinballGame extends PinballGame with FPSCounter, PanDetector { DebugPinballGame({ required CharacterTheme characterTheme, + required LeaderboardRepository leaderboardRepository, required AppLocalizations l10n, required PinballPlayer player, }) : super( characterTheme: characterTheme, player: player, + leaderboardRepository: leaderboardRepository, l10n: l10n, ) { controller = _GameBallsController(this); @@ -204,18 +210,6 @@ class DebugPinballGame extends PinballGame with FPSCounter, PanDetector { await add(PreviewLine()); await add(_DebugInformation()); - await add( - KeyboardInputController( - keyUp: { - LogicalKeyboardKey.escape: () { - read().add(const RoundLost()); - read().add(const RoundLost()); - read().add(const RoundLost()); - return true; - }, - }, - ), - ); } @override diff --git a/lib/game/view/pinball_game_page.dart b/lib/game/view/pinball_game_page.dart index b56e00f4..31ba304b 100644 --- a/lib/game/view/pinball_game_page.dart +++ b/lib/game/view/pinball_game_page.dart @@ -4,6 +4,7 @@ import 'package:flame/game.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/assets_manager/assets_manager.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/l10n/l10n.dart'; @@ -37,23 +38,25 @@ class PinballGamePage extends StatelessWidget { final characterTheme = context.read().state.characterTheme; final player = context.read(); - final pinballAudio = context.read(); + final leaderboardRepository = context.read(); final game = isDebugMode ? DebugPinballGame( characterTheme: characterTheme, player: player, + leaderboardRepository: leaderboardRepository, l10n: context.l10n, ) : PinballGame( characterTheme: characterTheme, player: player, + leaderboardRepository: leaderboardRepository, l10n: context.l10n, ); final loadables = [ ...game.preLoadAssets(), - ...pinballAudio.load(), + ...player.load(), ...BonusAnimation.loadAssets(), ...SelectedCharacter.loadAssets(), ]; diff --git a/lib/leaderboard/models/leader_board_entry.dart b/lib/leaderboard/models/leader_board_entry.dart index a86975dd..db4980a1 100644 --- a/lib/leaderboard/models/leader_board_entry.dart +++ b/lib/leaderboard/models/leader_board_entry.dart @@ -1,3 +1,4 @@ +import 'package:equatable/equatable.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball_theme/pinball_theme.dart'; @@ -6,9 +7,9 @@ import 'package:pinball_theme/pinball_theme.dart'; /// player's initials, score, and chosen character. /// /// {@endtemplate} -class LeaderboardEntry { +class LeaderboardEntry extends Equatable { /// {@macro leaderboard_entry} - LeaderboardEntry({ + const LeaderboardEntry({ required this.rank, required this.playerInitials, required this.score, @@ -26,6 +27,9 @@ class LeaderboardEntry { /// [CharacterTheme] for [LeaderboardEntry]. final AssetGenImage character; + + @override + List get props => [rank, playerInitials, score, character]; } /// Converts [LeaderboardEntryData] from repository to [LeaderboardEntry]. diff --git a/test/game/components/backbox/backbox_test.dart b/test/game/components/backbox/backbox_test.dart index 341198f8..f319d0fe 100644 --- a/test/game/components/backbox/backbox_test.dart +++ b/test/game/components/backbox/backbox_test.dart @@ -1,10 +1,15 @@ -// ignore_for_file: cascade_invocations +// ignore_for_file: cascade_invocations, prefer_const_constructors +import 'package:bloc_test/bloc_test.dart'; import 'package:flame/components.dart'; import 'package:flame_test/flame_test.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:mocktail/mocktail.dart'; -import 'package:pinball/game/components/backbox/displays/initials_input_display.dart'; +import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; +import 'package:pinball/game/components/backbox/displays/displays.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/l10n/l10n.dart'; import 'package:pinball_components/pinball_components.dart'; @@ -12,6 +17,24 @@ import 'package:pinball_theme/pinball_theme.dart' as theme; import '../../../helpers/helpers.dart'; +class _MockRawKeyUpEvent extends Mock implements RawKeyUpEvent { + @override + String toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) { + return super.toString(); + } +} + +RawKeyUpEvent _mockKeyUp(LogicalKeyboardKey key) { + final event = _MockRawKeyUpEvent(); + when(() => event.logicalKey).thenReturn(key); + return event; +} + +class _MockBackboxBloc extends Mock implements BackboxBloc {} + +class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { +} + class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get score => ''; @@ -33,13 +56,16 @@ class _MockAppLocalizations extends Mock implements AppLocalizations { @override String get toSubmit => ''; + + @override + String get loading => ''; } void main() { TestWidgetsFlutterBinding.ensureInitialized(); - final characterIconPath = theme.Assets.images.dash.leaderboardIcon.keyName; + const character = theme.AndroidTheme(); final assets = [ - characterIconPath, + character.leaderboardIcon.keyName, Assets.images.backbox.marquee.keyName, Assets.images.backbox.displayDivider.keyName, ]; @@ -50,11 +76,22 @@ void main() { ), ); + late BackboxBloc bloc; + + setUp(() { + bloc = _MockBackboxBloc(); + whenListen( + bloc, + Stream.value(LoadingState()), + initialState: LoadingState(), + ); + }); + group('Backbox', () { flameTester.test( 'loads correctly', (game) async { - final backbox = Backbox(); + final backbox = Backbox(bloc: bloc); await game.ensureAdd(backbox); expect(game.children, contains(backbox)); @@ -68,7 +105,9 @@ void main() { game.camera ..followVector2(Vector2(0, -130)) ..zoom = 6; - await game.ensureAdd(Backbox()); + await game.ensureAdd( + Backbox(bloc: bloc), + ); await tester.pump(); }, verify: (game, tester) async { @@ -80,18 +119,120 @@ void main() { ); flameTester.test( - 'initialsInput adds InitialsInputDisplay', + 'requestInitials adds InitialsInputDisplay', (game) async { - final backbox = Backbox(); + final backbox = Backbox( + bloc: BackboxBloc( + leaderboardRepository: _MockLeaderboardRepository(), + ), + ); await game.ensureAdd(backbox); - await backbox.initialsInput( + backbox.requestInitials( score: 0, - characterIconPath: characterIconPath, - onSubmit: (_) {}, + character: character, + ); + await game.ready(); + + expect( + backbox.descendants().whereType().length, + equals(1), + ); + }, + ); + + flameTester.test( + 'adds PlayerInitialsSubmited when initials are submitted', + (game) async { + final bloc = _MockBackboxBloc(); + final state = InitialsFormState( + score: 10, + character: theme.AndroidTheme(), + ); + whenListen( + bloc, + Stream.value(state), + initialState: state, + ); + final backbox = Backbox(bloc: bloc); + await game.ensureAdd(backbox); + + game.onKeyEvent(_mockKeyUp(LogicalKeyboardKey.enter), {}); + verify( + () => bloc.add( + PlayerInitialsSubmited( + score: 10, + initials: 'AAA', + character: theme.AndroidTheme(), + ), + ), + ).called(1); + }, + ); + + flameTester.test( + 'adds InitialsSubmissionSuccessDisplay on InitialsSuccessState', + (game) async { + whenListen( + bloc, + Stream.value(InitialsSuccessState()), + initialState: InitialsSuccessState(), + ); + final backbox = Backbox(bloc: bloc); + await game.ensureAdd(backbox); + + expect( + game + .descendants() + .whereType() + .length, + equals(1), + ); + }, + ); + + flameTester.test( + 'adds InitialsSubmissionFailureDisplay on InitialsFailureState', + (game) async { + whenListen( + bloc, + Stream.value(InitialsFailureState()), + initialState: InitialsFailureState(), + ); + final backbox = Backbox(bloc: bloc); + await game.ensureAdd(backbox); + + expect( + game + .descendants() + .whereType() + .length, + equals(1), ); + }, + ); + + flameTester.test( + 'closes the subscription when it is removed', + (game) async { + final backbox = Backbox(bloc: bloc); + await game.ensureAdd(backbox); + + backbox.removeFromParent(); await game.ready(); - expect(backbox.firstChild(), isNotNull); + whenListen( + bloc, + Stream.value(InitialsFailureState()), + initialState: InitialsFailureState(), + ); + + expect( + backbox + .descendants() + .whereType() + .isEmpty, + isTrue, + ); }, ); }); diff --git a/test/game/components/backbox/bloc/backbox_bloc_test.dart b/test/game/components/backbox/bloc/backbox_bloc_test.dart new file mode 100644 index 00000000..6cf92efc --- /dev/null +++ b/test/game/components/backbox/bloc/backbox_bloc_test.dart @@ -0,0 +1,92 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:leaderboard_repository/leaderboard_repository.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { +} + +void main() { + late LeaderboardRepository leaderboardRepository; + + group('BackboxBloc', () { + blocTest( + 'adds InitialsFormState on PlayerInitialsRequested', + setUp: () { + leaderboardRepository = _MockLeaderboardRepository(); + }, + build: () => BackboxBloc(leaderboardRepository: leaderboardRepository), + act: (bloc) => bloc.add( + PlayerInitialsRequested( + score: 100, + character: AndroidTheme(), + ), + ), + expect: () => [ + InitialsFormState(score: 100, character: AndroidTheme()), + ], + ); + + group('PlayerInitialsSubmited', () { + blocTest( + 'adds [LoadingState, InitialsSuccessState] when submission works', + setUp: () { + leaderboardRepository = _MockLeaderboardRepository(); + when( + () => leaderboardRepository.addLeaderboardEntry( + LeaderboardEntryData( + playerInitials: 'AAA', + score: 10, + character: CharacterType.dash, + ), + ), + ).thenAnswer((_) async {}); + }, + build: () => BackboxBloc(leaderboardRepository: leaderboardRepository), + act: (bloc) => bloc.add( + PlayerInitialsSubmited( + score: 10, + initials: 'AAA', + character: DashTheme(), + ), + ), + expect: () => [ + LoadingState(), + InitialsSuccessState(), + ], + ); + + blocTest( + 'adds [LoadingState, InitialsSuccessState] when submission works', + setUp: () { + leaderboardRepository = _MockLeaderboardRepository(); + when( + () => leaderboardRepository.addLeaderboardEntry( + LeaderboardEntryData( + playerInitials: 'AAA', + score: 10, + character: CharacterType.dash, + ), + ), + ).thenThrow(Exception('Error')); + }, + build: () => BackboxBloc(leaderboardRepository: leaderboardRepository), + act: (bloc) => bloc.add( + PlayerInitialsSubmited( + score: 10, + initials: 'AAA', + character: DashTheme(), + ), + ), + expect: () => [ + LoadingState(), + InitialsFailureState(), + ], + ); + }); + }); +} diff --git a/test/game/components/backbox/bloc/backbox_event_test.dart b/test/game/components/backbox/bloc/backbox_event_test.dart new file mode 100644 index 00000000..f0e334f4 --- /dev/null +++ b/test/game/components/backbox/bloc/backbox_event_test.dart @@ -0,0 +1,126 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('BackboxEvent', () { + group('PlayerInitialsRequested', () { + test('can be instantiated', () { + expect( + PlayerInitialsRequested(score: 0, character: AndroidTheme()), + isNotNull, + ); + }); + + test('supports value comparison', () { + expect( + PlayerInitialsRequested(score: 0, character: AndroidTheme()), + equals( + PlayerInitialsRequested(score: 0, character: AndroidTheme()), + ), + ); + + expect( + PlayerInitialsRequested(score: 0, character: AndroidTheme()), + isNot( + equals( + PlayerInitialsRequested(score: 1, character: AndroidTheme()), + ), + ), + ); + + expect( + PlayerInitialsRequested(score: 0, character: AndroidTheme()), + isNot( + equals( + PlayerInitialsRequested(score: 0, character: SparkyTheme()), + ), + ), + ); + }); + }); + + group('PlayerInitialsSubmited', () { + test('can be instantiated', () { + expect( + PlayerInitialsSubmited( + score: 0, + initials: 'AAA', + character: AndroidTheme(), + ), + isNotNull, + ); + }); + + test('supports value comparison', () { + expect( + PlayerInitialsSubmited( + score: 0, + initials: 'AAA', + character: AndroidTheme(), + ), + equals( + PlayerInitialsSubmited( + score: 0, + initials: 'AAA', + character: AndroidTheme(), + ), + ), + ); + + expect( + PlayerInitialsSubmited( + score: 0, + initials: 'AAA', + character: AndroidTheme(), + ), + isNot( + equals( + PlayerInitialsSubmited( + score: 1, + initials: 'AAA', + character: AndroidTheme(), + ), + ), + ), + ); + + expect( + PlayerInitialsSubmited( + score: 0, + initials: 'AAA', + character: AndroidTheme(), + ), + isNot( + equals( + PlayerInitialsSubmited( + score: 0, + initials: 'AAA', + character: SparkyTheme(), + ), + ), + ), + ); + + expect( + PlayerInitialsSubmited( + score: 0, + initials: 'AAA', + character: AndroidTheme(), + ), + isNot( + equals( + PlayerInitialsSubmited( + score: 0, + initials: 'BBB', + character: AndroidTheme(), + ), + ), + ), + ); + }); + }); + }); +} diff --git a/test/game/components/backbox/bloc/backbox_state_test.dart b/test/game/components/backbox/bloc/backbox_state_test.dart new file mode 100644 index 00000000..ff18a5b5 --- /dev/null +++ b/test/game/components/backbox/bloc/backbox_state_test.dart @@ -0,0 +1,126 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/game/components/backbox/bloc/backbox_bloc.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('BackboxState', () { + group('LoadingState', () { + test('can be instantiated', () { + expect(LoadingState(), isNotNull); + }); + + test('supports value comparison', () { + expect(LoadingState(), equals(LoadingState())); + }); + }); + + group('FailureState', () { + test('can be instantiated', () { + expect(FailureState(), isNotNull); + }); + + test('supports value comparison', () { + expect(FailureState(), equals(FailureState())); + }); + }); + + group('LeaderboardSuccessState', () { + test('can be instantiated', () { + expect(LeaderboardSuccessState(), isNotNull); + }); + + test('supports value comparison', () { + expect(LeaderboardSuccessState(), equals(LeaderboardSuccessState())); + }); + }); + + group('LeaderboardFailureState', () { + test('can be instantiated', () { + expect(LeaderboardFailureState(), isNotNull); + }); + + test('supports value comparison', () { + expect(LeaderboardFailureState(), equals(LeaderboardFailureState())); + }); + }); + + group('InitialsFormState', () { + test('can be InitialsFormState', () { + expect( + InitialsFormState( + score: 0, + character: AndroidTheme(), + ), + isNotNull, + ); + }); + + test('supports value comparison', () { + expect( + InitialsFormState( + score: 0, + character: AndroidTheme(), + ), + equals( + InitialsFormState( + score: 0, + character: AndroidTheme(), + ), + ), + ); + + expect( + InitialsFormState( + score: 0, + character: AndroidTheme(), + ), + isNot( + equals( + InitialsFormState( + score: 1, + character: AndroidTheme(), + ), + ), + ), + ); + + expect( + InitialsFormState( + score: 0, + character: AndroidTheme(), + ), + isNot( + equals( + InitialsFormState( + score: 0, + character: SparkyTheme(), + ), + ), + ), + ); + }); + }); + + group('InitialsSuccessState', () { + test('can be instantiated', () { + expect(InitialsSuccessState(), isNotNull); + }); + + test('supports value comparison', () { + expect(InitialsSuccessState(), equals(InitialsSuccessState())); + }); + + group('InitialsFailureState', () { + test('can be instantiated', () { + expect(InitialsFailureState(), isNotNull); + }); + + test('supports value comparison', () { + expect(InitialsFailureState(), equals(InitialsFailureState())); + }); + }); + }); + }); +} diff --git a/test/game/components/backbox/displays/initials_submission_failure_display_test.dart b/test/game/components/backbox/displays/initials_submission_failure_display_test.dart new file mode 100644 index 00000000..0e4553c0 --- /dev/null +++ b/test/game/components/backbox/displays/initials_submission_failure_display_test.dart @@ -0,0 +1,22 @@ +// 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/game/components/backbox/displays/initials_submission_failure_display.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('InitialsSubmissionFailureDisplay', () { + final flameTester = FlameTester(EmptyKeyboardPinballTestGame.new); + + flameTester.test('renders correctly', (game) async { + await game.ensureAdd(InitialsSubmissionFailureDisplay()); + + final component = game.firstChild(); + expect(component, isNotNull); + expect(component?.text, equals('Failure!')); + }); + }); +} diff --git a/test/game/components/backbox/displays/initials_submission_success_display_test.dart b/test/game/components/backbox/displays/initials_submission_success_display_test.dart new file mode 100644 index 00000000..bc3aca9c --- /dev/null +++ b/test/game/components/backbox/displays/initials_submission_success_display_test.dart @@ -0,0 +1,22 @@ +// 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/game/components/backbox/displays/initials_submission_success_display.dart'; + +import '../../../../helpers/helpers.dart'; + +void main() { + group('InitialsSubmissionSuccessDisplay', () { + final flameTester = FlameTester(EmptyKeyboardPinballTestGame.new); + + flameTester.test('renders correctly', (game) async { + await game.ensureAdd(InitialsSubmissionSuccessDisplay()); + + final component = game.firstChild(); + expect(component, isNotNull); + expect(component?.text, equals('Success!')); + }); + }); +} diff --git a/test/game/components/backbox/displays/loading_display_test.dart b/test/game/components/backbox/displays/loading_display_test.dart new file mode 100644 index 00000000..5a475b7c --- /dev/null +++ b/test/game/components/backbox/displays/loading_display_test.dart @@ -0,0 +1,50 @@ +// 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:mocktail/mocktail.dart'; +import 'package:pinball/game/components/backbox/displays/loading_display.dart'; +import 'package:pinball/l10n/l10n.dart'; + +import '../../../../helpers/helpers.dart'; + +class _MockAppLocalizations extends Mock implements AppLocalizations { + @override + String get loading => 'Loading'; +} + +void main() { + group('LoadingDisplay', () { + final flameTester = FlameTester(EmptyKeyboardPinballTestGame.new); + + flameTester.test('renders correctly', (game) async { + await game.ensureAdd(LoadingDisplay(l10n: _MockAppLocalizations())); + + final component = game.firstChild(); + expect(component, isNotNull); + expect(component?.text, equals('Loading')); + }); + + flameTester.test('use ellipses as animation', (game) async { + await game.ensureAdd(LoadingDisplay(l10n: _MockAppLocalizations())); + + final component = game.firstChild(); + expect(component?.text, equals('Loading')); + + final timer = component?.firstChild(); + + timer?.update(1.1); + expect(component?.text, equals('Loading.')); + + timer?.update(1.1); + expect(component?.text, equals('Loading..')); + + timer?.update(1.1); + expect(component?.text, equals('Loading...')); + + timer?.update(1.1); + expect(component?.text, equals('Loading')); + }); + }); +} diff --git a/test/game/components/backbox/failures/backbox_isolatedDiff.png b/test/game/components/backbox/failures/backbox_isolatedDiff.png new file mode 100644 index 00000000..a79937f1 Binary files /dev/null and b/test/game/components/backbox/failures/backbox_isolatedDiff.png differ diff --git a/test/game/components/backbox/failures/backbox_maskedDiff.png b/test/game/components/backbox/failures/backbox_maskedDiff.png new file mode 100644 index 00000000..a79937f1 Binary files /dev/null and b/test/game/components/backbox/failures/backbox_maskedDiff.png differ diff --git a/test/game/components/backbox/failures/backbox_masterImage.png b/test/game/components/backbox/failures/backbox_masterImage.png new file mode 100644 index 00000000..962573ab Binary files /dev/null and b/test/game/components/backbox/failures/backbox_masterImage.png differ diff --git a/test/game/components/backbox/failures/backbox_testImage.png b/test/game/components/backbox/failures/backbox_testImage.png new file mode 100644 index 00000000..128801a1 Binary files /dev/null and b/test/game/components/backbox/failures/backbox_testImage.png differ diff --git a/test/game/components/game_bloc_status_listener_test.dart b/test/game/components/game_bloc_status_listener_test.dart index 39c81115..a796b20f 100644 --- a/test/game/components/game_bloc_status_listener_test.dart +++ b/test/game/components/game_bloc_status_listener_test.dart @@ -20,6 +20,11 @@ class _MockPinballPlayer extends Mock implements PinballPlayer {} void main() { group('GameBlocStatusListener', () { + + setUpAll(() { + registerFallbackValue(AndroidTheme()); + }); + group('listenWhen', () { test('is true when the game over state has changed', () { final state = GameState( @@ -58,10 +63,9 @@ void main() { gameFlowController.mockGameRef(game); when( - () => backbox.initialsInput( + () => backbox.requestInitials( score: any(named: 'score'), - characterIconPath: any(named: 'characterIconPath'), - onSubmit: any(named: 'onSubmit'), + character: any(named: 'character'), ), ).thenAnswer((_) async {}); when(cameraController.focusOnWaitingBackbox).thenAnswer((_) async {}); @@ -92,10 +96,9 @@ void main() { gameFlowController.onNewState(state); verify( - () => backbox.initialsInput( - score: state.displayScore, - characterIconPath: any(named: 'characterIconPath'), - onSubmit: any(named: 'onSubmit'), + () => backbox.requestInitials( + score: any(named: 'score'), + character: any(named: 'character'), ), ).called(1); verify(cameraController.focusOnGameOverBackbox).called(1); diff --git a/test/helpers/test_games.dart b/test/helpers/test_games.dart index 6bb39e2d..220693c3 100644 --- a/test/helpers/test_games.dart +++ b/test/helpers/test_games.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flame/input.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; +import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/game.dart'; import 'package:pinball/l10n/l10n.dart'; @@ -15,6 +16,9 @@ class _MockPinballPlayer extends Mock implements PinballPlayer {} class _MockAppLocalizations extends Mock implements AppLocalizations {} +class _MockLeaderboardRepository extends Mock implements LeaderboardRepository { +} + class TestGame extends Forge2DGame with FlameBloc { TestGame() { images.prefix = ''; @@ -25,11 +29,14 @@ class PinballTestGame extends PinballGame { PinballTestGame({ List? assets, PinballPlayer? player, + LeaderboardRepository? leaderboardRepository, CharacterTheme? theme, AppLocalizations? l10n, }) : _assets = assets, super( player: player ?? _MockPinballPlayer(), + leaderboardRepository: + leaderboardRepository ?? _MockLeaderboardRepository(), characterTheme: theme ?? const DashTheme(), l10n: l10n ?? _MockAppLocalizations(), ); @@ -48,11 +55,14 @@ class DebugPinballTestGame extends DebugPinballGame { DebugPinballTestGame({ List? assets, PinballPlayer? player, + LeaderboardRepository? leaderboardRepository, CharacterTheme? theme, AppLocalizations? l10n, }) : _assets = assets, super( player: player ?? _MockPinballPlayer(), + leaderboardRepository: + leaderboardRepository ?? _MockLeaderboardRepository(), characterTheme: theme ?? const DashTheme(), l10n: l10n ?? _MockAppLocalizations(), ); diff --git a/test/leaderboard/models/leader_board_entry_test.dart b/test/leaderboard/models/leader_board_entry_test.dart new file mode 100644 index 00000000..aa0e10a7 --- /dev/null +++ b/test/leaderboard/models/leader_board_entry_test.dart @@ -0,0 +1,42 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:leaderboard_repository/leaderboard_repository.dart'; +import 'package:pinball/leaderboard/models/leader_board_entry.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('LeaderboardEntry', () { + group('toEntry', () { + test('returns the correct from a to entry data', () { + expect( + LeaderboardEntryData.empty.toEntry(1), + LeaderboardEntry( + rank: '1', + playerInitials: '', + score: 0, + character: CharacterType.dash.toTheme.leaderboardIcon, + ), + ); + }); + }); + + group('CharacterType', () { + test('toTheme returns the correct theme', () { + expect(CharacterType.dash.toTheme, equals(DashTheme())); + expect(CharacterType.sparky.toTheme, equals(SparkyTheme())); + expect(CharacterType.android.toTheme, equals(AndroidTheme())); + expect(CharacterType.dino.toTheme, equals(DinoTheme())); + }); + }); + + group('CharacterTheme', () { + test('toType returns the correct type', () { + expect(DashTheme().toType, equals(CharacterType.dash)); + expect(SparkyTheme().toType, equals(CharacterType.sparky)); + expect(AndroidTheme().toType, equals(CharacterType.android)); + expect(DinoTheme().toType, equals(CharacterType.dino)); + }); + }); + }); +}