From b4f07ccd1476f2939a29d21676d557fff1e36604 Mon Sep 17 00:00:00 2001 From: Vadim <44135514+vodemn@users.noreply.github.com> Date: Sat, 14 Oct 2023 20:41:39 +0200 Subject: [PATCH] Metering layout features tests --- integration_test/generate_screenshots.dart | 43 -- integration_test/metering_screen_test.dart | 417 ++++++++++++++---- .../mocks/paid_features_mock.dart | 17 +- integration_test/utils/expectations.dart | 40 +- lib/screens/metering/screen_metering.dart | 13 +- .../listener_metering_layout_feature.dart | 52 --- ...ialog_metering_screen_layout_features.dart | 11 +- 7 files changed, 384 insertions(+), 209 deletions(-) delete mode 100644 lib/screens/metering/utils/listener_metering_layout_feature.dart diff --git a/integration_test/generate_screenshots.dart b/integration_test/generate_screenshots.dart index 8c9b8c4..2000d3d 100644 --- a/integration_test/generate_screenshots.dart +++ b/integration_test/generate_screenshots.dart @@ -236,46 +236,3 @@ extension on WidgetTester { await pumpAndSettle(); } } - -final _mockEquipmentProfiles = [ - const EquipmentProfile( - id: '', - name: '', - apertureValues: ApertureValue.values, - ndValues: NdValue.values, - shutterSpeedValues: ShutterSpeedValue.values, - isoValues: IsoValue.values, - ), - EquipmentProfile( - id: '1', - name: 'Praktica + Zenitar', - apertureValues: ApertureValue.values.sublist( - ApertureValue.values.indexOf(const ApertureValue(1.7, StopType.half)), - ApertureValue.values.indexOf(const ApertureValue(16, StopType.full)) + 1, - ), - ndValues: NdValue.values.sublist(0, 3), - shutterSpeedValues: ShutterSpeedValue.values.sublist( - ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(1000, true, StopType.full)), - ShutterSpeedValue.values.indexOf(const ShutterSpeedValue(16, false, StopType.full)) + 1, - ), - isoValues: const [ - IsoValue(50, StopType.full), - IsoValue(100, StopType.full), - IsoValue(200, StopType.full), - IsoValue(250, StopType.third), - IsoValue(400, StopType.full), - IsoValue(500, StopType.third), - IsoValue(800, StopType.full), - IsoValue(1600, StopType.full), - IsoValue(3200, StopType.full), - ], - ), - const EquipmentProfile( - id: '2', - name: 'Praktica + Jupiter', - apertureValues: ApertureValue.values, - ndValues: NdValue.values, - shutterSpeedValues: ShutterSpeedValue.values, - isoValues: IsoValue.values, - ), -]; diff --git a/integration_test/metering_screen_test.dart b/integration_test/metering_screen_test.dart index 76630fd..78e35bf 100644 --- a/integration_test/metering_screen_test.dart +++ b/integration_test/metering_screen_test.dart @@ -27,6 +27,9 @@ import 'package:lightmeter/screens/metering/components/shared/readings_container import 'package:lightmeter/screens/metering/components/shared/readings_container/components/iso_picker/widget_picker_iso.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/nd_picker/widget_picker_nd.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/shared/animated_dialog_picker/components/dialog_picker/widget_picker_dialog.dart'; +import 'package:lightmeter/screens/metering/screen_metering.dart'; +import 'package:lightmeter/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart'; +import 'package:lightmeter/screens/settings/screen_settings.dart'; import 'package:lightmeter/screens/shared/icon_placeholder/widget_icon_placeholder.dart'; import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -63,7 +66,7 @@ void main() { setUpAll(() { mockUserPreferencesService = _MockUserPreferencesService(); - when(() => mockUserPreferencesService.evSourceType).thenReturn(EvSourceType.camera); + when(() => mockUserPreferencesService.evSourceType).thenReturn(EvSourceType.sensor); when(() => mockUserPreferencesService.stopType).thenReturn(StopType.third); when(() => mockUserPreferencesService.locale).thenReturn(SupportedLocale.en); when(() => mockUserPreferencesService.caffeine).thenReturn(true); @@ -94,8 +97,10 @@ void main() { when(() => mockHapticsService.errorVibration()).thenAnswer((_) async {}); mockPermissionsService = _MockPermissionsService(); - when(() => mockPermissionsService.requestCameraPermission()).thenAnswer((_) async => PermissionStatus.granted); - when(() => mockPermissionsService.checkCameraPermission()).thenAnswer((_) async => PermissionStatus.granted); + when(() => mockPermissionsService.requestCameraPermission()) + .thenAnswer((_) async => PermissionStatus.granted); + when(() => mockPermissionsService.checkCameraPermission()) + .thenAnswer((_) async => PermissionStatus.granted); mockLightSensorService = _MockLightSensorService(); when(() => mockLightSensorService.hasSensor()).thenAnswer((_) async => true); @@ -104,16 +109,24 @@ void main() { mockVolumeEventsService = _MockVolumeEventsService(); when(() => mockVolumeEventsService.setVolumeHandling(true)).thenAnswer((_) async => true); when(() => mockVolumeEventsService.setVolumeHandling(false)).thenAnswer((_) async => false); - when(() => mockVolumeEventsService.volumeButtonsEventStream()).thenAnswer((_) => const Stream.empty()); + when(() => mockVolumeEventsService.volumeButtonsEventStream()) + .thenAnswer((_) => const Stream.empty()); when(() => mockHapticsService.quickVibration()).thenAnswer((_) async {}); when(() => mockHapticsService.responseVibration()).thenAnswer((_) async {}); }); - Future pumpApplication(WidgetTester tester, IAPProductStatus purchaseStatus) async { + Future pumpApplication( + WidgetTester tester, + IAPProductStatus purchaseStatus, { + String selectedEquipmentProfileId = '', + Film selectedFilm = const Film.other(), + }) async { await tester.pumpWidget( MockIAPProviders( purchaseStatus: purchaseStatus, + selectedEquipmentProfileId: selectedEquipmentProfileId, + selectedFilm: selectedFilm, child: ServicesProvider( environment: const Environment.prod().copyWith(hasLightSensor: true), userPreferencesService: mockUserPreferencesService, @@ -131,90 +144,116 @@ void main() { await tester.pumpAndSettle(); } - group('Match extreme exposure pairs & pairs list edge values', () { - setUpAll(() { - when(() => mockUserPreferencesService.evSourceType).thenReturn(EvSourceType.sensor); - when(() => mockLightSensorService.luxStream()).thenAnswer((_) => Stream.fromIterable([100])); - }); - - testWidgets( - 'No exposure pairs', - (tester) async { - await pumpApplication(tester, IAPProductStatus.purchasable); - - final pickerFinder = find.byType(ExtremeExposurePairsContainer); - expect(pickerFinder, findsOneWidget); - expect(find.descendant(of: pickerFinder, matching: find.text(S.current.fastestExposurePair)), findsOneWidget); - expect(find.descendant(of: pickerFinder, matching: find.text(S.current.slowestExposurePair)), findsOneWidget); - expect(find.descendant(of: pickerFinder, matching: find.text('-')), findsNWidgets(2)); - + group( + 'Match extreme exposure pairs & pairs list edge values', + () { + void expectExposurePairsListItem(int index, String aperture, String shutterSpeed) { + final firstPairRow = find.byWidgetPredicate( + (widget) => widget is Row && widget.key == ValueKey(index), + ); expect( - find.descendant( - of: find.byType(ExposurePairsList), - matching: find.byWidgetPredicate( - (widget) => - widget is IconPlaceholder && - widget.icon == Icons.not_interested && - widget.text == S.current.noExposurePairs, - ), - ), + find.descendant(of: firstPairRow, matching: find.text(aperture)), findsOneWidget, ); - }, - ); - - testWidgets( - 'Multiple exposure pairs', - (tester) async { - await pumpApplication(tester, IAPProductStatus.purchasable); - await tester.tap(find.byType(MeteringMeasureButton)); - await tester.tap(find.byType(MeteringMeasureButton)); - await tester.pumpAndSettle(); - - final pickerFinder = find.byType(ExtremeExposurePairsContainer); - expect(pickerFinder, findsOneWidget); - expect(find.descendant(of: pickerFinder, matching: find.text(S.current.fastestExposurePair)), findsOneWidget); - expect(find.descendant(of: pickerFinder, matching: find.text(S.current.slowestExposurePair)), findsOneWidget); - expect(find.descendant(of: pickerFinder, matching: find.text('f/1.0 - 1/160')), findsOneWidget); - expect(find.descendant(of: pickerFinder, matching: find.text('f/45 - 13"')), findsOneWidget); - - final firstPairRow = find.byWidgetPredicate((widget) => widget is Row && widget.key == const ValueKey(0)); - expect(find.descendant(of: firstPairRow, matching: find.text('f/1.0')), findsOneWidget); - expect(find.descendant(of: firstPairRow, matching: find.text('1/160')), findsOneWidget); - - final lastPairRow = find.byWidgetPredicate( - (widget) => - widget is Row && widget.key == ValueKey(ApertureValue.values.whereStopType(StopType.third).length - 1), + expect( + find.descendant(of: firstPairRow, matching: find.text(shutterSpeed)), + findsOneWidget, ); - final listFinder = find.descendant(of: find.byType(ExposurePairsList), matching: find.byType(Scrollable)); - await tester.scrollUntilVisible(lastPairRow, 56, scrollable: listFinder); - expect(find.descendant(of: lastPairRow, matching: find.text('f/45')), findsOneWidget); - expect(find.descendant(of: lastPairRow, matching: find.text('13"')), findsOneWidget); - }, - ); - }); + } + + setUpAll(() { + when(() => mockUserPreferencesService.evSourceType).thenReturn(EvSourceType.sensor); + when(() => mockLightSensorService.luxStream()) + .thenAnswer((_) => Stream.fromIterable([100])); + }); - group( - 'Free version', - () { testWidgets( - 'Initial state', + 'No exposure pairs', (tester) async { await pumpApplication(tester, IAPProductStatus.purchasable); - expectAnimatedPicker(S.current.equipmentProfile, S.current.none); - expectAnimatedPicker(S.current.film, S.current.none); - expectAnimatedPicker(S.current.iso, '400'); - expectAnimatedPicker(S.current.nd, S.current.none); - await tester.openAnimatedPicker(); - expect(find.byType(DialogPicker), findsOneWidget); - // expect None selected and no other profiles present - await tester.tapCancelButton(); - expect(find.byType(DialogPicker), findsNothing); + final pickerFinder = find.byType(ExtremeExposurePairsContainer); + expect(pickerFinder, findsOneWidget); + expect( + find.descendant(of: pickerFinder, matching: find.text(S.current.fastestExposurePair)), + findsOneWidget, + ); + expect( + find.descendant(of: pickerFinder, matching: find.text(S.current.slowestExposurePair)), + findsOneWidget, + ); + expect(find.descendant(of: pickerFinder, matching: find.text('-')), findsNWidgets(2)); - await tester.openAnimatedPicker(); - await tester.openAnimatedPicker(); - await tester.openAnimatedPicker(); + expect( + find.descendant( + of: find.byType(ExposurePairsList), + matching: find.byWidgetPredicate( + (widget) => + widget is IconPlaceholder && + widget.icon == Icons.not_interested && + widget.text == S.current.noExposurePairs, + ), + ), + findsOneWidget, + ); + }, + ); + + testWidgets( + 'Multiple exposure pairs w/o reciprocity', + (tester) async { + await pumpApplication(tester, IAPProductStatus.purchasable); + await tester.toggleIncidentMetering(); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectExposurePairsListItem(0, 'f/1.0', '1/160'); + expectMeasureButton(7.3); + + final exposurePairs = MeteringContainerBuidler.buildExposureValues( + 7.3, + StopType.third, + defaultEquipmentProfile, + ); + await tester.scrollUntilVisible( + find.byWidgetPredicate( + (widget) => widget is Row && widget.key == ValueKey(exposurePairs.length - 1), + ), + 56, + scrollable: find.descendant( + of: find.byType(ExposurePairsList), + matching: find.byType(Scrollable), + ), + ); + expectExposurePairsListItem(exposurePairs.length - 1, 'f/45', '13"'); + expectMeasureButton(7.3); + }, + ); + + testWidgets( + 'Multiple exposure pairs w/ reciprocity', + (tester) async { + await pumpApplication(tester, IAPProductStatus.purchased, selectedFilm: mockFilms.first); + await tester.toggleIncidentMetering(); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 26"'); + expectExposurePairsListItem(0, 'f/1.0', '1/160'); + expectMeasureButton(7.3); + + final exposurePairs = MeteringContainerBuidler.buildExposureValues( + 7.3, + StopType.third, + defaultEquipmentProfile, + ); + await tester.scrollUntilVisible( + find.byWidgetPredicate( + (widget) => widget is Row && widget.key == ValueKey(exposurePairs.length - 1), + ), + 56, + scrollable: find.descendant( + of: find.byType(ExposurePairsList), + matching: find.byType(Scrollable), + ), + ); + expectExposurePairsListItem(exposurePairs.length - 1, 'f/45', '26"'); + expectMeasureButton(7.3); }, ); }, @@ -222,24 +261,192 @@ void main() { ); group( - 'Pro version', + 'Pickers tests', () { + group('Select film', () { + testWidgets( + 'with the same ISO', + (tester) async { + await pumpApplication(tester, IAPProductStatus.purchased); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + + await tester.openAnimatedPicker(); + expect(find.byType(DialogPicker), findsOneWidget); + await tester.tapRadioListTile('2'); + await tester.tapSelectButton(); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + + /// Make sure, that nothing is changed + await tester.tap(find.byType(MeteringMeasureButton)); + await tester.tap(find.byType(MeteringMeasureButton)); + await tester.pumpAndSettle(); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + }, + ); + }); + testWidgets( - 'Initial state', + 'Select ISO +1 EV', (tester) async { await pumpApplication(tester, IAPProductStatus.purchased); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + + await tester.openAnimatedPicker(); + expect(find.byType(DialogPicker), findsOneWidget); + await tester.tapRadioListTile('800'); + await tester.tapSelectButton(); + expectExposurePairsContainer('f/1.0 - 1/320', 'f/45 - 6"'); + expectMeasureButton(8.3); + + /// Make sure, that current ISO is used in metering + await tester.tap(find.byType(MeteringMeasureButton)); + await tester.tap(find.byType(MeteringMeasureButton)); + await tester.pumpAndSettle(); + expectExposurePairsContainer('f/1.0 - 1/320', 'f/45 - 6"'); + expectMeasureButton(8.3); }, ); testWidgets( - 'Film (push/pull)', + 'Select ND -1 EV', (tester) async { await pumpApplication(tester, IAPProductStatus.purchased); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + + await tester.openAnimatedPicker(); + expect(find.byType(DialogPicker), findsOneWidget); + await tester.tapRadioListTile('2'); + await tester.tapSelectButton(); + expectExposurePairsContainer('f/1.0 - 1/80', 'f/36 - 16"'); + expectMeasureButton(6.3); + + /// Make sure, that current ISO is used in metering + await tester.tap(find.byType(MeteringMeasureButton)); + await tester.tap(find.byType(MeteringMeasureButton)); + await tester.pumpAndSettle(); + expectExposurePairsContainer('f/1.0 - 1/80', 'f/36 - 16"'); + expectMeasureButton(6.3); }, ); }, skip: true, ); + + group( + 'Metering layout features', + () { + Future toggleFeatureAndClose(WidgetTester tester, String feature) async { + await tester.openSettings(); + expect(find.byType(SettingsScreen), findsOneWidget); + await tester.tap(find.text(S.current.meteringScreenLayout)); + await tester.pumpAndSettle(); + expect(find.byType(MeteringScreenLayoutFeaturesDialog), findsOneWidget); + await tester.tap( + find.descendant( + of: find.byType(SwitchListTile), + matching: find.text(feature), + ), + ); + await tester.tapSaveButton(); + expect(find.byType(MeteringScreenLayoutFeaturesDialog), findsNothing); + await tester.tap(find.byIcon(Icons.close)); + await tester.pumpAndSettle(); + } + + setUpAll(() { + when(() => mockUserPreferencesService.evSourceType).thenReturn(EvSourceType.sensor); + when(() => mockLightSensorService.luxStream()) + .thenAnswer((_) => Stream.fromIterable([100])); + when(() => mockUserPreferencesService.meteringScreenLayout).thenReturn({ + MeteringScreenLayoutFeature.equipmentProfiles: true, + MeteringScreenLayoutFeature.extremeExposurePairs: true, + MeteringScreenLayoutFeature.filmPicker: true, + MeteringScreenLayoutFeature.histogram: true, + }); + }); + + testWidgets( + 'Toggle equipmentProfiles & discard selected', + (tester) async { + await pumpApplication( + tester, + IAPProductStatus.purchased, + selectedEquipmentProfileId: mockEquipmentProfiles[0].id, + ); + await tester.toggleIncidentMetering(); + expectAnimatedPickerWith(value: mockEquipmentProfiles[0].name); + expectExposurePairsContainer('f/1.8 - 1/50', 'f/16 - 1.6"'); + expectMeasureButton(7.3); + + await toggleFeatureAndClose(tester, S.current.meteringScreenLayoutHintEquipmentProfiles); + expect(find.byType(EquipmentProfilePicker), findsNothing); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + + await toggleFeatureAndClose(tester, S.current.meteringScreenLayoutHintEquipmentProfiles); + expectAnimatedPickerWith(value: S.current.none); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + }, + ); + + testWidgets( + 'Toggle extremeExposurePairs', + (tester) async { + await pumpApplication(tester, IAPProductStatus.purchased); + await tester.toggleIncidentMetering(); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + + await toggleFeatureAndClose(tester, S.current.meteringScreenFeatureExtremeExposurePairs); + expect(find.byType(ExtremeExposurePairsContainer), findsNothing); + expectMeasureButton(7.3); + + await toggleFeatureAndClose(tester, S.current.meteringScreenFeatureExtremeExposurePairs); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + }, + ); + + testWidgets( + 'Toggle film & discard selected', + (tester) async { + await pumpApplication( + tester, + IAPProductStatus.purchased, + selectedFilm: mockFilms.first, + ); + await tester.toggleIncidentMetering(); + expectAnimatedPickerWith(value: mockFilms.first.name); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 26"'); + expectMeasureButton(7.3); + + await toggleFeatureAndClose(tester, S.current.meteringScreenFeatureFilmPicker); + expect(find.byType(FilmPicker), findsNothing); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + + await toggleFeatureAndClose(tester, S.current.meteringScreenFeatureFilmPicker); + expectAnimatedPickerWith(value: S.current.none); + expectExposurePairsContainer('f/1.0 - 1/160', 'f/45 - 13"'); + expectMeasureButton(7.3); + }, + ); + + testWidgets( + 'Toggle histogram', + (tester) async { + await pumpApplication(tester, IAPProductStatus.purchased); + }, + skip: true, // TODO(@vodemn) + ); + }, + ); } extension WidgetTesterActions on WidgetTester { @@ -248,21 +455,45 @@ extension WidgetTesterActions on WidgetTester { await pumpAndSettle(Dimens.durationL); } - Future tapSelectButton() async { - final cancelButton = find.byWidgetPredicate( - (widget) => widget is TextButton && widget.child is Text && (widget.child as Text?)?.data == S.current.select, + Future todo() async { + await tap(find.byType(T)); + await pumpAndSettle(Dimens.durationL); + expect(find.byType(DialogPicker), findsOneWidget); + await tapCancelButton(); + expect(find.byType(DialogPicker), findsNothing); + } + + Future tapRadioListTile(String value) async { + expect(find.descendant(of: find.byType(RadioListTile), matching: find.text(value)), + findsOneWidget); + await tap(find.descendant(of: find.byType(RadioListTile), matching: find.text(value))); + } + + Future tapSelectButton() => tapTextButton(S.current.select); + + Future tapCancelButton() => tapTextButton(S.current.cancel); + + Future tapSaveButton() => tapTextButton(S.current.save); + + Future tapTextButton(String text) async { + final button = find.byWidgetPredicate( + (widget) => + widget is TextButton && widget.child is Text && (widget.child as Text?)?.data == text, ); - expect(cancelButton, findsOneWidget); - await tap(cancelButton); + expect(button, findsOneWidget); + await tap(button); await pumpAndSettle(); } - Future tapCancelButton() async { - final cancelButton = find.byWidgetPredicate( - (widget) => widget is TextButton && widget.child is Text && (widget.child as Text?)?.data == S.current.cancel, - ); - expect(cancelButton, findsOneWidget); - await tap(cancelButton); + Future toggleIncidentMetering() async { + await tap(find.byType(MeteringMeasureButton)); + await tap(find.byType(MeteringMeasureButton)); + await pumpAndSettle(); + } + + Future openSettings() async { + expect(find.byTooltip(S.current.tooltipOpenSettings), findsOneWidget); + await tap(find.byTooltip(S.current.tooltipOpenSettings)); await pumpAndSettle(); } } diff --git a/integration_test/mocks/paid_features_mock.dart b/integration_test/mocks/paid_features_mock.dart index 27850b4..da358c9 100644 --- a/integration_test/mocks/paid_features_mock.dart +++ b/integration_test/mocks/paid_features_mock.dart @@ -67,6 +67,7 @@ class _MockIAPProvidersState extends State { storageService: mockIAPStorageService, child: FilmsProvider( storageService: mockIAPStorageService, + availableFilms: mockFilms, child: widget.child, ), ), @@ -119,4 +120,18 @@ final mockEquipmentProfiles = [ ), ]; -const mockFilms = [Film('Ilford HP5+', 400)]; +const mockFilms = [_MockFilm2x(), _MockFilm3x()]; + +class _MockFilm2x extends Film { + const _MockFilm2x() : super('Mock film 2x', 400); + + @override + double reciprocityFormula(double t) => t * 2; +} + +class _MockFilm3x extends Film { + const _MockFilm3x() : super('Mock film 3x', 800); + + @override + double reciprocityFormula(double t) => t * 3; +} diff --git a/integration_test/utils/expectations.dart b/integration_test/utils/expectations.dart index db5eb18..3fe633b 100644 --- a/integration_test/utils/expectations.dart +++ b/integration_test/utils/expectations.dart @@ -1,14 +1,46 @@ import 'package:flutter_test/flutter_test.dart'; +import 'package:lightmeter/generated/l10n.dart'; +import 'package:lightmeter/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart'; +import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart'; import 'package:lightmeter/screens/settings/components/shared/dialog_picker/widget_dialog_picker.dart'; - -void expectAnimatedPicker(String title, String value) { +/// Expects exactly one picker of the specified type and verifies `title` or/and `value` if any of the values is not null. +void expectAnimatedPickerWith({String? title, String? value}) { final pickerFinder = find.byType(T); - expect(find.descendant(of: pickerFinder, matching: find.text(title)), findsOneWidget); - expect(find.descendant(of: pickerFinder, matching: find.text(value)), findsOneWidget); + expect(pickerFinder, findsOneWidget); + if (title != null) { + expect( + find.descendant(of: pickerFinder, matching: find.text(title)), + findsOneWidget, + ); + } + if (value != null) { + expect( + find.descendant(of: pickerFinder, matching: find.text(value)), + findsOneWidget, + ); + } } /// Finds exactly one dialog picker of the provided value type void expectDialogPicker() { expect(find.byType(DialogPicker), findsOneWidget); } + +void expectMeasureButton(double ev) { + find.descendant( + of: find.byType(MeteringMeasureButton), + matching: find.text('${ev.toStringAsFixed(1)}\n${S.current.ev}'), + ); +} + +void expectExposurePairsContainer(String fastest, String slowest) { + final pickerFinder = find.byType(ExtremeExposurePairsContainer); + expect(pickerFinder, findsOneWidget); + expect(find.descendant(of: pickerFinder, matching: find.text(S.current.fastestExposurePair)), + findsOneWidget); + expect(find.descendant(of: pickerFinder, matching: find.text(fastest)), findsOneWidget); + expect(find.descendant(of: pickerFinder, matching: find.text(S.current.slowestExposurePair)), + findsOneWidget); + expect(find.descendant(of: pickerFinder, matching: find.text(slowest)), findsOneWidget); +} diff --git a/lib/screens/metering/screen_metering.dart b/lib/screens/metering/screen_metering.dart index 9591cae..6900159 100644 --- a/lib/screens/metering/screen_metering.dart +++ b/lib/screens/metering/screen_metering.dart @@ -4,9 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:lightmeter/data/models/ev_source_type.dart'; import 'package:lightmeter/data/models/exposure_pair.dart'; -import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; -import 'package:lightmeter/providers/films_provider.dart'; import 'package:lightmeter/providers/services_provider.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; import 'package:lightmeter/screens/metering/bloc_metering.dart'; @@ -15,7 +13,6 @@ import 'package:lightmeter/screens/metering/components/camera_container/provider import 'package:lightmeter/screens/metering/components/light_sensor_container/provider_container_light_sensor.dart'; import 'package:lightmeter/screens/metering/event_metering.dart'; import 'package:lightmeter/screens/metering/state_metering.dart'; -import 'package:lightmeter/screens/metering/utils/listener_metering_layout_feature.dart'; import 'package:lightmeter/screens/metering/utils/listsner_equipment_profiles.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; @@ -74,15 +71,7 @@ class _InheritedListeners extends StatelessWidget { onDidChangeDependencies: (value) { context.read().add(EquipmentProfileChangedEvent(value)); }, - child: MeteringScreenLayoutFeatureListener( - feature: MeteringScreenLayoutFeature.filmPicker, - onDidChangeDependencies: (value) { - if (!value) { - FilmsProvider.of(context).setFilm(const Film.other()); - } - }, - child: child, - ), + child: child, ); } } diff --git a/lib/screens/metering/utils/listener_metering_layout_feature.dart b/lib/screens/metering/utils/listener_metering_layout_feature.dart deleted file mode 100644 index c245ec3..0000000 --- a/lib/screens/metering/utils/listener_metering_layout_feature.dart +++ /dev/null @@ -1,52 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; -import 'package:lightmeter/providers/user_preferences_provider.dart'; - -/// Listening to multiple dependencies at the same time causes firing an event for all dependencies -/// even though some of them didn't change: -/// ```dart -/// @override -/// void didChangeDependencies() { -/// super.didChangeDependencies(); -/// _bloc.add(EquipmentProfileChangedEvent(EquipmentProfile.of(context))); -/// if (!MeteringScreenLayout.featureStatusOf(context, MeteringScreenLayoutFeature.filmPicker)) { -/// _bloc.add(const FilmChangedEvent(Film.other())); -/// } -/// } -/// ``` -/// To overcome this issue I've decided to create a generic listener, -/// that will listen to each dependency separately. -class MeteringScreenLayoutFeatureListener extends StatefulWidget { - final MeteringScreenLayoutFeature feature; - final ValueChanged onDidChangeDependencies; - final Widget child; - - const MeteringScreenLayoutFeatureListener({ - required this.feature, - required this.onDidChangeDependencies, - required this.child, - super.key, - }); - - @override - State createState() => - _MeteringScreenLayoutFeatureListenerState(); -} - -class _MeteringScreenLayoutFeatureListenerState extends State { - @override - void didChangeDependencies() { - super.didChangeDependencies(); - widget.onDidChangeDependencies( - UserPreferencesProvider.meteringScreenFeatureOf( - context, - widget.feature, - ), - ); - } - - @override - Widget build(BuildContext context) { - return widget.child; - } -} diff --git a/lib/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart b/lib/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart index 2d2fe72..2529e2e 100644 --- a/lib/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart +++ b/lib/screens/settings/components/metering/components/metering_screen_layout/components/meterins_screen_layout_features_dialog/widget_dialog_metering_screen_layout_features.dart @@ -2,20 +2,20 @@ import 'package:flutter/material.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/providers/equipment_profile_provider.dart'; +import 'package:lightmeter/providers/films_provider.dart'; import 'package:lightmeter/providers/user_preferences_provider.dart'; import 'package:lightmeter/res/dimens.dart'; +import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; class MeteringScreenLayoutFeaturesDialog extends StatefulWidget { const MeteringScreenLayoutFeaturesDialog({super.key}); @override - State createState() => - _MeteringScreenLayoutFeaturesDialogState(); + State createState() => _MeteringScreenLayoutFeaturesDialogState(); } class _MeteringScreenLayoutFeaturesDialogState extends State { - late final _features = - MeteringScreenLayoutConfig.from(UserPreferencesProvider.meteringScreenConfigOf(context)); + late final _features = MeteringScreenLayoutConfig.from(UserPreferencesProvider.meteringScreenConfigOf(context)); @override Widget build(BuildContext context) { @@ -57,6 +57,9 @@ class _MeteringScreenLayoutFeaturesDialogState extends State