Compare commits

...

5 commits

Author SHA1 Message Date
Vadim
450b613a99
Merge 7410d7ed60 into f820f9fbba 2025-05-12 14:32:19 +00:00
Vadim
7410d7ed60 cleanup 2025-05-12 16:32:12 +02:00
Vadim
1a7f9165b9 mock camera focal length for integration tests 2025-05-12 16:08:37 +02:00
Vadim
10794258b8 enable focal length feature by default 2025-05-12 16:04:24 +02:00
Vadim
eb90123a26 [ios] updated store screenshots 2025-05-12 15:29:36 +02:00
22 changed files with 69 additions and 50 deletions

View file

@ -64,7 +64,7 @@ void testE2E(String description) {
await tester.setApertureValues(mockEquipmentProfiles[0].apertureValues); await tester.setApertureValues(mockEquipmentProfiles[0].apertureValues);
await tester.setShutterSpeedValues(mockEquipmentProfiles[0].shutterSpeedValues); await tester.setShutterSpeedValues(mockEquipmentProfiles[0].shutterSpeedValues);
await tester.setZoomValue(mockEquipmentProfiles[0].lensZoom); await tester.setZoomValue(mockEquipmentProfiles[0].lensZoom);
expect(find.text('x1.91'), findsOneWidget); expect(find.text('50mm'), findsOneWidget);
expect(find.text('f/1.7 - f/16'), findsOneWidget); expect(find.text('f/1.7 - f/16'), findsOneWidget);
expect(find.text('1/1000 - B'), findsOneWidget); expect(find.text('1/1000 - B'), findsOneWidget);
await tester.saveEdits(); await tester.saveEdits();
@ -77,7 +77,7 @@ void testE2E(String description) {
await tester.enterProfileName(mockEquipmentProfiles[1].name); await tester.enterProfileName(mockEquipmentProfiles[1].name);
await tester.setApertureValues(mockEquipmentProfiles[1].apertureValues); await tester.setApertureValues(mockEquipmentProfiles[1].apertureValues);
await tester.setZoomValue(mockEquipmentProfiles[1].lensZoom); await tester.setZoomValue(mockEquipmentProfiles[1].lensZoom);
expect(find.text('x5.02'), findsOneWidget); expect(find.text('135mm'), findsOneWidget);
expect(find.text('f/3.5 - f/22'), findsOneWidget); expect(find.text('f/3.5 - f/22'), findsOneWidget);
expect(find.text('1/1000 - B'), findsNWidgets(1)); expect(find.text('1/1000 - B'), findsNWidgets(1));
await tester.saveEdits(); await tester.saveEdits();

View file

@ -1,12 +1,22 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import 'e2e_test.dart'; import 'e2e_test.dart';
import 'metering_screen_layout_test.dart'; import 'metering_screen_layout_test.dart';
import 'purchases_test.dart'; import 'purchases_test.dart';
import 'utils/platform_channel_mock.dart';
void main() { void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized(); IntegrationTestWidgetsFlutterBinding.ensureInitialized();
setUpAll(() {
mockCameraFocalLength();
});
tearDownAll(() {
mockCameraFocalLength();
});
testPurchases('Purchase & refund premium features'); testPurchases('Purchase & refund premium features');
testToggleLayoutFeatures('Toggle metering screen layout features'); testToggleLayoutFeatures('Toggle metering screen layout features');
testE2E('e2e'); testE2E('e2e');

View file

@ -1,8 +1,10 @@
import 'dart:io';
import 'dart:math'; import 'dart:math';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:light_sensor/light_sensor.dart'; import 'package:light_sensor/light_sensor.dart';
import 'package:lightmeter/data/camera_info_service.dart';
void setLightSensorAvilability({required bool hasSensor}) { void setLightSensorAvilability({required bool hasSensor}) {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
@ -57,3 +59,26 @@ void resetLightSensorStreamHandler() {
null, null,
); );
} }
void mockCameraFocalLength() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
CameraInfoService.cameraInfoPlatformChannel,
(methodCall) async {
switch (methodCall.method) {
case "mainCameraEfl":
return Platform.isAndroid
? 24.0 // Pixel 6
: 26.0; // iPhone 13 Pro
default:
return null;
}
},
);
}
void resetCameraFocalLength() {
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
CameraInfoService.cameraInfoPlatformChannel,
null,
);
}

View file

@ -9,8 +9,6 @@ class CameraInfoService {
const CameraInfoService(); const CameraInfoService();
// TODO: fix focal length for iOS screenshots
// TODO: fix integration test (find 1.91x)
Future<int?> mainCameraEfl() async { Future<int?> mainCameraEfl() async {
final focalLength = await cameraInfoPlatformChannel.invokeMethod<double?>('mainCameraEfl'); final focalLength = await cameraInfoPlatformChannel.invokeMethod<double?>('mainCameraEfl');
return focalLength?.round(); return focalLength?.round();

View file

@ -7,8 +7,20 @@ enum CameraFeature {
typedef CameraFeaturesConfig = Map<CameraFeature, bool>; typedef CameraFeaturesConfig = Map<CameraFeature, bool>;
extension CameraFeaturesConfigJson on CameraFeaturesConfig { extension CameraFeaturesConfigJson on CameraFeaturesConfig {
static CameraFeaturesConfig fromJson(Map<String, dynamic> data) => static CameraFeaturesConfig fromJson(Map<String, dynamic> data) {
<CameraFeature, bool>{for (final f in CameraFeature.values) f: data[f.name] as bool? ?? false}; MapEntry<CameraFeature, bool> valueOrBool(CameraFeature feature, {bool defaultValue = true}) => MapEntry(
feature,
data[feature.name] as bool? ?? defaultValue,
);
return Map.fromEntries(
[
valueOrBool(CameraFeature.spotMetering),
valueOrBool(CameraFeature.histogram, defaultValue: false),
valueOrBool(CameraFeature.showFocalLength),
],
);
}
Map<String, dynamic> toJson() => map((key, value) => MapEntry(key.name, value)); Map<String, dynamic> toJson() => map((key, value) => MapEntry(key.name, value));
} }

View file

@ -94,34 +94,16 @@ class UserPreferencesService {
set showEv100(bool value) => _sharedPreferences.setBool(showEv100Key, value); set showEv100(bool value) => _sharedPreferences.setBool(showEv100Key, value);
MeteringScreenLayoutConfig get meteringScreenLayout { MeteringScreenLayoutConfig get meteringScreenLayout {
final configJson = _sharedPreferences.getString(meteringScreenLayoutKey); final configJson = _sharedPreferences.getString(meteringScreenLayoutKey) ?? '{}';
if (configJson != null) { return MeteringScreenLayoutConfigJson.fromJson(json.decode(configJson) as Map<String, dynamic>);
return MeteringScreenLayoutConfigJson.fromJson(
json.decode(configJson) as Map<String, dynamic>,
);
} else {
return {
MeteringScreenLayoutFeature.equipmentProfiles: true,
MeteringScreenLayoutFeature.extremeExposurePairs: true,
MeteringScreenLayoutFeature.filmPicker: true,
};
}
} }
set meteringScreenLayout(MeteringScreenLayoutConfig value) => set meteringScreenLayout(MeteringScreenLayoutConfig value) =>
_sharedPreferences.setString(meteringScreenLayoutKey, json.encode(value.toJson())); _sharedPreferences.setString(meteringScreenLayoutKey, json.encode(value.toJson()));
CameraFeaturesConfig get cameraFeatures { CameraFeaturesConfig get cameraFeatures {
final configJson = _sharedPreferences.getString(cameraFeaturesKey); final configJson = _sharedPreferences.getString(cameraFeaturesKey) ?? '{}';
if (configJson != null) { return CameraFeaturesConfigJson.fromJson(json.decode(configJson) as Map<String, dynamic>);
return CameraFeaturesConfigJson.fromJson(json.decode(configJson) as Map<String, dynamic>);
} else {
return {
CameraFeature.spotMetering: false,
CameraFeature.histogram: false,
CameraFeature.showFocalLength: false,
};
}
} }
set cameraFeatures(CameraFeaturesConfig value) => set cameraFeatures(CameraFeaturesConfig value) =>

View file

@ -75,6 +75,4 @@ class MeteringInteractor {
Future<bool> hasAmbientLightSensor() async => _lightSensorService.hasSensor(); Future<bool> hasAmbientLightSensor() async => _lightSensorService.hasSensor();
Stream<int> luxStream() => _lightSensorService.luxStream(); Stream<int> luxStream() => _lightSensorService.luxStream();
void setCameraFocalLength(int focalLength) => _userPreferencesService.cameraFocalLength = focalLength;
} }

View file

@ -6,7 +6,6 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart'; import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart'; import 'package:integration_test/integration_test.dart';
import 'package:lightmeter/data/camera_info_service.dart';
import 'package:lightmeter/data/models/camera_feature.dart'; import 'package:lightmeter/data/models/camera_feature.dart';
import 'package:lightmeter/data/models/ev_source_type.dart'; import 'package:lightmeter/data/models/ev_source_type.dart';
import 'package:lightmeter/data/models/exposure_pair.dart'; import 'package:lightmeter/data/models/exposure_pair.dart';
@ -29,6 +28,7 @@ import 'package:m3_lightmeter_resources/m3_lightmeter_resources.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import '../integration_test/mocks/paid_features_mock.dart'; import '../integration_test/mocks/paid_features_mock.dart';
import '../integration_test/utils/platform_channel_mock.dart';
import '../integration_test/utils/widget_tester_actions.dart'; import '../integration_test/utils/widget_tester_actions.dart';
import 'models/screenshot_args.dart'; import 'models/screenshot_args.dart';
@ -96,19 +96,11 @@ void main() {
setUpAll(() async { setUpAll(() async {
if (Platform.isAndroid) await binding.convertFlutterSurfaceToImage(); if (Platform.isAndroid) await binding.convertFlutterSurfaceToImage();
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler( mockCameraFocalLength();
CameraInfoService.cameraInfoPlatformChannel, });
(methodCall) async {
switch (methodCall.method) { tearDownAll(() {
case "mainCameraEfl": resetCameraFocalLength();
return Platform.isAndroid
? 24.0 // Pixel 6
: 26.0; // iPhone 13 Pro
default:
return null;
}
},
);
}); });
/// Generates several screenshots with the light theme /// Generates several screenshots with the light theme

Binary file not shown.

Before

Width:  |  Height:  |  Size: 348 KiB

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 159 KiB

After

Width:  |  Height:  |  Size: 160 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 181 KiB

After

Width:  |  Height:  |  Size: 180 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 347 KiB

After

Width:  |  Height:  |  Size: 347 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 169 KiB

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

After

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 511 KiB

After

Width:  |  Height:  |  Size: 511 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 224 KiB

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 KiB

After

Width:  |  Height:  |  Size: 253 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 505 KiB

After

Width:  |  Height:  |  Size: 504 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 238 KiB

After

Width:  |  Height:  |  Size: 239 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 207 KiB

View file

@ -22,13 +22,13 @@ void main() {
); );
}); });
test('Legacy (no spotMetering & histogram)', () { test('Legacy', () {
expect( expect(
CameraFeaturesConfigJson.fromJson({}), CameraFeaturesConfigJson.fromJson({}),
{ {
CameraFeature.spotMetering: false, CameraFeature.spotMetering: true,
CameraFeature.histogram: false, CameraFeature.histogram: false,
CameraFeature.showFocalLength: false, CameraFeature.showFocalLength: true,
}, },
); );
}); });
@ -40,10 +40,12 @@ void main() {
{ {
CameraFeature.spotMetering: true, CameraFeature.spotMetering: true,
CameraFeature.histogram: true, CameraFeature.histogram: true,
CameraFeature.showFocalLength: true,
}.toJson(), }.toJson(),
{ {
'spotMetering': true, 'spotMetering': true,
'histogram': true, 'histogram': true,
'showFocalLength': true,
}, },
); );
}); });

View file

@ -270,9 +270,9 @@ void main() {
expect( expect(
service.cameraFeatures, service.cameraFeatures,
{ {
CameraFeature.spotMetering: false, CameraFeature.spotMetering: true,
CameraFeature.histogram: false, CameraFeature.histogram: false,
CameraFeature.showFocalLength: false, CameraFeature.showFocalLength: true,
}, },
); );
}); });
@ -285,7 +285,7 @@ void main() {
{ {
CameraFeature.spotMetering: false, CameraFeature.spotMetering: false,
CameraFeature.histogram: true, CameraFeature.histogram: true,
CameraFeature.showFocalLength: false, CameraFeature.showFocalLength: true,
}, },
); );
}); });