fixed tests

This commit is contained in:
Vadim 2024-11-06 15:28:16 +01:00
parent 4d02e71736
commit fce8d0bb6a
16 changed files with 265 additions and 320 deletions

View file

@ -48,7 +48,7 @@ void testE2E(String description) {
description, description,
(tester) async { (tester) async {
await tester.pumpApplication( await tester.pumpApplication(
equipmentProfiles: [], equipmentProfiles: {},
predefinedFilms: mockFilms.toFilmsMap(isUsed: true), predefinedFilms: mockFilms.toFilmsMap(isUsed: true),
customFilms: {}, customFilms: {},
); );
@ -58,7 +58,7 @@ void testE2E(String description) {
await tester.tapDescendantTextOf<SettingsScreen>(S.current.equipmentProfiles); await tester.tapDescendantTextOf<SettingsScreen>(S.current.equipmentProfiles);
await tester.tap(find.byIcon(Icons.add_outlined).first); await tester.tap(find.byIcon(Icons.add_outlined).first);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.setProfileName(mockEquipmentProfiles[0].name); await tester.selectProfileName(mockEquipmentProfiles[0].name);
await tester.expandEquipmentProfileContainer(mockEquipmentProfiles[0].name); await tester.expandEquipmentProfileContainer(mockEquipmentProfiles[0].name);
await tester.setIsoValues(0, mockEquipmentProfiles[0].isoValues); await tester.setIsoValues(0, mockEquipmentProfiles[0].isoValues);
await tester.setNdValues(0, mockEquipmentProfiles[0].ndValues); await tester.setNdValues(0, mockEquipmentProfiles[0].ndValues);
@ -72,7 +72,7 @@ void testE2E(String description) {
/// Create Praktica + Jupiter profile from Zenitar profile /// Create Praktica + Jupiter profile from Zenitar profile
await tester.tap(find.byIcon(Icons.copy_outlined).first); await tester.tap(find.byIcon(Icons.copy_outlined).first);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
await tester.setProfileName(mockEquipmentProfiles[1].name); await tester.selectProfileName(mockEquipmentProfiles[1].name);
await tester.expandEquipmentProfileContainer(mockEquipmentProfiles[1].name); await tester.expandEquipmentProfileContainer(mockEquipmentProfiles[1].name);
await tester.setApertureValues(1, mockEquipmentProfiles[1].apertureValues); await tester.setApertureValues(1, mockEquipmentProfiles[1].apertureValues);
await tester.setZoomValue(1, mockEquipmentProfiles[1].lensZoom); await tester.setZoomValue(1, mockEquipmentProfiles[1].lensZoom);
@ -152,7 +152,7 @@ extension EquipmentProfileActions on WidgetTester {
await pump(Dimens.durationM); await pump(Dimens.durationM);
} }
Future<void> setProfileName(String name) async { Future<void> selectProfileName(String name) async {
await enterText(find.byType(TextField), name); await enterText(find.byType(TextField), name);
await pump(); await pump();
await tapSaveButton(); await tapSaveButton();

View file

@ -5,12 +5,12 @@ 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';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
class _MockIAPStorageService extends Mock implements IAPStorageService {} class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {}
class _MockFilmsStorageService extends Mock implements FilmsStorageService {} class _MockFilmsStorageService extends Mock implements FilmsStorageService {}
class MockIAPProviders extends StatefulWidget { class MockIAPProviders extends StatefulWidget {
final List<EquipmentProfile>? equipmentProfiles; final Map<String, EquipmentProfile> equipmentProfiles;
final String selectedEquipmentProfileId; final String selectedEquipmentProfileId;
final Map<String, SelectableFilm<Film>> predefinedFilms; final Map<String, SelectableFilm<Film>> predefinedFilms;
final Map<String, SelectableFilm<FilmExponential>> customFilms; final Map<String, SelectableFilm<FilmExponential>> customFilms;
@ -18,14 +18,15 @@ class MockIAPProviders extends StatefulWidget {
final Widget child; final Widget child;
MockIAPProviders({ MockIAPProviders({
this.equipmentProfiles = const [], Map<String, EquipmentProfile>? equipmentProfiles,
this.selectedEquipmentProfileId = '', this.selectedEquipmentProfileId = '',
Map<String, SelectableFilm<Film>>? predefinedFilms, Map<String, SelectableFilm<Film>>? predefinedFilms,
Map<String, SelectableFilm<FilmExponential>>? customFilms, Map<String, SelectableFilm<FilmExponential>>? customFilms,
String? selectedFilmId, String? selectedFilmId,
required this.child, required this.child,
super.key, super.key,
}) : predefinedFilms = predefinedFilms ?? mockFilms.toFilmsMap(), }) : equipmentProfiles = equipmentProfiles ?? mockEquipmentProfiles.toProfilesMap(),
predefinedFilms = predefinedFilms ?? mockFilms.toFilmsMap(),
customFilms = customFilms ?? mockFilms.toFilmsMap(), customFilms = customFilms ?? mockFilms.toFilmsMap(),
selectedFilmId = selectedFilmId ?? const FilmStub().id; selectedFilmId = selectedFilmId ?? const FilmStub().id;
@ -34,15 +35,18 @@ class MockIAPProviders extends StatefulWidget {
} }
class _MockIAPProvidersState extends State<MockIAPProviders> { class _MockIAPProvidersState extends State<MockIAPProviders> {
late final _MockIAPStorageService mockIAPStorageService; late final _MockEquipmentProfilesStorageService mockEquipmentProfilesStorageService;
late final _MockFilmsStorageService mockFilmsStorageService; late final _MockFilmsStorageService mockFilmsStorageService;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
mockIAPStorageService = _MockIAPStorageService(); mockEquipmentProfilesStorageService = _MockEquipmentProfilesStorageService();
when(() => mockIAPStorageService.equipmentProfiles).thenReturn(widget.equipmentProfiles ?? mockEquipmentProfiles); when(() => mockEquipmentProfilesStorageService.init()).thenAnswer((_) async {});
when(() => mockIAPStorageService.selectedEquipmentProfileId).thenReturn(widget.selectedEquipmentProfileId); when(() => mockEquipmentProfilesStorageService.getProfiles())
.thenAnswer((_) => Future.value(widget.equipmentProfiles));
when(() => mockEquipmentProfilesStorageService.selectedEquipmentProfileId)
.thenReturn(widget.selectedEquipmentProfileId);
mockFilmsStorageService = _MockFilmsStorageService(); mockFilmsStorageService = _MockFilmsStorageService();
when(() => mockFilmsStorageService.init()).thenAnswer((_) async {}); when(() => mockFilmsStorageService.init()).thenAnswer((_) async {});
@ -53,10 +57,10 @@ class _MockIAPProvidersState extends State<MockIAPProviders> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return EquipmentProfileProvider( return EquipmentProfilesProvider(
storageService: mockIAPStorageService, storageService: mockEquipmentProfilesStorageService,
child: FilmsProvider( child: FilmsProvider(
filmsStorageService: mockFilmsStorageService, storageService: mockFilmsStorageService,
child: widget.child, child: widget.child,
), ),
); );
@ -142,6 +146,10 @@ const mockFilms = [
_FilmMultiplying(id: '4', name: 'Mock film 4', iso: 1200, reciprocityMultiplier: 1.5), _FilmMultiplying(id: '4', name: 'Mock film 4', iso: 1200, reciprocityMultiplier: 1.5),
]; ];
extension EquipmentProfileMapper on List<EquipmentProfile> {
Map<String, EquipmentProfile> toProfilesMap() => Map.fromEntries(map((e) => MapEntry(e.id, e)));
}
extension FilmMapper on List<Film> { extension FilmMapper on List<Film> {
Map<String, ({T film, bool isUsed})> toFilmsMap<T extends Film>({bool isUsed = true}) => Map<String, ({T film, bool isUsed})> toFilmsMap<T extends Film>({bool isUsed = true}) =>
Map.fromEntries(map((e) => MapEntry(e.id, (film: e as T, isUsed: isUsed)))); Map.fromEntries(map((e) => MapEntry(e.id, (film: e as T, isUsed: isUsed))));

View file

@ -20,7 +20,7 @@ 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, Map<String, EquipmentProfile>? equipmentProfiles,
String selectedEquipmentProfileId = '', String selectedEquipmentProfileId = '',
Map<String, SelectableFilm<Film>>? predefinedFilms, Map<String, SelectableFilm<Film>>? predefinedFilms,
Map<String, SelectableFilm<FilmExponential>>? customFilms, Map<String, SelectableFilm<FilmExponential>>? customFilms,

View file

@ -72,7 +72,7 @@ class _ApplicationWrapperState extends State<ApplicationWrapper> {
volumeEventsService: const VolumeEventsService(LocalPlatform()), volumeEventsService: const VolumeEventsService(LocalPlatform()),
child: RemoteConfigProvider( child: RemoteConfigProvider(
remoteConfigService: remoteConfigService, remoteConfigService: remoteConfigService,
child: EquipmentProfileProvider( child: EquipmentProfilesProvider(
storageService: equipmentProfilesStorageService, storageService: equipmentProfilesStorageService,
onInitialized: equipmentProfilesStorageServiceCompleter.complete, onInitialized: equipmentProfilesStorageServiceCompleter.complete,
child: FilmsProvider( child: FilmsProvider(

View file

@ -1,10 +1,10 @@
enum NavigationRoutes { enum NavigationRoutes {
meteringScreen, meteringScreen,
settingsScreen, settingsScreen,
filmsListScreen,
filmEditScreen,
equipmentProfilesListScreen, equipmentProfilesListScreen,
equipmentProfileEditScreen, equipmentProfileEditScreen,
filmsListScreen,
filmEditScreen,
proFeaturesScreen, proFeaturesScreen,
timerScreen, timerScreen,
} }

View file

@ -1,31 +1,11 @@
import 'package:collection/collection.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:lightmeter/utils/context_utils.dart'; import 'package:lightmeter/utils/context_utils.dart';
import 'package:lightmeter/utils/selectable_provider.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';
class EquipmentProfileProvider extends StatefulWidget { class EquipmentProfilesProvider extends StatefulWidget {
final EquipmentProfilesStorageService storageService; static const EquipmentProfile defaultProfile = EquipmentProfile(
final VoidCallback? onInitialized;
final Widget child;
const EquipmentProfileProvider({
required this.storageService,
this.onInitialized,
required this.child,
super.key,
});
static EquipmentProfileProviderState of(BuildContext context) {
return context.findAncestorStateOfType<EquipmentProfileProviderState>()!;
}
@override
State<EquipmentProfileProvider> createState() => EquipmentProfileProviderState();
}
class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
static const EquipmentProfile _defaultProfile = EquipmentProfile(
id: '', id: '',
name: '', name: '',
apertureValues: ApertureValue.values, apertureValues: ApertureValue.values,
@ -34,10 +14,30 @@ class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
isoValues: IsoValue.values, isoValues: IsoValue.values,
); );
final EquipmentProfilesStorageService storageService;
final VoidCallback? onInitialized;
final Widget child;
const EquipmentProfilesProvider({
required this.storageService,
this.onInitialized,
required this.child,
super.key,
});
static EquipmentProfilesProviderState of(BuildContext context) {
return context.findAncestorStateOfType<EquipmentProfilesProviderState>()!;
}
@override
State<EquipmentProfilesProvider> createState() => EquipmentProfilesProviderState();
}
class EquipmentProfilesProviderState extends State<EquipmentProfilesProvider> {
final Map<String, EquipmentProfile> _customProfiles = {}; final Map<String, EquipmentProfile> _customProfiles = {};
String _selectedId = ''; String _selectedId = '';
EquipmentProfile get _selectedProfile => _customProfiles[_selectedId] ?? _defaultProfile; EquipmentProfile get _selectedProfile => _customProfiles[_selectedId] ?? EquipmentProfilesProvider.defaultProfile;
@override @override
void initState() { void initState() {
@ -48,11 +48,8 @@ class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return EquipmentProfiles( return EquipmentProfiles(
values: [ profiles: context.isPro ? _customProfiles : {},
_defaultProfile, selected: context.isPro ? _selectedProfile : EquipmentProfilesProvider.defaultProfile,
if (context.isPro) ..._customProfiles.values,
],
selected: context.isPro ? _selectedProfile : _defaultProfile,
child: widget.child, child: widget.child,
); );
} }
@ -64,15 +61,6 @@ class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
widget.onInitialized?.call(); widget.onInitialized?.call();
} }
void setProfile(EquipmentProfile data) {
if (_selectedId != data.id) {
setState(() {
_selectedId = data.id;
});
widget.storageService.selectedEquipmentProfileId = _selectedProfile.id;
}
}
Future<void> addProfile(EquipmentProfile profile) async { Future<void> addProfile(EquipmentProfile profile) async {
await widget.storageService.addProfile(profile); await widget.storageService.addProfile(profile);
_customProfiles[profile.id] = profile; _customProfiles[profile.id] = profile;
@ -98,32 +86,58 @@ class EquipmentProfileProviderState extends State<EquipmentProfileProvider> {
Future<void> deleteProfile(EquipmentProfile profile) async { Future<void> deleteProfile(EquipmentProfile profile) async {
await widget.storageService.deleteProfile(profile.id); await widget.storageService.deleteProfile(profile.id);
if (profile.id == _selectedId) { if (profile.id == _selectedId) {
_selectedId = _defaultProfile.id; _selectedId = EquipmentProfilesProvider.defaultProfile.id;
widget.storageService.selectedEquipmentProfileId = _defaultProfile.id; widget.storageService.selectedEquipmentProfileId = EquipmentProfilesProvider.defaultProfile.id;
} }
_customProfiles.remove(profile.id); _customProfiles.remove(profile.id);
setState(() {}); setState(() {});
} }
void selectProfile(EquipmentProfile data) {
if (_selectedId != data.id) {
setState(() {
_selectedId = data.id;
});
widget.storageService.selectedEquipmentProfileId = _selectedProfile.id;
}
}
} }
class EquipmentProfiles extends SelectableInheritedModel<EquipmentProfile> { enum _EquipmentProfilesModelAspect {
profiles,
selected,
}
class EquipmentProfiles extends InheritedModel<_EquipmentProfilesModelAspect> {
final Map<String, EquipmentProfile> profiles;
final EquipmentProfile selected;
const EquipmentProfiles({ const EquipmentProfiles({
super.key, required this.profiles,
required super.values, required this.selected,
required super.selected,
required super.child, required super.child,
}); });
/// [_defaultProfile] + profiles created by the user /// _default + profiles create by the user
static List<EquipmentProfile> of(BuildContext context) { static List<EquipmentProfile> of(BuildContext context) {
return InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: SelectableAspect.list)!.values; final model =
InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: _EquipmentProfilesModelAspect.profiles)!;
return List.from([EquipmentProfilesProvider.defaultProfile, ...model.profiles.values]);
} }
static EquipmentProfile selectedOf(BuildContext context) { static EquipmentProfile selectedOf(BuildContext context) {
return InheritedModel.inheritFrom<EquipmentProfiles>( return InheritedModel.inheritFrom<EquipmentProfiles>(context, aspect: _EquipmentProfilesModelAspect.selected)!
context,
aspect: SelectableAspect.selected,
)!
.selected; .selected;
} }
@override
bool updateShouldNotify(EquipmentProfiles _) => true;
@override
bool updateShouldNotifyDependent(EquipmentProfiles oldWidget, Set<_EquipmentProfilesModelAspect> dependencies) {
return (dependencies.contains(_EquipmentProfilesModelAspect.selected) && oldWidget.selected != selected) ||
((dependencies.contains(_EquipmentProfilesModelAspect.profiles) ||
dependencies.contains(_EquipmentProfilesModelAspect.profiles)) &&
const DeepCollectionEquality().equals(oldWidget.profiles, profiles));
}
} }

View file

@ -6,22 +6,13 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:uuid/uuid.dart'; import 'package:uuid/uuid.dart';
class EquipmentProfileEditBloc extends Bloc<EquipmentProfileEditEvent, EquipmentProfileEditState> { class EquipmentProfileEditBloc extends Bloc<EquipmentProfileEditEvent, EquipmentProfileEditState> {
static const EquipmentProfile _defaultProfile = EquipmentProfile( final EquipmentProfilesProviderState profilesProvider;
id: '',
name: '',
apertureValues: ApertureValue.values,
ndValues: NdValue.values,
shutterSpeedValues: ShutterSpeedValue.values,
isoValues: IsoValue.values,
);
final EquipmentProfileProviderState profilesProvider;
final EquipmentProfile _originalEquipmentProfile; final EquipmentProfile _originalEquipmentProfile;
EquipmentProfile _newEquipmentProfile; EquipmentProfile _newEquipmentProfile;
final bool _isEdit; final bool _isEdit;
factory EquipmentProfileEditBloc( factory EquipmentProfileEditBloc(
EquipmentProfileProviderState profilesProvider, { EquipmentProfilesProviderState profilesProvider, {
required EquipmentProfile? profile, required EquipmentProfile? profile,
required bool isEdit, required bool isEdit,
}) => }) =>
@ -33,7 +24,7 @@ class EquipmentProfileEditBloc extends Bloc<EquipmentProfileEditEvent, Equipment
) )
: EquipmentProfileEditBloc._( : EquipmentProfileEditBloc._(
profilesProvider, profilesProvider,
_defaultProfile, EquipmentProfilesProvider.defaultProfile,
isEdit, isEdit,
); );

View file

@ -20,7 +20,7 @@ class EquipmentProfileEditFlow extends StatelessWidget {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlocProvider( return BlocProvider(
create: (_) => EquipmentProfileEditBloc( create: (_) => EquipmentProfileEditBloc(
EquipmentProfileProvider.of(context), EquipmentProfilesProvider.of(context),
profile: args.profile, profile: args.profile,
isEdit: args.profile != null, isEdit: args.profile != null,
), ),

View file

@ -16,7 +16,7 @@ class EquipmentProfilePicker extends StatelessWidget {
selectedValue: EquipmentProfiles.selectedOf(context), selectedValue: EquipmentProfiles.selectedOf(context),
values: EquipmentProfiles.of(context), values: EquipmentProfiles.of(context),
itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name), itemTitleBuilder: (_, value) => Text(value.id.isEmpty ? S.of(context).none : value.name),
onChanged: EquipmentProfileProvider.of(context).setProfile, onChanged: EquipmentProfilesProvider.of(context).selectProfile,
closedChild: ReadingValueContainer.singleValue( closedChild: ReadingValueContainer.singleValue(
value: ReadingValue( value: ReadingValue(
label: S.of(context).equipmentProfile, label: S.of(context).equipmentProfile,

View file

@ -36,7 +36,7 @@ class MeteringScreenLayoutListTile extends StatelessWidget {
}, },
onSave: (value) { onSave: (value) {
if (!value[MeteringScreenLayoutFeature.equipmentProfiles]!) { if (!value[MeteringScreenLayoutFeature.equipmentProfiles]!) {
EquipmentProfileProvider.of(context).setProfile(EquipmentProfiles.of(context).first); EquipmentProfilesProvider.of(context).selectProfile(EquipmentProfiles.of(context).first);
} }
if (!value[MeteringScreenLayoutFeature.filmPicker]!) { if (!value[MeteringScreenLayoutFeature.filmPicker]!) {
FilmsProvider.of(context).selectFilm(const FilmStub()); FilmsProvider.of(context).selectFilm(const FilmStub());

View file

@ -108,7 +108,6 @@ class _GoldenTestApplicationMockState extends State<GoldenTestApplicationMock> {
], ],
child: _MockApplicationWrapper( child: _MockApplicationWrapper(
child: MockIAPProviders( child: MockIAPProviders(
equipmentProfiles: mockEquipmentProfiles,
selectedEquipmentProfileId: mockEquipmentProfiles.first.id, selectedEquipmentProfileId: mockEquipmentProfiles.first.id,
selectedFilmId: mockFilms.first.id, selectedFilmId: mockFilms.first.id,
child: Builder( child: Builder(

View file

@ -5,14 +5,29 @@ 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';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
class _MockIAPStorageService extends Mock implements IAPStorageService {} import '../../integration_test/mocks/paid_features_mock.dart';
class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
late _MockIAPStorageService storageService; late _MockEquipmentProfilesStorageService storageService;
setUpAll(() { setUpAll(() {
storageService = _MockIAPStorageService(); storageService = _MockEquipmentProfilesStorageService();
});
setUp(() {
registerFallbackValue(_customProfiles.first);
when(() => storageService.addProfile(any<EquipmentProfile>())).thenAnswer((_) async {});
when(
() => storageService.updateProfile(
id: any<String>(named: 'id'),
name: any<String>(named: 'name'),
),
).thenAnswer((_) async {});
when(() => storageService.deleteProfile(any<String>())).thenAnswer((_) async {});
when(() => storageService.getProfiles()).thenAnswer((_) => Future.value(_customProfiles.toProfilesMap()));
}); });
tearDown(() { tearDown(() {
@ -29,35 +44,36 @@ void main() {
price: '0.0\$', price: '0.0\$',
), ),
], ],
child: EquipmentProfileProvider( child: EquipmentProfilesProvider(
storageService: storageService, storageService: storageService,
child: const _Application(), child: const _Application(),
), ),
), ),
); );
await tester.pumpAndSettle();
} }
void expectEquipmentProfilesCount(int count) { void expectEquipmentProfilesCount(int count) {
expect(find.text('Equipment profiles count: $count'), findsOneWidget); expect(find.text(_EquipmentProfilesCount.text(count)), findsOneWidget);
} }
void expectSelectedEquipmentProfileName(String name) { void expectSelectedEquipmentProfileName(String name) {
expect(find.text('Selected equipment profile: $name'), findsOneWidget); expect(find.text(_SelectedEquipmentProfile.text(name)), findsOneWidget);
} }
group( group(
'EquipmentProfileProvider dependency on IAPProductStatus', 'EquipmentProfilesProvider dependency on IAPProductStatus',
() { () {
setUp(() { setUp(() {
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles.first.id); when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles.first.id);
when(() => storageService.equipmentProfiles).thenReturn(_customProfiles); when(() => storageService.getProfiles()).thenAnswer((_) => Future.value(_customProfiles.toProfilesMap()));
}); });
testWidgets( testWidgets(
'IAPProductStatus.purchased - show all saved profiles', 'IAPProductStatus.purchased - show all saved profiles',
(tester) async { (tester) async {
await pumpTestWidget(tester, IAPProductStatus.purchased); await pumpTestWidget(tester, IAPProductStatus.purchased);
expectEquipmentProfilesCount(3); expectEquipmentProfilesCount(_customProfiles.length + 1);
expectSelectedEquipmentProfileName(_customProfiles.first.name); expectSelectedEquipmentProfileName(_customProfiles.first.name);
}, },
); );
@ -82,203 +98,105 @@ void main() {
}, },
); );
group('EquipmentProfileProvider CRUD', () { testWidgets(
testWidgets( 'EquipmentProfilesProvider CRUD',
'Add', (tester) async {
(tester) async { when(() => storageService.getProfiles()).thenAnswer((_) async => {});
when(() => storageService.equipmentProfiles).thenReturn([]); when(() => storageService.selectedEquipmentProfileId).thenReturn('');
when(() => storageService.selectedEquipmentProfileId).thenReturn('');
await pumpTestWidget(tester, IAPProductStatus.purchased); await pumpTestWidget(tester, IAPProductStatus.purchased);
expectEquipmentProfilesCount(1); expectEquipmentProfilesCount(1);
expectSelectedEquipmentProfileName(''); expectSelectedEquipmentProfileName('');
await tester.tap(find.byKey(_Application.addProfileButtonKey)); /// Add first profile and verify
await tester.pump(); await tester.equipmentProfilesProvider.addProfile(_customProfiles.first);
expectEquipmentProfilesCount(2); await tester.pump();
expectSelectedEquipmentProfileName(''); expectEquipmentProfilesCount(2);
expectSelectedEquipmentProfileName('');
verify(() => storageService.addProfile(any<EquipmentProfile>())).called(1);
verifyNever(() => storageService.selectedEquipmentProfileId = ''); /// Add the other profiles and select the 1st one
verify(() => storageService.equipmentProfiles = any<List<EquipmentProfile>>()).called(1); for (final profile in _customProfiles.skip(1)) {
}, await tester.equipmentProfilesProvider.addProfile(profile);
); }
tester.equipmentProfilesProvider.selectProfile(_customProfiles.first);
await tester.pump();
expectEquipmentProfilesCount(1 + _customProfiles.length);
expectSelectedEquipmentProfileName(_customProfiles.first.name);
testWidgets( /// Edit the selected profile
'Add from', final updatedName = "${_customProfiles.first} updated";
(tester) async { await tester.equipmentProfilesProvider.updateProfile(_customProfiles.first.copyWith(name: updatedName));
when(() => storageService.equipmentProfiles).thenReturn(List.from(_customProfiles)); await tester.pump();
when(() => storageService.selectedEquipmentProfileId).thenReturn(''); expectEquipmentProfilesCount(1 + _customProfiles.length);
expectSelectedEquipmentProfileName(updatedName);
verify(() => storageService.updateProfile(id: _customProfiles.first.id, name: updatedName)).called(1);
await pumpTestWidget(tester, IAPProductStatus.purchased); /// Delete a non-selected profile
expectEquipmentProfilesCount(3); await tester.equipmentProfilesProvider.deleteProfile(_customProfiles.last);
expectSelectedEquipmentProfileName(''); await tester.pump();
expectEquipmentProfilesCount(1 + _customProfiles.length - 1);
expectSelectedEquipmentProfileName(updatedName);
verifyNever(() => storageService.selectedEquipmentProfileId = '');
verify(() => storageService.deleteProfile(_customProfiles.last.id)).called(1);
await tester.tap(find.byKey(_Application.addFromProfileButtonKey(_customProfiles[0].id))); /// Delete the selected profile
await tester.pump(); await tester.equipmentProfilesProvider.deleteProfile(_customProfiles.first);
expectEquipmentProfilesCount(4); await tester.pump();
expectSelectedEquipmentProfileName(''); expectEquipmentProfilesCount(1 + _customProfiles.length - 2);
expectSelectedEquipmentProfileName('');
verify(() => storageService.selectedEquipmentProfileId = '').called(1);
verify(() => storageService.deleteProfile(_customProfiles.first.id)).called(1);
},
);
}
verifyNever(() => storageService.selectedEquipmentProfileId = ''); extension on WidgetTester {
verify(() => storageService.equipmentProfiles = any<List<EquipmentProfile>>()).called(1); EquipmentProfilesProviderState get equipmentProfilesProvider {
}, final BuildContext context = element(find.byType(_Application));
); return EquipmentProfilesProvider.of(context);
}
testWidgets(
'Edit selected',
(tester) async {
when(() => storageService.equipmentProfiles).thenReturn(List.from(_customProfiles));
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles[0].id);
await pumpTestWidget(tester, IAPProductStatus.purchased);
/// Change the name & limit ISO values of the both added profiles
await tester.tap(find.byKey(_Application.updateProfileButtonKey(_customProfiles[0].id)));
await tester.pumpAndSettle();
expectEquipmentProfilesCount(3);
expectSelectedEquipmentProfileName("${_customProfiles[0].name} updated");
verifyNever(() => storageService.selectedEquipmentProfileId = _customProfiles[0].id);
verify(() => storageService.equipmentProfiles = any<List<EquipmentProfile>>()).called(1);
},
);
testWidgets(
'Delete selected',
(tester) async {
when(() => storageService.equipmentProfiles).thenReturn(List.from(_customProfiles));
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles[0].id);
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectEquipmentProfilesCount(3);
expectSelectedEquipmentProfileName(_customProfiles[0].name);
/// Delete the selected profile
await tester.tap(find.byKey(_Application.deleteProfileButtonKey(_customProfiles[0].id)));
await tester.pumpAndSettle();
expectEquipmentProfilesCount(2);
expectSelectedEquipmentProfileName('');
verify(() => storageService.selectedEquipmentProfileId = '').called(1);
verify(() => storageService.equipmentProfiles = any<List<EquipmentProfile>>()).called(1);
},
);
testWidgets(
'Delete not selected',
(tester) async {
when(() => storageService.equipmentProfiles).thenReturn(List.from(_customProfiles));
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles[0].id);
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectEquipmentProfilesCount(3);
expectSelectedEquipmentProfileName(_customProfiles[0].name);
/// Delete the not selected profile
await tester.tap(find.byKey(_Application.deleteProfileButtonKey(_customProfiles[1].id)));
await tester.pumpAndSettle();
expectEquipmentProfilesCount(2);
expectSelectedEquipmentProfileName(_customProfiles[0].name);
verifyNever(() => storageService.selectedEquipmentProfileId = '');
verify(() => storageService.equipmentProfiles = any<List<EquipmentProfile>>()).called(1);
},
);
testWidgets(
'Select',
(tester) async {
when(() => storageService.equipmentProfiles).thenReturn(List.from(_customProfiles));
when(() => storageService.selectedEquipmentProfileId).thenReturn('');
await pumpTestWidget(tester, IAPProductStatus.purchased);
expectEquipmentProfilesCount(3);
expectSelectedEquipmentProfileName('');
/// Select the 1st custom profile
await tester.tap(find.byKey(_Application.setProfileButtonKey(_customProfiles[0].id)));
await tester.pumpAndSettle();
expectEquipmentProfilesCount(3);
expectSelectedEquipmentProfileName(_customProfiles[0].name);
verify(() => storageService.selectedEquipmentProfileId = _customProfiles[0].id).called(1);
verifyNever(() => storageService.equipmentProfiles = any<List<EquipmentProfile>>());
},
);
});
} }
class _Application extends StatelessWidget { class _Application extends StatelessWidget {
const _Application(); const _Application();
static ValueKey get addProfileButtonKey => const ValueKey('addProfileButtonKey');
static ValueKey addFromProfileButtonKey(String id) => ValueKey('addFromProfileButtonKey$id');
static ValueKey setProfileButtonKey(String id) => ValueKey('setProfileButtonKey$id');
static ValueKey updateProfileButtonKey(String id) => ValueKey('updateProfileButtonKey$id');
static ValueKey deleteProfileButtonKey(String id) => ValueKey('deleteProfileButtonKey$id');
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return const MaterialApp(
home: Scaffold( home: Scaffold(
appBar: AppBar(title: const Text('IAPProviders test')),
body: Center( body: Center(
child: Column( child: Column(
children: [ children: [
Text("Equipment profiles count: ${EquipmentProfiles.of(context).length}"), _EquipmentProfilesCount(),
Text("Selected equipment profile: ${EquipmentProfiles.selectedOf(context).name}"), _SelectedEquipmentProfile(),
ElevatedButton(
key: addProfileButtonKey,
onPressed: () {
EquipmentProfileProvider.of(context).addProfile('Test added');
},
child: const Text("Add"),
),
...EquipmentProfiles.of(context).map((e) => _equipmentProfilesCrudRow(context, e)),
], ],
), ),
), ),
), ),
); );
} }
}
Widget _equipmentProfilesCrudRow(BuildContext context, EquipmentProfile profile) { class _EquipmentProfilesCount extends StatelessWidget {
return Row( static String text(int count) => "Profiles count: $count";
children: [
ElevatedButton( const _EquipmentProfilesCount();
key: setProfileButtonKey(profile.id),
onPressed: () { @override
EquipmentProfileProvider.of(context).setProfile(profile); Widget build(BuildContext context) {
}, return Text(text(EquipmentProfiles.of(context).length));
child: const Text("Set"), }
), }
ElevatedButton(
key: addFromProfileButtonKey(profile.id), class _SelectedEquipmentProfile extends StatelessWidget {
onPressed: () { static String text(String name) => "Selected profile: $name}";
EquipmentProfileProvider.of(context).addProfile('Test from ${profile.name}', profile);
}, const _SelectedEquipmentProfile();
child: const Text("Add from"),
), @override
ElevatedButton( Widget build(BuildContext context) {
key: updateProfileButtonKey(profile.id), return Text(text(EquipmentProfiles.selectedOf(context).name));
onPressed: () {
EquipmentProfileProvider.of(context).updateProfile(
profile.copyWith(
name: '${profile.name} updated',
isoValues: _customProfiles.first.isoValues,
),
);
},
child: const Text("Update"),
),
ElevatedButton(
key: deleteProfileButtonKey(profile.id),
onPressed: () {
EquipmentProfileProvider.of(context).deleteProfile(profile);
},
child: const Text("Delete"),
),
],
);
} }
} }

View file

@ -9,25 +9,24 @@ class _MockFilmsStorageService extends Mock implements FilmsStorageService {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
late _MockFilmsStorageService mockFilmsStorageService; late _MockFilmsStorageService storageService;
setUpAll(() { setUpAll(() {
mockFilmsStorageService = _MockFilmsStorageService(); storageService = _MockFilmsStorageService();
}); });
setUp(() { setUp(() {
registerFallbackValue(mockCustomFilms.first); registerFallbackValue(mockCustomFilms.first);
when(() => mockFilmsStorageService.toggleFilm(any<Film>(), any<bool>())).thenAnswer((_) async {}); when(() => storageService.toggleFilm(any<Film>(), any<bool>())).thenAnswer((_) async {});
when(() => mockFilmsStorageService.addFilm(any<FilmExponential>())).thenAnswer((_) async {}); when(() => storageService.addFilm(any<FilmExponential>())).thenAnswer((_) async {});
when(() => mockFilmsStorageService.updateFilm(any<FilmExponential>())).thenAnswer((_) async {}); when(() => storageService.updateFilm(any<FilmExponential>())).thenAnswer((_) async {});
when(() => mockFilmsStorageService.deleteFilm(any<FilmExponential>())).thenAnswer((_) async {}); when(() => storageService.deleteFilm(any<FilmExponential>())).thenAnswer((_) async {});
when(() => mockFilmsStorageService.getPredefinedFilms()) when(() => storageService.getPredefinedFilms()).thenAnswer((_) => Future.value(mockPredefinedFilms.toFilmsMap()));
.thenAnswer((_) => Future.value(mockPredefinedFilms.toFilmsMap())); when(() => storageService.getCustomFilms()).thenAnswer((_) => Future.value(mockCustomFilms.toFilmsMap()));
when(() => mockFilmsStorageService.getCustomFilms()).thenAnswer((_) => Future.value(mockCustomFilms.toFilmsMap()));
}); });
tearDown(() { tearDown(() {
reset(mockFilmsStorageService); reset(storageService);
}); });
Future<void> pumpTestWidget(WidgetTester tester, IAPProductStatus productStatus) async { Future<void> pumpTestWidget(WidgetTester tester, IAPProductStatus productStatus) async {
@ -41,7 +40,7 @@ void main() {
), ),
], ],
child: FilmsProvider( child: FilmsProvider(
filmsStorageService: mockFilmsStorageService, storageService: storageService,
child: const _Application(), child: const _Application(),
), ),
), ),
@ -69,11 +68,10 @@ void main() {
'FilmsProvider dependency on IAPProductStatus', 'FilmsProvider dependency on IAPProductStatus',
() { () {
setUp(() { setUp(() {
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockPredefinedFilms.first.id); when(() => storageService.selectedFilmId).thenReturn(mockPredefinedFilms.first.id);
when(() => mockFilmsStorageService.getPredefinedFilms()) when(() => storageService.getPredefinedFilms())
.thenAnswer((_) => Future.value(mockPredefinedFilms.toFilmsMap())); .thenAnswer((_) => Future.value(mockPredefinedFilms.toFilmsMap()));
when(() => mockFilmsStorageService.getCustomFilms()) when(() => storageService.getCustomFilms()).thenAnswer((_) => Future.value(mockCustomFilms.toFilmsMap()));
.thenAnswer((_) => Future.value(mockCustomFilms.toFilmsMap()));
}); });
testWidgets( testWidgets(
@ -117,7 +115,7 @@ void main() {
testWidgets( testWidgets(
'toggle predefined film', 'toggle predefined film',
(tester) async { (tester) async {
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockPredefinedFilms.first.id); when(() => storageService.selectedFilmId).thenReturn(mockPredefinedFilms.first.id);
await pumpTestWidget(tester, IAPProductStatus.purchased); await pumpTestWidget(tester, IAPProductStatus.purchased);
expectPredefinedFilmsCount(mockPredefinedFilms.length); expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(mockCustomFilms.length); expectCustomFilmsCount(mockCustomFilms.length);
@ -131,15 +129,15 @@ void main() {
expectFilmsInUseCount(mockPredefinedFilms.length - 1 + mockCustomFilms.length + 1); expectFilmsInUseCount(mockPredefinedFilms.length - 1 + mockCustomFilms.length + 1);
expectSelectedFilmName(''); expectSelectedFilmName('');
verify(() => mockFilmsStorageService.toggleFilm(mockPredefinedFilms.first, false)).called(1); verify(() => storageService.toggleFilm(mockPredefinedFilms.first, false)).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = '').called(1); verify(() => storageService.selectedFilmId = '').called(1);
}, },
); );
testWidgets( testWidgets(
'toggle custom film', 'toggle custom film',
(tester) async { (tester) async {
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(mockCustomFilms.first.id); when(() => storageService.selectedFilmId).thenReturn(mockCustomFilms.first.id);
await pumpTestWidget(tester, IAPProductStatus.purchased); await pumpTestWidget(tester, IAPProductStatus.purchased);
expectPredefinedFilmsCount(mockPredefinedFilms.length); expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(mockCustomFilms.length); expectCustomFilmsCount(mockCustomFilms.length);
@ -153,8 +151,8 @@ void main() {
expectFilmsInUseCount(mockPredefinedFilms.length - 1 + mockCustomFilms.length + 1); expectFilmsInUseCount(mockPredefinedFilms.length - 1 + mockCustomFilms.length + 1);
expectSelectedFilmName(''); expectSelectedFilmName('');
verify(() => mockFilmsStorageService.toggleFilm(mockCustomFilms.first, false)).called(1); verify(() => storageService.toggleFilm(mockCustomFilms.first, false)).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = '').called(1); verify(() => storageService.selectedFilmId = '').called(1);
}, },
); );
}, },
@ -163,7 +161,7 @@ void main() {
testWidgets( testWidgets(
'selectFilm', 'selectFilm',
(tester) async { (tester) async {
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(''); when(() => storageService.selectedFilmId).thenReturn('');
await pumpTestWidget(tester, IAPProductStatus.purchased); await pumpTestWidget(tester, IAPProductStatus.purchased);
expectSelectedFilmName(''); expectSelectedFilmName('');
@ -175,16 +173,16 @@ void main() {
await tester.pump(); await tester.pump();
expectSelectedFilmName(mockCustomFilms.first.name); expectSelectedFilmName(mockCustomFilms.first.name);
verify(() => mockFilmsStorageService.selectedFilmId = mockPredefinedFilms.first.id).called(1); verify(() => storageService.selectedFilmId = mockPredefinedFilms.first.id).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = mockCustomFilms.first.id).called(1); verify(() => storageService.selectedFilmId = mockCustomFilms.first.id).called(1);
}, },
); );
testWidgets( testWidgets(
'Custom film CRUD', 'Custom film CRUD',
(tester) async { (tester) async {
when(() => mockFilmsStorageService.selectedFilmId).thenReturn(''); when(() => storageService.selectedFilmId).thenReturn('');
when(() => mockFilmsStorageService.getCustomFilms()).thenAnswer((_) => Future.value({})); when(() => storageService.getCustomFilms()).thenAnswer((_) => Future.value({}));
await pumpTestWidget(tester, IAPProductStatus.purchased); await pumpTestWidget(tester, IAPProductStatus.purchased);
expectPredefinedFilmsCount(mockPredefinedFilms.length); expectPredefinedFilmsCount(mockPredefinedFilms.length);
expectCustomFilmsCount(0); expectCustomFilmsCount(0);
@ -199,16 +197,16 @@ void main() {
expectCustomFilmsCount(1); expectCustomFilmsCount(1);
expectFilmsInUseCount(mockPredefinedFilms.length + 1 + 1); expectFilmsInUseCount(mockPredefinedFilms.length + 1 + 1);
expectSelectedFilmName(mockCustomFilms.first.name); expectSelectedFilmName(mockCustomFilms.first.name);
verify(() => mockFilmsStorageService.addFilm(mockCustomFilms.first)).called(1); verify(() => storageService.addFilm(mockCustomFilms.first)).called(1);
verify(() => mockFilmsStorageService.toggleFilm(mockCustomFilms.first, true)).called(1); verify(() => storageService.toggleFilm(mockCustomFilms.first, true)).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = mockCustomFilms.first.id).called(1); verify(() => storageService.selectedFilmId = mockCustomFilms.first.id).called(1);
const editedFilmName = 'Edited custom film 2x'; const editedFilmName = 'Edited custom film 2x';
final editedFilm = mockCustomFilms.first.copyWith(name: editedFilmName); final editedFilm = mockCustomFilms.first.copyWith(name: editedFilmName);
await tester.filmsProvider.updateCustomFilm(editedFilm); await tester.filmsProvider.updateCustomFilm(editedFilm);
await tester.pump(); await tester.pump();
expectSelectedFilmName(editedFilm.name); expectSelectedFilmName(editedFilm.name);
verify(() => mockFilmsStorageService.updateFilm(editedFilm)).called(1); verify(() => storageService.updateFilm(editedFilm)).called(1);
await tester.filmsProvider.deleteCustomFilm(editedFilm); await tester.filmsProvider.deleteCustomFilm(editedFilm);
await tester.pump(); await tester.pump();
@ -216,8 +214,8 @@ void main() {
expectCustomFilmsCount(0); expectCustomFilmsCount(0);
expectFilmsInUseCount(mockPredefinedFilms.length + 0 + 1); expectFilmsInUseCount(mockPredefinedFilms.length + 0 + 1);
expectSelectedFilmName(''); expectSelectedFilmName('');
verify(() => mockFilmsStorageService.deleteFilm(editedFilm)).called(1); verify(() => storageService.deleteFilm(editedFilm)).called(1);
verify(() => mockFilmsStorageService.selectedFilmId = '').called(1); verify(() => storageService.selectedFilmId = '').called(1);
}, },
); );
} }

View file

@ -7,18 +7,19 @@ 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';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import '../../../../../../integration_test/mocks/paid_features_mock.dart';
import '../../../../../application_mock.dart'; import '../../../../../application_mock.dart';
import 'utils.dart'; import 'utils.dart';
class _MockIAPStorageService extends Mock implements IAPStorageService {} class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {}
void main() { void main() {
late final _MockIAPStorageService mockIAPStorageService; late final _MockEquipmentProfilesStorageService storageService;
setUpAll(() { setUpAll(() {
mockIAPStorageService = _MockIAPStorageService(); storageService = _MockEquipmentProfilesStorageService();
when(() => mockIAPStorageService.equipmentProfiles).thenReturn(_mockEquipmentProfiles); when(() => storageService.getProfiles()).thenAnswer((_) async => _mockEquipmentProfiles.toProfilesMap());
when(() => mockIAPStorageService.selectedEquipmentProfileId).thenReturn(''); when(() => storageService.selectedEquipmentProfileId).thenReturn('');
}); });
Future<void> pumpApplication(WidgetTester tester) async { Future<void> pumpApplication(WidgetTester tester) async {
@ -31,8 +32,8 @@ void main() {
price: '0.0\$', price: '0.0\$',
), ),
], ],
child: EquipmentProfileProvider( child: EquipmentProfilesProvider(
storageService: mockIAPStorageService, storageService: storageService,
child: const WidgetTestApplicationMock( child: const WidgetTestApplicationMock(
child: Row(children: [Expanded(child: EquipmentProfilePicker())]), child: Row(children: [Expanded(child: EquipmentProfilePicker())]),
), ),
@ -59,7 +60,7 @@ void main() {
testWidgets( testWidgets(
'None', 'None',
(tester) async { (tester) async {
when(() => mockIAPStorageService.selectedEquipmentProfileId).thenReturn(''); when(() => storageService.selectedEquipmentProfileId).thenReturn('');
await pumpApplication(tester); await pumpApplication(tester);
expectReadingValueContainerText(S.current.none); expectReadingValueContainerText(S.current.none);
await tester.openAnimatedPicker<EquipmentProfilePicker>(); await tester.openAnimatedPicker<EquipmentProfilePicker>();
@ -70,7 +71,7 @@ void main() {
testWidgets( testWidgets(
'Praktica + Zenitar', 'Praktica + Zenitar',
(tester) async { (tester) async {
when(() => mockIAPStorageService.selectedEquipmentProfileId).thenReturn(_mockEquipmentProfiles.first.id); when(() => storageService.selectedEquipmentProfileId).thenReturn(_mockEquipmentProfiles.first.id);
await pumpApplication(tester); await pumpApplication(tester);
expectReadingValueContainerText(_mockEquipmentProfiles.first.name); expectReadingValueContainerText(_mockEquipmentProfiles.first.name);
await tester.openAnimatedPicker<EquipmentProfilePicker>(); await tester.openAnimatedPicker<EquipmentProfilePicker>();

View file

@ -36,7 +36,7 @@ void main() {
), ),
], ],
child: FilmsProvider( child: FilmsProvider(
filmsStorageService: mockFilmsStorageService, storageService: mockFilmsStorageService,
child: const WidgetTestApplicationMock( child: const WidgetTestApplicationMock(
child: Row( child: Row(
children: [ children: [

View file

@ -6,16 +6,29 @@ 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';
import 'package:mocktail/mocktail.dart'; import 'package:mocktail/mocktail.dart';
import '../../../../integration_test/mocks/paid_features_mock.dart';
import '../../../function_mock.dart'; import '../../../function_mock.dart';
class _MockIAPStorageService extends Mock implements IAPStorageService {} class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {}
void main() { void main() {
TestWidgetsFlutterBinding.ensureInitialized(); TestWidgetsFlutterBinding.ensureInitialized();
final storageService = _MockIAPStorageService(); final storageService = _MockEquipmentProfilesStorageService();
final equipmentProfileProviderKey = GlobalKey<EquipmentProfileProviderState>();
final onDidChangeDependencies = MockValueChanged<EquipmentProfile>(); final onDidChangeDependencies = MockValueChanged<EquipmentProfile>();
setUp(() {
registerFallbackValue(_customProfiles.first);
when(() => storageService.addProfile(any<EquipmentProfile>())).thenAnswer((_) async {});
when(
() => storageService.updateProfile(
id: any<String>(named: 'id'),
name: any<String>(named: 'name'),
),
).thenAnswer((_) async {});
when(() => storageService.deleteProfile(any<String>())).thenAnswer((_) async {});
when(() => storageService.getProfiles()).thenAnswer((_) => Future.value(_customProfiles.toProfilesMap()));
});
tearDown(() { tearDown(() {
reset(onDidChangeDependencies); reset(onDidChangeDependencies);
reset(storageService); reset(storageService);
@ -31,8 +44,7 @@ void main() {
price: '0.0\$', price: '0.0\$',
), ),
], ],
child: EquipmentProfileProvider( child: EquipmentProfilesProvider(
key: equipmentProfileProviderKey,
storageService: storageService, storageService: storageService,
child: MaterialApp( child: MaterialApp(
home: EquipmentProfileListener( home: EquipmentProfileListener(
@ -48,11 +60,10 @@ void main() {
testWidgets( testWidgets(
'Trigger `onDidChangeDependencies` by selecting a new profile', 'Trigger `onDidChangeDependencies` by selecting a new profile',
(tester) async { (tester) async {
when(() => storageService.equipmentProfiles).thenReturn(List.from(_customProfiles));
when(() => storageService.selectedEquipmentProfileId).thenReturn(''); when(() => storageService.selectedEquipmentProfileId).thenReturn('');
await pumpTestWidget(tester); await pumpTestWidget(tester);
equipmentProfileProviderKey.currentState!.setProfile(_customProfiles[0]); tester.equipmentProfilesProvider.selectProfile(_customProfiles[0]);
await tester.pump(); await tester.pump();
verify(() => onDidChangeDependencies.onChanged(_customProfiles[0])).called(1); verify(() => onDidChangeDependencies.onChanged(_customProfiles[0])).called(1);
}, },
@ -61,18 +72,17 @@ void main() {
testWidgets( testWidgets(
'Trigger `onDidChangeDependencies` by updating the selected profile', 'Trigger `onDidChangeDependencies` by updating the selected profile',
(tester) async { (tester) async {
when(() => storageService.equipmentProfiles).thenReturn(List.from(_customProfiles));
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles[0].id); when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles[0].id);
await pumpTestWidget(tester); await pumpTestWidget(tester);
final updatedProfile1 = _customProfiles[0].copyWith(name: 'Test 1 updated'); final updatedProfile1 = _customProfiles[0].copyWith(name: 'Test 1 updated');
equipmentProfileProviderKey.currentState!.updateProfile(updatedProfile1); await tester.equipmentProfilesProvider.updateProfile(updatedProfile1);
await tester.pump(); await tester.pump();
verify(() => onDidChangeDependencies.onChanged(updatedProfile1)).called(1); verify(() => onDidChangeDependencies.onChanged(updatedProfile1)).called(1);
/// Verify that updating the not selected profile doesn't trigger the callback /// Verify that updating the not selected profile doesn't trigger the callback
final updatedProfile2 = _customProfiles[1].copyWith(name: 'Test 2 updated'); final updatedProfile2 = _customProfiles[1].copyWith(name: 'Test 2 updated');
equipmentProfileProviderKey.currentState!.updateProfile(updatedProfile2); await tester.equipmentProfilesProvider.updateProfile(updatedProfile2);
await tester.pump(); await tester.pump();
verifyNever(() => onDidChangeDependencies.onChanged(updatedProfile2)); verifyNever(() => onDidChangeDependencies.onChanged(updatedProfile2));
}, },
@ -81,18 +91,24 @@ void main() {
testWidgets( testWidgets(
"Don't trigger `onDidChangeDependencies` by updating the unselected profile", "Don't trigger `onDidChangeDependencies` by updating the unselected profile",
(tester) async { (tester) async {
when(() => storageService.equipmentProfiles).thenReturn(List.from(_customProfiles));
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles[0].id); when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles[0].id);
await pumpTestWidget(tester); await pumpTestWidget(tester);
final updatedProfile2 = _customProfiles[1].copyWith(name: 'Test 2 updated'); final updatedProfile2 = _customProfiles[1].copyWith(name: 'Test 2 updated');
equipmentProfileProviderKey.currentState!.updateProfile(updatedProfile2); await tester.equipmentProfilesProvider.updateProfile(updatedProfile2);
await tester.pump(); await tester.pump();
verifyNever(() => onDidChangeDependencies.onChanged(updatedProfile2)); verifyNever(() => onDidChangeDependencies.onChanged(updatedProfile2));
}, },
); );
} }
extension on WidgetTester {
EquipmentProfilesProviderState get equipmentProfilesProvider {
final BuildContext context = element(find.byType(MaterialApp));
return EquipmentProfilesProvider.of(context);
}
}
final List<EquipmentProfile> _customProfiles = [ final List<EquipmentProfile> _customProfiles = [
const EquipmentProfile( const EquipmentProfile(
id: '1', id: '1',