Compare commits

..

No commits in common. "c79a5782861c64cb9d7d06ddc5991a62641abb58" and "a70ce5012a22c507e0e9cae0545d98f9c52d1787" have entirely different histories.

6 changed files with 81 additions and 417 deletions

View file

@ -67,7 +67,7 @@ jobs:
if: ${{ matrix.os == 'ubuntu-latest' }} if: ${{ matrix.os == 'ubuntu-latest' }}
uses: reactivecircus/android-emulator-runner@v2 uses: reactivecircus/android-emulator-runner@v2
with: with:
api-level: 32 api-level: 33
target: default target: default
arch: x86_64 arch: x86_64
profile: pixel_6 profile: pixel_6

View file

@ -1,300 +0,0 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:lightmeter/data/models/ev_source_type.dart';
import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
import 'package:lightmeter/data/shared_prefs_service.dart';
import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.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/equipment_profile_picker/widget_picker_equipment_profiles.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart';
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/settings/components/shared/dialog_filter/widget_dialog_filter.dart';
import 'package:lightmeter/screens/settings/components/shared/dialog_range_picker/widget_dialog_picker_range.dart';
import 'package:lightmeter/screens/settings/screen_settings.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../integration_test/utils/widget_tester_actions.dart';
import 'mocks/paid_features_mock.dart';
import 'utils/expectations.dart';
void main() {
setUp(() {
SharedPreferences.setMockInitialValues({
/// Metering values
UserPreferencesService.evSourceTypeKey: EvSourceType.camera.index,
UserPreferencesService.meteringScreenLayoutKey: json.encode(
{
MeteringScreenLayoutFeature.equipmentProfiles: true,
MeteringScreenLayoutFeature.extremeExposurePairs: true,
MeteringScreenLayoutFeature.filmPicker: true,
}.toJson(),
),
});
});
testWidgets(
'e2e',
(tester) async {
await tester.pumpApplication(equipmentProfiles: [], films: []);
/// Create Praktica + Zenitar profile from scratch
await tester.openSettings();
await tester.tapDescendantTextOf<SettingsScreen>(S.current.equipmentProfiles);
await tester.tap(find.byIcon(Icons.add).first);
await tester.pumpAndSettle();
await tester.setProfileName(mockEquipmentProfiles[0].name);
await tester.expandEquipmentProfileContainer(mockEquipmentProfiles[0].name);
await tester.setIsoValues(0, mockEquipmentProfiles[0].isoValues);
await tester.setNdValues(0, mockEquipmentProfiles[0].ndValues);
await tester.setApertureValues(0, mockEquipmentProfiles[0].apertureValues);
await tester.setShutterSpeedValues(0, mockEquipmentProfiles[0].shutterSpeedValues);
expect(find.text('f/1.7 - f/16'), findsOneWidget);
expect(find.text('1/1000 - 16"'), findsOneWidget);
/// Create Praktica + Jupiter profile from Zenitar profile
await tester.tap(find.byIcon(Icons.copy).first);
await tester.pumpAndSettle();
await tester.setProfileName(mockEquipmentProfiles[1].name);
await tester.expandEquipmentProfileContainer(mockEquipmentProfiles[1].name);
await tester.setApertureValues(1, mockEquipmentProfiles[1].apertureValues);
expect(find.text('f/3.5 - f/22'), findsOneWidget);
expect(find.text('1/1000 - 16"'), findsNWidgets(2));
await tester.navigatorPop();
/// Select some films
await tester.tap(find.text(S.current.filmsInUse));
await tester.pumpAndSettle();
await tester.setDialogFilterValues<Film>([mockFilms[0], mockFilms[1]], deselectAll: false);
await tester.navigatorPop();
/// Select some initial settings according to the selected gear and film
/// Then take a photo and verify, that exposure pairs range and EV matches the selected settings
await tester.openPickerAndSelect<EquipmentProfilePicker, EquipmentProfile>(mockEquipmentProfiles[0].name);
await tester.openPickerAndSelect<FilmPicker, Film>(mockFilms[0].name);
await tester.openPickerAndSelect<IsoValuePicker, IsoValue>('400');
expectPickerTitle<EquipmentProfilePicker>(mockEquipmentProfiles[0].name);
expectPickerTitle<FilmPicker>(mockFilms[0].name);
expectPickerTitle<IsoValuePicker>('400');
await tester.takePhoto();
await _expectMeteringState(
tester,
equipmentProfile: mockEquipmentProfiles[0],
film: mockFilms[0],
fastest: 'f/1.8 - 1/400',
slowest: 'f/16 - 1/5',
iso: '400',
nd: 'None',
ev: mockPhotoEv100 + 2,
);
/// Add ND to shoot another scene
await tester.openPickerAndSelect<NdValuePicker, NdValue>('2');
await _expectMeteringStateAndMeasure(
tester,
equipmentProfile: mockEquipmentProfiles[0],
film: mockFilms[0],
fastest: 'f/1.8 - 1/200',
slowest: 'f/16 - 1/2.5',
iso: '400',
nd: '2',
ev: mockPhotoEv100 + 2 - 1,
);
/// Select another lens without ND
await tester.openPickerAndSelect<EquipmentProfilePicker, EquipmentProfile>(mockEquipmentProfiles[1].name);
await tester.openPickerAndSelect<NdValuePicker, NdValue>('None');
await _expectMeteringStateAndMeasure(
tester,
equipmentProfile: mockEquipmentProfiles[1],
film: mockFilms[0],
fastest: 'f/3.5 - 1/100',
slowest: 'f/22 - 1/2.5',
iso: '400',
nd: 'None',
ev: mockPhotoEv100 + 2,
);
/// Set another film and another ISO
await tester.openPickerAndSelect<IsoValuePicker, IsoValue>('200');
await tester.openPickerAndSelect<FilmPicker, Film>(mockFilms[1].name);
await _expectMeteringStateAndMeasure(
tester,
equipmentProfile: mockEquipmentProfiles[1],
film: mockFilms[1],
fastest: 'f/3.5 - 1/50',
slowest: 'f/22 - 1/1.3',
iso: '200',
nd: 'None',
ev: mockPhotoEv100 + 1,
);
},
);
}
extension EquipmentProfileActions on WidgetTester {
Future<void> expandEquipmentProfileContainer(String name) async {
await tap(find.text(name));
await pump(Dimens.durationM);
}
Future<void> setProfileName(String name) async {
await enterText(find.byType(TextField), name);
await pump();
await tapSaveButton();
}
Future<void> setIsoValues(int profileIndex, List<IsoValue> values) =>
_openAndSetDialogFilterValues<IsoValue>(profileIndex, S.current.isoValues, values);
Future<void> setNdValues(int profileIndex, List<NdValue> values) =>
_openAndSetDialogFilterValues<NdValue>(profileIndex, S.current.ndFilters, values);
Future<void> _openAndSetDialogFilterValues<T extends PhotographyValue>(
int profileIndex,
String listTileTitle,
List<T> valuesToSelect, {
bool deselectAll = true,
}) async {
await tap(find.text(listTileTitle).at(profileIndex));
await pumpAndSettle();
await setDialogFilterValues(valuesToSelect, deselectAll: deselectAll);
}
Future<void> setApertureValues(int profileIndex, List<ApertureValue> values) =>
_setDialogRangePickerValues<ApertureValue>(profileIndex, S.current.apertureValues, values);
Future<void> setShutterSpeedValues(int profileIndex, List<ShutterSpeedValue> values) =>
_setDialogRangePickerValues<ShutterSpeedValue>(profileIndex, S.current.shutterSpeedValues, values);
}
extension on WidgetTester {
Future<void> openPickerAndSelect<P extends Widget, V>(String valueToSelect) async {
await openAnimatedPicker<P>();
await tapDescendantTextOf<DialogPicker<V>>(valueToSelect);
await tapSelectButton();
}
Future<void> setDialogFilterValues<T>(
List<T> valuesToSelect, {
bool deselectAll = true,
}) async {
if (deselectAll) {
await tap(find.byIcon(Icons.deselect));
await pump();
}
for (final value in valuesToSelect) {
final listTile = find.descendant(of: find.byType(CheckboxListTile), matching: find.text(value.toString()));
await scrollUntilVisible(
listTile,
56,
scrollable: find.descendant(of: find.byType(DialogFilter<T>), matching: find.byType(Scrollable)),
);
await tap(listTile);
await pump();
}
await tapSaveButton();
}
Future<void> _setDialogRangePickerValues<T extends PhotographyValue>(
int profileIndex,
String listTileTitle,
List<T> valuesToSelect,
) async {
await tap(find.text(listTileTitle).at(profileIndex));
await pumpAndSettle();
final dialog = widget<DialogRangePicker<T>>(find.byType(DialogRangePicker<T>));
final sliderFinder = find.byType(RangeSlider);
final divisions = widget<RangeSlider>(sliderFinder).divisions!;
final trackWidth = getSize(sliderFinder).width - (2 * Dimens.paddingL);
final trackStep = trackWidth / divisions;
final start = valuesToSelect.first;
final oldStart = dialog.values.indexWhere((e) => e.value == dialog.selectedValues.first.value) * trackStep;
final newStart = dialog.values.indexWhere((e) => e.value == start.value) * trackStep;
await dragFrom(
getTopLeft(sliderFinder) + Offset(Dimens.paddingL + oldStart, getSize(sliderFinder).height / 2),
Offset(newStart - oldStart, 0),
);
await pump();
final end = valuesToSelect.last;
final oldEnd = dialog.values.indexWhere((e) => e.value == dialog.selectedValues.last.value) * trackStep;
final newEnd = dialog.values.indexWhere((e) => e.value == end.value) * trackStep;
await dragFrom(
getTopLeft(sliderFinder) + Offset(Dimens.paddingL + oldEnd, getSize(sliderFinder).height / 2),
Offset(newEnd - oldEnd, 0),
);
await pump();
await tapSaveButton();
}
}
Future<void> _expectMeteringState(
WidgetTester tester, {
required EquipmentProfile equipmentProfile,
required Film film,
required String fastest,
required String slowest,
required String iso,
required String nd,
required double ev,
String? reason,
}) async {
expectPickerTitle<EquipmentProfilePicker>(equipmentProfile.name);
expectPickerTitle<FilmPicker>(film.name);
expectExtremeExposurePairs(fastest, slowest);
expectPickerTitle<IsoValuePicker>(iso);
expectPickerTitle<NdValuePicker>(nd);
expectExposurePairsListItem(tester, fastest.split(' - ')[0], fastest.split(' - ')[1]);
await tester.scrollToTheLastExposurePair(equipmentProfile: equipmentProfile);
expectExposurePairsListItem(tester, slowest.split(' - ')[0], slowest.split(' - ')[1]);
expectMeasureButton(ev);
}
Future<void> _expectMeteringStateAndMeasure(
WidgetTester tester, {
required EquipmentProfile equipmentProfile,
required Film film,
required String fastest,
required String slowest,
required String iso,
required String nd,
required double ev,
}) async {
await _expectMeteringState(
tester,
equipmentProfile: equipmentProfile,
film: film,
fastest: fastest,
slowest: slowest,
iso: iso,
nd: nd,
ev: ev,
);
await tester.takePhoto();
await _expectMeteringState(
tester,
equipmentProfile: equipmentProfile,
film: film,
fastest: fastest,
slowest: slowest,
iso: iso,
nd: nd,
ev: ev,
reason:
'Metering screen state must be the same before and after the measurement assuming that the scene is exactly the same.',
);
}
void expectMeasureButton(double ev) {
find.descendant(
of: find.byType(MeteringMeasureButton),
matching: find.text('${ev.toStringAsFixed(1)}\n${S.current.ev}'),
);
}

View file

@ -6,16 +6,21 @@ import 'package:lightmeter/data/models/ev_source_type.dart';
import 'package:lightmeter/data/models/metering_screen_layout_config.dart'; import 'package:lightmeter/data/models/metering_screen_layout_config.dart';
import 'package:lightmeter/data/shared_prefs_service.dart'; import 'package:lightmeter/data/shared_prefs_service.dart';
import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/components/exposure_pairs_list_item/widget_item_list_exposure_pairs.dart';
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/equipment_profile_picker/widget_picker_equipment_profiles.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/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart'; import 'package:lightmeter/screens/metering/components/shared/readings_container/components/film_picker/widget_picker_film.dart';
import 'package:lightmeter/screens/metering/screen_metering.dart';
import 'package:lightmeter/screens/settings/screen_settings.dart'; import 'package:lightmeter/screens/settings/screen_settings.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../integration_test/utils/widget_tester_actions.dart'; import '../integration_test/utils/widget_tester_actions.dart';
import 'mocks/paid_features_mock.dart'; import 'mocks/paid_features_mock.dart';
import 'utils/expectations.dart';
const _mockPhotoEv100 = 8.3;
@isTestGroup @isTestGroup
void testToggleLayoutFeatures(String description) { void testToggleLayoutFeatures(String description) {
@ -41,11 +46,11 @@ void testToggleLayoutFeatures(String description) {
(tester) async { (tester) async {
await tester.pumpApplication(selectedEquipmentProfileId: mockEquipmentProfiles.first.id); await tester.pumpApplication(selectedEquipmentProfileId: mockEquipmentProfiles.first.id);
await tester.takePhoto(); await tester.takePhoto();
expectPickerTitle<EquipmentProfilePicker>(mockEquipmentProfiles.first.name); _expectPickerTitle<EquipmentProfilePicker>(mockEquipmentProfiles.first.name);
expectExtremeExposurePairs('f/1.8 - 1/100', 'f/16 - 1/1.3'); _expectExtremeExposurePairs('f/1.8 - 1/100', 'f/16 - 1/1.3');
expectExposurePairsListItem(tester, 'f/1.8', '1/100'); _expectExposurePairsListItem(tester, 'f/1.8', '1/100');
await tester.scrollToTheLastExposurePair(equipmentProfile: mockEquipmentProfiles.first); await tester.scrollToTheLastExposurePair(mockEquipmentProfiles.first);
expectExposurePairsListItem(tester, 'f/16', '1/1.3'); _expectExposurePairsListItem(tester, 'f/16', '1/1.3');
// Disable layout feature // Disable layout feature
await tester.toggleLayoutFeature(S.current.meteringScreenLayoutHintEquipmentProfiles); await tester.toggleLayoutFeature(S.current.meteringScreenLayoutHintEquipmentProfiles);
@ -55,12 +60,12 @@ void testToggleLayoutFeatures(String description) {
reason: reason:
'Equipment profile picker must be hidden from the metering screen when the corresponding layout feature is disabled.', 'Equipment profile picker must be hidden from the metering screen when the corresponding layout feature is disabled.',
); );
expectExtremeExposurePairs( _expectExtremeExposurePairs(
'f/1.0 - 1/320', 'f/1.0 - 1/320',
'f/45 - 6"', 'f/45 - 6"',
reason: 'Aperture and shutter speed ranges must be reset to default values when equipment profile is reset', reason: 'Aperture and shutter speed ranges must be reset to default values when equipment profile is reset',
); );
expectExposurePairsListItem( _expectExposurePairsListItem(
tester, tester,
'f/1.0', 'f/1.0',
'1/320', '1/320',
@ -68,7 +73,7 @@ void testToggleLayoutFeatures(String description) {
'Aperture and shutter speed ranges must be reset to default values when equipment profile is reset.', 'Aperture and shutter speed ranges must be reset to default values when equipment profile is reset.',
); );
await tester.scrollToTheLastExposurePair(); await tester.scrollToTheLastExposurePair();
expectExposurePairsListItem( _expectExposurePairsListItem(
tester, tester,
'f/45', 'f/45',
'6"', '6"',
@ -78,7 +83,7 @@ void testToggleLayoutFeatures(String description) {
// Enable layout feature // Enable layout feature
await tester.toggleLayoutFeature(S.current.meteringScreenLayoutHintEquipmentProfiles); await tester.toggleLayoutFeature(S.current.meteringScreenLayoutHintEquipmentProfiles);
expectPickerTitle<EquipmentProfilePicker>( _expectPickerTitle<EquipmentProfilePicker>(
S.current.none, S.current.none,
reason: 'Equipment profile must remain unselected when the corresponding layout feature is re-enabled.', reason: 'Equipment profile must remain unselected when the corresponding layout feature is re-enabled.',
); );
@ -90,10 +95,10 @@ void testToggleLayoutFeatures(String description) {
(tester) async { (tester) async {
await tester.pumpApplication(); await tester.pumpApplication();
await tester.takePhoto(); await tester.takePhoto();
expectExtremeExposurePairs('f/1.0 - 1/320', 'f/45 - 6"'); _expectExtremeExposurePairs('f/1.0 - 1/320', 'f/45 - 6"');
expectExposurePairsListItem(tester, 'f/1.0', '1/320'); _expectExposurePairsListItem(tester, 'f/1.0', '1/320');
await tester.scrollToTheLastExposurePair(); await tester.scrollToTheLastExposurePair();
expectExposurePairsListItem(tester, 'f/45', '6"'); _expectExposurePairsListItem(tester, 'f/45', '6"');
// Disable layout feature // Disable layout feature
await tester.toggleLayoutFeature(S.current.meteringScreenFeatureExtremeExposurePairs); await tester.toggleLayoutFeature(S.current.meteringScreenFeatureExtremeExposurePairs);
@ -103,7 +108,7 @@ void testToggleLayoutFeatures(String description) {
reason: reason:
'Extreme exposure pairs container must be hidden from the metering screen when the corresponding layout feature is disabled.', 'Extreme exposure pairs container must be hidden from the metering screen when the corresponding layout feature is disabled.',
); );
expectExposurePairsListItem( _expectExposurePairsListItem(
tester, tester,
'f/1.0', 'f/1.0',
'1/320', '1/320',
@ -111,7 +116,7 @@ void testToggleLayoutFeatures(String description) {
'Exposure pairs list must not be affected by the visibility of the extreme exposure pairs container.', 'Exposure pairs list must not be affected by the visibility of the extreme exposure pairs container.',
); );
await tester.scrollToTheLastExposurePair(); await tester.scrollToTheLastExposurePair();
expectExposurePairsListItem( _expectExposurePairsListItem(
tester, tester,
'f/45', 'f/45',
'6"', '6"',
@ -121,7 +126,7 @@ void testToggleLayoutFeatures(String description) {
// Enable layout feature // Enable layout feature
await tester.toggleLayoutFeature(S.current.meteringScreenFeatureExtremeExposurePairs); await tester.toggleLayoutFeature(S.current.meteringScreenFeatureExtremeExposurePairs);
expectExtremeExposurePairs( _expectExtremeExposurePairs(
'f/1.0 - 1/320', 'f/1.0 - 1/320',
'f/45 - 6"', 'f/45 - 6"',
reason: reason:
@ -135,11 +140,11 @@ void testToggleLayoutFeatures(String description) {
(tester) async { (tester) async {
await tester.pumpApplication(selectedFilm: mockFilms.first); await tester.pumpApplication(selectedFilm: mockFilms.first);
await tester.takePhoto(); await tester.takePhoto();
expectPickerTitle<FilmPicker>(mockFilms.first.name); _expectPickerTitle<FilmPicker>(mockFilms.first.name);
expectExtremeExposurePairs('f/1.0 - 1/320', 'f/45 - 12"'); _expectExtremeExposurePairs('f/1.0 - 1/320', 'f/45 - 12"');
expectExposurePairsListItem(tester, 'f/1.0', '1/320'); _expectExposurePairsListItem(tester, 'f/1.0', '1/320');
await tester.scrollToTheLastExposurePair(); await tester.scrollToTheLastExposurePair();
expectExposurePairsListItem(tester, 'f/45', '12"'); _expectExposurePairsListItem(tester, 'f/45', '12"');
// Disable layout feature // Disable layout feature
await tester.toggleLayoutFeature(S.current.meteringScreenFeatureFilmPicker); await tester.toggleLayoutFeature(S.current.meteringScreenFeatureFilmPicker);
@ -149,19 +154,19 @@ void testToggleLayoutFeatures(String description) {
reason: reason:
'Film picker must be hidden from the metering screen when the corresponding layout feature is disabled.', 'Film picker must be hidden from the metering screen when the corresponding layout feature is disabled.',
); );
expectExtremeExposurePairs( _expectExtremeExposurePairs(
'f/1.0 - 1/320', 'f/1.0 - 1/320',
'f/45 - 6"', 'f/45 - 6"',
reason: 'Shutter speed must not be affected by reciprocity when film is discarded.', reason: 'Shutter speed must not be affected by reciprocity when film is discarded.',
); );
expectExposurePairsListItem( _expectExposurePairsListItem(
tester, tester,
'f/1.0', 'f/1.0',
'1/320', '1/320',
reason: 'Shutter speed must not be affected by reciprocity when film is discarded.', reason: 'Shutter speed must not be affected by reciprocity when film is discarded.',
); );
await tester.scrollToTheLastExposurePair(); await tester.scrollToTheLastExposurePair();
expectExposurePairsListItem( _expectExposurePairsListItem(
tester, tester,
'f/45', 'f/45',
'6"', '6"',
@ -170,7 +175,7 @@ void testToggleLayoutFeatures(String description) {
// Enable layout feature // Enable layout feature
await tester.toggleLayoutFeature(S.current.meteringScreenFeatureFilmPicker); await tester.toggleLayoutFeature(S.current.meteringScreenFeatureFilmPicker);
expectPickerTitle<FilmPicker>( _expectPickerTitle<FilmPicker>(
S.current.none, S.current.none,
reason: 'Film must remain unselected when the corresponding layout feature is re-enabled.', reason: 'Film must remain unselected when the corresponding layout feature is re-enabled.',
); );
@ -188,4 +193,46 @@ extension on WidgetTester {
await tapSaveButton(); await tapSaveButton();
await navigatorPop(); await navigatorPop();
} }
Future<void> scrollToTheLastExposurePair([EquipmentProfile equipmentProfile = defaultEquipmentProfile]) async {
final exposurePairs = MeteringContainerBuidler.buildExposureValues(
_mockPhotoEv100,
StopType.third,
equipmentProfile,
);
await 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)),
);
}
}
void _expectPickerTitle<T>(String title, {String? reason}) {
expect(find.descendant(of: find.byType(T), matching: find.text(title)), findsOneWidget, reason: reason);
}
void _expectExtremeExposurePairs(String fastest, String slowest, {String? reason}) {
final pickerFinder = find.byType(ExtremeExposurePairsContainer);
expect(find.descendant(of: pickerFinder, matching: find.text(fastest)), findsOneWidget, reason: reason);
expect(find.descendant(of: pickerFinder, matching: find.text(slowest)), findsOneWidget, reason: reason);
}
void _expectExposurePairsListItem(WidgetTester tester, String aperture, String shutterSpeed, {String? reason}) {
Key? findKey<T extends PhotographyStopValue<num>>(String value) => tester
.widget<Row>(
find.ancestor(
of: find.ancestor(
of: find.text(value),
matching: find.byType(ExposurePairsListItem<T>),
),
matching: find.descendant(of: find.byType(ExposurePairsList), matching: find.byType(Row)),
),
)
.key;
expect(
findKey<ApertureValue>(aperture),
findKey<ShutterSpeedValue>(shutterSpeed),
reason: reason,
);
} }

View file

@ -8,9 +8,9 @@ import 'package:mocktail/mocktail.dart';
class _MockIAPStorageService extends Mock implements IAPStorageService {} class _MockIAPStorageService extends Mock implements IAPStorageService {}
class MockIAPProviders extends StatefulWidget { class MockIAPProviders extends StatefulWidget {
final List<EquipmentProfile>? equipmentProfiles; final List<EquipmentProfile> equipmentProfiles;
final String selectedEquipmentProfileId; final String selectedEquipmentProfileId;
final List<Film>? films; final List<Film> films;
final Film selectedFilm; final Film selectedFilm;
final Widget child; final Widget child;
@ -34,9 +34,9 @@ class _MockIAPProvidersState extends State<MockIAPProviders> {
void initState() { void initState() {
super.initState(); super.initState();
mockIAPStorageService = _MockIAPStorageService(); mockIAPStorageService = _MockIAPStorageService();
when(() => mockIAPStorageService.equipmentProfiles).thenReturn(widget.equipmentProfiles ?? mockEquipmentProfiles); when(() => mockIAPStorageService.equipmentProfiles).thenReturn(mockEquipmentProfiles);
when(() => mockIAPStorageService.selectedEquipmentProfileId).thenReturn(widget.selectedEquipmentProfileId); when(() => mockIAPStorageService.selectedEquipmentProfileId).thenReturn(widget.selectedEquipmentProfileId);
when(() => mockIAPStorageService.filmsInUse).thenReturn(widget.films ?? mockFilms); when(() => mockIAPStorageService.filmsInUse).thenReturn(mockFilms);
when(() => mockIAPStorageService.selectedFilm).thenReturn(widget.selectedFilm); when(() => mockIAPStorageService.selectedFilm).thenReturn(widget.selectedFilm);
} }
@ -92,34 +92,13 @@ final mockEquipmentProfiles = [
IsoValue(3200, StopType.full), IsoValue(3200, StopType.full),
], ],
), ),
EquipmentProfile( const EquipmentProfile(
id: '2', id: '2',
name: 'Praktica + Jupiter', name: 'Praktica + Jupiter',
apertureValues: ApertureValue.values.sublist( apertureValues: ApertureValue.values,
ApertureValue.values.indexOf(const ApertureValue(3.5, StopType.third)), ndValues: NdValue.values,
ApertureValue.values.indexOf(const ApertureValue(22, StopType.full)) + 1, shutterSpeedValues: ShutterSpeedValue.values,
), isoValues: IsoValue.values,
ndValues: const [
NdValue(0),
NdValue(2),
NdValue(4),
NdValue(8),
],
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),
],
), ),
]; ];

View file

@ -1,35 +0,0 @@
import 'package:flutter/widgets.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/components/exposure_pairs_list_item/widget_item_list_exposure_pairs.dart';
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
import 'package:lightmeter/screens/metering/components/shared/readings_container/components/extreme_exposure_pairs_container/widget_container_extreme_exposure_pairs.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
void expectPickerTitle<P extends Widget>(String title, {String? reason}) {
expect(find.descendant(of: find.byType(P), matching: find.text(title)), findsOneWidget, reason: reason);
}
void expectExtremeExposurePairs(String fastest, String slowest, {String? reason}) {
final pickerFinder = find.byType(ExtremeExposurePairsContainer);
expect(find.descendant(of: pickerFinder, matching: find.text(fastest)), findsOneWidget, reason: reason);
expect(find.descendant(of: pickerFinder, matching: find.text(slowest)), findsOneWidget, reason: reason);
}
void expectExposurePairsListItem(WidgetTester tester, String aperture, String shutterSpeed, {String? reason}) {
Key? findKey<T extends PhotographyStopValue<num>>(String value) => tester
.widget<Row>(
find.ancestor(
of: find.ancestor(
of: find.text(value),
matching: find.byType(ExposurePairsListItem<T>),
),
matching: find.descendant(of: find.byType(ExposurePairsList), matching: find.byType(Row)),
),
)
.key;
expect(
findKey<ApertureValue>(aperture),
findKey<ShutterSpeedValue>(shutterSpeed),
reason: reason,
);
}

View file

@ -6,8 +6,6 @@ import 'package:lightmeter/environment.dart';
import 'package:lightmeter/generated/l10n.dart'; import 'package:lightmeter/generated/l10n.dart';
import 'package:lightmeter/res/dimens.dart'; import 'package:lightmeter/res/dimens.dart';
import 'package:lightmeter/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart'; import 'package:lightmeter/screens/metering/components/bottom_controls/components/measure_button/widget_button_measure.dart';
import 'package:lightmeter/screens/metering/components/shared/exposure_pairs_list/widget_list_exposure_pairs.dart';
import 'package:lightmeter/screens/metering/screen_metering.dart';
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart'; import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart'; import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
@ -15,14 +13,10 @@ import '../mocks/iap_products_mock.dart';
import '../mocks/paid_features_mock.dart'; import '../mocks/paid_features_mock.dart';
import 'platform_channel_mock.dart'; import 'platform_channel_mock.dart';
const mockPhotoEv100 = 8.3;
extension WidgetTesterCommonActions on WidgetTester { extension WidgetTesterCommonActions on WidgetTester {
Future<void> pumpApplication({ Future<void> pumpApplication({
IAPProductStatus productStatus = IAPProductStatus.purchased, IAPProductStatus productStatus = IAPProductStatus.purchased,
List<EquipmentProfile>? equipmentProfiles,
String selectedEquipmentProfileId = '', String selectedEquipmentProfileId = '',
List<Film>? films,
Film selectedFilm = const Film.other(), Film selectedFilm = const Film.other(),
}) async { }) async {
await pumpWidget( await pumpWidget(
@ -31,9 +25,7 @@ extension WidgetTesterCommonActions on WidgetTester {
child: ApplicationWrapper( child: ApplicationWrapper(
const Environment.dev(), const Environment.dev(),
child: MockIAPProviders( child: MockIAPProviders(
equipmentProfiles: equipmentProfiles,
selectedEquipmentProfileId: selectedEquipmentProfileId, selectedEquipmentProfileId: selectedEquipmentProfileId,
films: films,
selectedFilm: selectedFilm, selectedFilm: selectedFilm,
child: const Application(), child: const Application(),
), ),
@ -97,22 +89,3 @@ extension WidgetTesterTextButtonActions on WidgetTester {
await pumpAndSettle(); await pumpAndSettle();
} }
} }
extension WidgetTesterExposurePairsListActions on WidgetTester {
Future<void> scrollToTheLastExposurePair({
double ev = mockPhotoEv100,
StopType stopType = StopType.third,
EquipmentProfile equipmentProfile = defaultEquipmentProfile,
}) async {
final exposurePairs = MeteringContainerBuidler.buildExposureValues(
ev,
StopType.third,
equipmentProfile,
);
await 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)),
);
}
}