diff --git a/lib/leaderboard/bloc/leaderboard_bloc.dart b/lib/leaderboard/bloc/leaderboard_bloc.dart deleted file mode 100644 index 49a35474..00000000 --- a/lib/leaderboard/bloc/leaderboard_bloc.dart +++ /dev/null @@ -1,71 +0,0 @@ -import 'dart:async'; - -import 'package:bloc/bloc.dart'; -import 'package:equatable/equatable.dart'; -import 'package:leaderboard_repository/leaderboard_repository.dart'; -import 'package:pinball/leaderboard/leaderboard.dart'; - -part 'leaderboard_event.dart'; -part 'leaderboard_state.dart'; - -/// {@template leaderboard_bloc} -/// Manages leaderboard events. -/// -/// Uses a [LeaderboardRepository] to request and update players participations. -/// {@endtemplate} -class LeaderboardBloc extends Bloc { - /// {@macro leaderboard_bloc} - LeaderboardBloc(this._leaderboardRepository) - : super(const LeaderboardState.initial()) { - on(_onTop10Fetched); - on(_onLeaderboardEntryAdded); - } - - final LeaderboardRepository _leaderboardRepository; - - Future _onTop10Fetched( - Top10Fetched event, - Emitter emit, - ) async { - emit(state.copyWith(status: LeaderboardStatus.loading)); - try { - final top10Leaderboard = - await _leaderboardRepository.fetchTop10Leaderboard(); - - final leaderboardEntries = []; - top10Leaderboard.asMap().forEach( - (index, value) => leaderboardEntries.add(value.toEntry(index + 1)), - ); - - emit( - state.copyWith( - status: LeaderboardStatus.success, - leaderboard: leaderboardEntries, - ), - ); - } catch (error) { - emit(state.copyWith(status: LeaderboardStatus.error)); - addError(error); - } - } - - Future _onLeaderboardEntryAdded( - LeaderboardEntryAdded event, - Emitter emit, - ) async { - emit(state.copyWith(status: LeaderboardStatus.loading)); - try { - final ranking = - await _leaderboardRepository.addLeaderboardEntry(event.entry); - emit( - state.copyWith( - status: LeaderboardStatus.success, - ranking: ranking, - ), - ); - } catch (error) { - emit(state.copyWith(status: LeaderboardStatus.error)); - addError(error); - } - } -} diff --git a/lib/leaderboard/bloc/leaderboard_event.dart b/lib/leaderboard/bloc/leaderboard_event.dart deleted file mode 100644 index b9e6955a..00000000 --- a/lib/leaderboard/bloc/leaderboard_event.dart +++ /dev/null @@ -1,36 +0,0 @@ -part of 'leaderboard_bloc.dart'; - -/// {@template leaderboard_event} -/// Represents the events available for [LeaderboardBloc]. -/// {endtemplate} -abstract class LeaderboardEvent extends Equatable { - /// {@macro leaderboard_event} - const LeaderboardEvent(); -} - -/// {@template top_10_fetched} -/// Request the top 10 [LeaderboardEntryData]s. -/// {endtemplate} -class Top10Fetched extends LeaderboardEvent { - /// {@macro top_10_fetched} - const Top10Fetched(); - - @override - List get props => []; -} - -/// {@template leaderboard_entry_added} -/// Writes a new [LeaderboardEntryData]. -/// -/// Should be added when a player finishes a game. -/// {endtemplate} -class LeaderboardEntryAdded extends LeaderboardEvent { - /// {@macro leaderboard_entry_added} - const LeaderboardEntryAdded({required this.entry}); - - /// [LeaderboardEntryData] to be written to the remote storage. - final LeaderboardEntryData entry; - - @override - List get props => [entry]; -} diff --git a/lib/leaderboard/bloc/leaderboard_state.dart b/lib/leaderboard/bloc/leaderboard_state.dart deleted file mode 100644 index 20d68f0d..00000000 --- a/lib/leaderboard/bloc/leaderboard_state.dart +++ /dev/null @@ -1,59 +0,0 @@ -// ignore_for_file: public_member_api_docs - -part of 'leaderboard_bloc.dart'; - -/// Defines the request status. -enum LeaderboardStatus { - /// Request is being loaded. - loading, - - /// Request was processed successfully and received a valid response. - success, - - /// Request was processed unsuccessfully and received an error. - error, -} - -/// {@template leaderboard_state} -/// Represents the state of the leaderboard. -/// {@endtemplate} -class LeaderboardState extends Equatable { - /// {@macro leaderboard_state} - const LeaderboardState({ - required this.status, - required this.ranking, - required this.leaderboard, - }); - - const LeaderboardState.initial() - : status = LeaderboardStatus.loading, - ranking = const LeaderboardRanking( - ranking: 0, - outOf: 0, - ), - leaderboard = const []; - - /// The current [LeaderboardStatus] of the state. - final LeaderboardStatus status; - - /// Rank of the current player. - final LeaderboardRanking ranking; - - /// List of top-ranked players. - final List leaderboard; - - @override - List get props => [status, ranking, leaderboard]; - - LeaderboardState copyWith({ - LeaderboardStatus? status, - LeaderboardRanking? ranking, - List? leaderboard, - }) { - return LeaderboardState( - status: status ?? this.status, - ranking: ranking ?? this.ranking, - leaderboard: leaderboard ?? this.leaderboard, - ); - } -} diff --git a/lib/leaderboard/leaderboard.dart b/lib/leaderboard/leaderboard.dart deleted file mode 100644 index 08765743..00000000 --- a/lib/leaderboard/leaderboard.dart +++ /dev/null @@ -1,3 +0,0 @@ -export 'bloc/leaderboard_bloc.dart'; -export 'models/leader_board_entry.dart'; -export 'view/leaderboard_page.dart'; diff --git a/lib/leaderboard/view/leaderboard_page.dart b/lib/leaderboard/view/leaderboard_page.dart deleted file mode 100644 index b9866111..00000000 --- a/lib/leaderboard/view/leaderboard_page.dart +++ /dev/null @@ -1,306 +0,0 @@ -// ignore_for_file: public_member_api_docs - -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:leaderboard_repository/leaderboard_repository.dart'; -import 'package:pinball/l10n/l10n.dart'; -import 'package:pinball/leaderboard/leaderboard.dart'; -import 'package:pinball/select_character/select_character.dart'; -import 'package:pinball_theme/pinball_theme.dart'; - -class LeaderboardPage extends StatelessWidget { - const LeaderboardPage({Key? key, required this.theme}) : super(key: key); - - final CharacterTheme theme; - - static Route route({required CharacterTheme theme}) { - return MaterialPageRoute( - builder: (_) => LeaderboardPage(theme: theme), - ); - } - - @override - Widget build(BuildContext context) { - return BlocProvider( - create: (context) => LeaderboardBloc( - context.read(), - )..add(const Top10Fetched()), - child: LeaderboardView(theme: theme), - ); - } -} - -class LeaderboardView extends StatelessWidget { - const LeaderboardView({Key? key, required this.theme}) : super(key: key); - - final CharacterTheme theme; - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - - return Scaffold( - body: Center( - child: SingleChildScrollView( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const SizedBox(height: 80), - Text( - l10n.leaderboard, - style: Theme.of(context).textTheme.headline3, - ), - const SizedBox(height: 80), - BlocBuilder( - builder: (context, state) { - switch (state.status) { - case LeaderboardStatus.loading: - return _LeaderboardLoading(theme: theme); - case LeaderboardStatus.success: - return _LeaderboardRanking( - ranking: state.leaderboard, - theme: theme, - ); - case LeaderboardStatus.error: - return _LeaderboardError(theme: theme); - } - }, - ), - const SizedBox(height: 20), - TextButton( - onPressed: () => Navigator.of(context).push( - CharacterSelectionDialog.route(), - ), - child: Text(l10n.retry), - ), - ], - ), - ), - ), - ); - } -} - -class _LeaderboardLoading extends StatelessWidget { - const _LeaderboardLoading({Key? key, required this.theme}) : super(key: key); - - final CharacterTheme theme; - - @override - Widget build(BuildContext context) { - return const Center( - child: CircularProgressIndicator(), - ); - } -} - -class _LeaderboardError extends StatelessWidget { - const _LeaderboardError({Key? key, required this.theme}) : super(key: key); - - final CharacterTheme theme; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(20), - child: Text( - 'There was en error loading data!', - style: - Theme.of(context).textTheme.headline6?.copyWith(color: Colors.red), - ), - ); - } -} - -class _LeaderboardRanking extends StatelessWidget { - const _LeaderboardRanking({ - Key? key, - required this.ranking, - required this.theme, - }) : super(key: key); - - final List ranking; - final CharacterTheme theme; - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.all(20), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _LeaderboardHeaders(theme: theme), - _LeaderboardList( - ranking: ranking, - theme: theme, - ), - ], - ), - ); - } -} - -class _LeaderboardHeaders extends StatelessWidget { - const _LeaderboardHeaders({Key? key, required this.theme}) : super(key: key); - - final CharacterTheme theme; - - @override - Widget build(BuildContext context) { - final l10n = context.l10n; - - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _LeaderboardHeaderItem(title: l10n.rank, theme: theme), - _LeaderboardHeaderItem(title: l10n.character, theme: theme), - _LeaderboardHeaderItem(title: l10n.username, theme: theme), - _LeaderboardHeaderItem(title: l10n.score, theme: theme), - ], - ); - } -} - -class _LeaderboardHeaderItem extends StatelessWidget { - const _LeaderboardHeaderItem({ - Key? key, - required this.title, - required this.theme, - }) : super(key: key); - - final CharacterTheme theme; - final String title; - - @override - Widget build(BuildContext context) { - return Expanded( - child: DecoratedBox( - decoration: BoxDecoration( - color: theme.ballColor, - ), - child: Text( - title, - style: Theme.of(context).textTheme.headline5, - ), - ), - ); - } -} - -class _LeaderboardList extends StatelessWidget { - const _LeaderboardList({ - Key? key, - required this.ranking, - required this.theme, - }) : super(key: key); - - final List ranking; - final CharacterTheme theme; - - @override - Widget build(BuildContext context) { - return ListView.builder( - shrinkWrap: true, - itemBuilder: (_, index) => _LeaderBoardCompetitor( - entry: ranking[index], - theme: theme, - ), - itemCount: ranking.length, - ); - } -} - -class _LeaderBoardCompetitor extends StatelessWidget { - const _LeaderBoardCompetitor({ - Key? key, - required this.entry, - required this.theme, - }) : super(key: key); - - final CharacterTheme theme; - - final LeaderboardEntry entry; - - @override - Widget build(BuildContext context) { - return Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - _LeaderboardCompetitorField( - text: entry.rank, - theme: theme, - ), - _LeaderboardCompetitorCharacter( - characterAsset: entry.character, - theme: theme, - ), - _LeaderboardCompetitorField( - text: entry.playerInitials, - theme: theme, - ), - _LeaderboardCompetitorField( - text: entry.score.toString(), - theme: theme, - ), - ], - ); - } -} - -class _LeaderboardCompetitorField extends StatelessWidget { - const _LeaderboardCompetitorField({ - Key? key, - required this.text, - required this.theme, - }) : super(key: key); - - final CharacterTheme theme; - final String text; - - @override - Widget build(BuildContext context) { - return Expanded( - child: DecoratedBox( - decoration: BoxDecoration( - border: Border.all( - color: theme.ballColor, - width: 2, - ), - ), - child: Padding( - padding: const EdgeInsets.all(8), - child: Text(text), - ), - ), - ); - } -} - -class _LeaderboardCompetitorCharacter extends StatelessWidget { - const _LeaderboardCompetitorCharacter({ - Key? key, - required this.characterAsset, - required this.theme, - }) : super(key: key); - - final CharacterTheme theme; - final AssetGenImage characterAsset; - - @override - Widget build(BuildContext context) { - return Expanded( - child: DecoratedBox( - decoration: BoxDecoration( - border: Border.all( - color: theme.ballColor, - width: 2, - ), - ), - child: SizedBox( - height: 30, - child: characterAsset.image(), - ), - ), - ); - } -} diff --git a/test/helpers/mocks.dart b/test/helpers/mocks.dart index 6aab19a2..14a286e2 100644 --- a/test/helpers/mocks.dart +++ b/test/helpers/mocks.dart @@ -7,7 +7,6 @@ import 'package:flutter/services.dart'; import 'package:leaderboard_repository/leaderboard_repository.dart'; import 'package:mocktail/mocktail.dart'; import 'package:pinball/game/game.dart'; -import 'package:pinball/leaderboard/leaderboard.dart'; import 'package:pinball/select_character/select_character.dart'; import 'package:pinball/start_game/start_game.dart'; import 'package:pinball_audio/pinball_audio.dart'; @@ -35,8 +34,6 @@ class MockGameState extends Mock implements GameState {} class MockCharacterThemeCubit extends Mock implements CharacterThemeCubit {} -class MockLeaderboardBloc extends Mock implements LeaderboardBloc {} - class MockLeaderboardRepository extends Mock implements LeaderboardRepository {} class MockRawKeyDownEvent extends Mock implements RawKeyDownEvent { diff --git a/test/leaderboard/bloc/leaderboard_bloc_test.dart b/test/leaderboard/bloc/leaderboard_bloc_test.dart deleted file mode 100644 index 2b217704..00000000 --- a/test/leaderboard/bloc/leaderboard_bloc_test.dart +++ /dev/null @@ -1,203 +0,0 @@ -// 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/leaderboard/leaderboard.dart'; -import 'package:pinball_theme/pinball_theme.dart'; - -import '../../helpers/helpers.dart'; - -void main() { - group('LeaderboardBloc', () { - late LeaderboardRepository leaderboardRepository; - - setUp(() { - leaderboardRepository = MockLeaderboardRepository(); - }); - - test('initial state has state loading no ranking and empty leaderboard', - () { - final bloc = LeaderboardBloc(leaderboardRepository); - expect(bloc.state.status, equals(LeaderboardStatus.loading)); - expect(bloc.state.ranking.ranking, equals(0)); - expect(bloc.state.ranking.outOf, equals(0)); - expect(bloc.state.leaderboard.isEmpty, isTrue); - }); - - group('Top10Fetched', () { - const top10Scores = [ - 2500, - 2200, - 2200, - 2000, - 1800, - 1400, - 1300, - 1000, - 600, - 300, - 100, - ]; - - final top10Leaderboard = top10Scores - .map( - (score) => LeaderboardEntryData( - playerInitials: 'user$score', - score: score, - character: CharacterType.dash, - ), - ) - .toList(); - - blocTest( - 'emits [loading, success] statuses ' - 'when fetchTop10Leaderboard succeeds', - setUp: () { - when(() => leaderboardRepository.fetchTop10Leaderboard()).thenAnswer( - (_) async => top10Leaderboard, - ); - }, - build: () => LeaderboardBloc(leaderboardRepository), - act: (bloc) => bloc.add(Top10Fetched()), - expect: () => [ - LeaderboardState.initial(), - isA() - ..having( - (element) => element.status, - 'status', - equals(LeaderboardStatus.success), - ) - ..having( - (element) => element.leaderboard.length, - 'leaderboard', - equals(top10Leaderboard.length), - ) - ], - verify: (_) => - verify(() => leaderboardRepository.fetchTop10Leaderboard()) - .called(1), - ); - - blocTest( - 'emits [loading, error] statuses ' - 'when fetchTop10Leaderboard fails', - setUp: () { - when(() => leaderboardRepository.fetchTop10Leaderboard()).thenThrow( - Exception(), - ); - }, - build: () => LeaderboardBloc(leaderboardRepository), - act: (bloc) => bloc.add(Top10Fetched()), - expect: () => [ - LeaderboardState.initial(), - LeaderboardState.initial().copyWith(status: LeaderboardStatus.error), - ], - verify: (_) => - verify(() => leaderboardRepository.fetchTop10Leaderboard()) - .called(1), - errors: () => [isA()], - ); - }); - - group('LeaderboardEntryAdded', () { - final leaderboardEntry = LeaderboardEntryData( - playerInitials: 'ABC', - score: 1500, - character: CharacterType.dash, - ); - - final ranking = LeaderboardRanking(ranking: 3, outOf: 4); - - blocTest( - 'emits [loading, success] statuses ' - 'when addLeaderboardEntry succeeds', - setUp: () { - when( - () => leaderboardRepository.addLeaderboardEntry(leaderboardEntry), - ).thenAnswer( - (_) async => ranking, - ); - }, - build: () => LeaderboardBloc(leaderboardRepository), - act: (bloc) => bloc.add(LeaderboardEntryAdded(entry: leaderboardEntry)), - expect: () => [ - LeaderboardState.initial(), - isA() - ..having( - (element) => element.status, - 'status', - equals(LeaderboardStatus.success), - ) - ..having( - (element) => element.ranking, - 'ranking', - equals(ranking), - ) - ], - verify: (_) => verify( - () => leaderboardRepository.addLeaderboardEntry(leaderboardEntry), - ).called(1), - ); - - blocTest( - 'emits [loading, error] statuses ' - 'when addLeaderboardEntry fails', - setUp: () { - when( - () => leaderboardRepository.addLeaderboardEntry(leaderboardEntry), - ).thenThrow( - Exception(), - ); - }, - build: () => LeaderboardBloc(leaderboardRepository), - act: (bloc) => bloc.add(LeaderboardEntryAdded(entry: leaderboardEntry)), - expect: () => [ - LeaderboardState.initial(), - LeaderboardState.initial().copyWith(status: LeaderboardStatus.error), - ], - verify: (_) => verify( - () => leaderboardRepository.addLeaderboardEntry(leaderboardEntry), - ).called(1), - errors: () => [isA()], - ); - }); - }); - - group('CharacterTypeX', () { - test('converts CharacterType.android to AndroidTheme', () { - expect(CharacterType.android.toTheme, equals(AndroidTheme())); - }); - - test('converts CharacterType.dash to DashTheme', () { - expect(CharacterType.dash.toTheme, equals(DashTheme())); - }); - - test('converts CharacterType.dino to DinoTheme', () { - expect(CharacterType.dino.toTheme, equals(DinoTheme())); - }); - - test('converts CharacterType.sparky to SparkyTheme', () { - expect(CharacterType.sparky.toTheme, equals(SparkyTheme())); - }); - }); - - group('CharacterThemeX', () { - test('converts AndroidTheme to CharacterType.android', () { - expect(AndroidTheme().toType, equals(CharacterType.android)); - }); - - test('converts DashTheme to CharacterType.dash', () { - expect(DashTheme().toType, equals(CharacterType.dash)); - }); - - test('converts DinoTheme to CharacterType.dino', () { - expect(DinoTheme().toType, equals(CharacterType.dino)); - }); - - test('converts SparkyTheme to CharacterType.sparky', () { - expect(SparkyTheme().toType, equals(CharacterType.sparky)); - }); - }); -} diff --git a/test/leaderboard/bloc/leaderboard_event_test.dart b/test/leaderboard/bloc/leaderboard_event_test.dart deleted file mode 100644 index 33199ca1..00000000 --- a/test/leaderboard/bloc/leaderboard_event_test.dart +++ /dev/null @@ -1,41 +0,0 @@ -// ignore_for_file: prefer_const_constructors - -import 'package:flutter_test/flutter_test.dart'; -import 'package:leaderboard_repository/leaderboard_repository.dart'; -import 'package:pinball/leaderboard/leaderboard.dart'; - -void main() { - group('GameEvent', () { - group('Top10Fetched', () { - test('can be instantiated', () { - expect(const Top10Fetched(), isNotNull); - }); - - test('supports value equality', () { - expect( - Top10Fetched(), - equals(const Top10Fetched()), - ); - }); - }); - - group('LeaderboardEntryAdded', () { - const leaderboardEntry = LeaderboardEntryData( - playerInitials: 'ABC', - score: 1500, - character: CharacterType.dash, - ); - - test('can be instantiated', () { - expect(const LeaderboardEntryAdded(entry: leaderboardEntry), isNotNull); - }); - - test('supports value equality', () { - expect( - LeaderboardEntryAdded(entry: leaderboardEntry), - equals(const LeaderboardEntryAdded(entry: leaderboardEntry)), - ); - }); - }); - }); -} diff --git a/test/leaderboard/bloc/leaderboard_state_test.dart b/test/leaderboard/bloc/leaderboard_state_test.dart deleted file mode 100644 index 1b5d41d9..00000000 --- a/test/leaderboard/bloc/leaderboard_state_test.dart +++ /dev/null @@ -1,72 +0,0 @@ -// ignore_for_file: prefer_const_constructors - -import 'package:flutter_test/flutter_test.dart'; -import 'package:leaderboard_repository/leaderboard_repository.dart'; -import 'package:pinball/leaderboard/leaderboard.dart'; -import 'package:pinball_theme/pinball_theme.dart'; - -void main() { - group('LeaderboardState', () { - test('supports value equality', () { - expect( - LeaderboardState.initial(), - equals( - LeaderboardState.initial(), - ), - ); - }); - - group('constructor', () { - test('can be instantiated', () { - expect( - LeaderboardState.initial(), - isNotNull, - ); - }); - }); - - group('copyWith', () { - final leaderboardEntry = LeaderboardEntry( - rank: '1', - playerInitials: 'ABC', - score: 1500, - character: DashTheme().leaderboardIcon, - ); - - test( - 'copies correctly ' - 'when no argument specified', - () { - const leaderboardState = LeaderboardState.initial(); - expect( - leaderboardState.copyWith(), - equals(leaderboardState), - ); - }, - ); - - test( - 'copies correctly ' - 'when all arguments specified', - () { - const leaderboardState = LeaderboardState.initial(); - final otherLeaderboardState = LeaderboardState( - status: LeaderboardStatus.success, - ranking: LeaderboardRanking(ranking: 0, outOf: 0), - leaderboard: [leaderboardEntry], - ); - expect(leaderboardState, isNot(equals(otherLeaderboardState))); - - expect( - leaderboardState.copyWith( - status: otherLeaderboardState.status, - ranking: otherLeaderboardState.ranking, - leaderboard: otherLeaderboardState.leaderboard, - ), - equals(otherLeaderboardState), - ); - }, - ); - }); - }); -} diff --git a/test/leaderboard/view/leaderboard_page_test.dart b/test/leaderboard/view/leaderboard_page_test.dart deleted file mode 100644 index daacb4a7..00000000 --- a/test/leaderboard/view/leaderboard_page_test.dart +++ /dev/null @@ -1,165 +0,0 @@ -// ignore_for_file: prefer_const_constructors - -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/l10n/l10n.dart'; -import 'package:pinball/leaderboard/leaderboard.dart'; -import 'package:pinball_theme/pinball_theme.dart'; - -import '../../helpers/helpers.dart'; - -void main() { - group('LeaderboardPage', () { - testWidgets('renders LeaderboardView', (tester) async { - await tester.pumpApp( - LeaderboardPage( - theme: DashTheme(), - ), - ); - - expect(find.byType(LeaderboardView), findsOneWidget); - }); - - testWidgets('route returns a valid navigation route', (tester) async { - await expectNavigatesToRoute( - tester, - LeaderboardPage.route( - theme: DashTheme(), - ), - ); - }); - }); - - group('LeaderboardView', () { - late LeaderboardBloc leaderboardBloc; - - setUp(() { - leaderboardBloc = MockLeaderboardBloc(); - }); - - testWidgets('renders correctly', (tester) async { - final l10n = await AppLocalizations.delegate.load(Locale('en')); - whenListen( - leaderboardBloc, - const Stream.empty(), - initialState: LeaderboardState.initial(), - ); - - await tester.pumpApp( - BlocProvider.value( - value: leaderboardBloc, - child: LeaderboardView( - theme: DashTheme(), - ), - ), - ); - - expect(find.text(l10n.leaderboard), findsOneWidget); - expect(find.text(l10n.retry), findsOneWidget); - }); - - testWidgets('renders loading view when bloc emits [loading]', - (tester) async { - whenListen( - leaderboardBloc, - const Stream.empty(), - initialState: LeaderboardState.initial(), - ); - - await tester.pumpApp( - BlocProvider.value( - value: leaderboardBloc, - child: LeaderboardView( - theme: DashTheme(), - ), - ), - ); - - expect(find.byType(CircularProgressIndicator), findsOneWidget); - expect(find.text('There was en error loading data!'), findsNothing); - expect(find.byType(ListView), findsNothing); - }); - - testWidgets('renders error view when bloc emits [error]', (tester) async { - whenListen( - leaderboardBloc, - const Stream.empty(), - initialState: LeaderboardState.initial().copyWith( - status: LeaderboardStatus.error, - ), - ); - - await tester.pumpApp( - BlocProvider.value( - value: leaderboardBloc, - child: LeaderboardView( - theme: DashTheme(), - ), - ), - ); - - expect(find.byType(CircularProgressIndicator), findsNothing); - expect(find.text('There was en error loading data!'), findsOneWidget); - expect(find.byType(ListView), findsNothing); - }); - - testWidgets('renders success view when bloc emits [success]', - (tester) async { - final l10n = await AppLocalizations.delegate.load(Locale('en')); - whenListen( - leaderboardBloc, - const Stream.empty(), - initialState: LeaderboardState( - status: LeaderboardStatus.success, - ranking: LeaderboardRanking(ranking: 0, outOf: 0), - leaderboard: [ - LeaderboardEntry( - rank: '1', - playerInitials: 'ABC', - score: 10000, - character: DashTheme().leaderboardIcon, - ), - ], - ), - ); - - await tester.pumpApp( - BlocProvider.value( - value: leaderboardBloc, - child: LeaderboardView( - theme: DashTheme(), - ), - ), - ); - - expect(find.byType(CircularProgressIndicator), findsNothing); - expect(find.text('There was en error loading data!'), findsNothing); - expect(find.text(l10n.rank), findsOneWidget); - expect(find.text(l10n.character), findsOneWidget); - expect(find.text(l10n.username), findsOneWidget); - expect(find.text(l10n.score), findsOneWidget); - expect(find.byType(ListView), findsOneWidget); - }); - - testWidgets('navigates to CharacterSelectionPage when retry is tapped', - (tester) async { - final navigator = MockNavigator(); - when(() => navigator.push(any())).thenAnswer((_) async {}); - - await tester.pumpApp( - LeaderboardPage( - theme: DashTheme(), - ), - navigator: navigator, - ); - await tester.ensureVisible(find.byType(TextButton)); - await tester.tap(find.byType(TextButton)); - - verify(() => navigator.push(any())).called(1); - }); - }); -}