From 7db43fb83bdd1456d9678d954d823a0fa0bc89bf Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Tue, 29 Oct 2024 10:20:55 +0100 Subject: [PATCH] fixed unit tests --- lib/application_wrapper.dart | 1 - lib/providers/films_provider.dart | 12 +- test/providers/films_provider_test.dart | 283 +++++++++--------- ...extreme_exposure_pairs_container_test.dart | 4 +- .../readings_container/film_picker_test.dart | 30 +- 5 files changed, 169 insertions(+), 161 deletions(-) diff --git a/lib/application_wrapper.dart b/lib/application_wrapper.dart index 2a523e5..cfee009 100644 --- a/lib/application_wrapper.dart +++ b/lib/application_wrapper.dart @@ -58,7 +58,6 @@ class ApplicationWrapper extends StatelessWidget { storageService: iapService, child: FilmsProvider( filmsStorageService: filmsStorageService, - storageService: iapService, child: UserPreferencesProvider( hasLightSensor: hasLightSensor, userPreferencesService: userPreferencesService, diff --git a/lib/providers/films_provider.dart b/lib/providers/films_provider.dart index 004b224..e569b21 100644 --- a/lib/providers/films_provider.dart +++ b/lib/providers/films_provider.dart @@ -5,12 +5,10 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class FilmsProvider extends StatefulWidget { final FilmsStorageService filmsStorageService; - final IAPStorageService storageService; final Widget child; const FilmsProvider({ required this.filmsStorageService, - required this.storageService, required this.child, super.key, }); @@ -39,8 +37,8 @@ class FilmsProviderState extends State { @override Widget build(BuildContext context) { return Films( - predefinedFilms: predefinedFilms, - customFilms: customFilms, + predefinedFilms: context.isPro ? predefinedFilms : {}, + customFilms: context.isPro ? customFilms : {}, selected: context.isPro ? _selectedFilm : const FilmStub(), child: widget.child, ); @@ -64,7 +62,7 @@ class FilmsProviderState extends State { void selectFilm(Film film) { if (_selectedFilm != film) { _selectedId = film.id; - widget.storageService.selectedFilmId = _selectedId; + widget.filmsStorageService.selectedFilmId = _selectedId; setState(() {}); } } @@ -91,7 +89,7 @@ class FilmsProviderState extends State { } Future _init() async { - _selectedId = widget.storageService.selectedFilmId; + _selectedId = widget.filmsStorageService.selectedFilmId; predefinedFilms.addAll(await widget.filmsStorageService.getPredefinedFilms()); customFilms.addAll(await widget.filmsStorageService.getCustomFilms()); _discardSelectedIfNotIncluded(); @@ -105,7 +103,7 @@ class FilmsProviderState extends State { final isSelectedUsed = predefinedFilms[_selectedId]?.isUsed ?? customFilms[_selectedId]?.isUsed ?? false; if (!isSelectedUsed) { _selectedId = const FilmStub().id; - widget.storageService.selectedFilmId = _selectedId; + widget.filmsStorageService.selectedFilmId = _selectedId; } } } diff --git a/test/providers/films_provider_test.dart b/test/providers/films_provider_test.dart index 66c6908..9f659fd 100644 --- a/test/providers/films_provider_test.dart +++ b/test/providers/films_provider_test.dart @@ -5,18 +5,29 @@ import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:mocktail/mocktail.dart'; -class _MockIAPStorageService extends Mock implements IAPStorageService {} +class _MockFilmsStorageService extends Mock implements FilmsStorageService {} void main() { TestWidgetsFlutterBinding.ensureInitialized(); - late _MockIAPStorageService mockIAPStorageService; + late _MockFilmsStorageService mockFilmsStorageService; setUpAll(() { - mockIAPStorageService = _MockIAPStorageService(); + mockFilmsStorageService = _MockFilmsStorageService(); + }); + + setUp(() { + registerFallbackValue(mockCustomFilms.first); + when(() => mockFilmsStorageService.toggleFilm(any(), any())).thenAnswer((_) async {}); + when(() => mockFilmsStorageService.addFilm(any())).thenAnswer((_) async {}); + when(() => mockFilmsStorageService.updateFilm(any())).thenAnswer((_) async {}); + when(() => mockFilmsStorageService.deleteFilm(any())).thenAnswer((_) async {}); + when(() => mockFilmsStorageService.getPredefinedFilms()) + .thenAnswer((_) => Future.value(mockPredefinedFilms.toFilmsMap())); + when(() => mockFilmsStorageService.getCustomFilms()).thenAnswer((_) => Future.value(mockCustomFilms.toFilmsMap())); }); tearDown(() { - reset(mockIAPStorageService); + reset(mockFilmsStorageService); }); Future pumpTestWidget(WidgetTester tester, IAPProductStatus productStatus) async { @@ -30,16 +41,20 @@ void main() { ), ], child: FilmsProvider( - storageService: mockIAPStorageService, - availableFilms: mockFilms, + filmsStorageService: mockFilmsStorageService, child: const _Application(), ), ), ); + await tester.pumpAndSettle(); } - void expectFilmsCount(int count) { - expect(find.text('Films count: $count'), findsOneWidget); + void expectPredefinedFilmsCount(int count) { + expect(find.text('Predefined films count: $count'), findsOneWidget); + } + + void expectCustomFilmsCount(int count) { + expect(find.text('Custom films count: $count'), findsOneWidget); } void expectFilmsInUseCount(int count) { @@ -54,17 +69,21 @@ void main() { 'FilmsProvider dependency on IAPProductStatus', () { setUp(() { - when(() => mockIAPStorageService.selectedFilm).thenReturn(mockFilms.first); - when(() => mockIAPStorageService.filmsInUse).thenReturn(mockFilms); + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockPredefinedFilms.first.id); + when(() => mockFilmsStorageService.getPredefinedFilms()) + .thenAnswer((_) => Future.value(mockPredefinedFilms.toFilmsMap())); + when(() => mockFilmsStorageService.getCustomFilms()) + .thenAnswer((_) => Future.value(mockCustomFilms.toFilmsMap())); }); testWidgets( 'IAPProductStatus.purchased - show all saved films', (tester) async { await pumpTestWidget(tester, IAPProductStatus.purchased); - expectFilmsCount(mockFilms.length + 1); - expectFilmsInUseCount(mockFilms.length + 1); - expectSelectedFilmName(mockFilms.first.name); + expectPredefinedFilmsCount(mockPredefinedFilms.length); + expectCustomFilmsCount(mockCustomFilms.length); + expectFilmsInUseCount(mockPredefinedFilms.length + mockCustomFilms.length + 1); + expectSelectedFilmName(mockPredefinedFilms.first.name); }, ); @@ -72,7 +91,8 @@ void main() { 'IAPProductStatus.purchasable - show only default', (tester) async { await pumpTestWidget(tester, IAPProductStatus.purchasable); - expectFilmsCount(mockFilms.length + 1); + expectPredefinedFilmsCount(0); + expectCustomFilmsCount(0); expectFilmsInUseCount(1); expectSelectedFilmName(''); }, @@ -82,7 +102,8 @@ void main() { 'IAPProductStatus.pending - show only default', (tester) async { await pumpTestWidget(tester, IAPProductStatus.pending); - expectFilmsCount(mockFilms.length + 1); + expectPredefinedFilmsCount(0); + expectCustomFilmsCount(0); expectFilmsInUseCount(1); expectSelectedFilmName(''); }, @@ -91,126 +112,126 @@ void main() { ); group( - 'FilmsProvider CRUD', + 'toggleFilm', () { testWidgets( - 'Select films in use', + 'toggle predefined film', (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(const FilmStub()); - when(() => mockIAPStorageService.filmsInUse).thenReturn([]); - - /// Init + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockPredefinedFilms.first.id); await pumpTestWidget(tester, IAPProductStatus.purchased); - expectFilmsCount(mockFilms.length + 1); - expectFilmsInUseCount(1); + expectPredefinedFilmsCount(mockPredefinedFilms.length); + expectCustomFilmsCount(mockCustomFilms.length); + expectFilmsInUseCount(mockPredefinedFilms.length + mockCustomFilms.length + 1); + expectSelectedFilmName(mockPredefinedFilms.first.name); + + await tester.filmsProvider.toggleFilm(mockPredefinedFilms.first, false); + await tester.pump(); + expectPredefinedFilmsCount(mockPredefinedFilms.length); + expectCustomFilmsCount(mockCustomFilms.length); + expectFilmsInUseCount(mockPredefinedFilms.length - 1 + mockCustomFilms.length + 1); expectSelectedFilmName(''); - /// Select all filmsInUse - await tester.tap(find.byKey(_Application.saveFilmsButtonKey(0))); - await tester.pumpAndSettle(); - expectFilmsCount(mockFilms.length + 1); - expectFilmsInUseCount(mockFilms.length + 1); - expectSelectedFilmName(''); - - verify(() => mockIAPStorageService.filmsInUse = mockFilms.skip(0).toList()).called(1); - verifyNever(() => mockIAPStorageService.selectedFilm = const FilmStub()); + verify(() => mockFilmsStorageService.toggleFilm(mockPredefinedFilms.first, false)).called(1); + verify(() => mockFilmsStorageService.selectedFilmId = '').called(1); }, ); testWidgets( - 'Select film', + 'toggle custom film', (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(const FilmStub()); - when(() => mockIAPStorageService.filmsInUse).thenReturn(mockFilms); - - /// Init + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockCustomFilms.first.id); await pumpTestWidget(tester, IAPProductStatus.purchased); - expectFilmsCount(mockFilms.length + 1); - expectFilmsInUseCount(mockFilms.length + 1); + expectPredefinedFilmsCount(mockPredefinedFilms.length); + expectCustomFilmsCount(mockCustomFilms.length); + expectFilmsInUseCount(mockPredefinedFilms.length + mockCustomFilms.length + 1); + expectSelectedFilmName(mockCustomFilms.first.name); + + await tester.filmsProvider.toggleFilm(mockCustomFilms.first, false); + await tester.pump(); + expectPredefinedFilmsCount(mockPredefinedFilms.length); + expectCustomFilmsCount(mockCustomFilms.length); + expectFilmsInUseCount(mockPredefinedFilms.length - 1 + mockCustomFilms.length + 1); expectSelectedFilmName(''); - /// Select all filmsInUse - await tester.tap(find.byKey(_Application.setFilmButtonKey(0))); - await tester.pumpAndSettle(); - expectFilmsCount(mockFilms.length + 1); - expectFilmsInUseCount(mockFilms.length + 1); - expectSelectedFilmName(mockFilms.first.name); - - verifyNever(() => mockIAPStorageService.filmsInUse = any>()); - verify(() => mockIAPStorageService.selectedFilm = mockFilms.first).called(1); - }, - ); - - group( - 'Coming from free app', - () { - testWidgets( - 'Has selected film', - (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(mockFilms[2]); - when(() => mockIAPStorageService.filmsInUse).thenReturn([]); - - /// Init - await pumpTestWidget(tester, IAPProductStatus.purchased); - expectFilmsInUseCount(1); - expectSelectedFilmName(''); - - verifyNever(() => mockIAPStorageService.filmsInUse = any>()); - verify(() => mockIAPStorageService.selectedFilm = const FilmStub()).called(1); - }, - ); - - testWidgets( - 'None film selected', - (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(const FilmStub()); - when(() => mockIAPStorageService.filmsInUse).thenReturn([]); - - /// Init - await pumpTestWidget(tester, IAPProductStatus.purchased); - expectFilmsInUseCount(1); - expectSelectedFilmName(''); - - verifyNever(() => mockIAPStorageService.filmsInUse = any>()); - verifyNever(() => mockIAPStorageService.selectedFilm = const FilmStub()); - }, - ); - }, - ); - - testWidgets( - 'Discard selected (by filmsInUse list update)', - (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(mockFilms.first); - when(() => mockIAPStorageService.filmsInUse).thenReturn(mockFilms); - - /// Init - await pumpTestWidget(tester, IAPProductStatus.purchased); - expectFilmsCount(mockFilms.length + 1); - expectFilmsInUseCount(mockFilms.length + 1); - expectSelectedFilmName(mockFilms.first.name); - - /// Select all filmsInUse except the first one - await tester.tap(find.byKey(_Application.saveFilmsButtonKey(1))); - await tester.pumpAndSettle(); - expectFilmsCount(mockFilms.length + 1); - expectFilmsInUseCount((mockFilms.length - 1) + 1); - expectSelectedFilmName(''); - - verify(() => mockIAPStorageService.filmsInUse = mockFilms.skip(1).toList()).called(1); - verify(() => mockIAPStorageService.selectedFilm = const FilmStub()).called(1); + verify(() => mockFilmsStorageService.toggleFilm(mockCustomFilms.first, false)).called(1); + verify(() => mockFilmsStorageService.selectedFilmId = '').called(1); }, ); }, ); + + testWidgets( + 'selectFilm', + (tester) async { + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(''); + await pumpTestWidget(tester, IAPProductStatus.purchased); + expectSelectedFilmName(''); + + tester.filmsProvider.selectFilm(mockPredefinedFilms.first); + await tester.pump(); + expectSelectedFilmName(mockPredefinedFilms.first.name); + + tester.filmsProvider.selectFilm(mockCustomFilms.first); + await tester.pump(); + expectSelectedFilmName(mockCustomFilms.first.name); + + verify(() => mockFilmsStorageService.selectedFilmId = mockPredefinedFilms.first.id).called(1); + verify(() => mockFilmsStorageService.selectedFilmId = mockCustomFilms.first.id).called(1); + }, + ); + + testWidgets( + 'Custom film CRUD', + (tester) async { + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(''); + when(() => mockFilmsStorageService.getCustomFilms()).thenAnswer((_) => Future.value({})); + await pumpTestWidget(tester, IAPProductStatus.purchased); + expectPredefinedFilmsCount(mockPredefinedFilms.length); + expectCustomFilmsCount(0); + expectFilmsInUseCount(mockPredefinedFilms.length + 0 + 1); + expectSelectedFilmName(''); + + await tester.filmsProvider.addCustomFilm(mockCustomFilms.first); + await tester.filmsProvider.toggleFilm(mockCustomFilms.first, true); + tester.filmsProvider.selectFilm(mockCustomFilms.first); + await tester.pump(); + expectPredefinedFilmsCount(mockPredefinedFilms.length); + expectCustomFilmsCount(1); + expectFilmsInUseCount(mockPredefinedFilms.length + 1 + 1); + expectSelectedFilmName(mockCustomFilms.first.name); + verify(() => mockFilmsStorageService.addFilm(mockCustomFilms.first)).called(1); + verify(() => mockFilmsStorageService.toggleFilm(mockCustomFilms.first, true)).called(1); + verify(() => mockFilmsStorageService.selectedFilmId = mockCustomFilms.first.id).called(1); + + const editedFilmName = 'Edited custom film 2x'; + final editedFilm = mockCustomFilms.first.copyWith(name: editedFilmName); + await tester.filmsProvider.updateCustomFilm(editedFilm); + await tester.pump(); + expectSelectedFilmName(editedFilm.name); + verify(() => mockFilmsStorageService.updateFilm(editedFilm)).called(1); + + await tester.filmsProvider.deleteCustomFilm(editedFilm); + await tester.pump(); + expectPredefinedFilmsCount(mockPredefinedFilms.length); + expectCustomFilmsCount(0); + expectFilmsInUseCount(mockPredefinedFilms.length + 0 + 1); + expectSelectedFilmName(''); + verify(() => mockFilmsStorageService.deleteFilm(editedFilm)).called(1); + verify(() => mockFilmsStorageService.selectedFilmId = '').called(1); + }, + ); +} + +extension on WidgetTester { + FilmsProviderState get filmsProvider { + final BuildContext context = element(find.byType(_Application)); + return FilmsProvider.of(context); + } } class _Application extends StatelessWidget { const _Application(); - static ValueKey saveFilmsButtonKey(int index) => ValueKey('saveFilmsButtonKey$index'); - static ValueKey setFilmButtonKey(int index) => ValueKey('setFilmButtonKey$index'); - @override Widget build(BuildContext context) { return MaterialApp( @@ -218,42 +239,30 @@ class _Application extends StatelessWidget { body: Center( child: Column( children: [ - Text("Films count: ${Films.of(context).length}"), + Text("Predefined films count: ${Films.predefinedFilmsOf(context).length}"), + Text("Custom films count: ${Films.customFilmsOf(context).length}"), Text("Films in use count: ${Films.inUseOf(context).length}"), Text("Selected film: ${Films.selectedOf(context).name}"), - _filmRow(context, 0), - _filmRow(context, 1), ], ), ), ), ); } - - Widget _filmRow(BuildContext context, int index) { - return Row( - children: [ - ElevatedButton( - key: saveFilmsButtonKey(index), - onPressed: () { - FilmsProvider.of(context).saveFilms(mockFilms.skip(index).toList()); - }, - child: const Text("Save filmsInUse"), - ), - ElevatedButton( - key: setFilmButtonKey(index), - onPressed: () { - FilmsProvider.of(context).setFilm(mockFilms[index]); - }, - child: const Text("Set film"), - ), - ], - ); - } } -const mockFilms = [ +const mockPredefinedFilms = [ FilmExponential(id: '1', name: 'Mock film 2x', iso: 400, exponent: 1.34), FilmExponential(id: '2', name: 'Mock film 3x', iso: 800, exponent: 1.34), FilmExponential(id: '3', name: 'Mock film 4x', iso: 1200, exponent: 1.34), ]; + +const mockCustomFilms = [ + FilmExponential(id: '1abc', name: 'Mock custom film 2x', iso: 400, exponent: 1.34), + FilmExponential(id: '2abc', name: 'Mock custom film 3x', iso: 800, exponent: 1.34), +]; + +extension on List { + Map toFilmsMap() => + Map.fromEntries(map((e) => MapEntry(e.id, (film: e as T, isUsed: true)))); +} diff --git a/test/screens/metering/components/shared/readings_container/extreme_exposure_pairs_container_test.dart b/test/screens/metering/components/shared/readings_container/extreme_exposure_pairs_container_test.dart index 7d79c1f..62ae5d0 100644 --- a/test/screens/metering/components/shared/readings_container/extreme_exposure_pairs_container_test.dart +++ b/test/screens/metering/components/shared/readings_container/extreme_exposure_pairs_container_test.dart @@ -50,8 +50,8 @@ extension WidgetTesterActions on WidgetTester { }) async { await pumpWidget( Films( - values: const [FilmStub()], - filmsInUse: const [FilmStub()], + predefinedFilms: const {}, + customFilms: const {}, selected: const FilmStub(), child: WidgetTestApplicationMock( child: Row( diff --git a/test/screens/metering/components/shared/readings_container/film_picker_test.dart b/test/screens/metering/components/shared/readings_container/film_picker_test.dart index afd8a2f..30a2989 100644 --- a/test/screens/metering/components/shared/readings_container/film_picker_test.dart +++ b/test/screens/metering/components/shared/readings_container/film_picker_test.dart @@ -10,14 +10,16 @@ import 'package:mocktail/mocktail.dart'; import '../../../../../application_mock.dart'; import 'utils.dart'; -class _MockIAPStorageService extends Mock implements IAPStorageService {} +class _MockFilmsStorageService extends Mock implements FilmsStorageService {} void main() { - late final _MockIAPStorageService mockIAPStorageService; + late final _MockFilmsStorageService mockFilmsStorageService; setUpAll(() { - mockIAPStorageService = _MockIAPStorageService(); - when(() => mockIAPStorageService.filmsInUse).thenReturn(_films); + mockFilmsStorageService = _MockFilmsStorageService(); + when(() => mockFilmsStorageService.getPredefinedFilms()).thenAnswer( + (_) => Future.value(Map.fromEntries(_films.map((e) => MapEntry(e.id, (film: e, isUsed: true))))), + ); }); Future pumpApplication(WidgetTester tester) async { @@ -31,7 +33,7 @@ void main() { ), ], child: FilmsProvider( - storageService: mockIAPStorageService, + filmsStorageService: mockFilmsStorageService, child: const WidgetTestApplicationMock( child: Row( children: [ @@ -51,7 +53,7 @@ void main() { testWidgets( 'Film.other()', (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(const FilmStub()); + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(const FilmStub().id); await pumpApplication(tester); expectReadingValueContainerText(S.current.film); expectReadingValueContainerText(S.current.none); @@ -61,7 +63,7 @@ void main() { testWidgets( 'Film with the same ISO', (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(_films[1]); + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(_films[1].id); await pumpApplication(tester); expectReadingValueContainerText(S.current.film); expectReadingValueContainerText(_films[1].name); @@ -71,7 +73,7 @@ void main() { testWidgets( 'Film with greater ISO', (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(_films[2]); + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(_films[2].id); await pumpApplication(tester); expectReadingValueContainerText(S.current.filmPull); expectReadingValueContainerText(_films[2].name); @@ -81,7 +83,7 @@ void main() { testWidgets( 'Film with lower ISO', (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(_films[0]); + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(_films[0].id); await pumpApplication(tester); expectReadingValueContainerText(S.current.filmPush); expectReadingValueContainerText(_films[0].name); @@ -92,7 +94,7 @@ void main() { testWidgets( 'Film picker shows only films in use', (tester) async { - when(() => mockIAPStorageService.selectedFilm).thenReturn(_films[0]); + when(() => mockFilmsStorageService.selectedFilmId).thenReturn(_films[0].id); await pumpApplication(tester); await tester.openAnimatedPicker(); expectRadioListTile(S.current.none, isSelected: true); @@ -104,8 +106,8 @@ void main() { } const _films = [ - FilmExponential(id: '', name: 'ISO 100 Film', iso: 100, exponent: 1.34), - FilmExponential(id: '', name: 'ISO 400 Film', iso: 400, exponent: 1.34), - FilmExponential(id: '', name: 'ISO 800 Film', iso: 800, exponent: 1.34), - FilmExponential(id: '', name: 'ISO 1600 Film', iso: 1600, exponent: 1.34), + FilmExponential(id: '1', name: 'ISO 100 Film', iso: 100, exponent: 1.34), + FilmExponential(id: '2', name: 'ISO 400 Film', iso: 400, exponent: 1.34), + FilmExponential(id: '3', name: 'ISO 800 Film', iso: 800, exponent: 1.34), + FilmExponential(id: '4', name: 'ISO 1600 Film', iso: 1600, exponent: 1.34), ];