diff --git a/lib/leaderboard/bloc/leaderboard_event.dart b/lib/leaderboard/bloc/leaderboard_event.dart index 48123221..066922e7 100644 --- a/lib/leaderboard/bloc/leaderboard_event.dart +++ b/lib/leaderboard/bloc/leaderboard_event.dart @@ -7,4 +7,6 @@ abstract class LeaderboardEvent extends Equatable { List get props => []; } -class LeaderboardRequested extends LeaderboardEvent {} +class LeaderboardRequested extends LeaderboardEvent { + const LeaderboardRequested(); +} diff --git a/test/leaderboard/bloc/leaderboard_bloc_test.dart b/test/leaderboard/bloc/leaderboard_bloc_test.dart new file mode 100644 index 00000000..f8ff8fd6 --- /dev/null +++ b/test/leaderboard/bloc/leaderboard_bloc_test.dart @@ -0,0 +1,101 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:bloc_test/bloc_test.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:mocktail/mocktail.dart'; +import 'package:pinball/leaderboard/leaderboard.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +class MockLeaderboardRepository extends Mock implements LeaderboardRepository {} + +void main() { + group('LeaderboardBloc', () { + late LeaderboardRepository leaderboardRepository; + + setUp(() { + leaderboardRepository = MockLeaderboardRepository(); + }); + + test('initial state has state loading and empty ranking', () { + final bloc = LeaderboardBloc(leaderboardRepository); + expect(bloc.state.status, equals(LeaderboardStatus.loading)); + expect(bloc.state.ranking.isEmpty, isTrue); + }); + + group('LeaderboardRequested', () { + final ranking = [ + Competitor( + rank: 1, + characterTheme: DashTheme(), + initials: 'ABC', + score: 100, + ), + Competitor( + rank: 2, + characterTheme: SparkyTheme(), + initials: 'DEF', + score: 200, + ), + Competitor( + rank: 3, + characterTheme: AndroidTheme(), + initials: 'GHI', + score: 300, + ), + Competitor( + rank: 4, + characterTheme: DinoTheme(), + initials: 'JKL', + score: 400, + ), + ]; + + blocTest( + 'emits [loading, success] statuses ' + 'when fetchRanking succeeds', + setUp: () { + when(() => leaderboardRepository.fetchRanking()).thenAnswer( + (_) async => ranking, + ); + }, + build: () => LeaderboardBloc(leaderboardRepository), + act: (bloc) => bloc.add(LeaderboardRequested()), + expect: () => [ + const LeaderboardState(), + isA() + ..having( + (element) => element.status, + 'status', + equals(LeaderboardStatus.success), + ) + ..having( + (element) => element.ranking.length, + 'ranking', + equals(ranking.length), + ) + ], + verify: (_) => + verify(() => leaderboardRepository.fetchRanking()).called(1), + ); + + blocTest( + 'emits [loading, error] statuses ' + 'when fetchRanking fails', + setUp: () { + when(() => leaderboardRepository.fetchRanking()).thenThrow( + Exception(), + ); + }, + build: () => LeaderboardBloc(leaderboardRepository), + act: (bloc) => bloc.add(LeaderboardRequested()), + expect: () => [ + const LeaderboardState(), + const LeaderboardState(status: LeaderboardStatus.error), + ], + verify: (_) => + verify(() => leaderboardRepository.fetchRanking()).called(1), + errors: () => [isA()], + ); + }); + }); +} diff --git a/test/leaderboard/bloc/leaderboard_event_test.dart b/test/leaderboard/bloc/leaderboard_event_test.dart new file mode 100644 index 00000000..1589dd1c --- /dev/null +++ b/test/leaderboard/bloc/leaderboard_event_test.dart @@ -0,0 +1,19 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/leaderboard/leaderboard.dart'; + +void main() { + group('GameEvent', () { + group('LeaderboardRequested', () { + test('can be instantiated', () { + expect(const LeaderboardRequested(), isNotNull); + }); + + test('supports value equality', () { + expect( + LeaderboardRequested(), + equals(const LeaderboardRequested()), + ); + }); + }); + }); +} diff --git a/test/leaderboard/bloc/leaderboard_state_test.dart b/test/leaderboard/bloc/leaderboard_state_test.dart new file mode 100644 index 00000000..95cafe3c --- /dev/null +++ b/test/leaderboard/bloc/leaderboard_state_test.dart @@ -0,0 +1,69 @@ +// ignore_for_file: prefer_const_constructors + +import 'package:flutter_test/flutter_test.dart'; +import 'package:pinball/leaderboard/leaderboard.dart'; +import 'package:pinball_theme/pinball_theme.dart'; + +void main() { + group('LeaderboardState', () { + test('supports value equality', () { + expect( + LeaderboardState(), + equals( + LeaderboardState(), + ), + ); + }); + + group('constructor', () { + test('can be instantiated', () { + expect( + LeaderboardState(), + isNotNull, + ); + }); + }); + + group('copyWith', () { + test( + 'copies correctly ' + 'when no argument specified', + () { + const leaderboardState = LeaderboardState(); + expect( + leaderboardState.copyWith(), + equals(leaderboardState), + ); + }, + ); + + test( + 'copies correctly ' + 'when all arguments specified', + () { + const leaderboardState = LeaderboardState(); + final otherLeaderboardState = LeaderboardState( + status: LeaderboardStatus.success, + ranking: const [ + Competitor( + rank: 1, + characterTheme: DashTheme(), + initials: 'ABC', + score: 10, + ), + ], + ); + expect(leaderboardState, isNot(equals(otherLeaderboardState))); + + expect( + leaderboardState.copyWith( + status: otherLeaderboardState.status, + ranking: otherLeaderboardState.ranking, + ), + equals(otherLeaderboardState), + ); + }, + ); + }); + }); +}