mirror of
https://github.com/vodemn/m3_lightmeter.git
synced 2024-11-22 15:30:59 +00:00
test granting and revoking pro features
This commit is contained in:
parent
134af8ad28
commit
0f3d2a1f13
3 changed files with 170 additions and 7 deletions
45
integration_test/mocks/iap_products_mock.dart
Normal file
45
integration_test/mocks/iap_products_mock.dart
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
|
||||||
|
@visibleForTesting
|
||||||
|
class MockIAPProductsProvider extends StatefulWidget {
|
||||||
|
final bool initialyPurchased;
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
const MockIAPProductsProvider({this.initialyPurchased = true, required this.child, super.key});
|
||||||
|
|
||||||
|
static MockIAPProductsProviderState of(BuildContext context) => MockIAPProductsProvider.maybeOf(context)!;
|
||||||
|
|
||||||
|
static MockIAPProductsProviderState? maybeOf(BuildContext context) {
|
||||||
|
return context.findAncestorStateOfType<MockIAPProductsProviderState>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<MockIAPProductsProvider> createState() => MockIAPProductsProviderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockIAPProductsProviderState extends State<MockIAPProductsProvider> {
|
||||||
|
late bool _purchased = widget.initialyPurchased;
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return IAPProducts(
|
||||||
|
products: List.from([
|
||||||
|
IAPProduct(
|
||||||
|
storeId: IAPProductType.paidFeatures.storeId,
|
||||||
|
status: _purchased ? IAPProductStatus.purchased : IAPProductStatus.purchasable,
|
||||||
|
)
|
||||||
|
]),
|
||||||
|
child: widget.child,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void buy() {
|
||||||
|
_purchased = true;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
|
||||||
|
void clearPurchases() {
|
||||||
|
_purchased = false;
|
||||||
|
setState(() {});
|
||||||
|
}
|
||||||
|
}
|
122
integration_test/purchases_test.dart
Normal file
122
integration_test/purchases_test.dart
Normal file
|
@ -0,0 +1,122 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:integration_test/integration_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/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/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/settings/components/shared/disable/widget_disable.dart';
|
||||||
|
import 'package:lightmeter/screens/settings/screen_settings.dart';
|
||||||
|
import 'package:m3_lightmeter_iap/m3_lightmeter_iap.dart';
|
||||||
|
import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
|
import '../integration_test/utils/widget_tester_actions.dart';
|
||||||
|
import 'mocks/iap_products_mock.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
void mockSharedPrefs() {
|
||||||
|
// ignore: invalid_use_of_visible_for_testing_member
|
||||||
|
SharedPreferences.setMockInitialValues({
|
||||||
|
/// Metering values
|
||||||
|
UserPreferencesService.evSourceTypeKey: EvSourceType.camera.index,
|
||||||
|
UserPreferencesService.showEv100Key: true,
|
||||||
|
UserPreferencesService.meteringScreenLayoutKey: json.encode(
|
||||||
|
{
|
||||||
|
MeteringScreenLayoutFeature.equipmentProfiles: true,
|
||||||
|
MeteringScreenLayoutFeature.extremeExposurePairs: true,
|
||||||
|
MeteringScreenLayoutFeature.filmPicker: true,
|
||||||
|
}.toJson(),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
setUpAll(() {
|
||||||
|
mockSharedPrefs();
|
||||||
|
});
|
||||||
|
|
||||||
|
/// Generates several screenshots with the light theme
|
||||||
|
testWidgets(
|
||||||
|
'Purchase & refund premium features',
|
||||||
|
(tester) async {
|
||||||
|
await tester.pumpApplication(productStatus: IAPProductStatus.purchasable);
|
||||||
|
await tester.takePhoto();
|
||||||
|
|
||||||
|
/// Expect the bare minimum free functionallity
|
||||||
|
_expectProMeteringScreen(enabled: false);
|
||||||
|
|
||||||
|
/// Check, that premium settings are disabled
|
||||||
|
await tester.tap(find.byTooltip(S.current.tooltipOpenSettings));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await _expectProSettingsScreen(tester, enabled: false);
|
||||||
|
|
||||||
|
/// Make purchase
|
||||||
|
(tester.state(find.byType(MockIAPProductsProvider)) as MockIAPProductsProviderState).buy();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
/// Check, that premium settings are enabled
|
||||||
|
await _expectProSettingsScreen(tester, enabled: true);
|
||||||
|
|
||||||
|
/// Expect, that all the premium controls are now available to user
|
||||||
|
(tester.state(find.byType(Navigator)) as NavigatorState).pop();
|
||||||
|
await tester.pumpAndSettle(Dimens.durationML);
|
||||||
|
_expectProMeteringScreen(enabled: true);
|
||||||
|
|
||||||
|
/// Refund
|
||||||
|
(tester.state(find.byType(MockIAPProductsProvider)) as MockIAPProductsProviderState).clearPurchases();
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
|
||||||
|
/// Expect the bare minimum free functionallity
|
||||||
|
_expectProMeteringScreen(enabled: false);
|
||||||
|
|
||||||
|
/// Check, that premium settings are disabled
|
||||||
|
await tester.tap(find.byTooltip(S.current.tooltipOpenSettings));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
await _expectProSettingsScreen(tester, enabled: false);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _expectProMeteringScreen({required bool enabled}) {
|
||||||
|
expect(find.byType(EquipmentProfilePicker), enabled ? findsOneWidget : findsNothing);
|
||||||
|
expect(find.byType(ExtremeExposurePairsContainer), findsOneWidget);
|
||||||
|
expect(find.byType(FilmPicker), enabled ? findsOneWidget : findsNothing);
|
||||||
|
expect(find.byType(IsoValuePicker), findsOneWidget);
|
||||||
|
expect(find.byType(NdValuePicker), findsOneWidget);
|
||||||
|
expect(
|
||||||
|
find.descendant(
|
||||||
|
of: find.byType(MeteringMeasureButton),
|
||||||
|
matching: find.byWidgetPredicate((widget) => widget is Text && widget.data!.contains('\u2081\u2080\u2080')),
|
||||||
|
),
|
||||||
|
enabled ? findsOneWidget : findsNothing,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _expectProSettingsScreen(WidgetTester tester, {required bool enabled}) async {
|
||||||
|
void expectDisabled(String title, bool disabled) {
|
||||||
|
find.ancestor(
|
||||||
|
of: find.text(title),
|
||||||
|
matching: find.byWidgetPredicate((widget) => widget is Disable && widget.disable == disabled),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
expectDisabled(S.current.showEv100, !enabled);
|
||||||
|
expectDisabled(S.current.equipmentProfiles, !enabled);
|
||||||
|
expectDisabled(S.current.filmsInUse, !enabled);
|
||||||
|
expectDisabled(S.current.cameraFeatures, !enabled);
|
||||||
|
await tester.tapDescendantTextOf<SettingsScreen>(S.current.meteringScreenLayout);
|
||||||
|
expectDisabled(S.current.meteringScreenLayoutHintEquipmentProfiles, !enabled);
|
||||||
|
expectDisabled(S.current.meteringScreenFeatureExtremeExposurePairs, false); // must be always enabled
|
||||||
|
expectDisabled(S.current.meteringScreenFeatureFilmPicker, !enabled);
|
||||||
|
await tester.tapCancelButton();
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import 'package:lightmeter/screens/metering/components/bottom_controls/component
|
||||||
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';
|
||||||
|
|
||||||
|
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';
|
||||||
|
|
||||||
|
@ -19,13 +20,8 @@ extension WidgetTesterCommonActions on WidgetTester {
|
||||||
Film selectedFilm = const Film.other(),
|
Film selectedFilm = const Film.other(),
|
||||||
}) async {
|
}) async {
|
||||||
await pumpWidget(
|
await pumpWidget(
|
||||||
IAPProducts(
|
MockIAPProductsProvider(
|
||||||
products: [
|
initialyPurchased: productStatus == IAPProductStatus.purchased,
|
||||||
IAPProduct(
|
|
||||||
storeId: IAPProductType.paidFeatures.storeId,
|
|
||||||
status: productStatus,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
child: ApplicationWrapper(
|
child: ApplicationWrapper(
|
||||||
const Environment.dev(),
|
const Environment.dev(),
|
||||||
child: MockIAPProviders(
|
child: MockIAPProviders(
|
||||||
|
|
Loading…
Reference in a new issue