diff --git a/lib/game/components/backbox/displays/leaderboard_display.dart b/lib/game/components/backbox/displays/leaderboard_display.dart index ac7a798d..ab418ccc 100644 --- a/lib/game/components/backbox/displays/leaderboard_display.dart +++ b/lib/game/components/backbox/displays/leaderboard_display.dart @@ -1,4 +1,6 @@ +// cSpell:ignore sublist import 'package:flame/components.dart'; +import 'package:flame/effects.dart'; import 'package:flutter/material.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:pinball/l10n/l10n.dart'; @@ -23,6 +25,23 @@ final _bodyTextPaint = TextPaint( ), ); +double _calcY(int i) => (i * 3.2) + 3.2; + +const _columns = [-14.0, 0.0, 14.0]; + +String _rank(int number) { + switch (number) { + case 1: + return '${number}st'; + case 2: + return '${number}nd'; + case 3: + return '${number}rd'; + default: + return '${number}th'; + } +} + /// {@template leaderboard_display} /// Component that builds the leaderboard list of the Backbox. /// {@endtemplate} @@ -33,21 +52,47 @@ class LeaderboardDisplay extends PositionComponent with HasGameRef { final List _entries; - double _calcY(int i) => (i * 3.2) + 3.2; + _MovePageArrow _findArrow({required bool active}) { + return descendants() + .whereType<_MovePageArrow>() + .firstWhere((arrow) => arrow.active == active); + } - static const _columns = [-15.0, 0.0, 15.0]; + void _changePage(List ranking, int offset) { + final current = descendants().whereType<_RankingPage>().single; + final activeArrow = _findArrow(active: true); + final inactiveArrow = _findArrow(active: false); - String _rank(int number) { - switch (number) { - case 1: - return '${number}st'; - case 2: - return '${number}nd'; - case 3: - return '${number}rd'; - default: - return '${number}th'; - } + activeArrow.active = false; + + current.add( + ScaleEffect.to( + Vector2(0, 1), + EffectController( + duration: 0.5, + curve: Curves.easeIn, + ), + )..onFinishCallback = () { + current.removeFromParent(); + inactiveArrow.active = true; + firstChild()?.add( + _RankingPage( + ranking: ranking, + offset: offset, + ) + ..scale = Vector2(0, 1) + ..add( + ScaleEffect.to( + Vector2(1, 1), + EffectController( + duration: 0.5, + curve: Curves.easeIn, + ), + ), + ), + ); + }, + ); } @override @@ -60,6 +105,20 @@ class LeaderboardDisplay extends PositionComponent with HasGameRef { PositionComponent( position: Vector2(0, 4), children: [ + _MovePageArrow( + position: Vector2(20, 9), + onTap: () { + _changePage(_entries.sublist(5), 5); + }, + ), + _MovePageArrow( + position: Vector2(-20, 9), + direction: ArrowIconDirection.left, + active: false, + onTap: () { + _changePage(_entries.take(5).toList(), 0); + }, + ), PositionComponent( children: [ TextComponent( @@ -82,39 +141,106 @@ class LeaderboardDisplay extends PositionComponent with HasGameRef { ), ], ), - for (var i = 0; i < ranking.length; i++) - PositionComponent( - children: [ - TextComponent( - text: _rank(i + 1), - textRenderer: _bodyTextPaint, - position: Vector2(_columns[0], _calcY(i)), - anchor: Anchor.center, - ), - TextComponent( - text: ranking[i].score.formatScore(), - textRenderer: _bodyTextPaint, - position: Vector2(_columns[1], _calcY(i)), - anchor: Anchor.center, - ), - SpriteComponent.fromImage( - gameRef.images.fromCache( - ranking[i].character.toTheme.leaderboardIcon.keyName, - ), - anchor: Anchor.center, - size: Vector2(1.8, 1.8), - position: Vector2(_columns[2] - 2.5, _calcY(i) + .25), - ), - TextComponent( - text: ranking[i].playerInitials, - textRenderer: _bodyTextPaint, - position: Vector2(_columns[2] + 1, _calcY(i)), - anchor: Anchor.center, - ), - ], - ), + _RankingPage( + ranking: ranking, + offset: 0, + ), ], ), ); } } + +class _RankingPage extends PositionComponent with HasGameRef { + _RankingPage({ + required this.ranking, + required this.offset, + }) : super(children: []); + + final List ranking; + final int offset; + + @override + Future onLoad() async { + await addAll([ + for (var i = 0; i < ranking.length; i++) + PositionComponent( + children: [ + TextComponent( + text: _rank(i + 1 + offset), + textRenderer: _bodyTextPaint, + position: Vector2(_columns[0], _calcY(i)), + anchor: Anchor.center, + ), + TextComponent( + text: ranking[i].score.formatScore(), + textRenderer: _bodyTextPaint, + position: Vector2(_columns[1], _calcY(i)), + anchor: Anchor.center, + ), + SpriteComponent.fromImage( + gameRef.images.fromCache( + ranking[i].character.toTheme.leaderboardIcon.keyName, + ), + anchor: Anchor.center, + size: Vector2(1.8, 1.8), + position: Vector2(_columns[2] - 3, _calcY(i) + .25), + ), + TextComponent( + text: ranking[i].playerInitials, + textRenderer: _bodyTextPaint, + position: Vector2(_columns[2] + 1, _calcY(i)), + anchor: Anchor.center, + ), + ], + ), + ]); + } +} + +class _MovePageArrow extends PositionComponent { + _MovePageArrow({ + required Vector2 position, + required this.onTap, + this.direction = ArrowIconDirection.right, + bool active = true, + }) : super( + position: position, + children: [ + if (active) + ArrowIcon( + position: Vector2.zero(), + direction: direction, + onTap: onTap, + ), + SequenceEffect( + [ + ScaleEffect.to( + Vector2.all(1.2), + EffectController(duration: 1), + ), + ScaleEffect.to(Vector2.all(1), EffectController(duration: 1)), + ], + infinite: true, + ), + ], + ); + + final ArrowIconDirection direction; + final VoidCallback onTap; + + bool get active => children.whereType().isNotEmpty; + set active(bool value) { + if (value) { + add( + ArrowIcon( + position: Vector2.zero(), + direction: direction, + onTap: onTap, + ), + ); + } else { + firstChild()?.removeFromParent(); + } + } +} diff --git a/lib/game/game_assets.dart b/lib/game/game_assets.dart index 2f386695..15a52afb 100644 --- a/lib/game/game_assets.dart +++ b/lib/game/game_assets.dart @@ -141,6 +141,8 @@ extension PinballGameAssetsX on PinballGame { images.load(components.Assets.images.skillShot.pin.keyName), images.load(components.Assets.images.skillShot.lit.keyName), images.load(components.Assets.images.skillShot.dimmed.keyName), + images.load(components.Assets.images.displayArrows.arrowLeft.keyName), + images.load(components.Assets.images.displayArrows.arrowRight.keyName), images.load(androidTheme.leaderboardIcon.keyName), images.load(androidTheme.background.keyName), images.load(androidTheme.ball.keyName), diff --git a/packages/pinball_components/assets/images/display_arrows/arrow_left.png b/packages/pinball_components/assets/images/display_arrows/arrow_left.png new file mode 100644 index 00000000..e851bef4 Binary files /dev/null and b/packages/pinball_components/assets/images/display_arrows/arrow_left.png differ diff --git a/packages/pinball_components/assets/images/display_arrows/arrow_right.png b/packages/pinball_components/assets/images/display_arrows/arrow_right.png new file mode 100644 index 00000000..c8d5d2f2 Binary files /dev/null and b/packages/pinball_components/assets/images/display_arrows/arrow_right.png differ diff --git a/packages/pinball_components/lib/gen/assets.gen.dart b/packages/pinball_components/lib/gen/assets.gen.dart index f233596c..0708878f 100644 --- a/packages/pinball_components/lib/gen/assets.gen.dart +++ b/packages/pinball_components/lib/gen/assets.gen.dart @@ -23,6 +23,9 @@ class $AssetsImagesGen { $AssetsImagesDashGen get dash => const $AssetsImagesDashGen(); $AssetsImagesDinoGen get dino => const $AssetsImagesDinoGen(); + $AssetsImagesDisplayArrowsGen get displayArrows => + const $AssetsImagesDisplayArrowsGen(); + /// File path: assets/images/error_background.png AssetGenImage get errorBackground => const AssetGenImage('assets/images/error_background.png'); @@ -140,6 +143,15 @@ class $AssetsImagesDinoGen { const AssetGenImage('assets/images/dino/top_wall_tunnel.png'); } +class $AssetsImagesDisplayArrowsGen { + const $AssetsImagesDisplayArrowsGen(); + + AssetGenImage get arrowLeft => + const AssetGenImage('assets/images/display_arrows/arrow_left.png'); + AssetGenImage get arrowRight => + const AssetGenImage('assets/images/display_arrows/arrow_right.png'); +} + class $AssetsImagesFlapperGen { const $AssetsImagesFlapperGen(); diff --git a/packages/pinball_components/lib/src/components/arrow_icon.dart b/packages/pinball_components/lib/src/components/arrow_icon.dart new file mode 100644 index 00000000..0dc33b10 --- /dev/null +++ b/packages/pinball_components/lib/src/components/arrow_icon.dart @@ -0,0 +1,49 @@ +import 'package:flame/components.dart'; +import 'package:flame/input.dart'; +import 'package:flutter/material.dart'; +import 'package:pinball_components/pinball_components.dart'; + +/// enum with the available directions for an [ArrowIcon]. +enum ArrowIconDirection { + /// Left. + left, + + /// Right. + right, +} + +/// {@template arrow_icon} +/// A [SpriteComponent] that renders a simple arrow icon. +/// {@endtemplate} +class ArrowIcon extends SpriteComponent with Tappable, HasGameRef { + /// {@macro arrow_icon} + ArrowIcon({ + required Vector2 position, + required this.direction, + required this.onTap, + }) : super(position: position); + + final ArrowIconDirection direction; + final VoidCallback onTap; + + @override + Future onLoad() async { + anchor = Anchor.center; + final sprite = Sprite( + gameRef.images.fromCache( + direction == ArrowIconDirection.left + ? Assets.images.displayArrows.arrowLeft.keyName + : Assets.images.displayArrows.arrowRight.keyName, + ), + ); + + size = sprite.originalSize / 20; + this.sprite = sprite; + } + + @override + bool onTapUp(TapUpInfo info) { + onTap(); + return true; + } +} diff --git a/packages/pinball_components/lib/src/components/components.dart b/packages/pinball_components/lib/src/components/components.dart index 75ba12dc..76d4e189 100644 --- a/packages/pinball_components/lib/src/components/components.dart +++ b/packages/pinball_components/lib/src/components/components.dart @@ -2,6 +2,7 @@ export 'android_animatronic.dart'; export 'android_bumper/android_bumper.dart'; export 'android_spaceship/android_spaceship.dart'; export 'arcade_background/arcade_background.dart'; +export 'arrow_icon.dart'; export 'ball/ball.dart'; export 'baseboard.dart'; export 'board_background_sprite_component.dart'; diff --git a/packages/pinball_components/pubspec.yaml b/packages/pinball_components/pubspec.yaml index 3301a0fc..60651eb2 100644 --- a/packages/pinball_components/pubspec.yaml +++ b/packages/pinball_components/pubspec.yaml @@ -93,6 +93,7 @@ flutter: - assets/images/backbox/button/ - assets/images/flapper/ - assets/images/skill_shot/ + - assets/images/display_arrows/ flutter_gen: line_length: 80 diff --git a/packages/pinball_components/sandbox/lib/main.dart b/packages/pinball_components/sandbox/lib/main.dart index 714bbee5..fb948a89 100644 --- a/packages/pinball_components/sandbox/lib/main.dart +++ b/packages/pinball_components/sandbox/lib/main.dart @@ -21,6 +21,7 @@ void main() { addScoreStories(dashbook); addMultiballStories(dashbook); addMultipliersStories(dashbook); + addArrowIconStories(dashbook); runApp(dashbook); } diff --git a/packages/pinball_components/sandbox/lib/stories/arrow_icon/arrow_icon_game.dart b/packages/pinball_components/sandbox/lib/stories/arrow_icon/arrow_icon_game.dart new file mode 100644 index 00000000..23af63b0 --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/arrow_icon/arrow_icon_game.dart @@ -0,0 +1,37 @@ +import 'package:flame/game.dart'; +import 'package:pinball_components/pinball_components.dart'; +import 'package:sandbox/common/games.dart'; + +class ArrowIconGame extends AssetsGame with HasTappables { + ArrowIconGame() + : super( + imagesFileNames: [ + Assets.images.displayArrows.arrowLeft.keyName, + Assets.images.displayArrows.arrowRight.keyName, + ], + ); + + static const description = 'Shows how ArrowIcons are rendered.'; + + @override + Future onLoad() async { + await super.onLoad(); + camera.followVector2(Vector2.zero()); + + await add( + ArrowIcon( + position: Vector2.zero(), + direction: ArrowIconDirection.left, + onTap: () {}, + ), + ); + + await add( + ArrowIcon( + position: Vector2(0, 20), + direction: ArrowIconDirection.right, + onTap: () {}, + ), + ); + } +} diff --git a/packages/pinball_components/sandbox/lib/stories/arrow_icon/stories.dart b/packages/pinball_components/sandbox/lib/stories/arrow_icon/stories.dart new file mode 100644 index 00000000..05a8a8ff --- /dev/null +++ b/packages/pinball_components/sandbox/lib/stories/arrow_icon/stories.dart @@ -0,0 +1,11 @@ +import 'package:dashbook/dashbook.dart'; +import 'package:sandbox/common/common.dart'; +import 'package:sandbox/stories/arrow_icon/arrow_icon_game.dart'; + +void addArrowIconStories(Dashbook dashbook) { + dashbook.storiesOf('ArrowIcon').addGame( + title: 'Basic', + description: ArrowIconGame.description, + gameBuilder: (context) => ArrowIconGame(), + ); +} diff --git a/packages/pinball_components/sandbox/lib/stories/stories.dart b/packages/pinball_components/sandbox/lib/stories/stories.dart index 0a514eb9..8a165693 100644 --- a/packages/pinball_components/sandbox/lib/stories/stories.dart +++ b/packages/pinball_components/sandbox/lib/stories/stories.dart @@ -1,4 +1,5 @@ export 'android_acres/stories.dart'; +export 'arrow_icon/stories.dart'; export 'ball/stories.dart'; export 'bottom_group/stories.dart'; export 'boundaries/stories.dart'; diff --git a/packages/pinball_components/test/helpers/test_game.dart b/packages/pinball_components/test/helpers/test_game.dart index 1f8b9ee6..57c7961c 100644 --- a/packages/pinball_components/test/helpers/test_game.dart +++ b/packages/pinball_components/test/helpers/test_game.dart @@ -1,3 +1,4 @@ +import 'package:flame/game.dart'; import 'package:flame/input.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; @@ -20,3 +21,7 @@ class TestGame extends Forge2DGame { class KeyboardTestGame extends TestGame with HasKeyboardHandlerComponents { KeyboardTestGame([List? assets]) : super(assets); } + +class TappablesTestGame extends TestGame with HasTappables { + TappablesTestGame([List? assets]) : super(assets); +} diff --git a/packages/pinball_components/test/src/components/arrow_icon_test.dart b/packages/pinball_components/test/src/components/arrow_icon_test.dart new file mode 100644 index 00000000..c8a1c5aa --- /dev/null +++ b/packages/pinball_components/test/src/components/arrow_icon_test.dart @@ -0,0 +1,96 @@ +// ignore_for_file: cascade_invocations, one_member_abstracts + +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_components/pinball_components.dart'; + +import '../../helpers/helpers.dart'; + +abstract class _VoidCallbackStubBase { + void onCall(); +} + +class _VoidCallbackStub extends Mock implements _VoidCallbackStubBase {} + +void main() { + group('ArrowIcon', () { + TestWidgetsFlutterBinding.ensureInitialized(); + final assets = [ + Assets.images.displayArrows.arrowLeft.keyName, + Assets.images.displayArrows.arrowRight.keyName, + ]; + final flameTester = FlameTester(() => TappablesTestGame(assets)); + + flameTester.testGameWidget( + 'is tappable', + setUp: (game, tester) async { + final stub = _VoidCallbackStub(); + await game.images.loadAll(assets); + await game.ensureAdd( + ArrowIcon( + position: Vector2.zero(), + direction: ArrowIconDirection.left, + onTap: stub.onCall, + ), + ); + await tester.pump(); + await tester.tapAt(Offset.zero); + await tester.pump(); + }, + verify: (game, tester) async { + final icon = game.descendants().whereType().single; + verify(icon.onTap).called(1); + }, + ); + + group('left', () { + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.images.loadAll(assets); + game.camera.followVector2(Vector2.zero()); + await game.add( + ArrowIcon( + position: Vector2.zero(), + direction: ArrowIconDirection.left, + onTap: () {}, + ), + ); + await tester.pump(); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/arrow_icon_left.png'), + ); + }, + ); + }); + + group('right', () { + flameTester.testGameWidget( + 'renders correctly', + setUp: (game, tester) async { + await game.images.loadAll(assets); + game.camera.followVector2(Vector2.zero()); + await game.add( + ArrowIcon( + position: Vector2.zero(), + direction: ArrowIconDirection.right, + onTap: () {}, + ), + ); + await tester.pump(); + }, + verify: (game, tester) async { + await expectLater( + find.byGame(), + matchesGoldenFile('golden/arrow_icon_right.png'), + ); + }, + ); + }); + }); +} diff --git a/packages/pinball_components/test/src/components/golden/arrow_icon_left.png b/packages/pinball_components/test/src/components/golden/arrow_icon_left.png new file mode 100644 index 00000000..dab87d41 Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/arrow_icon_left.png differ diff --git a/packages/pinball_components/test/src/components/golden/arrow_icon_right.png b/packages/pinball_components/test/src/components/golden/arrow_icon_right.png new file mode 100644 index 00000000..185e9a9a Binary files /dev/null and b/packages/pinball_components/test/src/components/golden/arrow_icon_right.png differ diff --git a/test/game/components/backbox/backbox_test.dart b/test/game/components/backbox/backbox_test.dart index b99b86ab..cfc2437a 100644 --- a/test/game/components/backbox/backbox_test.dart +++ b/test/game/components/backbox/backbox_test.dart @@ -42,11 +42,16 @@ class _TestGame extends Forge2DGame Assets.images.backbox.button.facebook.keyName, Assets.images.backbox.button.twitter.keyName, Assets.images.backbox.displayTitleDecoration.keyName, + Assets.images.displayArrows.arrowLeft.keyName, + Assets.images.displayArrows.arrowRight.keyName, ]); } - Future pump(Backbox component) { - return ensureAdd( + Future pump(Backbox component) async { + // Not needed once https://github.com/flame-engine/flame/issues/1607 + // is fixed + await onLoad(); + await ensureAdd( FlameBlocProvider.value( value: GameBloc(), children: [ diff --git a/test/game/components/backbox/displays/leaderboard_display_test.dart b/test/game/components/backbox/displays/leaderboard_display_test.dart index 263222fc..46fe6cdc 100644 --- a/test/game/components/backbox/displays/leaderboard_display_test.dart +++ b/test/game/components/backbox/displays/leaderboard_display_test.dart @@ -1,6 +1,7 @@ // ignore_for_file: cascade_invocations import 'package:flame/components.dart'; +import 'package:flame/game.dart'; import 'package:flame_forge2d/forge2d_game.dart'; import 'package:flame_test/flame_test.dart'; import 'package:flutter_test/flutter_test.dart'; @@ -8,8 +9,9 @@ import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/components/backbox/displays/leaderboard_display.dart'; import 'package:pinball/l10n/l10n.dart'; +import 'package:pinball_components/pinball_components.dart'; import 'package:pinball_flame/pinball_flame.dart'; -import 'package:pinball_theme/pinball_theme.dart'; +import 'package:pinball_theme/pinball_theme.dart' hide Assets; class _MockAppLocalizations extends Mock implements AppLocalizations { @override @@ -22,12 +24,16 @@ class _MockAppLocalizations extends Mock implements AppLocalizations { String get name => 'name'; } -class _TestGame extends Forge2DGame { +class _TestGame extends Forge2DGame with HasTappables { @override Future onLoad() async { await super.onLoad(); images.prefix = ''; - await images.load(const AndroidTheme().leaderboardIcon.keyName); + await images.loadAll([ + const AndroidTheme().leaderboardIcon.keyName, + Assets.images.displayArrows.arrowLeft.keyName, + Assets.images.displayArrows.arrowRight.keyName, + ]); } Future pump(LeaderboardDisplay component) { @@ -40,6 +46,59 @@ class _TestGame extends Forge2DGame { } } +const leaderboard = [ + LeaderboardEntryData( + playerInitials: 'AAA', + score: 123, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'BBB', + score: 1234, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'CCC', + score: 12345, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'DDD', + score: 12346, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'EEE', + score: 123467, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'FFF', + score: 123468, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'GGG', + score: 1234689, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'HHH', + score: 12346891, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'III', + score: 123468912, + character: CharacterType.android, + ), + LeaderboardEntryData( + playerInitials: 'JJJ', + score: 1234689121, + character: CharacterType.android, + ), +]; + void main() { group('LeaderboardDisplay', () { TestWidgetsFlutterBinding.ensureInitialized(); @@ -57,43 +116,20 @@ void main() { expect(textComponents[2].text, equals('name')); }); - flameTester.test('renders the entries', (game) async { - await game.pump( - LeaderboardDisplay( - entries: const [ - LeaderboardEntryData( - playerInitials: 'AAA', - score: 123, - character: CharacterType.android, - ), - LeaderboardEntryData( - playerInitials: 'BBB', - score: 1234, - character: CharacterType.android, - ), - LeaderboardEntryData( - playerInitials: 'CCC', - score: 12345, - character: CharacterType.android, - ), - LeaderboardEntryData( - playerInitials: 'DDD', - score: 12346, - character: CharacterType.android, - ), - ], - ), - ); + flameTester.test('renders the first 5 entries', (game) async { + await game.pump(LeaderboardDisplay(entries: leaderboard)); for (final text in [ 'AAA', 'BBB', 'CCC', 'DDD', + 'EEE', '1st', '2nd', '3rd', - '4th' + '4th', + '5th', ]) { expect( game @@ -105,5 +141,120 @@ void main() { ); } }); + + flameTester.test('can open the second page', (game) async { + final display = LeaderboardDisplay(entries: leaderboard); + await game.pump(display); + + final arrow = game + .descendants() + .whereType() + .where((arrow) => arrow.direction == ArrowIconDirection.right) + .single; + + // Tap the arrow + arrow.onTap(); + // Wait for the transition to finish + display.updateTree(5); + await game.ready(); + + for (final text in [ + 'FFF', + 'GGG', + 'HHH', + 'III', + 'JJJ', + '6th', + '7th', + '8th', + '9th', + '10th', + ]) { + expect( + game + .descendants() + .whereType() + .where((textComponent) => textComponent.text == text) + .length, + equals(1), + ); + } + }); + + flameTester.test( + 'can open the second page and go back to the first', + (game) async { + final display = LeaderboardDisplay(entries: leaderboard); + await game.pump(display); + + var arrow = game + .descendants() + .whereType() + .where((arrow) => arrow.direction == ArrowIconDirection.right) + .single; + + // Tap the arrow + arrow.onTap(); + // Wait for the transition to finish + display.updateTree(5); + await game.ready(); + + for (final text in [ + 'FFF', + 'GGG', + 'HHH', + 'III', + 'JJJ', + '6th', + '7th', + '8th', + '9th', + '10th', + ]) { + expect( + game + .descendants() + .whereType() + .where((textComponent) => textComponent.text == text) + .length, + equals(1), + ); + } + + arrow = game + .descendants() + .whereType() + .where((arrow) => arrow.direction == ArrowIconDirection.left) + .single; + + // Tap the arrow + arrow.onTap(); + // Wait for the transition to finish + display.updateTree(5); + await game.ready(); + + for (final text in [ + 'AAA', + 'BBB', + 'CCC', + 'DDD', + 'EEE', + '1st', + '2nd', + '3rd', + '4th', + '5th', + ]) { + expect( + game + .descendants() + .whereType() + .where((textComponent) => textComponent.text == text) + .length, + equals(1), + ); + } + }, + ); }); } diff --git a/test/game/components/game_bloc_status_listener_test.dart b/test/game/components/game_bloc_status_listener_test.dart index cc1729b8..c1ca9b3e 100644 --- a/test/game/components/game_bloc_status_listener_test.dart +++ b/test/game/components/game_bloc_status_listener_test.dart @@ -1,6 +1,7 @@ // ignore_for_file: cascade_invocations import 'package:flame/components.dart'; +import 'package:flame/game.dart'; import 'package:flame_bloc/flame_bloc.dart'; import 'package:flame_forge2d/flame_forge2d.dart'; import 'package:flame_test/flame_test.dart'; @@ -16,7 +17,7 @@ import 'package:pinball_flame/pinball_flame.dart'; import 'package:pinball_theme/pinball_theme.dart' as theme; import 'package:share_repository/share_repository.dart'; -class _TestGame extends Forge2DGame { +class _TestGame extends Forge2DGame with HasTappables { @override Future onLoad() async { images.prefix = ''; @@ -25,6 +26,8 @@ class _TestGame extends Forge2DGame { const theme.DashTheme().leaderboardIcon.keyName, Assets.images.backbox.marquee.keyName, Assets.images.backbox.displayDivider.keyName, + Assets.images.displayArrows.arrowLeft.keyName, + Assets.images.displayArrows.arrowRight.keyName, ], ); }