mirror of https://github.com/flutter/pinball.git
parent
5fe5fc007d
commit
55969fb056
@ -0,0 +1,49 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// A [Component] that controls its game camera focus
|
||||
class CameraController extends Component with HasGameRef, KeyboardHandler {
|
||||
/// The camera position for the board
|
||||
static final gamePosition = Vector2(0, -7.8);
|
||||
|
||||
/// The camera position for the pinball panel
|
||||
static final backboardPosition = Vector2(0, -100.8);
|
||||
|
||||
/// The zoom value for the game mode
|
||||
late final double gameZoom;
|
||||
|
||||
/// The zoom value for the panel mode
|
||||
late final double backboardZoom;
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await super.onLoad();
|
||||
|
||||
gameZoom = gameRef.size.y / 16;
|
||||
backboardZoom = gameRef.size.y / 18;
|
||||
|
||||
// Game starts with the camera focused on the panel
|
||||
gameRef.camera
|
||||
..speed = 100
|
||||
..followVector2(backboardPosition)
|
||||
..zoom = backboardZoom;
|
||||
}
|
||||
|
||||
/// Move the camera focus to the game board
|
||||
Future<void> focusOnGame() async {
|
||||
final zoom = CameraZoom(value: gameZoom);
|
||||
unawaited(gameRef.add(zoom));
|
||||
await zoom.completed;
|
||||
gameRef.camera.moveTo(gamePosition);
|
||||
}
|
||||
|
||||
/// Move the camera focus to the backboard
|
||||
Future<void> focusOnBackboard() async {
|
||||
final zoom = CameraZoom(value: backboardZoom);
|
||||
unawaited(gameRef.add(zoom));
|
||||
await zoom.completed;
|
||||
gameRef.camera.moveTo(backboardPosition);
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:flame_bloc/flame_bloc.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// A [Component] that controls the game over and game restart logic
|
||||
class GameController extends Component
|
||||
with BlocComponent<GameBloc, GameState>, HasGameRef {
|
||||
@override
|
||||
bool listenWhen(GameState? previousState, GameState newState) {
|
||||
return previousState?.isGameOver != newState.isGameOver;
|
||||
}
|
||||
|
||||
@override
|
||||
void onNewState(GameState state) {
|
||||
if (state.isGameOver) {
|
||||
gameOver();
|
||||
} else {
|
||||
start();
|
||||
}
|
||||
}
|
||||
|
||||
/// Puts the game on a game over state
|
||||
void gameOver() {
|
||||
gameRef.firstChild<Backboard>()?.gameOverMode();
|
||||
gameRef.firstChild<CameraController>()?.focusOnBackboard();
|
||||
}
|
||||
|
||||
/// Puts the game on a playing state
|
||||
void start() {
|
||||
gameRef.firstChild<Backboard>()?.waitingMode();
|
||||
gameRef.firstChild<CameraController>()?.focusOnGame();
|
||||
gameRef.overlays.remove(PinballGame.playButtonOverlay);
|
||||
}
|
||||
}
|
@ -1,172 +0,0 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:leaderboard_repository/leaderboard_repository.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball/l10n/l10n.dart';
|
||||
import 'package:pinball/leaderboard/leaderboard.dart';
|
||||
import 'package:pinball_theme/pinball_theme.dart';
|
||||
|
||||
/// {@template game_over_dialog}
|
||||
/// [Dialog] displayed when the [PinballGame] is over.
|
||||
/// {@endtemplate}
|
||||
class GameOverDialog extends StatelessWidget {
|
||||
/// {@macro game_over_dialog}
|
||||
const GameOverDialog({Key? key, required this.score, required this.theme})
|
||||
: super(key: key);
|
||||
|
||||
/// Score achieved by the current user.
|
||||
final int score;
|
||||
|
||||
/// Theme of the current user.
|
||||
final CharacterTheme theme;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider(
|
||||
create: (context) => LeaderboardBloc(
|
||||
context.read<LeaderboardRepository>(),
|
||||
),
|
||||
child: GameOverDialogView(score: score, theme: theme),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// {@template game_over_dialog_view}
|
||||
/// View for showing final score when the game is finished.
|
||||
/// {@endtemplate}
|
||||
@visibleForTesting
|
||||
class GameOverDialogView extends StatefulWidget {
|
||||
/// {@macro game_over_dialog_view}
|
||||
const GameOverDialogView({
|
||||
Key? key,
|
||||
required this.score,
|
||||
required this.theme,
|
||||
}) : super(key: key);
|
||||
|
||||
/// Score achieved by the current user.
|
||||
final int score;
|
||||
|
||||
/// Theme of the current user.
|
||||
final CharacterTheme theme;
|
||||
|
||||
@override
|
||||
State<GameOverDialogView> createState() => _GameOverDialogViewState();
|
||||
}
|
||||
|
||||
class _GameOverDialogViewState extends State<GameOverDialogView> {
|
||||
final playerInitialsInputController = TextEditingController();
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
playerInitialsInputController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
|
||||
// TODO(ruimiguel): refactor this view once UI design finished.
|
||||
return Dialog(
|
||||
child: SizedBox(
|
||||
width: 200,
|
||||
height: 250,
|
||||
child: Center(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(10),
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
l10n.gameOver,
|
||||
style: Theme.of(context).textTheme.headline4,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 20,
|
||||
),
|
||||
Text(
|
||||
'${l10n.yourScore} ${widget.score}',
|
||||
style: Theme.of(context).textTheme.headline6,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 15,
|
||||
),
|
||||
TextField(
|
||||
key: const Key('player_initials_text_field'),
|
||||
controller: playerInitialsInputController,
|
||||
textCapitalization: TextCapitalization.characters,
|
||||
decoration: InputDecoration(
|
||||
border: const OutlineInputBorder(),
|
||||
hintText: l10n.enterInitials,
|
||||
),
|
||||
maxLength: 3,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
_GameOverDialogActions(
|
||||
score: widget.score,
|
||||
theme: widget.theme,
|
||||
playerInitialsInputController:
|
||||
playerInitialsInputController,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _GameOverDialogActions extends StatelessWidget {
|
||||
const _GameOverDialogActions({
|
||||
Key? key,
|
||||
required this.score,
|
||||
required this.theme,
|
||||
required this.playerInitialsInputController,
|
||||
}) : super(key: key);
|
||||
|
||||
final int score;
|
||||
final CharacterTheme theme;
|
||||
final TextEditingController playerInitialsInputController;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
|
||||
return BlocBuilder<LeaderboardBloc, LeaderboardState>(
|
||||
builder: (context, state) {
|
||||
switch (state.status) {
|
||||
case LeaderboardStatus.loading:
|
||||
return TextButton(
|
||||
onPressed: () {
|
||||
context.read<LeaderboardBloc>().add(
|
||||
LeaderboardEntryAdded(
|
||||
entry: LeaderboardEntryData(
|
||||
playerInitials:
|
||||
playerInitialsInputController.text.toUpperCase(),
|
||||
score: score,
|
||||
character: theme.toType,
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(l10n.addUser),
|
||||
);
|
||||
case LeaderboardStatus.success:
|
||||
return TextButton(
|
||||
onPressed: () => Navigator.of(context).push<void>(
|
||||
LeaderboardPage.route(theme: theme),
|
||||
),
|
||||
child: Text(l10n.leaderboard),
|
||||
);
|
||||
case LeaderboardStatus.error:
|
||||
return Text(l10n.error);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pinball/game/pinball_game.dart';
|
||||
import 'package:pinball/l10n/l10n.dart';
|
||||
|
||||
/// {@template play_button_overlay}
|
||||
/// [Widget] that renders the button responsible to starting the game
|
||||
/// {@endtemplate}
|
||||
class PlayButtonOverlay extends StatelessWidget {
|
||||
/// {@macro play_button_overlay}
|
||||
const PlayButtonOverlay({
|
||||
Key? key,
|
||||
required PinballGame game,
|
||||
}) : _game = game,
|
||||
super(key: key);
|
||||
|
||||
final PinballGame _game;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = context.l10n;
|
||||
return Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: _game.gameController.start,
|
||||
child: Text(l10n.play),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,2 +1,2 @@
|
||||
export 'game_hud.dart';
|
||||
export 'game_over_dialog.dart';
|
||||
export 'play_button_overlay.dart';
|
||||
|
After Width: | Height: | Size: 955 KiB |
After Width: | Height: | Size: 2.4 MiB |
@ -0,0 +1,36 @@
|
||||
import 'package:flame/components.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
/// {@template backboard}
|
||||
/// The [Backboard] of the pinball machine
|
||||
/// {@endtemplate}
|
||||
class Backboard extends SpriteComponent with HasGameRef {
|
||||
/// {@macro backboard}
|
||||
Backboard({
|
||||
required Vector2 position,
|
||||
}) : super(
|
||||
position: position..clone().multiply(Vector2(1, -1)),
|
||||
anchor: Anchor.bottomCenter,
|
||||
);
|
||||
|
||||
@override
|
||||
Future<void> onLoad() async {
|
||||
await waitingMode();
|
||||
}
|
||||
|
||||
/// Sets the Backboard on the waiting mode, where the scoreboard is show
|
||||
Future<void> waitingMode() async {
|
||||
size = Vector2(120, 100);
|
||||
sprite = await gameRef.loadSprite(
|
||||
Assets.images.backboard.backboardScores.keyName,
|
||||
);
|
||||
}
|
||||
|
||||
/// Sets the Backboard on the game over mode, where the score input is show
|
||||
Future<void> gameOverMode() async {
|
||||
size = Vector2(100, 100);
|
||||
sprite = await gameRef.loadSprite(
|
||||
Assets.images.backboard.backboardGameOver.keyName,
|
||||
);
|
||||
}
|
||||
}
|
@ -0,0 +1,83 @@
|
||||
// ignore_for_file: cascade_invocations
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flame_test/flame_test.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:pinball/game/components/camera_controller.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
void main() {
|
||||
group('CameraController', () {
|
||||
late FlameGame game;
|
||||
late CameraController controller;
|
||||
|
||||
setUp(() async {
|
||||
game = FlameGame()..onGameResize(Vector2(100, 200));
|
||||
|
||||
controller = CameraController();
|
||||
await game.ensureAdd(controller);
|
||||
});
|
||||
|
||||
test('loads correctly', () async {
|
||||
expect(game.firstChild<CameraController>(), isNotNull);
|
||||
});
|
||||
|
||||
test('correctly calculates the zooms', () async {
|
||||
expect(controller.gameZoom.toInt(), equals(12));
|
||||
expect(controller.backboardZoom.toInt(), equals(11));
|
||||
});
|
||||
|
||||
test('correctly sets the initial zoom and position', () async {
|
||||
expect(game.camera.zoom, equals(controller.backboardZoom));
|
||||
expect(game.camera.follow, equals(CameraController.backboardPosition));
|
||||
});
|
||||
|
||||
group('focusOnBoard', () {
|
||||
test('changes the zoom', () async {
|
||||
unawaited(controller.focusOnGame());
|
||||
|
||||
await game.ready();
|
||||
final zoom = game.firstChild<CameraZoom>();
|
||||
expect(zoom, isNotNull);
|
||||
expect(zoom?.value, equals(controller.gameZoom));
|
||||
});
|
||||
|
||||
test('moves the camera after the zoom is completed', () async {
|
||||
final future = controller.focusOnGame();
|
||||
await game.ready();
|
||||
|
||||
game.update(10);
|
||||
game.update(0);
|
||||
|
||||
await future;
|
||||
|
||||
expect(game.camera.position, Vector2(-4, -108.8));
|
||||
});
|
||||
});
|
||||
|
||||
group('focusOnBackboard', () {
|
||||
test('changes the zoom', () async {
|
||||
unawaited(controller.focusOnBackboard());
|
||||
|
||||
await game.ready();
|
||||
final zoom = game.firstChild<CameraZoom>();
|
||||
expect(zoom, isNotNull);
|
||||
expect(zoom?.value, equals(controller.backboardZoom));
|
||||
});
|
||||
|
||||
test('moves the camera after the zoom is completed', () async {
|
||||
final future = controller.focusOnBackboard();
|
||||
await game.ready();
|
||||
|
||||
game.update(10);
|
||||
game.update(0);
|
||||
|
||||
await future;
|
||||
|
||||
expect(game.camera.position, Vector2(-4.5, -109.8));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,99 @@
|
||||
// ignore_for_file: type_annotate_public_apis, prefer_const_constructors
|
||||
|
||||
import 'package:flame/game.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball_components/pinball_components.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
// TODO(erickzanardo: This will not be needed anymore when
|
||||
// this issue is merged: https://github.com/flame-engine/flame/issues/1513
|
||||
class WrappedGameController extends GameController {
|
||||
WrappedGameController(this._gameRef);
|
||||
|
||||
final PinballGame _gameRef;
|
||||
|
||||
@override
|
||||
PinballGame get gameRef => _gameRef;
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('GameController', () {
|
||||
group('listenWhen', () {
|
||||
test('is true when the game over state has changed', () {
|
||||
final state = GameState(
|
||||
score: 10,
|
||||
balls: 0,
|
||||
activatedBonusLetters: const [],
|
||||
bonusHistory: const [],
|
||||
activatedDashNests: const {},
|
||||
);
|
||||
|
||||
final previous = GameState.initial();
|
||||
expect(
|
||||
GameController().listenWhen(previous, state),
|
||||
isTrue,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
group('onNewState', () {
|
||||
late PinballGame game;
|
||||
late Backboard backboard;
|
||||
late CameraController cameraController;
|
||||
late GameController gameController;
|
||||
late ActiveOverlaysNotifier overlays;
|
||||
|
||||
setUp(() {
|
||||
game = MockPinballGame();
|
||||
backboard = MockBackboard();
|
||||
cameraController = MockCameraController();
|
||||
gameController = WrappedGameController(game);
|
||||
overlays = MockActiveOverlaysNotifier();
|
||||
|
||||
when(backboard.gameOverMode).thenAnswer((_) async {});
|
||||
when(backboard.waitingMode).thenAnswer((_) async {});
|
||||
when(cameraController.focusOnBackboard).thenAnswer((_) async {});
|
||||
when(cameraController.focusOnGame).thenAnswer((_) async {});
|
||||
|
||||
when(() => overlays.remove(any())).thenAnswer((_) => true);
|
||||
|
||||
when(game.firstChild<Backboard>).thenReturn(backboard);
|
||||
when(game.firstChild<CameraController>).thenReturn(cameraController);
|
||||
when(() => game.overlays).thenReturn(overlays);
|
||||
});
|
||||
|
||||
test(
|
||||
'changes the backboard and camera correctly when it is a game over',
|
||||
() {
|
||||
gameController.onNewState(
|
||||
GameState(
|
||||
score: 10,
|
||||
balls: 0,
|
||||
activatedBonusLetters: const [],
|
||||
bonusHistory: const [],
|
||||
activatedDashNests: const {},
|
||||
),
|
||||
);
|
||||
|
||||
verify(backboard.gameOverMode).called(1);
|
||||
verify(cameraController.focusOnBackboard).called(1);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'changes the backboard and camera correctly when it is not a game over',
|
||||
() {
|
||||
gameController.onNewState(GameState.initial());
|
||||
|
||||
verify(backboard.waitingMode).called(1);
|
||||
verify(cameraController.focusOnGame).called(1);
|
||||
verify(() => overlays.remove(PinballGame.playButtonOverlay))
|
||||
.called(1);
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:mocktail/mocktail.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
|
||||
import '../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('PlayButtonOverlay', () {
|
||||
late PinballGame game;
|
||||
late GameController gameController;
|
||||
|
||||
setUp(() {
|
||||
game = MockPinballGame();
|
||||
gameController = MockGameController();
|
||||
|
||||
when(() => game.gameController).thenReturn(gameController);
|
||||
when(gameController.start).thenAnswer((_) {});
|
||||
});
|
||||
|
||||
testWidgets('renders correctly', (tester) async {
|
||||
await tester.pumpApp(PlayButtonOverlay(game: game));
|
||||
|
||||
expect(find.text('Play'), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('calls gameController.start when taped', (tester) async {
|
||||
await tester.pumpApp(PlayButtonOverlay(game: game));
|
||||
|
||||
await tester.tap(find.text('Play'));
|
||||
await tester.pump();
|
||||
|
||||
verify(gameController.start).called(1);
|
||||
});
|
||||
});
|
||||
}
|
@ -1,195 +0,0 @@
|
||||
// ignore_for_file: prefer_const_constructors
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:bloc_test/bloc_test.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:leaderboard_repository/leaderboard_repository.dart';
|
||||
import 'package:mockingjay/mockingjay.dart';
|
||||
import 'package:pinball/game/game.dart';
|
||||
import 'package:pinball/l10n/l10n.dart';
|
||||
import 'package:pinball/leaderboard/leaderboard.dart';
|
||||
import 'package:pinball_theme/pinball_theme.dart';
|
||||
|
||||
import '../../../helpers/helpers.dart';
|
||||
|
||||
void main() {
|
||||
group('GameOverDialog', () {
|
||||
testWidgets('renders GameOverDialogView', (tester) async {
|
||||
await tester.pumpApp(
|
||||
GameOverDialog(
|
||||
score: 1000,
|
||||
theme: DashTheme(),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.byType(GameOverDialogView), findsOneWidget);
|
||||
});
|
||||
|
||||
group('GameOverDialogView', () {
|
||||
late LeaderboardBloc leaderboardBloc;
|
||||
|
||||
final leaderboard = [
|
||||
LeaderboardEntry(
|
||||
rank: '1',
|
||||
playerInitials: 'ABC',
|
||||
score: 5000,
|
||||
character: DashTheme().characterAsset,
|
||||
),
|
||||
];
|
||||
final entryData = LeaderboardEntryData(
|
||||
playerInitials: 'VGV',
|
||||
score: 10000,
|
||||
character: CharacterType.dash,
|
||||
);
|
||||
|
||||
setUp(() {
|
||||
leaderboardBloc = MockLeaderboardBloc();
|
||||
whenListen(
|
||||
leaderboardBloc,
|
||||
const Stream<LeaderboardState>.empty(),
|
||||
initialState: const LeaderboardState.initial(),
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('renders input text view when bloc emits [loading]',
|
||||
(tester) async {
|
||||
final l10n = await AppLocalizations.delegate.load(Locale('en'));
|
||||
|
||||
await tester.pumpApp(
|
||||
BlocProvider.value(
|
||||
value: leaderboardBloc,
|
||||
child: GameOverDialogView(
|
||||
score: entryData.score,
|
||||
theme: entryData.character.toTheme,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.widgetWithText(TextButton, l10n.addUser), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders error view when bloc emits [error]', (tester) async {
|
||||
final l10n = await AppLocalizations.delegate.load(Locale('en'));
|
||||
|
||||
whenListen(
|
||||
leaderboardBloc,
|
||||
const Stream<LeaderboardState>.empty(),
|
||||
initialState: LeaderboardState.initial()
|
||||
.copyWith(status: LeaderboardStatus.error),
|
||||
);
|
||||
|
||||
await tester.pumpApp(
|
||||
BlocProvider.value(
|
||||
value: leaderboardBloc,
|
||||
child: GameOverDialogView(
|
||||
score: entryData.score,
|
||||
theme: entryData.character.toTheme,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(find.text(l10n.error), findsOneWidget);
|
||||
});
|
||||
|
||||
testWidgets('renders success view when bloc emits [success]',
|
||||
(tester) async {
|
||||
final l10n = await AppLocalizations.delegate.load(Locale('en'));
|
||||
|
||||
whenListen(
|
||||
leaderboardBloc,
|
||||
const Stream<LeaderboardState>.empty(),
|
||||
initialState: LeaderboardState(
|
||||
status: LeaderboardStatus.success,
|
||||
ranking: LeaderboardRanking(ranking: 1, outOf: 2),
|
||||
leaderboard: leaderboard,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpApp(
|
||||
BlocProvider.value(
|
||||
value: leaderboardBloc,
|
||||
child: GameOverDialogView(
|
||||
score: entryData.score,
|
||||
theme: entryData.character.toTheme,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
expect(
|
||||
find.widgetWithText(TextButton, l10n.leaderboard),
|
||||
findsOneWidget,
|
||||
);
|
||||
});
|
||||
|
||||
testWidgets('adds LeaderboardEntryAdded when tap on add user button',
|
||||
(tester) async {
|
||||
final l10n = await AppLocalizations.delegate.load(Locale('en'));
|
||||
|
||||
whenListen(
|
||||
leaderboardBloc,
|
||||
const Stream<LeaderboardState>.empty(),
|
||||
initialState: LeaderboardState.initial(),
|
||||
);
|
||||
|
||||
await tester.pumpApp(
|
||||
BlocProvider.value(
|
||||
value: leaderboardBloc,
|
||||
child: GameOverDialogView(
|
||||
score: entryData.score,
|
||||
theme: entryData.character.toTheme,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
await tester.enterText(
|
||||
find.byKey(const Key('player_initials_text_field')),
|
||||
entryData.playerInitials,
|
||||
);
|
||||
|
||||
final button = find.widgetWithText(TextButton, l10n.addUser);
|
||||
await tester.ensureVisible(button);
|
||||
await tester.tap(button);
|
||||
|
||||
verify(
|
||||
() => leaderboardBloc.add(LeaderboardEntryAdded(entry: entryData)),
|
||||
).called(1);
|
||||
});
|
||||
|
||||
testWidgets('navigates to LeaderboardPage when tap on leaderboard button',
|
||||
(tester) async {
|
||||
final l10n = await AppLocalizations.delegate.load(Locale('en'));
|
||||
final navigator = MockNavigator();
|
||||
when(() => navigator.push<void>(any())).thenAnswer((_) async {});
|
||||
whenListen(
|
||||
leaderboardBloc,
|
||||
const Stream<LeaderboardState>.empty(),
|
||||
initialState: LeaderboardState(
|
||||
status: LeaderboardStatus.success,
|
||||
ranking: LeaderboardRanking(ranking: 1, outOf: 2),
|
||||
leaderboard: leaderboard,
|
||||
),
|
||||
);
|
||||
|
||||
await tester.pumpApp(
|
||||
BlocProvider.value(
|
||||
value: leaderboardBloc,
|
||||
child: GameOverDialogView(
|
||||
score: entryData.score,
|
||||
theme: entryData.character.toTheme,
|
||||
),
|
||||
),
|
||||
navigator: navigator,
|
||||
);
|
||||
|
||||
final button = find.widgetWithText(TextButton, l10n.leaderboard);
|
||||
await tester.ensureVisible(button);
|
||||
await tester.tap(button);
|
||||
|
||||
verify(() => navigator.push<void>(any())).called(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in new issue