mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-23 16:00:41 +00:00
30418a9cfd
* integrated `EquipmentProfilesStorageService` * implemented `EquipmentProfileEditScreen` * added equipment profiles screens to navigation * fixed tests * fixed splashscreen removal * replaced old `EquipmentProfilesScreen` * typo * use outlined icons * fixed storage mock for integration tests * recovered copy feature for profiles * added profile deletion to e2e test * added translations * added film translations * wip * add ability to toggle equipment profiles * lints * fixed tests * sync with iap rename * use `Toggleable` from resources * use iap 2.1.0 * use outlined edit icon
307 lines
10 KiB
Dart
307 lines
10 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:lightmeter/providers/equipment_profile_provider.dart';
|
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
|
import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
|
|
import 'package:mocktail/mocktail.dart';
|
|
|
|
class _MockEquipmentProfilesStorageService extends Mock implements EquipmentProfilesStorageService {}
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
late _MockEquipmentProfilesStorageService storageService;
|
|
|
|
setUpAll(() {
|
|
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'),
|
|
isUsed: any<bool>(named: 'isUsed'),
|
|
),
|
|
).thenAnswer((_) async {});
|
|
when(() => storageService.deleteProfile(any<String>())).thenAnswer((_) async {});
|
|
when(() => storageService.getProfiles()).thenAnswer((_) => Future.value(_customProfiles.toTogglableMap()));
|
|
});
|
|
|
|
tearDown(() {
|
|
reset(storageService);
|
|
});
|
|
|
|
Future<void> pumpTestWidget(WidgetTester tester, IAPProductStatus productStatus) async {
|
|
await tester.pumpWidget(
|
|
IAPProducts(
|
|
products: [
|
|
IAPProduct(
|
|
storeId: IAPProductType.paidFeatures.storeId,
|
|
status: productStatus,
|
|
price: '0.0\$',
|
|
),
|
|
],
|
|
child: EquipmentProfilesProvider(
|
|
storageService: storageService,
|
|
child: const _Application(),
|
|
),
|
|
),
|
|
);
|
|
await tester.pumpAndSettle();
|
|
}
|
|
|
|
void expectEquipmentProfilesCount(int count) {
|
|
expect(find.text(_EquipmentProfilesCount.text(count)), findsOneWidget);
|
|
}
|
|
|
|
void expectEquipmentProfilesInUseCount(int count) {
|
|
expect(find.text(_EquipmentProfilesInUseCount.text(count)), findsOneWidget);
|
|
}
|
|
|
|
void expectSelectedEquipmentProfileName(String name) {
|
|
expect(find.text(_SelectedEquipmentProfile.text(name)), findsOneWidget);
|
|
}
|
|
|
|
group(
|
|
'EquipmentProfilesProvider dependency on IAPProductStatus',
|
|
() {
|
|
setUp(() {
|
|
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles.first.id);
|
|
when(() => storageService.getProfiles()).thenAnswer((_) => Future.value(_customProfiles.toTogglableMap()));
|
|
});
|
|
|
|
testWidgets(
|
|
'IAPProductStatus.purchased - show all saved profiles',
|
|
(tester) async {
|
|
await pumpTestWidget(tester, IAPProductStatus.purchased);
|
|
expectEquipmentProfilesCount(_customProfiles.length + 1);
|
|
expectSelectedEquipmentProfileName(_customProfiles.first.name);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'IAPProductStatus.purchasable - show only default',
|
|
(tester) async {
|
|
await pumpTestWidget(tester, IAPProductStatus.purchasable);
|
|
expectEquipmentProfilesCount(1);
|
|
expectSelectedEquipmentProfileName('');
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'IAPProductStatus.pending - show only default',
|
|
(tester) async {
|
|
await pumpTestWidget(tester, IAPProductStatus.pending);
|
|
expectEquipmentProfilesCount(1);
|
|
expectSelectedEquipmentProfileName('');
|
|
},
|
|
);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'toggleProfile',
|
|
(tester) async {
|
|
when(() => storageService.selectedEquipmentProfileId).thenReturn(_customProfiles.first.id);
|
|
await pumpTestWidget(tester, IAPProductStatus.purchased);
|
|
expectEquipmentProfilesCount(_customProfiles.length + 1);
|
|
expectEquipmentProfilesInUseCount(_customProfiles.length + 1);
|
|
expectSelectedEquipmentProfileName(_customProfiles.first.name);
|
|
|
|
await tester.equipmentProfilesProvider.toggleProfile(_customProfiles.first, false);
|
|
await tester.pump();
|
|
expectEquipmentProfilesCount(_customProfiles.length + 1);
|
|
expectEquipmentProfilesInUseCount(_customProfiles.length + 1 - 1);
|
|
expectSelectedEquipmentProfileName('');
|
|
|
|
verify(() => storageService.updateProfile(id: _customProfiles.first.id, isUsed: false)).called(1);
|
|
verify(() => storageService.selectedEquipmentProfileId = '').called(1);
|
|
},
|
|
);
|
|
|
|
testWidgets(
|
|
'EquipmentProfilesProvider CRUD',
|
|
(tester) async {
|
|
when(() => storageService.getProfiles()).thenAnswer((_) async => {});
|
|
when(() => storageService.selectedEquipmentProfileId).thenReturn('');
|
|
|
|
await pumpTestWidget(tester, IAPProductStatus.purchased);
|
|
expectEquipmentProfilesCount(1);
|
|
expectSelectedEquipmentProfileName('');
|
|
|
|
/// Add first profile and verify
|
|
await tester.equipmentProfilesProvider.addProfile(_customProfiles.first);
|
|
await tester.pump();
|
|
expectEquipmentProfilesCount(2);
|
|
expectSelectedEquipmentProfileName('');
|
|
verify(() => storageService.addProfile(any<EquipmentProfile>())).called(1);
|
|
|
|
/// Add the other profiles and select the 1st one
|
|
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);
|
|
|
|
/// Edit the selected profile
|
|
final updatedName = "${_customProfiles.first} updated";
|
|
await tester.equipmentProfilesProvider.updateProfile(_customProfiles.first.copyWith(name: updatedName));
|
|
await tester.pump();
|
|
expectEquipmentProfilesCount(1 + _customProfiles.length);
|
|
expectSelectedEquipmentProfileName(updatedName);
|
|
verify(() => storageService.updateProfile(id: _customProfiles.first.id, name: updatedName)).called(1);
|
|
|
|
/// Delete a non-selected profile
|
|
await tester.equipmentProfilesProvider.deleteProfile(_customProfiles.last);
|
|
await tester.pump();
|
|
expectEquipmentProfilesCount(1 + _customProfiles.length - 1);
|
|
expectSelectedEquipmentProfileName(updatedName);
|
|
verifyNever(() => storageService.selectedEquipmentProfileId = '');
|
|
verify(() => storageService.deleteProfile(_customProfiles.last.id)).called(1);
|
|
|
|
/// Delete the selected profile
|
|
await tester.equipmentProfilesProvider.deleteProfile(_customProfiles.first);
|
|
await tester.pump();
|
|
expectEquipmentProfilesCount(1 + _customProfiles.length - 2);
|
|
expectSelectedEquipmentProfileName('');
|
|
verify(() => storageService.selectedEquipmentProfileId = '').called(1);
|
|
verify(() => storageService.deleteProfile(_customProfiles.first.id)).called(1);
|
|
},
|
|
);
|
|
}
|
|
|
|
extension on WidgetTester {
|
|
EquipmentProfilesProviderState get equipmentProfilesProvider {
|
|
final BuildContext context = element(find.byType(_Application));
|
|
return EquipmentProfilesProvider.of(context);
|
|
}
|
|
}
|
|
|
|
class _Application extends StatelessWidget {
|
|
const _Application();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return const MaterialApp(
|
|
home: Scaffold(
|
|
body: Center(
|
|
child: Column(
|
|
children: [
|
|
_EquipmentProfilesCount(),
|
|
_EquipmentProfilesInUseCount(),
|
|
_SelectedEquipmentProfile(),
|
|
],
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
}
|
|
|
|
class _EquipmentProfilesCount extends StatelessWidget {
|
|
static String text(int count) => "Profiles count: $count";
|
|
|
|
const _EquipmentProfilesCount();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Text(text(EquipmentProfiles.of(context).length));
|
|
}
|
|
}
|
|
|
|
class _EquipmentProfilesInUseCount extends StatelessWidget {
|
|
static String text(int count) => "Profiles in use count: $count";
|
|
|
|
const _EquipmentProfilesInUseCount();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Text(text(EquipmentProfiles.inUseOf(context).length));
|
|
}
|
|
}
|
|
|
|
class _SelectedEquipmentProfile extends StatelessWidget {
|
|
static String text(String name) => "Selected profile: $name}";
|
|
|
|
const _SelectedEquipmentProfile();
|
|
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
return Text(text(EquipmentProfiles.selectedOf(context).name));
|
|
}
|
|
}
|
|
|
|
final List<EquipmentProfile> _customProfiles = [
|
|
const EquipmentProfile(
|
|
id: '1',
|
|
name: 'Test 1',
|
|
apertureValues: [
|
|
ApertureValue(4.0, StopType.full),
|
|
ApertureValue(4.5, StopType.third),
|
|
ApertureValue(4.8, StopType.half),
|
|
ApertureValue(5.0, StopType.third),
|
|
ApertureValue(5.6, StopType.full),
|
|
ApertureValue(6.3, StopType.third),
|
|
ApertureValue(6.7, StopType.half),
|
|
ApertureValue(7.1, StopType.third),
|
|
ApertureValue(8, StopType.full),
|
|
],
|
|
ndValues: [
|
|
NdValue(0),
|
|
NdValue(2),
|
|
NdValue(4),
|
|
NdValue(8),
|
|
NdValue(16),
|
|
NdValue(32),
|
|
NdValue(64),
|
|
],
|
|
shutterSpeedValues: ShutterSpeedValue.values,
|
|
isoValues: [
|
|
IsoValue(100, StopType.full),
|
|
IsoValue(125, StopType.third),
|
|
IsoValue(160, StopType.third),
|
|
IsoValue(200, StopType.full),
|
|
IsoValue(250, StopType.third),
|
|
IsoValue(320, StopType.third),
|
|
IsoValue(400, StopType.full),
|
|
],
|
|
),
|
|
const EquipmentProfile(
|
|
id: '2',
|
|
name: 'Test 2',
|
|
apertureValues: [
|
|
ApertureValue(4.0, StopType.full),
|
|
ApertureValue(4.5, StopType.third),
|
|
ApertureValue(4.8, StopType.half),
|
|
ApertureValue(5.0, StopType.third),
|
|
ApertureValue(5.6, StopType.full),
|
|
ApertureValue(6.3, StopType.third),
|
|
ApertureValue(6.7, StopType.half),
|
|
ApertureValue(7.1, StopType.third),
|
|
ApertureValue(8, StopType.full),
|
|
],
|
|
ndValues: [
|
|
NdValue(0),
|
|
NdValue(2),
|
|
NdValue(4),
|
|
NdValue(8),
|
|
NdValue(16),
|
|
NdValue(32),
|
|
NdValue(64),
|
|
],
|
|
shutterSpeedValues: ShutterSpeedValue.values,
|
|
isoValues: [
|
|
IsoValue(100, StopType.full),
|
|
IsoValue(125, StopType.third),
|
|
IsoValue(160, StopType.third),
|
|
IsoValue(200, StopType.full),
|
|
IsoValue(250, StopType.third),
|
|
IsoValue(320, StopType.third),
|
|
IsoValue(400, StopType.full),
|
|
],
|
|
),
|
|
];
|