From 1336acdd12318eacdce5f162c073c770689b9ad1 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 4 Oct 2024 18:09:36 +0530 Subject: [PATCH 001/163] Setup test suite --- dart_test.yml | 3 + lib/app.dart | 2 +- lib/core/router/router.gr.dart | 68 +- lib/core/service/injection_container.dart | 9 +- lib/core/utils/common.dart | 13 + lib/features/home/home.dart | 1 + lib/features/home/session_model.dart | 2 +- lib/features/vpn/vpn_model.dart | 2 +- lib/main.dart | 9 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 55 +- pubspec.yaml | 8 +- test/app_test.dart | 77 +- test/base_screen_test.dart | 182 +- test/features/home/home_test.dart | 69 + test/features/home/session_model_test.dart | 7 + test/utils/test.mocks.dart | 8 + test/utils/test.mocks.mocks.dart | 2617 +++++++++++++++++ test/utils/test_common.dart | 25 + test/utils/widgets.dart | 33 + 20 files changed, 3021 insertions(+), 171 deletions(-) create mode 100644 dart_test.yml create mode 100644 test/features/home/home_test.dart create mode 100644 test/features/home/session_model_test.dart create mode 100644 test/utils/test.mocks.dart create mode 100644 test/utils/test.mocks.mocks.dart create mode 100644 test/utils/test_common.dart create mode 100644 test/utils/widgets.dart diff --git a/dart_test.yml b/dart_test.yml new file mode 100644 index 0000000000..8660c6433b --- /dev/null +++ b/dart_test.yml @@ -0,0 +1,3 @@ +tags: + golden: + timeout: 15s \ No newline at end of file diff --git a/lib/app.dart b/lib/app.dart index f6fcbf1d7e..4c089194c1 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -25,7 +25,7 @@ enum AppFontFamily { } class LanternApp extends StatefulWidget { - const LanternApp({Key? key}) : super(key: key); + const LanternApp({super.key}); @override State createState() => _LanternAppState(); diff --git a/lib/core/router/router.gr.dart b/lib/core/router/router.gr.dart index 82d8a1998b..13ef9d6233 100644 --- a/lib/core/router/router.gr.dart +++ b/lib/core/router/router.gr.dart @@ -42,9 +42,9 @@ import 'package:lantern/features/checkout/checkout.dart' as _i15; import 'package:lantern/features/checkout/checkout_legacy.dart' as _i16; import 'package:lantern/features/checkout/plans.dart' as _i32; import 'package:lantern/features/checkout/play_checkout.dart' as _i33; -import 'package:lantern/features/checkout/reseller_checkout.dart' as _i46; +import 'package:lantern/features/checkout/reseller_checkout.dart' as _i45; import 'package:lantern/features/checkout/reseller_checkout_legacy.dart' - as _i45; + as _i46; import 'package:lantern/features/checkout/store_checkout.dart' as _i53; import 'package:lantern/features/checkout/stripe_checkout.dart' as _i54; import 'package:lantern/features/device_linking/add_device.dart' as _i3; @@ -1712,107 +1712,107 @@ class ReportIssueArgs { /// generated route for /// [_i45.ResellerCodeCheckout] -class ResellerCodeCheckoutLegacy - extends _i57.PageRouteInfo { - ResellerCodeCheckoutLegacy({ +class ResellerCodeCheckout + extends _i57.PageRouteInfo { + ResellerCodeCheckout({ required bool isPro, + required String email, + String? otp, _i59.Key? key, List<_i57.PageRouteInfo>? children, }) : super( - ResellerCodeCheckoutLegacy.name, - args: ResellerCodeCheckoutLegacyArgs( + ResellerCodeCheckout.name, + args: ResellerCodeCheckoutArgs( isPro: isPro, + email: email, + otp: otp, key: key, ), initialChildren: children, ); - static const String name = 'ResellerCodeCheckoutLegacy'; + static const String name = 'ResellerCodeCheckout'; static _i57.PageInfo page = _i57.PageInfo( name, builder: (data) { - final args = data.argsAs(); + final args = data.argsAs(); return _i45.ResellerCodeCheckout( isPro: args.isPro, + email: args.email, + otp: args.otp, key: args.key, ); }, ); } -class ResellerCodeCheckoutLegacyArgs { - const ResellerCodeCheckoutLegacyArgs({ +class ResellerCodeCheckoutArgs { + const ResellerCodeCheckoutArgs({ required this.isPro, + required this.email, + this.otp, this.key, }); final bool isPro; + final String email; + + final String? otp; + final _i59.Key? key; @override String toString() { - return 'ResellerCodeCheckoutLegacyArgs{isPro: $isPro, key: $key}'; + return 'ResellerCodeCheckoutArgs{isPro: $isPro, email: $email, otp: $otp, key: $key}'; } } /// generated route for /// [_i46.ResellerCodeCheckout] -class ResellerCodeCheckout - extends _i57.PageRouteInfo { - ResellerCodeCheckout({ +class ResellerCodeCheckoutLegacy + extends _i57.PageRouteInfo { + ResellerCodeCheckoutLegacy({ required bool isPro, - required String email, - String? otp, _i59.Key? key, List<_i57.PageRouteInfo>? children, }) : super( - ResellerCodeCheckout.name, - args: ResellerCodeCheckoutArgs( + ResellerCodeCheckoutLegacy.name, + args: ResellerCodeCheckoutLegacyArgs( isPro: isPro, - email: email, - otp: otp, key: key, ), initialChildren: children, ); - static const String name = 'ResellerCodeCheckout'; + static const String name = 'ResellerCodeCheckoutLegacy'; static _i57.PageInfo page = _i57.PageInfo( name, builder: (data) { - final args = data.argsAs(); + final args = data.argsAs(); return _i46.ResellerCodeCheckout( isPro: args.isPro, - email: args.email, - otp: args.otp, key: args.key, ); }, ); } -class ResellerCodeCheckoutArgs { - const ResellerCodeCheckoutArgs({ +class ResellerCodeCheckoutLegacyArgs { + const ResellerCodeCheckoutLegacyArgs({ required this.isPro, - required this.email, - this.otp, this.key, }); final bool isPro; - final String email; - - final String? otp; - final _i59.Key? key; @override String toString() { - return 'ResellerCodeCheckoutArgs{isPro: $isPro, email: $email, otp: $otp, key: $key}'; + return 'ResellerCodeCheckoutLegacyArgs{isPro: $isPro, key: $key}'; } } diff --git a/lib/core/service/injection_container.dart b/lib/core/service/injection_container.dart index e2c92d3412..874593e07c 100644 --- a/lib/core/service/injection_container.dart +++ b/lib/core/service/injection_container.dart @@ -1,9 +1,16 @@ import 'package:get_it/get_it.dart'; import 'package:lantern/core/service/app_purchase.dart'; +import 'package:lantern/core/utils/common.dart'; final GetIt sl = GetIt.instance; void init() { //Inject - sl.registerLazySingleton(() => AppPurchase()); + + if (isMobile()) { + sl.registerLazySingleton(() => AppPurchase()); + sl().init(); + } + sl.registerLazySingleton(() => SessionModel()); + sl.registerLazySingleton(() => VpnModel()); } diff --git a/lib/core/utils/common.dart b/lib/core/utils/common.dart index 46f1c60235..ccf42a858c 100644 --- a/lib/core/utils/common.dart +++ b/lib/core/utils/common.dart @@ -3,6 +3,8 @@ import 'dart:io'; import 'package:lantern/features/replica/common.dart'; import 'package:logger/logger.dart'; +import 'common.dart'; + export 'dart:async'; export 'dart:convert'; export 'dart:io'; @@ -134,11 +136,22 @@ final appLogger = Logger( output: ConsoleOutput(), ); +// We need to check platform using flutter foundation defaultTargetPlatform +// so while testing we can easily override the platform +// Als this recommended way to check platform from Flutter Team bool isMobile() { + if (kDebugMode) { + return (defaultTargetPlatform == TargetPlatform.android || + defaultTargetPlatform == TargetPlatform.iOS); + } return Platform.isAndroid || Platform.isIOS; } bool isDesktop() { + if (kDebugMode) { + return (defaultTargetPlatform == TargetPlatform.macOS || + defaultTargetPlatform == TargetPlatform.linux || defaultTargetPlatform == TargetPlatform.windows); + } return Platform.isMacOS || Platform.isLinux || Platform.isWindows; } diff --git a/lib/features/home/home.dart b/lib/features/home/home.dart index 8254958aa5..5edb82bf89 100644 --- a/lib/features/home/home.dart +++ b/lib/features/home/home.dart @@ -267,6 +267,7 @@ class _HomePageState extends State with TrayListener, WindowListener { final tabModel = context.watch(); return sessionModel.acceptedTermsVersion( (BuildContext context, int version, Widget? child) { + print("accepted terms version $version"); return sessionModel.developmentMode( (BuildContext context, bool developmentMode, Widget? child) { if (developmentMode) { diff --git a/lib/features/home/session_model.dart b/lib/features/home/session_model.dart index c4476e32b5..4f10ffd90d 100644 --- a/lib/features/home/session_model.dart +++ b/lib/features/home/session_model.dart @@ -6,7 +6,7 @@ import 'package:lantern/features/replica/common.dart'; import '../../core/utils/common.dart'; import '../../core/utils/common_desktop.dart'; -final sessionModel = SessionModel(); +final sessionModel = sl(); const TAB_CHATS = 'chats'; const TAB_VPN = 'vpn'; diff --git a/lib/features/vpn/vpn_model.dart b/lib/features/vpn/vpn_model.dart index 3365cb7dbc..847e8bcea9 100644 --- a/lib/features/vpn/vpn_model.dart +++ b/lib/features/vpn/vpn_model.dart @@ -2,7 +2,7 @@ import 'package:lantern/core/utils/common_desktop.dart'; import 'package:lantern/features/vpn/vpn.dart'; import 'package:lantern/features/vpn/vpn_notifier.dart'; -final vpnModel = VpnModel(); +final vpnModel = sl(); class VpnModel extends Model { VpnModel() : super('vpn'); diff --git a/lib/main.dart b/lib/main.dart index 575fb783f4..ecb5128fd7 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,12 +1,9 @@ -import 'dart:ui' as ui; - import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_driver/driver_extension.dart'; import 'package:google_mobile_ads/google_mobile_ads.dart'; import 'package:lantern/app.dart'; import 'package:lantern/core/utils/common.dart'; import 'package:lantern/core/utils/common_desktop.dart'; -import 'package:lantern/core/service/app_purchase.dart'; import 'package:lantern/features/replica/ui/utils.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; import 'package:window_manager/window_manager.dart'; @@ -30,15 +27,15 @@ Future main() async { appLogger.e("Error loading .env file: $error"); } + // Inject all the services + init(); + if (isDesktop()) { LanternFFI.startDesktopService(); await WebsocketSubscriber().connect(); await windowManager.ensureInitialized(); } else { await _initGoogleMobileAds(); - // Inject all the services - init(); - sl().init(); // Due to replica we are using lot of cache // clear if goes to above limit CustomCacheManager().clearCacheIfExceeded(); diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 677709830d..745b16986c 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -17,6 +17,7 @@ import flutter_local_notifications import in_app_purchase_storekit import package_info_plus import path_provider_foundation +import patrol import screen_retriever import sentry_flutter import share_plus @@ -40,6 +41,7 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { InAppPurchasePlugin.register(with: registry.registrar(forPlugin: "InAppPurchasePlugin")) FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + PatrolPlugin.register(with: registry.registrar(forPlugin: "PatrolPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SentryFlutterPlugin.register(with: registry.registrar(forPlugin: "SentryFlutterPlugin")) SharePlusMacosPlugin.register(with: registry.registrar(forPlugin: "SharePlusMacosPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 9f4b11e594..e936967148 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -5,18 +5,31 @@ packages: dependency: transitive description: name: _fe_analyzer_shared - sha256: "0b2f2bd91ba804e53a61d757b986f89f1f9eaed5b11e4b2f5a2468d86d6c9fc7" + sha256: f256b0c0ba6c7577c15e2e4e114755640a875e885099367bf6e012b19314c834 url: "https://pub.dev" source: hosted - version: "67.0.0" - analyzer: + version: "72.0.0" + _macros: dependency: transitive + description: dart + source: sdk + version: "0.3.2" + alchemist: + dependency: "direct dev" + description: + name: alchemist + sha256: "2fcd35d7422070cdf7f276bc04dd2d38c9164adb7d58bd00b7bebd8b6927d112" + url: "https://pub.dev" + source: hosted + version: "0.10.0" + analyzer: + dependency: "direct dev" description: name: analyzer - sha256: "37577842a27e4338429a1cbc32679d508836510b056f1eedf0c8d20e39c1383d" + sha256: b652861553cd3990d8ed361f7979dc6d7053a9ac8843fa73820ab68ce5410139 url: "https://pub.dev" source: hosted - version: "6.4.1" + version: "6.7.0" animated_loading_border: dependency: "direct main" description: @@ -991,6 +1004,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.2" + golden_toolkit: + dependency: "direct dev" + description: + name: golden_toolkit + sha256: "8f74adab33154fe7b731395782797021f97d2edc52f7bfb85ff4f1b5c4a215f0" + url: "https://pub.dev" + source: hosted + version: "0.15.0" google_mobile_ads: dependency: "direct main" description: @@ -1220,6 +1241,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.2.0" + macros: + dependency: transitive + description: + name: macros + sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536" + url: "https://pub.dev" + source: hosted + version: "0.1.2-main.4" markdown: dependency: transitive description: @@ -1396,6 +1425,22 @@ packages: url: "https://pub.dev" source: hosted version: "2.3.0" + patrol: + dependency: "direct dev" + description: + name: patrol + sha256: "6ac5e239384282da389a6b78d68aa86d8760ad8d5481d665937c4eb586c0b3dc" + url: "https://pub.dev" + source: hosted + version: "3.11.0" + patrol_finders: + dependency: transitive + description: + name: patrol_finders + sha256: "6bf2c3093fbccd02f80f73fafc1bd021d76410cbab6e329be220b5e3bc58f072" + url: "https://pub.dev" + source: hosted + version: "2.1.2" permission_handler: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index a37bf3da81..0b5fcd7d37 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -152,11 +152,17 @@ dev_dependencies: mockito: ^5.4.4 auto_route_generator: ^9.0.0 build_runner: ^2.4.12 + analyzer: ^6.7.0 + golden_toolkit: ^0.15.0 +# Testing + alchemist: ^0.10.0 + patrol: ^3.11.0 + + # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec -dependency_overrides: # The following section is specific to Flutter. flutter: diff --git a/test/app_test.dart b/test/app_test.dart index b7ceedda36..7ce530d66a 100644 --- a/test/app_test.dart +++ b/test/app_test.dart @@ -1,30 +1,47 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:lantern/app.dart'; -import 'package:lantern/core/utils/common.dart'; - -void main() { - group( - 'Widget startup', - () { - testWidgets( - 'Check for everything being loaded on the root', - (WidgetTester tester) async { - print('Load the root widget without catcher'); - await tester.pumpWidget(LanternApp()); - print('Declare a variable of type [GlobalLoaderOverlay]'); - var globalLoaderOverlay = find.byType(GlobalLoaderOverlay); - print( - 'If the root was loaded successfully it should find [GlobalLoaderOverlay]', - ); - expect(globalLoaderOverlay, findsOneWidget); - print('Declare a variable of type [MaterialApp.router]'); - var appRouter = find.byType(MaterialApp); - print( - 'If the root was loaded successfully it should find [MaterialApp.router]', - ); - expect(appRouter, findsOneWidget); - }, - ); - }, - ); -} +// import 'package:lantern/common/ui/custom/internet_checker.dart'; +// import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; +// import 'package:lantern/features/home/home.dart'; +// import 'package:lantern/features/vpn/vpn_notifier.dart'; +// +// import 'utils/test_common.dart'; +// +// +// +// final emptyBuilder = (context, value, child) => Container(); +// +// void main() { +// +// // Mocks the providers also +// group( +// "Home widget test for mobile devices", +// () { +// testWidgets("home widget look okay", (widgetTester) async { +// when(mockSessionModel.acceptedTermsVersion((emptyBuilder))).thenAnswer( +// (invocation) { +// final builder = invocation.namedArguments[const Symbol('builder')] +// as ValueWidgetBuilder; +// return builder(mockBuildContext, 1, null); +// }, +// ); +// +// final myApp = MaterialApp( +// home: MultiProvider( +// providers: [ +// ChangeNotifierProvider( +// create: (context) => BottomBarChangeNotifier()), +// ChangeNotifierProvider(create: (context) => VPNChangeNotifier()), +// ChangeNotifierProvider( +// create: (context) => InternetStatusProvider()) +// ], +// child: HomePage(), +// ), +// ); +// +// await widgetTester.pumpWidget(myApp); +// // await widgetTester.pumpAndSettle(); +// +// expect(find.byType(BottomAppBar), findsOneWidget); +// }, variant: TargetPlatformVariant.only(TargetPlatform.android)); +// }, +// ); +// } diff --git a/test/base_screen_test.dart b/test/base_screen_test.dart index a66ca39f33..4b6788fcea 100644 --- a/test/base_screen_test.dart +++ b/test/base_screen_test.dart @@ -1,91 +1,91 @@ -import 'package:audioplayers/audioplayers.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:lantern/app.dart'; -import 'package:lantern/core/utils/common.dart'; -import 'package:lantern/features/home/home.dart'; -import 'package:lantern/features/messaging/messaging_model.dart'; -import 'package:mockito/mockito.dart'; - -class MockNavigationObserver extends Mock implements NavigatorObserver {} - -void main() { - final mockObserver = MockNavigationObserver(); - - setUp(() { - reset(mockObserver); - }); - - group( - 'Widget startup', - () { - Future _buildHomeScreen(WidgetTester tester) async { - await tester.pumpWidget( - MultiProvider( - providers: [ - Provider(create: (context) => MessagingModel()), - Provider(create: (context) => VpnModel()), - Provider(create: (context) => AudioPlayer()), - Provider(create: (context) => SessionModel()), - Provider( - create: (context) => EventManager('lantern_event_channel'), - ), - Provider( - create: (context) => - const MethodChannel('lantern_method_channel'), - ), - ], - child: FutureBuilder( - future: Localization.ensureInitialized(), - builder: (BuildContext context, AsyncSnapshot snapshot) { - return GlobalLoaderOverlay( - child: I18n( - initialLocale: const Locale('en', 'US'), - child: MaterialApp.router( - debugShowCheckedModeBanner: false, - title: 'Lantern Messenger', - localizationsDelegates: [ - GlobalMaterialLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - ], - routeInformationParser: globalRouter.defaultRouteParser(), - routerDelegate: globalRouter.delegate( - navigatorObservers: () => [ - mockObserver, - ], - ), - // TODO <08-08-22, kalli> Confirm we can use BotToast - //builder: BotToastInit(), - supportedLocales: [ - const Locale('ar', 'EG'), - const Locale('fr', 'FR'), - const Locale('en', 'US'), - const Locale('fa', 'IR'), - const Locale('th', 'TH'), - const Locale('ms', 'MY'), - const Locale('ru', 'RU'), - const Locale('ur', 'IN'), - const Locale('zh', 'CN'), - const Locale('zh', 'HK'), - const Locale('es', 'ES'), - const Locale('tr', 'TR'), - const Locale('vi', 'VN'), - const Locale('my', 'MM'), - ], - ), - ), - ); - }, - ), - ), - ); - await tester.pumpAndSettle(); - expect(find.byType(HomePage), findsOneWidget); - } - - testWidgets('Should display home page', (WidgetTester tester) async { - await _buildHomeScreen(tester); - }); - }, - ); -} +// import 'package:audioplayers/audioplayers.dart'; +// import 'package:flutter_test/flutter_test.dart'; +// import 'package:lantern/app_test.dart'; +// import 'package:lantern/core/utils/common.dart'; +// import 'package:lantern/features/home/home.dart'; +// import 'package:lantern/features/messaging/messaging_model.dart'; +// import 'package:mockito/mockito.dart'; +// +// class MockNavigationObserver extends Mock implements NavigatorObserver {} +// +// void main() { +// final mockObserver = MockNavigationObserver(); +// +// setUp(() { +// reset(mockObserver); +// }); +// +// group( +// 'Widget startup', +// () { +// Future _buildHomeScreen(WidgetTester tester) async { +// await tester.pumpWidget( +// MultiProvider( +// providers: [ +// Provider(create: (context) => MessagingModel()), +// Provider(create: (context) => VpnModel()), +// Provider(create: (context) => AudioPlayer()), +// Provider(create: (context) => SessionModel()), +// Provider( +// create: (context) => EventManager('lantern_event_channel'), +// ), +// Provider( +// create: (context) => +// const MethodChannel('lantern_method_channel'), +// ), +// ], +// child: FutureBuilder( +// future: Localization.ensureInitialized(), +// builder: (BuildContext context, AsyncSnapshot snapshot) { +// return GlobalLoaderOverlay( +// child: I18n( +// initialLocale: const Locale('en', 'US'), +// child: MaterialApp.router( +// debugShowCheckedModeBanner: false, +// title: 'Lantern Messenger', +// localizationsDelegates: [ +// GlobalMaterialLocalizations.delegate, +// GlobalWidgetsLocalizations.delegate, +// GlobalCupertinoLocalizations.delegate, +// ], +// routeInformationParser: globalRouter.defaultRouteParser(), +// routerDelegate: globalRouter.delegate( +// navigatorObservers: () => [ +// mockObserver, +// ], +// ), +// // TODO <08-08-22, kalli> Confirm we can use BotToast +// //builder: BotToastInit(), +// supportedLocales: [ +// const Locale('ar', 'EG'), +// const Locale('fr', 'FR'), +// const Locale('en', 'US'), +// const Locale('fa', 'IR'), +// const Locale('th', 'TH'), +// const Locale('ms', 'MY'), +// const Locale('ru', 'RU'), +// const Locale('ur', 'IN'), +// const Locale('zh', 'CN'), +// const Locale('zh', 'HK'), +// const Locale('es', 'ES'), +// const Locale('tr', 'TR'), +// const Locale('vi', 'VN'), +// const Locale('my', 'MM'), +// ], +// ), +// ), +// ); +// }, +// ), +// ), +// ); +// await tester.pumpAndSettle(); +// expect(find.byType(HomePage), findsOneWidget); +// } +// +// testWidgets('Should display home page', (WidgetTester tester) async { +// await _buildHomeScreen(tester); +// }); +// }, +// ); +// } diff --git a/test/features/home/home_test.dart b/test/features/home/home_test.dart new file mode 100644 index 0000000000..aade559528 --- /dev/null +++ b/test/features/home/home_test.dart @@ -0,0 +1,69 @@ +import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; +import 'package:lantern/features/home/home.dart'; + +import '../../utils/test_common.dart'; +import '../../utils/widgets.dart'; + +void main() { + late MockSessionModel mockSessionModel; + late MockBuildContext mockBuildContext; + + setUpAll( + () { + sl.registerLazySingleton(() => SessionModel()); + sl.registerLazySingleton(() => VpnModel()); + mockSessionModel = MockSessionModel(); + mockBuildContext = MockBuildContext(); + }, + ); + + tearDownAll( + () { + sl.reset(); + }, + ); + + group( + "Home widget render properly for mobile", + () { + testWidgets( + "Home widget started", + (widgetTester) async { + final homeWidget = MultiProvider(providers: [ + ChangeNotifierProvider( + create: (context) => BottomBarChangeNotifier()), + // ChangeNotifierProvider(create: (context) => VPNChangeNotifier()), + // ChangeNotifierProvider(create: (context) => InternetStatusProvider()) + ], child: wrapWithMaterialApp(const HomePage())); + + /// Now stub all daa widgets + + when(mockSessionModel.acceptedTermsVersion(intEmptyBuilder)) + .thenAnswer( + (invocation) { + final builder = invocation.namedArguments[const Symbol('builder')] + as ValueWidgetBuilder; + return builder(mockBuildContext, 1, null); + }, + ); + + when(mockSessionModel.developmentMode(boolEmptyBuilder)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, true, null); + }, + ); + + when(mockSessionModel.isAuthEnabled).thenAnswer( + (realInvocation) { + return ValueNotifier(false); + }, + ); + + + + widgetTester.pumpWidget(homeWidget); + }, + ); + }, + ); +} diff --git a/test/features/home/session_model_test.dart b/test/features/home/session_model_test.dart new file mode 100644 index 0000000000..c197d4ee92 --- /dev/null +++ b/test/features/home/session_model_test.dart @@ -0,0 +1,7 @@ + + +void main() { + + + +} \ No newline at end of file diff --git a/test/utils/test.mocks.dart b/test/utils/test.mocks.dart new file mode 100644 index 0000000000..bdbeb3ace2 --- /dev/null +++ b/test/utils/test.mocks.dart @@ -0,0 +1,8 @@ +import 'package:lantern/features/home/session_model.dart'; +import 'package:mockito/annotations.dart'; + +/// All generate mock should happened or add here +/// For generate mock run flutter pub run build_runner build --delete-conflicting-outputs +/// So for most other test cases we need import only one class +@GenerateNiceMocks([MockSpec()]) +void main() {} diff --git a/test/utils/test.mocks.mocks.dart b/test/utils/test.mocks.mocks.dart new file mode 100644 index 0000000000..aa29dcc854 --- /dev/null +++ b/test/utils/test.mocks.mocks.dart @@ -0,0 +1,2617 @@ +// Mocks generated by Mockito 5.4.4 from annotations +// in lantern/test/utils/test.mocks.dart. +// Do not manually edit this file. + +// ignore_for_file: no_leading_underscores_for_library_prefixes +import 'dart:async' as _i4; +import 'dart:ffi' as _i5; +import 'dart:typed_data' as _i8; + +import 'package:lantern/core/utils/common.dart' as _i2; +import 'package:lantern/core/utils/common_desktop.dart' as _i3; +import 'package:lantern/core/utils/utils.dart' as _i7; +import 'package:mockito/mockito.dart' as _i1; +import 'package:mockito/src/dummies.dart' as _i6; + +// ignore_for_file: type=lint +// ignore_for_file: avoid_redundant_argument_values +// ignore_for_file: avoid_setters_without_getters +// ignore_for_file: comment_references +// ignore_for_file: deprecated_member_use +// ignore_for_file: deprecated_member_use_from_same_package +// ignore_for_file: implementation_imports +// ignore_for_file: invalid_use_of_visible_for_testing_member +// ignore_for_file: prefer_const_constructors +// ignore_for_file: unnecessary_parenthesis +// ignore_for_file: camel_case_types +// ignore_for_file: subtype_of_sealed_class + +class _FakeEventManager_0 extends _i1.SmartFake implements _i2.EventManager { + _FakeEventManager_0( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeValueNotifier_1 extends _i1.SmartFake + implements _i2.ValueNotifier { + _FakeValueNotifier_1( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeFfiListNotifier_2 extends _i1.SmartFake + implements _i3.FfiListNotifier { + _FakeFfiListNotifier_2( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeMethodChannel_3 extends _i1.SmartFake implements _i2.MethodChannel { + _FakeMethodChannel_3( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeWidget_4 extends _i1.SmartFake implements _i2.Widget { + _FakeWidget_4( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + + @override + String toString({_i2.DiagnosticLevel? minLevel = _i2.DiagnosticLevel.info}) => + super.toString(); +} + +class _FakeFuture_5 extends _i1.SmartFake implements _i4.Future { + _FakeFuture_5( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeValueListenableBuilder_6 extends _i1.SmartFake + implements _i2.ValueListenableBuilder { + _FakeValueListenableBuilder_6( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); + + @override + String toString({_i2.DiagnosticLevel? minLevel = _i2.DiagnosticLevel.info}) => + super.toString(); +} + +/// A class which mocks [SessionModel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockSessionModel extends _i1.Mock implements _i2.SessionModel { + @override + _i2.EventManager get eventManager => (super.noSuchMethod( + Invocation.getter(#eventManager), + returnValue: _FakeEventManager_0( + this, + Invocation.getter(#eventManager), + ), + returnValueForMissingStub: _FakeEventManager_0( + this, + Invocation.getter(#eventManager), + ), + ) as _i2.EventManager); + + @override + set eventManager(_i2.EventManager? _eventManager) => super.noSuchMethod( + Invocation.setter( + #eventManager, + _eventManager, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get networkAvailable => (super.noSuchMethod( + Invocation.getter(#networkAvailable), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#networkAvailable), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#networkAvailable), + ), + ) as _i2.ValueNotifier); + + @override + set networkAvailable(_i2.ValueNotifier? _networkAvailable) => + super.noSuchMethod( + Invocation.setter( + #networkAvailable, + _networkAvailable, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get isTestPlayVersion => (super.noSuchMethod( + Invocation.getter(#isTestPlayVersion), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#isTestPlayVersion), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#isTestPlayVersion), + ), + ) as _i2.ValueNotifier); + + @override + set isTestPlayVersion(_i2.ValueNotifier? _isTestPlayVersion) => + super.noSuchMethod( + Invocation.setter( + #isTestPlayVersion, + _isTestPlayVersion, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get isStoreVersion => (super.noSuchMethod( + Invocation.getter(#isStoreVersion), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#isStoreVersion), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#isStoreVersion), + ), + ) as _i2.ValueNotifier); + + @override + set isStoreVersion(_i2.ValueNotifier? _isStoreVersion) => + super.noSuchMethod( + Invocation.setter( + #isStoreVersion, + _isStoreVersion, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get proxyAvailable => (super.noSuchMethod( + Invocation.getter(#proxyAvailable), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#proxyAvailable), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#proxyAvailable), + ), + ) as _i2.ValueNotifier); + + @override + set proxyAvailable(_i2.ValueNotifier? _proxyAvailable) => + super.noSuchMethod( + Invocation.setter( + #proxyAvailable, + _proxyAvailable, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get proUserNotifier => (super.noSuchMethod( + Invocation.getter(#proUserNotifier), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#proUserNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#proUserNotifier), + ), + ) as _i2.ValueNotifier); + + @override + set proUserNotifier(_i2.ValueNotifier? _proUserNotifier) => + super.noSuchMethod( + Invocation.setter( + #proUserNotifier, + _proUserNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier<_i3.ConfigOptions?> get configNotifier => + (super.noSuchMethod( + Invocation.getter(#configNotifier), + returnValue: _FakeValueNotifier_1<_i3.ConfigOptions?>( + this, + Invocation.getter(#configNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1<_i3.ConfigOptions?>( + this, + Invocation.getter(#configNotifier), + ), + ) as _i2.ValueNotifier<_i3.ConfigOptions?>); + + @override + set configNotifier(_i2.ValueNotifier<_i3.ConfigOptions?>? _configNotifier) => + super.noSuchMethod( + Invocation.setter( + #configNotifier, + _configNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get country => (super.noSuchMethod( + Invocation.getter(#country), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#country), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#country), + ), + ) as _i2.ValueNotifier); + + @override + set country(_i2.ValueNotifier? _country) => super.noSuchMethod( + Invocation.setter( + #country, + _country, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get referralNotifier => (super.noSuchMethod( + Invocation.getter(#referralNotifier), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#referralNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#referralNotifier), + ), + ) as _i2.ValueNotifier); + + @override + set referralNotifier(_i2.ValueNotifier? _referralNotifier) => + super.noSuchMethod( + Invocation.setter( + #referralNotifier, + _referralNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get deviceIdNotifier => (super.noSuchMethod( + Invocation.getter(#deviceIdNotifier), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#deviceIdNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#deviceIdNotifier), + ), + ) as _i2.ValueNotifier); + + @override + set deviceIdNotifier(_i2.ValueNotifier? _deviceIdNotifier) => + super.noSuchMethod( + Invocation.setter( + #deviceIdNotifier, + _deviceIdNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get langNotifier => (super.noSuchMethod( + Invocation.getter(#langNotifier), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#langNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#langNotifier), + ), + ) as _i2.ValueNotifier); + + @override + set langNotifier(_i2.ValueNotifier? _langNotifier) => + super.noSuchMethod( + Invocation.setter( + #langNotifier, + _langNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get proxyAllNotifier => (super.noSuchMethod( + Invocation.getter(#proxyAllNotifier), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#proxyAllNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#proxyAllNotifier), + ), + ) as _i2.ValueNotifier); + + @override + set proxyAllNotifier(_i2.ValueNotifier? _proxyAllNotifier) => + super.noSuchMethod( + Invocation.setter( + #proxyAllNotifier, + _proxyAllNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier<_i2.ServerInfo?> get serverInfoNotifier => + (super.noSuchMethod( + Invocation.getter(#serverInfoNotifier), + returnValue: _FakeValueNotifier_1<_i2.ServerInfo?>( + this, + Invocation.getter(#serverInfoNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1<_i2.ServerInfo?>( + this, + Invocation.getter(#serverInfoNotifier), + ), + ) as _i2.ValueNotifier<_i2.ServerInfo?>); + + @override + set serverInfoNotifier( + _i2.ValueNotifier<_i2.ServerInfo?>? _serverInfoNotifier) => + super.noSuchMethod( + Invocation.setter( + #serverInfoNotifier, + _serverInfoNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get userEmail => (super.noSuchMethod( + Invocation.getter(#userEmail), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#userEmail), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#userEmail), + ), + ) as _i2.ValueNotifier); + + @override + set userEmail(_i2.ValueNotifier? _userEmail) => super.noSuchMethod( + Invocation.setter( + #userEmail, + _userEmail, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get linkingCodeNotifier => (super.noSuchMethod( + Invocation.getter(#linkingCodeNotifier), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#linkingCodeNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#linkingCodeNotifier), + ), + ) as _i2.ValueNotifier); + + @override + set linkingCodeNotifier(_i2.ValueNotifier? _linkingCodeNotifier) => + super.noSuchMethod( + Invocation.setter( + #linkingCodeNotifier, + _linkingCodeNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier<_i2.Devices?> get devicesNotifier => (super.noSuchMethod( + Invocation.getter(#devicesNotifier), + returnValue: _FakeValueNotifier_1<_i2.Devices?>( + this, + Invocation.getter(#devicesNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1<_i2.Devices?>( + this, + Invocation.getter(#devicesNotifier), + ), + ) as _i2.ValueNotifier<_i2.Devices?>); + + @override + set devicesNotifier(_i2.ValueNotifier<_i2.Devices?>? _devicesNotifier) => + super.noSuchMethod( + Invocation.setter( + #devicesNotifier, + _devicesNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get hasUserSignedInNotifier => (super.noSuchMethod( + Invocation.getter(#hasUserSignedInNotifier), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#hasUserSignedInNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#hasUserSignedInNotifier), + ), + ) as _i2.ValueNotifier); + + @override + set hasUserSignedInNotifier( + _i2.ValueNotifier? _hasUserSignedInNotifier) => + super.noSuchMethod( + Invocation.setter( + #hasUserSignedInNotifier, + _hasUserSignedInNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get isAuthEnabled => (super.noSuchMethod( + Invocation.getter(#isAuthEnabled), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#isAuthEnabled), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#isAuthEnabled), + ), + ) as _i2.ValueNotifier); + + @override + set isAuthEnabled(_i2.ValueNotifier? _isAuthEnabled) => + super.noSuchMethod( + Invocation.setter( + #isAuthEnabled, + _isAuthEnabled, + ), + returnValueForMissingStub: null, + ); + + @override + _i3.FfiListNotifier<_i2.Plan> get plansNotifier => (super.noSuchMethod( + Invocation.getter(#plansNotifier), + returnValue: _FakeFfiListNotifier_2<_i2.Plan>( + this, + Invocation.getter(#plansNotifier), + ), + returnValueForMissingStub: _FakeFfiListNotifier_2<_i2.Plan>( + this, + Invocation.getter(#plansNotifier), + ), + ) as _i3.FfiListNotifier<_i2.Plan>); + + @override + set plansNotifier(_i3.FfiListNotifier<_i2.Plan>? _plansNotifier) => + super.noSuchMethod( + Invocation.setter( + #plansNotifier, + _plansNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i3.FfiListNotifier<_i2.PaymentMethod> get paymentMethodsNotifier => + (super.noSuchMethod( + Invocation.getter(#paymentMethodsNotifier), + returnValue: _FakeFfiListNotifier_2<_i2.PaymentMethod>( + this, + Invocation.getter(#paymentMethodsNotifier), + ), + returnValueForMissingStub: _FakeFfiListNotifier_2<_i2.PaymentMethod>( + this, + Invocation.getter(#paymentMethodsNotifier), + ), + ) as _i3.FfiListNotifier<_i2.PaymentMethod>); + + @override + set paymentMethodsNotifier( + _i3.FfiListNotifier<_i2.PaymentMethod>? _paymentMethodsNotifier) => + super.noSuchMethod( + Invocation.setter( + #paymentMethodsNotifier, + _paymentMethodsNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier<_i2.Bandwidth?> get bandwidthNotifier => + (super.noSuchMethod( + Invocation.getter(#bandwidthNotifier), + returnValue: _FakeValueNotifier_1<_i2.Bandwidth?>( + this, + Invocation.getter(#bandwidthNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1<_i2.Bandwidth?>( + this, + Invocation.getter(#bandwidthNotifier), + ), + ) as _i2.ValueNotifier<_i2.Bandwidth?>); + + @override + set bandwidthNotifier( + _i2.ValueNotifier<_i2.Bandwidth?>? _bandwidthNotifier) => + super.noSuchMethod( + Invocation.setter( + #bandwidthNotifier, + _bandwidthNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.MethodChannel get methodChannel => (super.noSuchMethod( + Invocation.getter(#methodChannel), + returnValue: _FakeMethodChannel_3( + this, + Invocation.getter(#methodChannel), + ), + returnValueForMissingStub: _FakeMethodChannel_3( + this, + Invocation.getter(#methodChannel), + ), + ) as _i2.MethodChannel); + + @override + set methodChannel(_i2.MethodChannel? _methodChannel) => super.noSuchMethod( + Invocation.setter( + #methodChannel, + _methodChannel, + ), + returnValueForMissingStub: null, + ); + + @override + set event(_i2.Event? _event) => super.noSuchMethod( + Invocation.setter( + #event, + _event, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier pathValueNotifier( + String? path, + T? defaultValue, + ) => + (super.noSuchMethod( + Invocation.method( + #pathValueNotifier, + [ + path, + defaultValue, + ], + ), + returnValue: _FakeValueNotifier_1( + this, + Invocation.method( + #pathValueNotifier, + [ + path, + defaultValue, + ], + ), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.method( + #pathValueNotifier, + [ + path, + defaultValue, + ], + ), + ), + ) as _i2.ValueNotifier); + + @override + _i4.Future updateUserDetails() => (super.noSuchMethod( + Invocation.method( + #updateUserDetails, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget configValueBuilder( + String? path, + _i2.ValueWidgetBuilder? builder, + T Function(_i3.ConfigOptions?)? onConfigUpdate, + ) => + (super.noSuchMethod( + Invocation.method( + #configValueBuilder, + [ + path, + builder, + onConfigUpdate, + ], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #configValueBuilder, + [ + path, + builder, + onConfigUpdate, + ], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #configValueBuilder, + [ + path, + builder, + onConfigUpdate, + ], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget proUser(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #proUser, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #proUser, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #proUser, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget bandwidth(_i2.ValueWidgetBuilder<_i2.Bandwidth>? builder) => + (super.noSuchMethod( + Invocation.method( + #bandwidth, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #bandwidth, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #bandwidth, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget developmentMode(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #developmentMode, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #developmentMode, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #developmentMode, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget paymentTestMode(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #paymentTestMode, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #paymentTestMode, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #paymentTestMode, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future setPaymentTestMode(bool? on) => (super.noSuchMethod( + Invocation.method( + #setPaymentTestMode, + [on], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future acceptTerms() => (super.noSuchMethod( + Invocation.method( + #acceptTerms, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget acceptedTermsVersion(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #acceptedTermsVersion, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #acceptedTermsVersion, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #acceptedTermsVersion, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget forceCountry(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #forceCountry, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #forceCountry, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #forceCountry, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future setForceCountry(String? countryCode) => (super.noSuchMethod( + Invocation.method( + #setForceCountry, + [countryCode], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget geoCountryCode(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #geoCountryCode, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #geoCountryCode, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #geoCountryCode, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget storeVersion(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #storeVersion, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #storeVersion, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #storeVersion, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget testPlayVersion(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #testPlayVersion, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #testPlayVersion, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #testPlayVersion, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future setTestPlayVersion(bool? on) => (super.noSuchMethod( + Invocation.method( + #setTestPlayVersion, + [on], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget language(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #language, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #language, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #language, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget emailAddress(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #emailAddress, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #emailAddress, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #emailAddress, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget expiryDate(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #expiryDate, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #expiryDate, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #expiryDate, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget referralCode(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #referralCode, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #referralCode, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #referralCode, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget deviceId(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #deviceId, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #deviceId, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #deviceId, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget devices(_i2.ValueWidgetBuilder<_i2.Devices>? builder) => + (super.noSuchMethod( + Invocation.method( + #devices, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #devices, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #devices, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future testProviderRequest( + String? email, + String? paymentProvider, + String? planId, + ) => + (super.noSuchMethod( + Invocation.method( + #testProviderRequest, + [ + email, + paymentProvider, + planId, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i5.Pointer<_i3.Utf8> ffiIsUserLoggedIn() => (super.noSuchMethod( + Invocation.method( + #ffiIsUserLoggedIn, + [], + ), + returnValue: _i6.dummyValue<_i5.Pointer<_i3.Utf8>>( + this, + Invocation.method( + #ffiIsUserLoggedIn, + [], + ), + ), + returnValueForMissingStub: _i6.dummyValue<_i5.Pointer<_i3.Utf8>>( + this, + Invocation.method( + #ffiIsUserLoggedIn, + [], + ), + ), + ) as _i5.Pointer<_i3.Utf8>); + + @override + _i2.Widget isUserSignedIn(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #isUserSignedIn, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #isUserSignedIn, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #isUserSignedIn, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future signUp( + String? email, + String? password, + ) => + (super.noSuchMethod( + Invocation.method( + #signUp, + [ + email, + password, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future signUpEmailResendCode(String? email) => (super.noSuchMethod( + Invocation.method( + #signUpEmailResendCode, + [email], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future signupEmailConfirmation( + String? email, + String? code, + ) => + (super.noSuchMethod( + Invocation.method( + #signupEmailConfirmation, + [ + email, + code, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future login( + String? email, + String? password, + ) => + (super.noSuchMethod( + Invocation.method( + #login, + [ + email, + password, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future startRecoveryByEmail(String? email) => (super.noSuchMethod( + Invocation.method( + #startRecoveryByEmail, + [email], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future completeRecoveryByEmail( + String? email, + String? password, + String? code, + ) => + (super.noSuchMethod( + Invocation.method( + #completeRecoveryByEmail, + [ + email, + password, + code, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future validateRecoveryCode( + String? email, + String? code, + ) => + (super.noSuchMethod( + Invocation.method( + #validateRecoveryCode, + [ + email, + code, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future startChangeEmail( + String? email, + String? newEmail, + String? password, + ) => + (super.noSuchMethod( + Invocation.method( + #startChangeEmail, + [ + email, + newEmail, + password, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future completeChangeEmail( + String? email, + String? newEmail, + String? password, + String? code, + ) => + (super.noSuchMethod( + Invocation.method( + #completeChangeEmail, + [ + email, + newEmail, + password, + code, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future signOut() => (super.noSuchMethod( + Invocation.method( + #signOut, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future deleteAccount(String? password) => (super.noSuchMethod( + Invocation.method( + #deleteAccount, + [password], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future isUserFirstTimeVisit() => (super.noSuchMethod( + Invocation.method( + #isUserFirstTimeVisit, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future setFirstTimeVisit() => (super.noSuchMethod( + Invocation.method( + #setFirstTimeVisit, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setProxyAll(bool? isOn) => (super.noSuchMethod( + Invocation.method( + #setProxyAll, + [isOn], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future proxyAddr() => (super.noSuchMethod( + Invocation.method( + #proxyAddr, + [], + ), + returnValue: _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #proxyAddr, + [], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #proxyAddr, + [], + ), + )), + ) as _i4.Future); + + @override + _i4.Future getCountryCode() => (super.noSuchMethod( + Invocation.method( + #getCountryCode, + [], + ), + returnValue: _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getCountryCode, + [], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getCountryCode, + [], + ), + )), + ) as _i4.Future); + + @override + _i4.Future setLanguage(String? lang) => (super.noSuchMethod( + Invocation.method( + #setLanguage, + [lang], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future userEmailRequest(String? email) => (super.noSuchMethod( + Invocation.method( + #userEmailRequest, + [email], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future authorizeViaEmail(String? emailAddress) => + (super.noSuchMethod( + Invocation.method( + #authorizeViaEmail, + [emailAddress], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future validateDeviceRecoveryCode( + String? code, + String? email, + ) => + (super.noSuchMethod( + Invocation.method( + #validateDeviceRecoveryCode, + [ + code, + email, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future approveDevice(String? code) => (super.noSuchMethod( + Invocation.method( + #approveDevice, + [code], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future removeDevice(String? deviceId) => (super.noSuchMethod( + Invocation.method( + #removeDevice, + [deviceId], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future resendRecoveryCode() => (super.noSuchMethod( + Invocation.method( + #resendRecoveryCode, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + void setSelectedTab( + _i2.BuildContext? context, + String? tab, + ) => + super.noSuchMethod( + Invocation.method( + #setSelectedTab, + [ + context, + tab, + ], + ), + returnValueForMissingStub: null, + ); + + @override + _i2.Widget shouldShowAds(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #shouldShowAds, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #shouldShowAds, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #shouldShowAds, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget replicaAddr(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #replicaAddr, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #replicaAddr, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #replicaAddr, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget countryCode(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #countryCode, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #countryCode, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #countryCode, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future getReplicaAddr() => (super.noSuchMethod( + Invocation.method( + #getReplicaAddr, + [], + ), + returnValue: _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getReplicaAddr, + [], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #getReplicaAddr, + [], + ), + )), + ) as _i4.Future); + + @override + _i2.Widget chatEnabled(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #chatEnabled, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #chatEnabled, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #chatEnabled, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget sdkVersion(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #sdkVersion, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #sdkVersion, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #sdkVersion, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future getChatEnabled() => (super.noSuchMethod( + Invocation.method( + #getChatEnabled, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future checkForUpdates() => (super.noSuchMethod( + Invocation.method( + #checkForUpdates, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future restoreAccount( + String? email, + String? code, + ) => + (super.noSuchMethod( + Invocation.method( + #restoreAccount, + [ + email, + code, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future updatePaymentPlans() => (super.noSuchMethod( + Invocation.method( + #updatePaymentPlans, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future hasUpdatePlansOrBuy() => (super.noSuchMethod( + Invocation.method( + #hasUpdatePlansOrBuy, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i2.Widget plans( + {required _i2 + .ValueWidgetBuilder>>? + builder}) => + (super.noSuchMethod( + Invocation.method( + #plans, + [], + {#builder: builder}, + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #plans, + [], + {#builder: builder}, + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #plans, + [], + {#builder: builder}, + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget paymentMethods( + {required _i2.ValueWidgetBuilder< + Iterable<_i2.PathAndValue<_i2.PaymentMethod>>>? + builder}) => + (super.noSuchMethod( + Invocation.method( + #paymentMethods, + [], + {#builder: builder}, + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #paymentMethods, + [], + {#builder: builder}, + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #paymentMethods, + [], + {#builder: builder}, + ), + ), + ) as _i2.Widget); + + @override + _i4.Future applyRefCode(String? refCode) => (super.noSuchMethod( + Invocation.method( + #applyRefCode, + [refCode], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future reportIssue( + String? email, + String? issue, + String? description, + ) => + (super.noSuchMethod( + Invocation.method( + #reportIssue, + [ + email, + issue, + description, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget getUserId(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #getUserId, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #getUserId, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #getUserId, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget userStatus(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #userStatus, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #userStatus, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #userStatus, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget serverInfo(_i2.ValueWidgetBuilder<_i2.ServerInfo?>? builder) => + (super.noSuchMethod( + Invocation.method( + #serverInfo, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #serverInfo, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #serverInfo, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future trackUserAction( + String? name, + String? url, [ + String? title = r'', + ]) => + (super.noSuchMethod( + Invocation.method( + #trackUserAction, + [ + name, + url, + title, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future requestLinkCode() => (super.noSuchMethod( + Invocation.method( + #requestLinkCode, + [], + ), + returnValue: _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #requestLinkCode, + [], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #requestLinkCode, + [], + ), + )), + ) as _i4.Future); + + @override + _i4.Future redeemLinkCode() => (super.noSuchMethod( + Invocation.method( + #redeemLinkCode, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget deviceLinkingCode(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #deviceLinkingCode, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #deviceLinkingCode, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #deviceLinkingCode, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future redeemResellerCode( + String? email, + String? currency, + String? deviceName, + String? resellerCode, + ) => + (super.noSuchMethod( + Invocation.method( + #redeemResellerCode, + [ + email, + currency, + deviceName, + resellerCode, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future submitBitcoinPayment( + String? planID, + String? email, + ) => + (super.noSuchMethod( + Invocation.method( + #submitBitcoinPayment, + [ + planID, + email, + ], + ), + returnValue: _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #submitBitcoinPayment, + [ + planID, + email, + ], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #submitBitcoinPayment, + [ + planID, + email, + ], + ), + )), + ) as _i4.Future); + + @override + _i4.Future generatePaymentRedirectUrl({ + required String? planID, + required String? email, + required _i7.Providers? paymentProvider, + }) => + (super.noSuchMethod( + Invocation.method( + #generatePaymentRedirectUrl, + [], + { + #planID: planID, + #email: email, + #paymentProvider: paymentProvider, + }, + ), + returnValue: _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #generatePaymentRedirectUrl, + [], + { + #planID: planID, + #email: email, + #paymentProvider: paymentProvider, + }, + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #generatePaymentRedirectUrl, + [], + { + #planID: planID, + #email: email, + #paymentProvider: paymentProvider, + }, + ), + )), + ) as _i4.Future); + + @override + _i4.Future isGooglePlayServiceAvailable() => (super.noSuchMethod( + Invocation.method( + #isGooglePlayServiceAvailable, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future submitPlayPayment( + String? planID, + String? email, + ) => + (super.noSuchMethod( + Invocation.method( + #submitPlayPayment, + [ + planID, + email, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future paymentRedirectForDesktop( + _i2.BuildContext? context, + String? planID, + String? email, + _i7.Providers? provider, + ) => + (super.noSuchMethod( + Invocation.method( + #paymentRedirectForDesktop, + [ + context, + planID, + email, + provider, + ], + ), + returnValue: _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #paymentRedirectForDesktop, + [ + context, + planID, + email, + provider, + ], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i6.dummyValue( + this, + Invocation.method( + #paymentRedirectForDesktop, + [ + context, + planID, + email, + provider, + ], + ), + )), + ) as _i4.Future); + + @override + _i4.Future submitApplePlay( + String? email, + String? planID, + String? purchaseToken, + ) => + (super.noSuchMethod( + Invocation.method( + #submitApplePlay, + [ + email, + planID, + purchaseToken, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future submitStripePayment( + String? planID, + String? email, + String? cardNumber, + String? expDate, + String? cvc, + ) => + (super.noSuchMethod( + Invocation.method( + #submitStripePayment, + [ + planID, + email, + cardNumber, + expDate, + cvc, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future submitFreekassa( + String? email, + String? planID, + String? currencyPrice, + ) => + (super.noSuchMethod( + Invocation.method( + #submitFreekassa, + [ + email, + planID, + currencyPrice, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future checkEmailExists(String? email) => (super.noSuchMethod( + Invocation.method( + #checkEmailExists, + [email], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future refreshAppsList() => (super.noSuchMethod( + Invocation.method( + #refreshAppsList, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget splitTunneling(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #splitTunneling, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #splitTunneling, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #splitTunneling, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget proxyAll(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #proxyAll, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #proxyAll, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #proxyAll, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future setSplitTunneling(bool? on) => (super.noSuchMethod( + Invocation.method( + #setSplitTunneling, + [on], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget appsData( + {required _i2 + .ValueWidgetBuilder>>? + builder}) => + (super.noSuchMethod( + Invocation.method( + #appsData, + [], + {#builder: builder}, + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #appsData, + [], + {#builder: builder}, + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #appsData, + [], + {#builder: builder}, + ), + ), + ) as _i2.Widget); + + @override + _i4.Future allowAppAccess(String? packageName) => (super.noSuchMethod( + Invocation.method( + #allowAppAccess, + [packageName], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future denyAppAccess(String? packageName) => (super.noSuchMethod( + Invocation.method( + #denyAppAccess, + [packageName], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future enableScreenShot() => (super.noSuchMethod( + Invocation.method( + #enableScreenShot, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future disableScreenShot() => (super.noSuchMethod( + Invocation.method( + #disableScreenShot, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future get(String? path) => (super.noSuchMethod( + Invocation.method( + #get, + [path], + ), + returnValue: _i6.ifNotNull( + _i6.dummyValueOrNull( + this, + Invocation.method( + #get, + [path], + ), + ), + (T v) => _i4.Future.value(v), + ) ?? + _FakeFuture_5( + this, + Invocation.method( + #get, + [path], + ), + ), + returnValueForMissingStub: _i6.ifNotNull( + _i6.dummyValueOrNull( + this, + Invocation.method( + #get, + [path], + ), + ), + (T v) => _i4.Future.value(v), + ) ?? + _FakeFuture_5( + this, + Invocation.method( + #get, + [path], + ), + ), + ) as _i4.Future); + + @override + _i4.Future> list( + String? path, { + int? start = 0, + int? count = 34, + String? fullTextSearch, + bool? reverseSort = false, + T Function(_i8.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #list, + [path], + { + #start: start, + #count: count, + #fullTextSearch: fullTextSearch, + #reverseSort: reverseSort, + #deserialize: deserialize, + }, + ), + returnValue: _i4.Future>.value([]), + returnValueForMissingStub: _i4.Future>.value([]), + ) as _i4.Future>); + + @override + _i2.ValueListenableBuilder subscribedSingleValueBuilder( + String? path, { + T? defaultValue, + required _i2.ValueWidgetBuilder? builder, + bool? details = false, + T Function(_i8.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueListenableBuilder); + + @override + _i2.ValueNotifier singleValueNotifier( + String? path, + T? defaultValue, { + bool? details = false, + T Function(_i8.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueNotifier_1( + this, + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueNotifier); + + @override + _i2.ValueListenableBuilder<_i2.ChangeTrackingList> + subscribedListBuilder( + String? path, { + required _i2.ValueWidgetBuilder>>? builder, + bool? details = false, + int Function( + String, + String, + )? compare, + T Function(_i8.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + returnValue: + _FakeValueListenableBuilder_6<_i2.ChangeTrackingList>( + this, + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: + _FakeValueListenableBuilder_6<_i2.ChangeTrackingList>( + this, + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueListenableBuilder<_i2.ChangeTrackingList>); + + @override + _i2.ValueNotifier<_i2.ChangeTrackingList> listNotifier( + String? path, { + bool? details = false, + int Function( + String, + String, + )? compare, + T Function(_i8.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueNotifier_1<_i2.ChangeTrackingList>( + this, + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: + _FakeValueNotifier_1<_i2.ChangeTrackingList>( + this, + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueNotifier<_i2.ChangeTrackingList>); + + @override + _i2.ValueListenableBuilder listChildBuilder( + _i2.BuildContext? context, + String? path, { + required T? defaultValue, + required _i2.ValueWidgetBuilder? builder, + }) => + (super.noSuchMethod( + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + returnValue: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + ), + returnValueForMissingStub: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + ), + ) as _i2.ValueListenableBuilder); +} diff --git a/test/utils/test_common.dart b/test/utils/test_common.dart new file mode 100644 index 0000000000..632b2dc73c --- /dev/null +++ b/test/utils/test_common.dart @@ -0,0 +1,25 @@ + +import 'package:flutter/material.dart'; +import 'package:mockito/mockito.dart'; + +export 'package:lantern/core/utils/common.dart' hide Verification; +export 'test.mocks.mocks.dart'; +export 'package:mockito/mockito.dart'; +export 'package:flutter_test/flutter_test.dart'; + + +class MockBuildContext extends Mock implements BuildContext {} + + + + + +///Empty builder we can reuse across the test +ValueWidgetBuilder intEmptyBuilder = (context, value, child) => const SizedBox(); +ValueWidgetBuilder doubleEmptyBuilder = (context, value, child) => const SizedBox(); +ValueWidgetBuilder boolEmptyBuilder = (context, value, child) => const SizedBox(); + + + + + diff --git a/test/utils/widgets.dart b/test/utils/widgets.dart new file mode 100644 index 0000000000..f433e90204 --- /dev/null +++ b/test/utils/widgets.dart @@ -0,0 +1,33 @@ +import 'test_common.dart'; + + + + +Widget wrapWithMaterialApp(Widget widget) { + return MaterialApp( + title: 'app_name'.i18n, + home: widget, + localizationsDelegates: const [ + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [ + Locale('ar', 'EG'), + Locale('fr', 'FR'), + Locale('en', 'US'), + Locale('fa', 'IR'), + Locale('th', 'TH'), + Locale('ms', 'MY'), + Locale('ru', 'RU'), + Locale('ur', 'IN'), + Locale('zh', 'CN'), + Locale('zh', 'HK'), + Locale('es', 'ES'), + Locale('es', 'CU'), + Locale('tr', 'TR'), + Locale('vi', 'VN'), + Locale('my', 'MM'), + ], + ); +} From bfe7a284f61dccbc6752448a9eeab81dbb83550a Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Mon, 7 Oct 2024 16:17:19 +0530 Subject: [PATCH 002/163] Implement dependency injection for model setup --- lib/core/service/injection_container.dart | 6 +- lib/features/home/home.dart | 6 +- lib/features/home/session_model.dart | 2 +- lib/features/messaging/messaging_model.dart | 2 +- .../replica/models/replica_model.dart | 2 +- lib/main.dart | 2 +- test/features/home/home_test.dart | 73 +- test/utils/test.mocks.dart | 11 +- test/utils/test.mocks.mocks.dart | 2730 ++++++++++++++++- test/utils/test_common.dart | 2 + 10 files changed, 2778 insertions(+), 58 deletions(-) diff --git a/lib/core/service/injection_container.dart b/lib/core/service/injection_container.dart index 874593e07c..fc55b7fe90 100644 --- a/lib/core/service/injection_container.dart +++ b/lib/core/service/injection_container.dart @@ -1,10 +1,12 @@ import 'package:get_it/get_it.dart'; import 'package:lantern/core/service/app_purchase.dart'; import 'package:lantern/core/utils/common.dart'; +import 'package:lantern/features/messaging/messaging_model.dart'; +import 'package:lantern/features/replica/models/replica_model.dart'; final GetIt sl = GetIt.instance; -void init() { +void initServices() { //Inject if (isMobile()) { @@ -12,5 +14,7 @@ void init() { sl().init(); } sl.registerLazySingleton(() => SessionModel()); + sl.registerLazySingleton(() => MessagingModel()); + sl.registerLazySingleton(() => ReplicaModel()); sl.registerLazySingleton(() => VpnModel()); } diff --git a/lib/features/home/home.dart b/lib/features/home/home.dart index 5edb82bf89..b49e53bd38 100644 --- a/lib/features/home/home.dart +++ b/lib/features/home/home.dart @@ -35,7 +35,7 @@ class _HomePageState extends State with TrayListener, WindowListener { @override void initState() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - _startupSequence(); + // _startupSequence(); }); super.initState(); } @@ -270,14 +270,14 @@ class _HomePageState extends State with TrayListener, WindowListener { print("accepted terms version $version"); return sessionModel.developmentMode( (BuildContext context, bool developmentMode, Widget? child) { + print("Development mode $developmentMode"); if (developmentMode) { Logger.level = Level.trace; } else { Logger.level = Level.error; } - bool isPlayVersion = - (sessionModel.isTestPlayVersion.value ?? false); + bool isPlayVersion = (sessionModel.isTestPlayVersion.value ?? false); bool isStoreVersion = (sessionModel.isStoreVersion.value ?? false); if ((isStoreVersion || isPlayVersion) && version == 0) { diff --git a/lib/features/home/session_model.dart b/lib/features/home/session_model.dart index 4f10ffd90d..3bead4c8d2 100644 --- a/lib/features/home/session_model.dart +++ b/lib/features/home/session_model.dart @@ -173,7 +173,7 @@ class SessionModel extends Model { Widget acceptedTermsVersion(ValueWidgetBuilder builder) { if (isMobile()) { return subscribedSingleValueBuilder('accepted_terms_version', - builder: builder, defaultValue: 0); + builder: builder); } return configValueBuilder('accepted_terms_version', builder, (value) => value?.chat.acceptedTermsVersion ?? 0); diff --git a/lib/features/messaging/messaging_model.dart b/lib/features/messaging/messaging_model.dart index d247471890..cc3e6c4ca3 100644 --- a/lib/features/messaging/messaging_model.dart +++ b/lib/features/messaging/messaging_model.dart @@ -3,7 +3,7 @@ import 'package:lantern/core/utils/common_desktop.dart'; import 'messaging.dart'; -final messagingModel = MessagingModel(); +final messagingModel = sl(); class MessagingModel extends Model { late LRUCache _thumbnailCache; diff --git a/lib/features/replica/models/replica_model.dart b/lib/features/replica/models/replica_model.dart index ffd8deb947..c8aff4e982 100644 --- a/lib/features/replica/models/replica_model.dart +++ b/lib/features/replica/models/replica_model.dart @@ -1,7 +1,7 @@ import 'package:lantern/core/utils/common.dart'; import 'package:lantern/features/replica/common.dart'; -final replicaModel = ReplicaModel(); +final replicaModel = sl(); class ReplicaModel extends Model { ReplicaModel() : super('replica'); diff --git a/lib/main.dart b/lib/main.dart index ecb5128fd7..e57939e5b3 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -28,7 +28,7 @@ Future main() async { } // Inject all the services - init(); + initServices(); if (isDesktop()) { LanternFFI.startDesktopService(); diff --git a/test/features/home/home_test.dart b/test/features/home/home_test.dart index aade559528..c3150e39e4 100644 --- a/test/features/home/home_test.dart +++ b/test/features/home/home_test.dart @@ -1,19 +1,28 @@ +import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; import 'package:lantern/features/home/home.dart'; +import 'package:lantern/features/messaging/messaging_model.dart'; +import 'package:lantern/features/vpn/vpn_notifier.dart'; import '../../utils/test_common.dart'; import '../../utils/widgets.dart'; +// https://dev.to/mjablecnik/take-screenshot-during-flutter-integration-tests-435k void main() { late MockSessionModel mockSessionModel; late MockBuildContext mockBuildContext; + late MockMessagingModel mockMessagingModel; setUpAll( () { - sl.registerLazySingleton(() => SessionModel()); - sl.registerLazySingleton(() => VpnModel()); mockSessionModel = MockSessionModel(); mockBuildContext = MockBuildContext(); + mockMessagingModel = MockMessagingModel(); + + sl.registerLazySingleton(() => mockSessionModel); + sl.registerLazySingleton(() => mockMessagingModel); + + sl.registerLazySingleton(() => VpnModel()); }, ); @@ -27,42 +36,76 @@ void main() { "Home widget render properly for mobile", () { testWidgets( - "Home widget started", + "Home widget started ", (widgetTester) async { final homeWidget = MultiProvider(providers: [ ChangeNotifierProvider( create: (context) => BottomBarChangeNotifier()), - // ChangeNotifierProvider(create: (context) => VPNChangeNotifier()), - // ChangeNotifierProvider(create: (context) => InternetStatusProvider()) + ChangeNotifierProvider(create: (context) => VPNChangeNotifier()), + ChangeNotifierProvider( + create: (context) => InternetStatusProvider()) ], child: wrapWithMaterialApp(const HomePage())); /// Now stub all daa widgets - when(mockSessionModel.acceptedTermsVersion(intEmptyBuilder)) - .thenAnswer( + // / Use Mockito's `any` matcher to match the argument passed to `acceptedTermsVersion` + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder( + mockBuildContext, 0, null); + }); + + when(mockSessionModel.developmentMode(any)).thenAnswer( (invocation) { - final builder = invocation.namedArguments[const Symbol('builder')] - as ValueWidgetBuilder; - return builder(mockBuildContext, 1, null); + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder( + mockBuildContext, true, null); }, ); - when(mockSessionModel.developmentMode(boolEmptyBuilder)).thenAnswer( + + + when(mockSessionModel.isTestPlayVersion).thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion).thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isAuthEnabled).thenAnswer((realInvocation) => ValueNotifier(false)); + + + when(mockMessagingModel.getOnBoardingStatus(any)) + .thenAnswer( (realInvocation) { - return boolEmptyBuilder(mockBuildContext, true, null); + final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); }, ); - when(mockSessionModel.isAuthEnabled).thenAnswer( + when(mockSessionModel.chatEnabled(any)).thenAnswer( (realInvocation) { - return ValueNotifier(false); + final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + // + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); }, ); + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, false, null); + }, + ); + await widgetTester.pumpWidget(homeWidget); - widgetTester.pumpWidget(homeWidget); + expect(find.byType(BottomNavigationBar), findsOneWidget); }, + variant: TargetPlatformVariant.only(TargetPlatform.android), ); }, ); diff --git a/test/utils/test.mocks.dart b/test/utils/test.mocks.dart index bdbeb3ace2..ea6cd59123 100644 --- a/test/utils/test.mocks.dart +++ b/test/utils/test.mocks.dart @@ -1,8 +1,15 @@ -import 'package:lantern/features/home/session_model.dart'; +import 'package:lantern/core/utils/common.dart'; +import 'package:lantern/features/messaging/messaging_model.dart'; +import 'package:lantern/features/replica/models/replica_model.dart'; import 'package:mockito/annotations.dart'; /// All generate mock should happened or add here /// For generate mock run flutter pub run build_runner build --delete-conflicting-outputs /// So for most other test cases we need import only one class -@GenerateNiceMocks([MockSpec()]) +@GenerateNiceMocks([ + MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), +]) void main() {} diff --git a/test/utils/test.mocks.mocks.dart b/test/utils/test.mocks.mocks.dart index aa29dcc854..b6c414cc0e 100644 --- a/test/utils/test.mocks.mocks.dart +++ b/test/utils/test.mocks.mocks.dart @@ -4,14 +4,16 @@ // ignore_for_file: no_leading_underscores_for_library_prefixes import 'dart:async' as _i4; -import 'dart:ffi' as _i5; -import 'dart:typed_data' as _i8; +import 'dart:ffi' as _i6; +import 'dart:typed_data' as _i9; import 'package:lantern/core/utils/common.dart' as _i2; import 'package:lantern/core/utils/common_desktop.dart' as _i3; -import 'package:lantern/core/utils/utils.dart' as _i7; +import 'package:lantern/core/utils/utils.dart' as _i8; +import 'package:lantern/features/messaging/messaging.dart' as _i5; +import 'package:lantern/features/replica/common.dart' as _i10; import 'package:mockito/mockito.dart' as _i1; -import 'package:mockito/src/dummies.dart' as _i6; +import 'package:mockito/src/dummies.dart' as _i7; // ignore_for_file: type=lint // ignore_for_file: avoid_redundant_argument_values @@ -107,6 +109,37 @@ class _FakeValueListenableBuilder_6 extends _i1.SmartFake super.toString(); } +class _FakeChatNumber_7 extends _i1.SmartFake implements _i5.ChatNumber { + _FakeChatNumber_7( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeContact_8 extends _i1.SmartFake implements _i5.Contact { + _FakeContact_8( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeValueListenable_9 extends _i1.SmartFake + implements _i2.ValueListenable { + _FakeValueListenable_9( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [SessionModel]. /// /// See the documentation for Mockito's code generation for more information. @@ -1119,26 +1152,26 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ) as _i4.Future); @override - _i5.Pointer<_i3.Utf8> ffiIsUserLoggedIn() => (super.noSuchMethod( + _i6.Pointer<_i3.Utf8> ffiIsUserLoggedIn() => (super.noSuchMethod( Invocation.method( #ffiIsUserLoggedIn, [], ), - returnValue: _i6.dummyValue<_i5.Pointer<_i3.Utf8>>( + returnValue: _i7.dummyValue<_i6.Pointer<_i3.Utf8>>( this, Invocation.method( #ffiIsUserLoggedIn, [], ), ), - returnValueForMissingStub: _i6.dummyValue<_i5.Pointer<_i3.Utf8>>( + returnValueForMissingStub: _i7.dummyValue<_i6.Pointer<_i3.Utf8>>( this, Invocation.method( #ffiIsUserLoggedIn, [], ), ), - ) as _i5.Pointer<_i3.Utf8>); + ) as _i6.Pointer<_i3.Utf8>); @override _i2.Widget isUserSignedIn(_i2.ValueWidgetBuilder? builder) => @@ -1366,7 +1399,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { #proxyAddr, [], ), - returnValue: _i4.Future.value(_i6.dummyValue( + returnValue: _i4.Future.value(_i7.dummyValue( this, Invocation.method( #proxyAddr, @@ -1374,7 +1407,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ), )), returnValueForMissingStub: - _i4.Future.value(_i6.dummyValue( + _i4.Future.value(_i7.dummyValue( this, Invocation.method( #proxyAddr, @@ -1389,7 +1422,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { #getCountryCode, [], ), - returnValue: _i4.Future.value(_i6.dummyValue( + returnValue: _i4.Future.value(_i7.dummyValue( this, Invocation.method( #getCountryCode, @@ -1397,7 +1430,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ), )), returnValueForMissingStub: - _i4.Future.value(_i6.dummyValue( + _i4.Future.value(_i7.dummyValue( this, Invocation.method( #getCountryCode, @@ -1575,7 +1608,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { #getReplicaAddr, [], ), - returnValue: _i4.Future.value(_i6.dummyValue( + returnValue: _i4.Future.value(_i7.dummyValue( this, Invocation.method( #getReplicaAddr, @@ -1583,7 +1616,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ), )), returnValueForMissingStub: - _i4.Future.value(_i6.dummyValue( + _i4.Future.value(_i7.dummyValue( this, Invocation.method( #getReplicaAddr, @@ -1876,7 +1909,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { #requestLinkCode, [], ), - returnValue: _i4.Future.value(_i6.dummyValue( + returnValue: _i4.Future.value(_i7.dummyValue( this, Invocation.method( #requestLinkCode, @@ -1884,7 +1917,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ), )), returnValueForMissingStub: - _i4.Future.value(_i6.dummyValue( + _i4.Future.value(_i7.dummyValue( this, Invocation.method( #requestLinkCode, @@ -1960,7 +1993,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { email, ], ), - returnValue: _i4.Future.value(_i6.dummyValue( + returnValue: _i4.Future.value(_i7.dummyValue( this, Invocation.method( #submitBitcoinPayment, @@ -1971,7 +2004,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ), )), returnValueForMissingStub: - _i4.Future.value(_i6.dummyValue( + _i4.Future.value(_i7.dummyValue( this, Invocation.method( #submitBitcoinPayment, @@ -1987,7 +2020,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { _i4.Future generatePaymentRedirectUrl({ required String? planID, required String? email, - required _i7.Providers? paymentProvider, + required _i8.Providers? paymentProvider, }) => (super.noSuchMethod( Invocation.method( @@ -1999,7 +2032,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { #paymentProvider: paymentProvider, }, ), - returnValue: _i4.Future.value(_i6.dummyValue( + returnValue: _i4.Future.value(_i7.dummyValue( this, Invocation.method( #generatePaymentRedirectUrl, @@ -2012,7 +2045,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ), )), returnValueForMissingStub: - _i4.Future.value(_i6.dummyValue( + _i4.Future.value(_i7.dummyValue( this, Invocation.method( #generatePaymentRedirectUrl, @@ -2058,7 +2091,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { _i2.BuildContext? context, String? planID, String? email, - _i7.Providers? provider, + _i8.Providers? provider, ) => (super.noSuchMethod( Invocation.method( @@ -2070,7 +2103,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { provider, ], ), - returnValue: _i4.Future.value(_i6.dummyValue( + returnValue: _i4.Future.value(_i7.dummyValue( this, Invocation.method( #paymentRedirectForDesktop, @@ -2083,7 +2116,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ), )), returnValueForMissingStub: - _i4.Future.value(_i6.dummyValue( + _i4.Future.value(_i7.dummyValue( this, Invocation.method( #paymentRedirectForDesktop, @@ -2309,8 +2342,2639 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { #get, [path], ), - returnValue: _i6.ifNotNull( - _i6.dummyValueOrNull( + returnValue: _i7.ifNotNull( + _i7.dummyValueOrNull( + this, + Invocation.method( + #get, + [path], + ), + ), + (T v) => _i4.Future.value(v), + ) ?? + _FakeFuture_5( + this, + Invocation.method( + #get, + [path], + ), + ), + returnValueForMissingStub: _i7.ifNotNull( + _i7.dummyValueOrNull( + this, + Invocation.method( + #get, + [path], + ), + ), + (T v) => _i4.Future.value(v), + ) ?? + _FakeFuture_5( + this, + Invocation.method( + #get, + [path], + ), + ), + ) as _i4.Future); + + @override + _i4.Future> list( + String? path, { + int? start = 0, + int? count = 34, + String? fullTextSearch, + bool? reverseSort = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #list, + [path], + { + #start: start, + #count: count, + #fullTextSearch: fullTextSearch, + #reverseSort: reverseSort, + #deserialize: deserialize, + }, + ), + returnValue: _i4.Future>.value([]), + returnValueForMissingStub: _i4.Future>.value([]), + ) as _i4.Future>); + + @override + _i2.ValueListenableBuilder subscribedSingleValueBuilder( + String? path, { + T? defaultValue, + required _i2.ValueWidgetBuilder? builder, + bool? details = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueListenableBuilder); + + @override + _i2.ValueNotifier singleValueNotifier( + String? path, + T? defaultValue, { + bool? details = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueNotifier_1( + this, + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueNotifier); + + @override + _i2.ValueListenableBuilder<_i2.ChangeTrackingList> + subscribedListBuilder( + String? path, { + required _i2.ValueWidgetBuilder>>? builder, + bool? details = false, + int Function( + String, + String, + )? compare, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + returnValue: + _FakeValueListenableBuilder_6<_i2.ChangeTrackingList>( + this, + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: + _FakeValueListenableBuilder_6<_i2.ChangeTrackingList>( + this, + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueListenableBuilder<_i2.ChangeTrackingList>); + + @override + _i2.ValueNotifier<_i2.ChangeTrackingList> listNotifier( + String? path, { + bool? details = false, + int Function( + String, + String, + )? compare, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueNotifier_1<_i2.ChangeTrackingList>( + this, + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: + _FakeValueNotifier_1<_i2.ChangeTrackingList>( + this, + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueNotifier<_i2.ChangeTrackingList>); + + @override + _i2.ValueListenableBuilder listChildBuilder( + _i2.BuildContext? context, + String? path, { + required T? defaultValue, + required _i2.ValueWidgetBuilder? builder, + }) => + (super.noSuchMethod( + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + returnValue: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + ), + returnValueForMissingStub: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + ), + ) as _i2.ValueListenableBuilder); +} + +/// A class which mocks [VpnModel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockVpnModel extends _i1.Mock implements _i2.VpnModel { + @override + _i2.MethodChannel get methodChannel => (super.noSuchMethod( + Invocation.getter(#methodChannel), + returnValue: _FakeMethodChannel_3( + this, + Invocation.getter(#methodChannel), + ), + returnValueForMissingStub: _FakeMethodChannel_3( + this, + Invocation.getter(#methodChannel), + ), + ) as _i2.MethodChannel); + + @override + set methodChannel(_i2.MethodChannel? _methodChannel) => super.noSuchMethod( + Invocation.setter( + #methodChannel, + _methodChannel, + ), + returnValueForMissingStub: null, + ); + + @override + set event(_i2.Event? _event) => super.noSuchMethod( + Invocation.setter( + #event, + _event, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier<_i3.ConfigOptions?> get configNotifier => + (super.noSuchMethod( + Invocation.getter(#configNotifier), + returnValue: _FakeValueNotifier_1<_i3.ConfigOptions?>( + this, + Invocation.getter(#configNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1<_i3.ConfigOptions?>( + this, + Invocation.getter(#configNotifier), + ), + ) as _i2.ValueNotifier<_i3.ConfigOptions?>); + + @override + set configNotifier(_i2.ValueNotifier<_i3.ConfigOptions?>? _configNotifier) => + super.noSuchMethod( + Invocation.setter( + #configNotifier, + _configNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i4.Future switchVPN(bool? on) => (super.noSuchMethod( + Invocation.method( + #switchVPN, + [on], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future connectingDelay(bool? on) => (super.noSuchMethod( + Invocation.method( + #connectingDelay, + [on], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget vpnStatus( + _i2.BuildContext? context, + _i2.ValueWidgetBuilder? builder, + ) => + (super.noSuchMethod( + Invocation.method( + #vpnStatus, + [ + context, + builder, + ], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #vpnStatus, + [ + context, + builder, + ], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #vpnStatus, + [ + context, + builder, + ], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future isVpnConnected() => (super.noSuchMethod( + Invocation.method( + #isVpnConnected, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future get(String? path) => (super.noSuchMethod( + Invocation.method( + #get, + [path], + ), + returnValue: _i7.ifNotNull( + _i7.dummyValueOrNull( + this, + Invocation.method( + #get, + [path], + ), + ), + (T v) => _i4.Future.value(v), + ) ?? + _FakeFuture_5( + this, + Invocation.method( + #get, + [path], + ), + ), + returnValueForMissingStub: _i7.ifNotNull( + _i7.dummyValueOrNull( + this, + Invocation.method( + #get, + [path], + ), + ), + (T v) => _i4.Future.value(v), + ) ?? + _FakeFuture_5( + this, + Invocation.method( + #get, + [path], + ), + ), + ) as _i4.Future); + + @override + _i4.Future> list( + String? path, { + int? start = 0, + int? count = 34, + String? fullTextSearch, + bool? reverseSort = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #list, + [path], + { + #start: start, + #count: count, + #fullTextSearch: fullTextSearch, + #reverseSort: reverseSort, + #deserialize: deserialize, + }, + ), + returnValue: _i4.Future>.value([]), + returnValueForMissingStub: _i4.Future>.value([]), + ) as _i4.Future>); + + @override + _i2.ValueListenableBuilder subscribedSingleValueBuilder( + String? path, { + T? defaultValue, + required _i2.ValueWidgetBuilder? builder, + bool? details = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueListenableBuilder); + + @override + _i2.ValueNotifier singleValueNotifier( + String? path, + T? defaultValue, { + bool? details = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueNotifier_1( + this, + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueNotifier); + + @override + _i2.ValueListenableBuilder<_i2.ChangeTrackingList> + subscribedListBuilder( + String? path, { + required _i2.ValueWidgetBuilder>>? builder, + bool? details = false, + int Function( + String, + String, + )? compare, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + returnValue: + _FakeValueListenableBuilder_6<_i2.ChangeTrackingList>( + this, + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: + _FakeValueListenableBuilder_6<_i2.ChangeTrackingList>( + this, + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueListenableBuilder<_i2.ChangeTrackingList>); + + @override + _i2.ValueNotifier<_i2.ChangeTrackingList> listNotifier( + String? path, { + bool? details = false, + int Function( + String, + String, + )? compare, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueNotifier_1<_i2.ChangeTrackingList>( + this, + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: + _FakeValueNotifier_1<_i2.ChangeTrackingList>( + this, + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueNotifier<_i2.ChangeTrackingList>); + + @override + _i2.ValueListenableBuilder listChildBuilder( + _i2.BuildContext? context, + String? path, { + required T? defaultValue, + required _i2.ValueWidgetBuilder? builder, + }) => + (super.noSuchMethod( + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + returnValue: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + ), + returnValueForMissingStub: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + ), + ) as _i2.ValueListenableBuilder); +} + +/// A class which mocks [MessagingModel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockMessagingModel extends _i1.Mock implements _i5.MessagingModel { + @override + _i2.ValueNotifier get copiedRecoveryStatusNotifier => + (super.noSuchMethod( + Invocation.getter(#copiedRecoveryStatusNotifier), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#copiedRecoveryStatusNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#copiedRecoveryStatusNotifier), + ), + ) as _i2.ValueNotifier); + + @override + set copiedRecoveryStatusNotifier( + _i2.ValueNotifier? _copiedRecoveryStatusNotifier) => + super.noSuchMethod( + Invocation.setter( + #copiedRecoveryStatusNotifier, + _copiedRecoveryStatusNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.MethodChannel get methodChannel => (super.noSuchMethod( + Invocation.getter(#methodChannel), + returnValue: _FakeMethodChannel_3( + this, + Invocation.getter(#methodChannel), + ), + returnValueForMissingStub: _FakeMethodChannel_3( + this, + Invocation.getter(#methodChannel), + ), + ) as _i2.MethodChannel); + + @override + set methodChannel(_i2.MethodChannel? _methodChannel) => super.noSuchMethod( + Invocation.setter( + #methodChannel, + _methodChannel, + ), + returnValueForMissingStub: null, + ); + + @override + set event(_i2.Event? _event) => super.noSuchMethod( + Invocation.setter( + #event, + _event, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier<_i3.ConfigOptions?> get configNotifier => + (super.noSuchMethod( + Invocation.getter(#configNotifier), + returnValue: _FakeValueNotifier_1<_i3.ConfigOptions?>( + this, + Invocation.getter(#configNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1<_i3.ConfigOptions?>( + this, + Invocation.getter(#configNotifier), + ), + ) as _i2.ValueNotifier<_i3.ConfigOptions?>); + + @override + set configNotifier(_i2.ValueNotifier<_i3.ConfigOptions?>? _configNotifier) => + super.noSuchMethod( + Invocation.setter( + #configNotifier, + _configNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i4.Future start() => (super.noSuchMethod( + Invocation.method( + #start, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future kill() => (super.noSuchMethod( + Invocation.method( + #kill, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future wipeData() => (super.noSuchMethod( + Invocation.method( + #wipeData, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future<_i5.ChatNumber> findChatNumberByShortNumber(String? shortNumber) => + (super.noSuchMethod( + Invocation.method( + #findChatNumberByShortNumber, + [shortNumber], + ), + returnValue: _i4.Future<_i5.ChatNumber>.value(_FakeChatNumber_7( + this, + Invocation.method( + #findChatNumberByShortNumber, + [shortNumber], + ), + )), + returnValueForMissingStub: + _i4.Future<_i5.ChatNumber>.value(_FakeChatNumber_7( + this, + Invocation.method( + #findChatNumberByShortNumber, + [shortNumber], + ), + )), + ) as _i4.Future<_i5.ChatNumber>); + + @override + _i4.Future> addProvisionalContact( + String? contactId, + String? source, + ) => + (super.noSuchMethod( + Invocation.method( + #addProvisionalContact, + [ + contactId, + source, + ], + ), + returnValue: + _i4.Future>.value({}), + returnValueForMissingStub: + _i4.Future>.value({}), + ) as _i4.Future>); + + @override + _i4.Future deleteProvisionalContact(String? contactId) => + (super.noSuchMethod( + Invocation.method( + #deleteProvisionalContact, + [contactId], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future<_i5.Contact> addOrUpdateDirectContact({ + String? unsafeId, + _i5.ChatNumber? chatNumber, + String? displayName, + String? source, + }) => + (super.noSuchMethod( + Invocation.method( + #addOrUpdateDirectContact, + [], + { + #unsafeId: unsafeId, + #chatNumber: chatNumber, + #displayName: displayName, + #source: source, + }, + ), + returnValue: _i4.Future<_i5.Contact>.value(_FakeContact_8( + this, + Invocation.method( + #addOrUpdateDirectContact, + [], + { + #unsafeId: unsafeId, + #chatNumber: chatNumber, + #displayName: displayName, + #source: source, + }, + ), + )), + returnValueForMissingStub: _i4.Future<_i5.Contact>.value(_FakeContact_8( + this, + Invocation.method( + #addOrUpdateDirectContact, + [], + { + #unsafeId: unsafeId, + #chatNumber: chatNumber, + #displayName: displayName, + #source: source, + }, + ), + )), + ) as _i4.Future<_i5.Contact>); + + @override + _i4.Future acceptDirectContact(String? unsafeId) => (super.noSuchMethod( + Invocation.method( + #acceptDirectContact, + [unsafeId], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future markDirectContactVerified(String? unsafeId) => + (super.noSuchMethod( + Invocation.method( + #markDirectContactVerified, + [unsafeId], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future blockDirectContact(String? unsafeId) => (super.noSuchMethod( + Invocation.method( + #blockDirectContact, + [unsafeId], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future unblockDirectContact(String? unsafeId) => + (super.noSuchMethod( + Invocation.method( + #unblockDirectContact, + [unsafeId], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setCurrentConversationContact( + String? currentConversationContact) => + (super.noSuchMethod( + Invocation.method( + #setCurrentConversationContact, + [currentConversationContact], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future clearCurrentConversationContact() => (super.noSuchMethod( + Invocation.method( + #clearCurrentConversationContact, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future<_i5.Contact?> getContact(String? contactPath) => + (super.noSuchMethod( + Invocation.method( + #getContact, + [contactPath], + ), + returnValue: _i4.Future<_i5.Contact?>.value(), + returnValueForMissingStub: _i4.Future<_i5.Contact?>.value(), + ) as _i4.Future<_i5.Contact?>); + + @override + _i4.Future deleteDirectContact(String? id) => (super.noSuchMethod( + Invocation.method( + #deleteDirectContact, + [id], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future introduce(List? recipientIds) => (super.noSuchMethod( + Invocation.method( + #introduce, + [recipientIds], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future acceptIntroduction( + String? fromId, + String? toId, + ) => + (super.noSuchMethod( + Invocation.method( + #acceptIntroduction, + [ + fromId, + toId, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future rejectIntroduction( + String? fromId, + String? toId, + ) => + (super.noSuchMethod( + Invocation.method( + #rejectIntroduction, + [ + fromId, + toId, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget bestIntroductions( + {required _i2.ValueWidgetBuilder< + Iterable<_i2.PathAndValue<_i5.StoredMessage>>>? + builder}) => + (super.noSuchMethod( + Invocation.method( + #bestIntroductions, + [], + {#builder: builder}, + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #bestIntroductions, + [], + {#builder: builder}, + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #bestIntroductions, + [], + {#builder: builder}, + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget contactsByActivity( + {required _i2 + .ValueWidgetBuilder>>? + builder}) => + (super.noSuchMethod( + Invocation.method( + #contactsByActivity, + [], + {#builder: builder}, + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #contactsByActivity, + [], + {#builder: builder}, + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #contactsByActivity, + [], + {#builder: builder}, + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget contacts( + {required _i2 + .ValueWidgetBuilder>>? + builder}) => + (super.noSuchMethod( + Invocation.method( + #contacts, + [], + {#builder: builder}, + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #contacts, + [], + {#builder: builder}, + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #contacts, + [], + {#builder: builder}, + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget contact( + _i2.BuildContext? context, + _i2.PathAndValue<_i5.Contact>? contact, + _i2.ValueWidgetBuilder<_i5.Contact>? builder, + ) => + (super.noSuchMethod( + Invocation.method( + #contact, + [ + context, + contact, + builder, + ], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #contact, + [ + context, + contact, + builder, + ], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #contact, + [ + context, + contact, + builder, + ], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget singleContact( + _i5.Contact? contact, + _i2.ValueWidgetBuilder<_i5.Contact>? builder, + ) => + (super.noSuchMethod( + Invocation.method( + #singleContact, + [ + contact, + builder, + ], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #singleContact, + [ + contact, + builder, + ], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #singleContact, + [ + contact, + builder, + ], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget singleContactById( + _i5.ContactId? contactId, + _i2.ValueWidgetBuilder<_i5.Contact>? builder, + ) => + (super.noSuchMethod( + Invocation.method( + #singleContactById, + [ + contactId, + builder, + ], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #singleContactById, + [ + contactId, + builder, + ], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #singleContactById, + [ + contactId, + builder, + ], + ), + ), + ) as _i2.Widget); + + @override + _i2.ValueNotifier<_i5.Contact?> contactNotifier(String? contactId) => + (super.noSuchMethod( + Invocation.method( + #contactNotifier, + [contactId], + ), + returnValue: _FakeValueNotifier_1<_i5.Contact?>( + this, + Invocation.method( + #contactNotifier, + [contactId], + ), + ), + returnValueForMissingStub: _FakeValueNotifier_1<_i5.Contact?>( + this, + Invocation.method( + #contactNotifier, + [contactId], + ), + ), + ) as _i2.ValueNotifier<_i5.Contact?>); + + @override + _i2.Widget contactMessages( + _i5.Contact? contact, { + required _i2 + .ValueWidgetBuilder>>? + builder, + }) => + (super.noSuchMethod( + Invocation.method( + #contactMessages, + [contact], + {#builder: builder}, + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #contactMessages, + [contact], + {#builder: builder}, + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #contactMessages, + [contact], + {#builder: builder}, + ), + ), + ) as _i2.Widget); + + @override + _i4.Future<_i5.Contact> getDirectContact(String? contactId) => + (super.noSuchMethod( + Invocation.method( + #getDirectContact, + [contactId], + ), + returnValue: _i4.Future<_i5.Contact>.value(_FakeContact_8( + this, + Invocation.method( + #getDirectContact, + [contactId], + ), + )), + returnValueForMissingStub: _i4.Future<_i5.Contact>.value(_FakeContact_8( + this, + Invocation.method( + #getDirectContact, + [contactId], + ), + )), + ) as _i4.Future<_i5.Contact>); + + @override + _i2.Widget message( + _i2.BuildContext? context, + _i2.PathAndValue<_i5.StoredMessage>? message, + _i2.ValueWidgetBuilder<_i5.StoredMessage>? builder, + ) => + (super.noSuchMethod( + Invocation.method( + #message, + [ + context, + message, + builder, + ], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #message, + [ + context, + message, + builder, + ], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #message, + [ + context, + message, + builder, + ], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget singleMessage( + String? senderId, + String? messageId, + _i2.ValueWidgetBuilder<_i5.StoredMessage>? builder, + ) => + (super.noSuchMethod( + Invocation.method( + #singleMessage, + [ + senderId, + messageId, + builder, + ], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #singleMessage, + [ + senderId, + messageId, + builder, + ], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #singleMessage, + [ + senderId, + messageId, + builder, + ], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget me(_i2.ValueWidgetBuilder<_i5.Contact>? builder) => + (super.noSuchMethod( + Invocation.method( + #me, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #me, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #me, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future recover(String? recoveryCode) => (super.noSuchMethod( + Invocation.method( + #recover, + [recoveryCode], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future getRecoveryCode() => (super.noSuchMethod( + Invocation.method( + #getRecoveryCode, + [], + ), + returnValue: _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #getRecoveryCode, + [], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #getRecoveryCode, + [], + ), + )), + ) as _i4.Future); + + @override + _i4.Future sendToDirectContact( + String? identityKey, { + String? text, + List<_i9.Uint8List>? attachments, + String? replyToId, + String? replyToSenderId, + }) => + (super.noSuchMethod( + Invocation.method( + #sendToDirectContact, + [identityKey], + { + #text: text, + #attachments: attachments, + #replyToId: replyToId, + #replyToSenderId: replyToSenderId, + }, + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future react( + _i5.StoredMessage? message, + String? reaction, + ) => + (super.noSuchMethod( + Invocation.method( + #react, + [ + message, + reaction, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future markViewed(_i5.StoredMessage? message) => + (super.noSuchMethod( + Invocation.method( + #markViewed, + [message], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future deleteLocally(_i5.StoredMessage? message) => + (super.noSuchMethod( + Invocation.method( + #deleteLocally, + [message], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future deleteGlobally(_i5.StoredMessage? message) => + (super.noSuchMethod( + Invocation.method( + #deleteGlobally, + [message], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setDisappearSettings( + _i5.Contact? contact, + int? seconds, + ) => + (super.noSuchMethod( + Invocation.method( + #setDisappearSettings, + [ + contact, + seconds, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future startRecordingVoiceMemo() => (super.noSuchMethod( + Invocation.method( + #startRecordingVoiceMemo, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future<_i9.Uint8List> stopRecordingVoiceMemo() => (super.noSuchMethod( + Invocation.method( + #stopRecordingVoiceMemo, + [], + ), + returnValue: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), + returnValueForMissingStub: + _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), + ) as _i4.Future<_i9.Uint8List>); + + @override + _i4.Future<_i9.Uint8List> filePickerLoadAttachment( + String? filePath, + Map? metadata, + ) => + (super.noSuchMethod( + Invocation.method( + #filePickerLoadAttachment, + [ + filePath, + metadata, + ], + ), + returnValue: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), + returnValueForMissingStub: + _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), + ) as _i4.Future<_i9.Uint8List>); + + @override + _i2.ValueListenable<_i2.CachedValue<_i9.Uint8List>> thumbnail( + _i5.StoredAttachment? attachment) => + (super.noSuchMethod( + Invocation.method( + #thumbnail, + [attachment], + ), + returnValue: _FakeValueListenable_9<_i2.CachedValue<_i9.Uint8List>>( + this, + Invocation.method( + #thumbnail, + [attachment], + ), + ), + returnValueForMissingStub: + _FakeValueListenable_9<_i2.CachedValue<_i9.Uint8List>>( + this, + Invocation.method( + #thumbnail, + [attachment], + ), + ), + ) as _i2.ValueListenable<_i2.CachedValue<_i9.Uint8List>>); + + @override + _i4.Future<_i9.Uint8List> decryptAttachment( + _i5.StoredAttachment? attachment) => + (super.noSuchMethod( + Invocation.method( + #decryptAttachment, + [attachment], + ), + returnValue: _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), + returnValueForMissingStub: + _i4.Future<_i9.Uint8List>.value(_i9.Uint8List(0)), + ) as _i4.Future<_i9.Uint8List>); + + @override + _i4.Future decryptVideoForPlayback( + _i5.StoredAttachment? attachment) => + (super.noSuchMethod( + Invocation.method( + #decryptVideoForPlayback, + [attachment], + ), + returnValue: _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #decryptVideoForPlayback, + [attachment], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #decryptVideoForPlayback, + [attachment], + ), + )), + ) as _i4.Future); + + @override + _i4.Future allocateRelayAddress(String? localAddr) => + (super.noSuchMethod( + Invocation.method( + #allocateRelayAddress, + [localAddr], + ), + returnValue: _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #allocateRelayAddress, + [localAddr], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #allocateRelayAddress, + [localAddr], + ), + )), + ) as _i4.Future); + + @override + _i4.Future relayTo(String? relayAddr) => (super.noSuchMethod( + Invocation.method( + #relayTo, + [relayAddr], + ), + returnValue: _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #relayTo, + [relayAddr], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #relayTo, + [relayAddr], + ), + )), + ) as _i4.Future); + + @override + _i4.Future>> searchContacts( + String? query, + int? numTokens, + ) => + (super.noSuchMethod( + Invocation.method( + #searchContacts, + [ + query, + numTokens, + ], + ), + returnValue: _i4.Future>>.value( + <_i2.SearchResult<_i5.Contact>>[]), + returnValueForMissingStub: + _i4.Future>>.value( + <_i2.SearchResult<_i5.Contact>>[]), + ) as _i4.Future>>); + + @override + _i4.Future>> searchMessages( + String? query, + int? numTokens, + ) => + (super.noSuchMethod( + Invocation.method( + #searchMessages, + [ + query, + numTokens, + ], + ), + returnValue: + _i4.Future>>.value( + <_i2.SearchResult<_i5.StoredMessage>>[]), + returnValueForMissingStub: + _i4.Future>>.value( + <_i2.SearchResult<_i5.StoredMessage>>[]), + ) as _i4.Future>>); + + @override + String sanitizeQuery(String? query) => (super.noSuchMethod( + Invocation.method( + #sanitizeQuery, + [query], + ), + returnValue: _i7.dummyValue( + this, + Invocation.method( + #sanitizeQuery, + [query], + ), + ), + returnValueForMissingStub: _i7.dummyValue( + this, + Invocation.method( + #sanitizeQuery, + [query], + ), + ), + ) as String); + + @override + _i4.Future dismissVerificationReminder(String? unsafeId) => + (super.noSuchMethod( + Invocation.method( + #dismissVerificationReminder, + [unsafeId], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future markIsOnboarded() => (super.noSuchMethod( + Invocation.method( + #markIsOnboarded, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget getOnBoardingStatus(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #getOnBoardingStatus, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #getOnBoardingStatus, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #getOnBoardingStatus, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future markCopiedRecoveryKey() => (super.noSuchMethod( + Invocation.method( + #markCopiedRecoveryKey, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget getCopiedRecoveryStatus(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #getCopiedRecoveryStatus, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #getCopiedRecoveryStatus, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #getCopiedRecoveryStatus, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future saveNotificationsTS() => (super.noSuchMethod( + Invocation.method( + #saveNotificationsTS, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget getLastDismissedNotificationTS( + _i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #getLastDismissedNotificationTS, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #getLastDismissedNotificationTS, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #getLastDismissedNotificationTS, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future getDefaultRingtoneUri() => (super.noSuchMethod( + Invocation.method( + #getDefaultRingtoneUri, + [], + ), + returnValue: _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #getDefaultRingtoneUri, + [], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #getDefaultRingtoneUri, + [], + ), + )), + ) as _i4.Future); + + @override + _i4.Future shouldShowTryLanternChatModal() => (super.noSuchMethod( + Invocation.method( + #shouldShowTryLanternChatModal, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future dismissTryLanternChatBadge() => (super.noSuchMethod( + Invocation.method( + #dismissTryLanternChatBadge, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget getFirstShownTryLanternChatModalTS( + _i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #getFirstShownTryLanternChatModalTS, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #getFirstShownTryLanternChatModalTS, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #getFirstShownTryLanternChatModalTS, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future resetTimestamps() => (super.noSuchMethod( + Invocation.method( + #resetTimestamps, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future resetFlags() => (super.noSuchMethod( + Invocation.method( + #resetFlags, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + void addDummyContacts() => super.noSuchMethod( + Invocation.method( + #addDummyContacts, + [], + ), + returnValueForMissingStub: null, + ); + + @override + _i4.Future saveDummyAttachment( + String? url, + String? displayName, + ) => + (super.noSuchMethod( + Invocation.method( + #saveDummyAttachment, + [ + url, + displayName, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future sendDummyAttachment( + String? fileName, + Map? metadata, + ) => + (super.noSuchMethod( + Invocation.method( + #sendDummyAttachment, + [ + fileName, + metadata, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future get(String? path) => (super.noSuchMethod( + Invocation.method( + #get, + [path], + ), + returnValue: _i7.ifNotNull( + _i7.dummyValueOrNull( + this, + Invocation.method( + #get, + [path], + ), + ), + (T v) => _i4.Future.value(v), + ) ?? + _FakeFuture_5( + this, + Invocation.method( + #get, + [path], + ), + ), + returnValueForMissingStub: _i7.ifNotNull( + _i7.dummyValueOrNull( + this, + Invocation.method( + #get, + [path], + ), + ), + (T v) => _i4.Future.value(v), + ) ?? + _FakeFuture_5( + this, + Invocation.method( + #get, + [path], + ), + ), + ) as _i4.Future); + + @override + _i4.Future> list( + String? path, { + int? start = 0, + int? count = 34, + String? fullTextSearch, + bool? reverseSort = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #list, + [path], + { + #start: start, + #count: count, + #fullTextSearch: fullTextSearch, + #reverseSort: reverseSort, + #deserialize: deserialize, + }, + ), + returnValue: _i4.Future>.value([]), + returnValueForMissingStub: _i4.Future>.value([]), + ) as _i4.Future>); + + @override + _i2.ValueListenableBuilder subscribedSingleValueBuilder( + String? path, { + T? defaultValue, + required _i2.ValueWidgetBuilder? builder, + bool? details = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #subscribedSingleValueBuilder, + [path], + { + #defaultValue: defaultValue, + #builder: builder, + #details: details, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueListenableBuilder); + + @override + _i2.ValueNotifier singleValueNotifier( + String? path, + T? defaultValue, { + bool? details = false, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueNotifier_1( + this, + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.method( + #singleValueNotifier, + [ + path, + defaultValue, + ], + { + #details: details, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueNotifier); + + @override + _i2.ValueListenableBuilder<_i2.ChangeTrackingList> + subscribedListBuilder( + String? path, { + required _i2.ValueWidgetBuilder>>? builder, + bool? details = false, + int Function( + String, + String, + )? compare, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + returnValue: + _FakeValueListenableBuilder_6<_i2.ChangeTrackingList>( + this, + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: + _FakeValueListenableBuilder_6<_i2.ChangeTrackingList>( + this, + Invocation.method( + #subscribedListBuilder, + [path], + { + #builder: builder, + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueListenableBuilder<_i2.ChangeTrackingList>); + + @override + _i2.ValueNotifier<_i2.ChangeTrackingList> listNotifier( + String? path, { + bool? details = false, + int Function( + String, + String, + )? compare, + T Function(_i9.Uint8List)? deserialize, + }) => + (super.noSuchMethod( + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + returnValue: _FakeValueNotifier_1<_i2.ChangeTrackingList>( + this, + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + returnValueForMissingStub: + _FakeValueNotifier_1<_i2.ChangeTrackingList>( + this, + Invocation.method( + #listNotifier, + [path], + { + #details: details, + #compare: compare, + #deserialize: deserialize, + }, + ), + ), + ) as _i2.ValueNotifier<_i2.ChangeTrackingList>); + + @override + _i2.ValueListenableBuilder listChildBuilder( + _i2.BuildContext? context, + String? path, { + required T? defaultValue, + required _i2.ValueWidgetBuilder? builder, + }) => + (super.noSuchMethod( + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + returnValue: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + ), + returnValueForMissingStub: _FakeValueListenableBuilder_6( + this, + Invocation.method( + #listChildBuilder, + [ + context, + path, + ], + { + #defaultValue: defaultValue, + #builder: builder, + }, + ), + ), + ) as _i2.ValueListenableBuilder); +} + +/// A class which mocks [ReplicaModel]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockReplicaModel extends _i1.Mock implements _i10.ReplicaModel { + @override + _i2.MethodChannel get methodChannel => (super.noSuchMethod( + Invocation.getter(#methodChannel), + returnValue: _FakeMethodChannel_3( + this, + Invocation.getter(#methodChannel), + ), + returnValueForMissingStub: _FakeMethodChannel_3( + this, + Invocation.getter(#methodChannel), + ), + ) as _i2.MethodChannel); + + @override + set methodChannel(_i2.MethodChannel? _methodChannel) => super.noSuchMethod( + Invocation.setter( + #methodChannel, + _methodChannel, + ), + returnValueForMissingStub: null, + ); + + @override + set event(_i2.Event? _event) => super.noSuchMethod( + Invocation.setter( + #event, + _event, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier<_i3.ConfigOptions?> get configNotifier => + (super.noSuchMethod( + Invocation.getter(#configNotifier), + returnValue: _FakeValueNotifier_1<_i3.ConfigOptions?>( + this, + Invocation.getter(#configNotifier), + ), + returnValueForMissingStub: _FakeValueNotifier_1<_i3.ConfigOptions?>( + this, + Invocation.getter(#configNotifier), + ), + ) as _i2.ValueNotifier<_i3.ConfigOptions?>); + + @override + set configNotifier(_i2.ValueNotifier<_i3.ConfigOptions?>? _configNotifier) => + super.noSuchMethod( + Invocation.setter( + #configNotifier, + _configNotifier, + ), + returnValueForMissingStub: null, + ); + + @override + _i4.Future downloadFile( + String? url, + String? displayName, + ) => + (super.noSuchMethod( + Invocation.method( + #downloadFile, + [ + url, + displayName, + ], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget withReplicaApi(_i2.ValueWidgetBuilder<_i10.ReplicaApi>? builder) => + (super.noSuchMethod( + Invocation.method( + #withReplicaApi, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #withReplicaApi, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #withReplicaApi, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future getSuppressUploadWarning() => (super.noSuchMethod( + Invocation.method( + #getSuppressUploadWarning, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setSuppressUploadWarning(bool? suppress) => + (super.noSuchMethod( + Invocation.method( + #setSuppressUploadWarning, + [suppress], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setSearchTerm(String? searchTerm) => (super.noSuchMethod( + Invocation.method( + #setSearchTerm, + [searchTerm], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setSearchTab(int? searchTab) => (super.noSuchMethod( + Invocation.method( + #setSearchTab, + [searchTab], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i4.Future setShowNewBadge(bool? showNewBadge) => (super.noSuchMethod( + Invocation.method( + #setShowNewBadge, + [showNewBadge], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + _i2.Widget getShowNewBadgeWidget(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #getShowNewBadgeWidget, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #getShowNewBadgeWidget, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #getShowNewBadgeWidget, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget getSearchTermWidget(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #getSearchTermWidget, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #getSearchTermWidget, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #getSearchTermWidget, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i2.Widget getSearchTabWidget(_i2.ValueWidgetBuilder? builder) => + (super.noSuchMethod( + Invocation.method( + #getSearchTabWidget, + [builder], + ), + returnValue: _FakeWidget_4( + this, + Invocation.method( + #getSearchTabWidget, + [builder], + ), + ), + returnValueForMissingStub: _FakeWidget_4( + this, + Invocation.method( + #getSearchTabWidget, + [builder], + ), + ), + ) as _i2.Widget); + + @override + _i4.Future getShowNewBadge() => (super.noSuchMethod( + Invocation.method( + #getShowNewBadge, + [], + ), + returnValue: _i4.Future.value(false), + returnValueForMissingStub: _i4.Future.value(false), + ) as _i4.Future); + + @override + _i4.Future getSearchTerm() => (super.noSuchMethod( + Invocation.method( + #getSearchTerm, + [], + ), + returnValue: _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #getSearchTerm, + [], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #getSearchTerm, + [], + ), + )), + ) as _i4.Future); + + @override + _i4.Future getSearchTab() => (super.noSuchMethod( + Invocation.method( + #getSearchTab, + [], + ), + returnValue: _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #getSearchTab, + [], + ), + )), + returnValueForMissingStub: + _i4.Future.value(_i7.dummyValue( + this, + Invocation.method( + #getSearchTab, + [], + ), + )), + ) as _i4.Future); + + @override + _i4.Future get(String? path) => (super.noSuchMethod( + Invocation.method( + #get, + [path], + ), + returnValue: _i7.ifNotNull( + _i7.dummyValueOrNull( this, Invocation.method( #get, @@ -2326,8 +4990,8 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { [path], ), ), - returnValueForMissingStub: _i6.ifNotNull( - _i6.dummyValueOrNull( + returnValueForMissingStub: _i7.ifNotNull( + _i7.dummyValueOrNull( this, Invocation.method( #get, @@ -2352,7 +5016,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { int? count = 34, String? fullTextSearch, bool? reverseSort = false, - T Function(_i8.Uint8List)? deserialize, + T Function(_i9.Uint8List)? deserialize, }) => (super.noSuchMethod( Invocation.method( @@ -2376,7 +5040,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { T? defaultValue, required _i2.ValueWidgetBuilder? builder, bool? details = false, - T Function(_i8.Uint8List)? deserialize, + T Function(_i9.Uint8List)? deserialize, }) => (super.noSuchMethod( Invocation.method( @@ -2422,7 +5086,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { String? path, T? defaultValue, { bool? details = false, - T Function(_i8.Uint8List)? deserialize, + T Function(_i9.Uint8List)? deserialize, }) => (super.noSuchMethod( Invocation.method( @@ -2476,7 +5140,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { String, String, )? compare, - T Function(_i8.Uint8List)? deserialize, + T Function(_i9.Uint8List)? deserialize, }) => (super.noSuchMethod( Invocation.method( @@ -2527,7 +5191,7 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { String, String, )? compare, - T Function(_i8.Uint8List)? deserialize, + T Function(_i9.Uint8List)? deserialize, }) => (super.noSuchMethod( Invocation.method( diff --git a/test/utils/test_common.dart b/test/utils/test_common.dart index 632b2dc73c..a6791f2bdc 100644 --- a/test/utils/test_common.dart +++ b/test/utils/test_common.dart @@ -18,6 +18,8 @@ class MockBuildContext extends Mock implements BuildContext {} ValueWidgetBuilder intEmptyBuilder = (context, value, child) => const SizedBox(); ValueWidgetBuilder doubleEmptyBuilder = (context, value, child) => const SizedBox(); ValueWidgetBuilder boolEmptyBuilder = (context, value, child) => const SizedBox(); +ValueWidgetBuilder boolNullableEmptyBuilder = (context, value, child) => const SizedBox(); +ValueWidgetBuilder stringEmptyBuilder = (context, value, child) => const SizedBox(); From a6a298a1f9d8b5bf0758ca874bef7f8f621d1955 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Mon, 7 Oct 2024 17:01:20 +0530 Subject: [PATCH 003/163] Start adding test cases for home. --- test/features/home/home_test.dart | 164 ++++++++++++--- test/utils/test.mocks.dart | 8 +- test/utils/test.mocks.mocks.dart | 332 ++++++++++++++++++++++++++++++ test/utils/test_common.dart | 8 +- 4 files changed, 486 insertions(+), 26 deletions(-) diff --git a/test/features/home/home_test.dart b/test/features/home/home_test.dart index c3150e39e4..3d5d7f4879 100644 --- a/test/features/home/home_test.dart +++ b/test/features/home/home_test.dart @@ -1,7 +1,9 @@ import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; +import 'package:lantern/core/widgtes/custom_bottom_item.dart'; import 'package:lantern/features/home/home.dart'; import 'package:lantern/features/messaging/messaging_model.dart'; +import 'package:lantern/features/replica/common.dart'; import 'package:lantern/features/vpn/vpn_notifier.dart'; import '../../utils/test_common.dart'; @@ -12,17 +14,33 @@ void main() { late MockSessionModel mockSessionModel; late MockBuildContext mockBuildContext; late MockMessagingModel mockMessagingModel; + late MockBottomBarChangeNotifier mockBottomBarChangeNotifier; + late MockVPNChangeNotifier mockVPNChangeNotifier; + late MockInternetStatusProvider mockInternetStatusProvider; + late MockReplicaModel mockReplicaModel; + late MockVpnModel mockVpnModel; setUpAll( () { mockSessionModel = MockSessionModel(); mockBuildContext = MockBuildContext(); mockMessagingModel = MockMessagingModel(); + mockBottomBarChangeNotifier = MockBottomBarChangeNotifier(); + mockVPNChangeNotifier = MockVPNChangeNotifier(); + mockInternetStatusProvider = MockInternetStatusProvider(); + mockReplicaModel = MockReplicaModel(); + mockVpnModel = MockVpnModel(); + // Injection models sl.registerLazySingleton(() => mockSessionModel); sl.registerLazySingleton(() => mockMessagingModel); + sl.registerLazySingleton(() => mockReplicaModel); + sl.registerLazySingleton(() => mockVpnModel); - sl.registerLazySingleton(() => VpnModel()); + // mock the providers + mockBottomBarChangeNotifier = MockBottomBarChangeNotifier(); + mockVPNChangeNotifier = MockVPNChangeNotifier(); + mockInternetStatusProvider = MockInternetStatusProvider(); }, ); @@ -36,61 +54,158 @@ void main() { "Home widget render properly for mobile", () { testWidgets( - "Home widget started ", + "Home widget started with all taps showing", (widgetTester) async { final homeWidget = MultiProvider(providers: [ - ChangeNotifierProvider( - create: (context) => BottomBarChangeNotifier()), - ChangeNotifierProvider(create: (context) => VPNChangeNotifier()), - ChangeNotifierProvider( - create: (context) => InternetStatusProvider()) + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), ], child: wrapWithMaterialApp(const HomePage())); - /// Now stub all daa widgets + /// stub providers + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + - // / Use Mockito's `any` matcher to match the argument passed to `acceptedTermsVersion` + /// Stub session model when(mockSessionModel.acceptedTermsVersion(any)) .thenAnswer((invocation) { final builder = invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder( - mockBuildContext, 0, null); + return builder(mockBuildContext, 0, null); }); when(mockSessionModel.developmentMode(any)).thenAnswer( (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder( - mockBuildContext, true, null); + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(false)); + + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + // + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "test", null); + }, + ); + + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, false, null); + }, + ); + + + ///Stub messaging model + when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); }, ); + ///stub vpn model + when(mockVpnModel.vpnStatus(any,any)).thenAnswer((realInvocation) { + final builder = realInvocation.positionalArguments[1] as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + },); - when(mockSessionModel.isTestPlayVersion).thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.isStoreVersion).thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.isAuthEnabled).thenAnswer((realInvocation) => ValueNotifier(false)); + ///stub replica model + when(mockReplicaModel.getShowNewBadgeWidget(any)).thenAnswer((realInvocation) { + final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + },); + + await widgetTester.pumpWidget(homeWidget); + + final bottombar = find.byType(BottomNavigationBar); + expect(bottombar, findsOneWidget); + // three item since replica is disable + expect(find.byType(CustomBottomBarItem), findsAtLeast(4)); + }, + variant: TargetPlatformVariant.only(TargetPlatform.android), + ); + + testWidgets( + "Home widget started with replica disabled", + (widgetTester) async { + final homeWidget = MultiProvider(providers: [ + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), + ], child: wrapWithMaterialApp(const HomePage())); + + /// stub providers + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + + /// Now stub all daa widgets + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockMessagingModel.getOnBoardingStatus(any)) - .thenAnswer( + when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( (realInvocation) { - final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); when(mockSessionModel.chatEnabled(any)).thenAnswer( (realInvocation) { - final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); // when(mockSessionModel.replicaAddr(any)).thenAnswer( (realInvocation) { - final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; return builder(mockBuildContext, "", null); }, ); @@ -103,7 +218,10 @@ void main() { await widgetTester.pumpWidget(homeWidget); - expect(find.byType(BottomNavigationBar), findsOneWidget); + final bottombar = find.byType(BottomNavigationBar); + expect(bottombar, findsOneWidget); + // three item since replica is disable + expect(find.byType(CustomBottomBarItem), findsAtLeast(3)); }, variant: TargetPlatformVariant.only(TargetPlatform.android), ); diff --git a/test/utils/test.mocks.dart b/test/utils/test.mocks.dart index ea6cd59123..74fb1372d8 100644 --- a/test/utils/test.mocks.dart +++ b/test/utils/test.mocks.dart @@ -1,6 +1,9 @@ +import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/core/utils/common.dart'; +import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; import 'package:lantern/features/messaging/messaging_model.dart'; -import 'package:lantern/features/replica/models/replica_model.dart'; +import 'package:lantern/features/replica/common.dart'; +import 'package:lantern/features/vpn/vpn_notifier.dart'; import 'package:mockito/annotations.dart'; /// All generate mock should happened or add here @@ -11,5 +14,8 @@ import 'package:mockito/annotations.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), + MockSpec(), + MockSpec(), ]) void main() {} diff --git a/test/utils/test.mocks.mocks.dart b/test/utils/test.mocks.mocks.dart index b6c414cc0e..b11859d3a4 100644 --- a/test/utils/test.mocks.mocks.dart +++ b/test/utils/test.mocks.mocks.dart @@ -6,12 +6,16 @@ import 'dart:async' as _i4; import 'dart:ffi' as _i6; import 'dart:typed_data' as _i9; +import 'dart:ui' as _i12; +import 'package:lantern/common/ui/custom/internet_checker.dart' as _i14; import 'package:lantern/core/utils/common.dart' as _i2; import 'package:lantern/core/utils/common_desktop.dart' as _i3; import 'package:lantern/core/utils/utils.dart' as _i8; +import 'package:lantern/core/widgtes/custom_bottom_bar.dart' as _i11; import 'package:lantern/features/messaging/messaging.dart' as _i5; import 'package:lantern/features/replica/common.dart' as _i10; +import 'package:lantern/features/vpn/vpn_notifier.dart' as _i13; import 'package:mockito/mockito.dart' as _i1; import 'package:mockito/src/dummies.dart' as _i7; @@ -5279,3 +5283,331 @@ class MockReplicaModel extends _i1.Mock implements _i10.ReplicaModel { ), ) as _i2.ValueListenableBuilder); } + +/// A class which mocks [BottomBarChangeNotifier]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockBottomBarChangeNotifier extends _i1.Mock + implements _i11.BottomBarChangeNotifier { + @override + String get currentIndex => (super.noSuchMethod( + Invocation.getter(#currentIndex), + returnValue: _i7.dummyValue( + this, + Invocation.getter(#currentIndex), + ), + returnValueForMissingStub: _i7.dummyValue( + this, + Invocation.getter(#currentIndex), + ), + ) as String); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + void setCurrentIndex(String? tabName) => super.noSuchMethod( + Invocation.method( + #setCurrentIndex, + [tabName], + ), + returnValueForMissingStub: null, + ); + + @override + void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [VPNChangeNotifier]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockVPNChangeNotifier extends _i1.Mock implements _i13.VPNChangeNotifier { + @override + set timer(_i4.Timer? _timer) => super.noSuchMethod( + Invocation.setter( + #timer, + _timer, + ), + returnValueForMissingStub: null, + ); + + @override + bool get isFlashlightInitialized => (super.noSuchMethod( + Invocation.getter(#isFlashlightInitialized), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + set isFlashlightInitialized(bool? _isFlashlightInitialized) => + super.noSuchMethod( + Invocation.setter( + #isFlashlightInitialized, + _isFlashlightInitialized, + ), + returnValueForMissingStub: null, + ); + + @override + bool get isFlashlightInitializedFailed => (super.noSuchMethod( + Invocation.getter(#isFlashlightInitializedFailed), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + set isFlashlightInitializedFailed(bool? _isFlashlightInitializedFailed) => + super.noSuchMethod( + Invocation.setter( + #isFlashlightInitializedFailed, + _isFlashlightInitializedFailed, + ), + returnValueForMissingStub: null, + ); + + @override + String get flashlightState => (super.noSuchMethod( + Invocation.getter(#flashlightState), + returnValue: _i7.dummyValue( + this, + Invocation.getter(#flashlightState), + ), + returnValueForMissingStub: _i7.dummyValue( + this, + Invocation.getter(#flashlightState), + ), + ) as String); + + @override + set flashlightState(String? _flashlightState) => super.noSuchMethod( + Invocation.setter( + #flashlightState, + _flashlightState, + ), + returnValueForMissingStub: null, + ); + + @override + _i2.ValueNotifier get vpnStatus => (super.noSuchMethod( + Invocation.getter(#vpnStatus), + returnValue: _FakeValueNotifier_1( + this, + Invocation.getter(#vpnStatus), + ), + returnValueForMissingStub: _FakeValueNotifier_1( + this, + Invocation.getter(#vpnStatus), + ), + ) as _i2.ValueNotifier); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool isConnected() => (super.noSuchMethod( + Invocation.method( + #isConnected, + [], + ), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + void toggleConnection() => super.noSuchMethod( + Invocation.method( + #toggleConnection, + [], + ), + returnValueForMissingStub: null, + ); + + @override + _i4.Future updateVpnStatus(String? status) => (super.noSuchMethod( + Invocation.method( + #updateVpnStatus, + [status], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + void initCallbacksDesktop() => super.noSuchMethod( + Invocation.method( + #initCallbacksDesktop, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void initCallbackForMobile() => super.noSuchMethod( + Invocation.method( + #initCallbackForMobile, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void updateStatus( + bool? proxy, + bool? config, + bool? success, + ) => + super.noSuchMethod( + Invocation.method( + #updateStatus, + [ + proxy, + config, + success, + ], + ), + returnValueForMissingStub: null, + ); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} + +/// A class which mocks [InternetStatusProvider]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockInternetStatusProvider extends _i1.Mock + implements _i14.InternetStatusProvider { + @override + bool get isConnected => (super.noSuchMethod( + Invocation.getter(#isConnected), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + bool get hasListeners => (super.noSuchMethod( + Invocation.getter(#hasListeners), + returnValue: false, + returnValueForMissingStub: false, + ) as bool); + + @override + _i4.Future checkInternetConnection() => (super.noSuchMethod( + Invocation.method( + #checkInternetConnection, + [], + ), + returnValue: _i4.Future.value(), + returnValueForMissingStub: _i4.Future.value(), + ) as _i4.Future); + + @override + void dispose() => super.noSuchMethod( + Invocation.method( + #dispose, + [], + ), + returnValueForMissingStub: null, + ); + + @override + void addListener(_i12.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #addListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void removeListener(_i12.VoidCallback? listener) => super.noSuchMethod( + Invocation.method( + #removeListener, + [listener], + ), + returnValueForMissingStub: null, + ); + + @override + void notifyListeners() => super.noSuchMethod( + Invocation.method( + #notifyListeners, + [], + ), + returnValueForMissingStub: null, + ); +} diff --git a/test/utils/test_common.dart b/test/utils/test_common.dart index a6791f2bdc..f5d5461c27 100644 --- a/test/utils/test_common.dart +++ b/test/utils/test_common.dart @@ -1,5 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:lantern/common/ui/custom/internet_checker.dart'; +import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; +import 'package:lantern/features/vpn/vpn_notifier.dart'; import 'package:mockito/mockito.dart'; export 'package:lantern/core/utils/common.dart' hide Verification; @@ -12,8 +15,6 @@ class MockBuildContext extends Mock implements BuildContext {} - - ///Empty builder we can reuse across the test ValueWidgetBuilder intEmptyBuilder = (context, value, child) => const SizedBox(); ValueWidgetBuilder doubleEmptyBuilder = (context, value, child) => const SizedBox(); @@ -22,6 +23,9 @@ ValueWidgetBuilder boolNullableEmptyBuilder = (context, value, child) => ValueWidgetBuilder stringEmptyBuilder = (context, value, child) => const SizedBox(); +///Utils mock + + From 07bd393c3e8fdc572b446d212dd9dee696462fe8 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 9 Oct 2024 15:41:47 +0530 Subject: [PATCH 004/163] Added home test cases for IOS. --- lib/features/home/home.dart | 2 +- lib/features/home/session_model.dart | 4 +- test/features/home/home_test.dart | 304 ++++++++++++++++++++++++--- test/utils/test.mocks.dart | 3 +- test/utils/test.mocks.mocks.dart | 179 ++++++++++++++++ 5 files changed, 458 insertions(+), 34 deletions(-) diff --git a/lib/features/home/home.dart b/lib/features/home/home.dart index b49e53bd38..b41d6abf8e 100644 --- a/lib/features/home/home.dart +++ b/lib/features/home/home.dart @@ -35,7 +35,7 @@ class _HomePageState extends State with TrayListener, WindowListener { @override void initState() { WidgetsBinding.instance.addPostFrameCallback((timeStamp) { - // _startupSequence(); + _startupSequence(); }); super.initState(); } diff --git a/lib/features/home/session_model.dart b/lib/features/home/session_model.dart index 3bead4c8d2..935308faec 100644 --- a/lib/features/home/session_model.dart +++ b/lib/features/home/session_model.dart @@ -16,7 +16,8 @@ const TAB_ACCOUNT = 'account'; const TAB_DEVELOPER = 'developer'; class SessionModel extends Model { - late final EventManager eventManager; + final EventManager eventManager = EventManager('lantern_event_channel'); + ValueNotifier networkAvailable = ValueNotifier(true); late ValueNotifier isTestPlayVersion; late ValueNotifier isStoreVersion; @@ -40,7 +41,6 @@ class SessionModel extends Model { SessionModel() : super('session') { if (isMobile()) { - eventManager = EventManager('lantern_event_channel'); isStoreVersion = singleValueNotifier( 'storeVersion', false, diff --git a/test/features/home/home_test.dart b/test/features/home/home_test.dart index 3d5d7f4879..3412c82c03 100644 --- a/test/features/home/home_test.dart +++ b/test/features/home/home_test.dart @@ -19,9 +19,10 @@ void main() { late MockInternetStatusProvider mockInternetStatusProvider; late MockReplicaModel mockReplicaModel; late MockVpnModel mockVpnModel; + late MockEventManager mockEventManager; setUpAll( - () { + () { mockSessionModel = MockSessionModel(); mockBuildContext = MockBuildContext(); mockMessagingModel = MockMessagingModel(); @@ -30,6 +31,7 @@ void main() { mockInternetStatusProvider = MockInternetStatusProvider(); mockReplicaModel = MockReplicaModel(); mockVpnModel = MockVpnModel(); + mockEventManager = MockEventManager(); // Injection models sl.registerLazySingleton(() => mockSessionModel); @@ -45,17 +47,17 @@ void main() { ); tearDownAll( - () { + () { sl.reset(); }, ); group( - "Home widget render properly for mobile", - () { + "Home widget render properly for Android", + () { testWidgets( "Home widget started with all taps showing", - (widgetTester) async { + (widgetTester) async { final homeWidget = MultiProvider(providers: [ ChangeNotifierProvider.value( value: mockBottomBarChangeNotifier), @@ -73,14 +75,14 @@ void main() { when(mockSessionModel.acceptedTermsVersion(any)) .thenAnswer((invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); }); when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { + (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, true, null); }, ); @@ -92,29 +94,39 @@ void main() { when(mockSessionModel.isAuthEnabled) .thenAnswer((realInvocation) => ValueNotifier(false)); - + when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); - // + when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, "test", null); }, ); when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { + (realInvocation) { return boolEmptyBuilder(mockBuildContext, false, null); }, ); + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)).thenAnswer(( + realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation + .positionalArguments[1] as void Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); ///Stub messaging model when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( @@ -127,14 +139,17 @@ void main() { ///stub vpn model - when(mockVpnModel.vpnStatus(any,any)).thenAnswer((realInvocation) { - final builder = realInvocation.positionalArguments[1] as ValueWidgetBuilder; + when(mockVpnModel.vpnStatus(any, any)).thenAnswer((realInvocation) { + final builder = realInvocation + .positionalArguments[1] as ValueWidgetBuilder; return builder(mockBuildContext, 'disconnected', null); },); ///stub replica model - when(mockReplicaModel.getShowNewBadgeWidget(any)).thenAnswer((realInvocation) { - final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; + when(mockReplicaModel.getShowNewBadgeWidget(any)).thenAnswer(( + realInvocation) { + final builder = realInvocation + .positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, true, null); },); @@ -150,7 +165,7 @@ void main() { testWidgets( "Home widget started with replica disabled", - (widgetTester) async { + (widgetTester) async { final homeWidget = MultiProvider(providers: [ ChangeNotifierProvider.value( value: mockBottomBarChangeNotifier), @@ -167,14 +182,14 @@ void main() { when(mockSessionModel.acceptedTermsVersion(any)) .thenAnswer((invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); }); when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { + (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, true, null); }, ); @@ -187,35 +202,46 @@ void main() { .thenAnswer((realInvocation) => ValueNotifier(false)); when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); // when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, "", null); }, ); when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { + (realInvocation) { return boolEmptyBuilder(mockBuildContext, false, null); }, ); + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)).thenAnswer(( + realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation + .positionalArguments[1] as void Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + await widgetTester.pumpWidget(homeWidget); final bottombar = find.byType(BottomNavigationBar); @@ -227,4 +253,222 @@ void main() { ); }, ); + + // IOS tests + group( + "Home widget render properly for IOS", + () { + testWidgets( + "Home widget started with all taps showing", + (widgetTester) async { + final homeWidget = MultiProvider(providers: [ + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), + ], child: wrapWithMaterialApp(const HomePage())); + + /// stub providers + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + + + /// Stub session model + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(false)); + + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)).thenAnswer(( + realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation + .positionalArguments[1] as void Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + + ///Stub messaging model + when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + + ///stub vpn model + when(mockVpnModel.vpnStatus(any, any)).thenAnswer((realInvocation) { + final builder = realInvocation + .positionalArguments[1] as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + },); + + + await widgetTester.pumpWidget(homeWidget); + + final bottombar = find.byType(BottomNavigationBar); + expect(bottombar, findsOneWidget); + expect(find.byType(CustomBottomBarItem), findsAtLeast(3)); + //replica should be disable on IOS + expect(find.text('discover'.i18n,), findsNothing); + }, + variant: TargetPlatformVariant.only(TargetPlatform.iOS), + ); + }, + ); + + + + // Dekstop tests + group( + "Home widget render properly for IOS", + () { + testWidgets( + "Home widget started with all taps showing", + (widgetTester) async { + final homeWidget = MultiProvider(providers: [ + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), + ], child: wrapWithMaterialApp(const HomePage())); + + /// stub providers + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + + + /// Stub session model + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(false)); + + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)).thenAnswer(( + realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation + .positionalArguments[1] as void Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + + ///Stub messaging model + when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + + ///stub vpn model + when(mockVpnModel.vpnStatus(any, any)).thenAnswer((realInvocation) { + final builder = realInvocation + .positionalArguments[1] as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + },); + + + await widgetTester.pumpWidget(homeWidget); + + final bottombar = find.byType(BottomNavigationBar); + expect(bottombar, findsOneWidget); + expect(find.byType(CustomBottomBarItem), findsAtLeast(3)); + //replica should be disable on IOS + expect(find.text('discover'.i18n,), findsNothing); + }, + variant: TargetPlatformVariant.desktop(), + ); + }, + ); } diff --git a/test/utils/test.mocks.dart b/test/utils/test.mocks.dart index 74fb1372d8..d238ed0905 100644 --- a/test/utils/test.mocks.dart +++ b/test/utils/test.mocks.dart @@ -7,7 +7,7 @@ import 'package:lantern/features/vpn/vpn_notifier.dart'; import 'package:mockito/annotations.dart'; /// All generate mock should happened or add here -/// For generate mock run flutter pub run build_runner build --delete-conflicting-outputs +/// For generate mock run dart run run build_runner build --delete-conflicting-outputs /// So for most other test cases we need import only one class @GenerateNiceMocks([ MockSpec(), @@ -17,5 +17,6 @@ import 'package:mockito/annotations.dart'; MockSpec(), MockSpec(), MockSpec(), + MockSpec(), ]) void main() {} diff --git a/test/utils/test.mocks.mocks.dart b/test/utils/test.mocks.mocks.dart index b11859d3a4..067902f87a 100644 --- a/test/utils/test.mocks.mocks.dart +++ b/test/utils/test.mocks.mocks.dart @@ -144,6 +144,38 @@ class _FakeValueListenable_9 extends _i1.SmartFake ); } +class _FakeMethodCodec_10 extends _i1.SmartFake implements _i2.MethodCodec { + _FakeMethodCodec_10( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeBinaryMessenger_11 extends _i1.SmartFake + implements _i2.BinaryMessenger { + _FakeBinaryMessenger_11( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + +class _FakeStreamSubscription_12 extends _i1.SmartFake + implements _i4.StreamSubscription { + _FakeStreamSubscription_12( + Object parent, + Invocation parentInvocation, + ) : super( + parent, + parentInvocation, + ); +} + /// A class which mocks [SessionModel]. /// /// See the documentation for Mockito's code generation for more information. @@ -5611,3 +5643,150 @@ class MockInternetStatusProvider extends _i1.Mock returnValueForMissingStub: null, ); } + +/// A class which mocks [EventManager]. +/// +/// See the documentation for Mockito's code generation for more information. +class MockEventManager extends _i1.Mock implements _i2.EventManager { + @override + int get nextSubscriberID => (super.noSuchMethod( + Invocation.getter(#nextSubscriberID), + returnValue: 0, + returnValueForMissingStub: 0, + ) as int); + + @override + set nextSubscriberID(int? _nextSubscriberID) => super.noSuchMethod( + Invocation.setter( + #nextSubscriberID, + _nextSubscriberID, + ), + returnValueForMissingStub: null, + ); + + @override + Map< + int, + void Function( + _i2.Event, + Map, + )> get subscribers => (super.noSuchMethod( + Invocation.getter(#subscribers), + returnValue: , + )>{}, + returnValueForMissingStub: , + )>{}, + ) as Map< + int, + void Function( + _i2.Event, + Map, + )>); + + @override + Map> get subscriptions => + (super.noSuchMethod( + Invocation.getter(#subscriptions), + returnValue: >{}, + returnValueForMissingStub: >{}, + ) as Map>); + + @override + String get name => (super.noSuchMethod( + Invocation.getter(#name), + returnValue: _i7.dummyValue( + this, + Invocation.getter(#name), + ), + returnValueForMissingStub: _i7.dummyValue( + this, + Invocation.getter(#name), + ), + ) as String); + + @override + _i2.MethodCodec get codec => (super.noSuchMethod( + Invocation.getter(#codec), + returnValue: _FakeMethodCodec_10( + this, + Invocation.getter(#codec), + ), + returnValueForMissingStub: _FakeMethodCodec_10( + this, + Invocation.getter(#codec), + ), + ) as _i2.MethodCodec); + + @override + _i2.BinaryMessenger get binaryMessenger => (super.noSuchMethod( + Invocation.getter(#binaryMessenger), + returnValue: _FakeBinaryMessenger_11( + this, + Invocation.getter(#binaryMessenger), + ), + returnValueForMissingStub: _FakeBinaryMessenger_11( + this, + Invocation.getter(#binaryMessenger), + ), + ) as _i2.BinaryMessenger); + + @override + void Function() subscribe( + _i2.Event? event, + void Function( + _i2.Event, + Map, + )? onNewEvent, + ) => + (super.noSuchMethod( + Invocation.method( + #subscribe, + [ + event, + onNewEvent, + ], + ), + returnValue: () {}, + returnValueForMissingStub: () {}, + ) as void Function()); + + @override + _i4.StreamSubscription listen(_i4.Stream? stream) => + (super.noSuchMethod( + Invocation.method( + #listen, + [stream], + ), + returnValue: _FakeStreamSubscription_12( + this, + Invocation.method( + #listen, + [stream], + ), + ), + returnValueForMissingStub: _FakeStreamSubscription_12( + this, + Invocation.method( + #listen, + [stream], + ), + ), + ) as _i4.StreamSubscription); + + @override + _i4.Stream receiveBroadcastStream([dynamic arguments]) => + (super.noSuchMethod( + Invocation.method( + #receiveBroadcastStream, + [arguments], + ), + returnValue: _i4.Stream.empty(), + returnValueForMissingStub: _i4.Stream.empty(), + ) as _i4.Stream); +} From c35c821ec240ec51a94fa22abc60b66ecec659c5 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 9 Oct 2024 18:05:12 +0530 Subject: [PATCH 005/163] started adding test cases for vpn tap. --- lib/core/utils/common.dart | 7 + lib/features/account/split_tunneling.dart | 4 +- lib/features/vpn/vpn_tab.dart | 3 +- macos/Podfile.lock | 11 ++ test/features/home/home_test.dart | 205 +++++++++++----------- test/features/vpn/vpn_tap_test.dart | 189 ++++++++++++++++++++ test/utils/test_common.dart | 5 +- 7 files changed, 317 insertions(+), 107 deletions(-) create mode 100644 test/features/vpn/vpn_tap_test.dart diff --git a/lib/core/utils/common.dart b/lib/core/utils/common.dart index ccf42a858c..c0bc531ca8 100644 --- a/lib/core/utils/common.dart +++ b/lib/core/utils/common.dart @@ -147,6 +147,13 @@ bool isMobile() { return Platform.isAndroid || Platform.isIOS; } +bool isAndroid() { + if (kDebugMode) { + return (defaultTargetPlatform == TargetPlatform.android); + } + return Platform.isAndroid ; +} + bool isDesktop() { if (kDebugMode) { return (defaultTargetPlatform == TargetPlatform.macOS || diff --git a/lib/features/account/split_tunneling.dart b/lib/features/account/split_tunneling.dart index f569761d00..e340f9b4a1 100644 --- a/lib/features/account/split_tunneling.dart +++ b/lib/features/account/split_tunneling.dart @@ -301,6 +301,8 @@ class _SplitTunnelingAppsListState extends State { // SplitTunnelingWidget is the split tunneling widget that appears on the main VPN screen class SplitTunnelingWidget extends StatelessWidget { + const SplitTunnelingWidget({super.key}); + @override Widget build(BuildContext context) { return sessionModel.splitTunneling( @@ -309,7 +311,7 @@ class SplitTunnelingWidget extends StatelessWidget { Navigator.push( context, MaterialPageRoute( - builder: (context) => SplitTunneling(), + builder: (context) => const SplitTunneling(), ), ); }, diff --git a/lib/features/vpn/vpn_tab.dart b/lib/features/vpn/vpn_tab.dart index 69e88a3941..458134e7e5 100644 --- a/lib/features/vpn/vpn_tab.dart +++ b/lib/features/vpn/vpn_tab.dart @@ -87,7 +87,7 @@ class VPNTab extends StatelessWidget { VPNStatus(), const CDivider(height: 32.0), const ServerLocationWidget(), - if (Platform.isAndroid) ...{ + if (isAndroid()) ...{ const CDivider(height: 32.0), SplitTunnelingWidget(), }, @@ -114,6 +114,7 @@ class VPNTapSkeleton extends StatelessWidget { @override Widget build(BuildContext context) { + print("VPNTapSkeleton"); return Shimmer.fromColors( baseColor: Colors.grey.shade100, highlightColor: Colors.grey.shade200, diff --git a/macos/Podfile.lock b/macos/Podfile.lock index fccd90a3e7..ef0d64c9cf 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -3,6 +3,7 @@ PODS: - FlutterMacOS - audioplayers_darwin (0.0.1): - FlutterMacOS + - CocoaAsyncSocket (7.6.5) - connectivity_plus (0.0.1): - Flutter - FlutterMacOS @@ -29,6 +30,10 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - patrol (0.0.1): + - CocoaAsyncSocket (~> 7.6) + - Flutter + - FlutterMacOS - screen_retriever (0.0.1): - FlutterMacOS - Sentry/HybridSDK (8.36.0) @@ -68,6 +73,7 @@ DEPENDENCIES: - in_app_purchase_storekit (from `Flutter/ephemeral/.symlinks/plugins/in_app_purchase_storekit/darwin`) - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) + - patrol (from `Flutter/ephemeral/.symlinks/plugins/patrol/darwin`) - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) - sentry_flutter (from `Flutter/ephemeral/.symlinks/plugins/sentry_flutter/macos`) - share_plus (from `Flutter/ephemeral/.symlinks/plugins/share_plus/macos`) @@ -80,6 +86,7 @@ DEPENDENCIES: SPEC REPOS: trunk: + - CocoaAsyncSocket - OrderedSet - Sentry @@ -110,6 +117,8 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos path_provider_foundation: :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin + patrol: + :path: Flutter/ephemeral/.symlinks/plugins/patrol/darwin screen_retriever: :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos sentry_flutter: @@ -132,6 +141,7 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: app_links: 10e0a0ab602ffaf34d142cd4862f29d34b303b2a audioplayers_darwin: dcad41de4fbd0099cb3749f7ab3b0cb8f70b810c + CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db desktop_webview_window: d4365e71bcd4e1aa0c14cf0377aa24db0c16a7e2 device_info_plus: ce1b7762849d3ec103d0e0517299f2db7ad60720 @@ -144,6 +154,7 @@ SPEC CHECKSUMS: OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 package_info_plus: fa739dd842b393193c5ca93c26798dff6e3d0e0c path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + patrol: 0564cee315ff6c86fb802b3647db05cc2d3d0624 screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 Sentry: f8374b5415bc38dfb5645941b3ae31230fbeae57 sentry_flutter: 0eb93e5279eb41e2392212afe1ccd2fecb4f8cbe diff --git a/test/features/home/home_test.dart b/test/features/home/home_test.dart index 3412c82c03..5f97fdb64f 100644 --- a/test/features/home/home_test.dart +++ b/test/features/home/home_test.dart @@ -22,7 +22,7 @@ void main() { late MockEventManager mockEventManager; setUpAll( - () { + () { mockSessionModel = MockSessionModel(); mockBuildContext = MockBuildContext(); mockMessagingModel = MockMessagingModel(); @@ -47,17 +47,17 @@ void main() { ); tearDownAll( - () { + () { sl.reset(); }, ); group( "Home widget render properly for Android", - () { + () { testWidgets( "Home widget started with all taps showing", - (widgetTester) async { + (widgetTester) async { final homeWidget = MultiProvider(providers: [ ChangeNotifierProvider.value( value: mockBottomBarChangeNotifier), @@ -70,19 +70,18 @@ void main() { /// stub providers when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); - /// Stub session model when(mockSessionModel.acceptedTermsVersion(any)) .thenAnswer((invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); }); when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { + (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, true, null); }, ); @@ -94,9 +93,8 @@ void main() { when(mockSessionModel.isAuthEnabled) .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, false, null); @@ -104,25 +102,25 @@ void main() { ); when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, "test", null); }, ); when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { + (realInvocation) { return boolEmptyBuilder(mockBuildContext, false, null); }, ); when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)).thenAnswer(( - realInvocation) { + when(mockEventManager.subscribe(any, any)) + .thenAnswer((realInvocation) { final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation - .positionalArguments[1] as void Function(Event, Map); + final onNewEvent = realInvocation.positionalArguments[1] as void + Function(Event, Map); return () { onNewEvent(event, {}); }; @@ -130,28 +128,30 @@ void main() { ///Stub messaging model when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); - ///stub vpn model - when(mockVpnModel.vpnStatus(any, any)).thenAnswer((realInvocation) { - final builder = realInvocation - .positionalArguments[1] as ValueWidgetBuilder; - return builder(mockBuildContext, 'disconnected', null); - },); + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[1] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + }, + ); ///stub replica model - when(mockReplicaModel.getShowNewBadgeWidget(any)).thenAnswer(( - realInvocation) { - final builder = realInvocation - .positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, true, null); - },); + when(mockReplicaModel.getShowNewBadgeWidget(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); await widgetTester.pumpWidget(homeWidget); @@ -165,7 +165,7 @@ void main() { testWidgets( "Home widget started with replica disabled", - (widgetTester) async { + (widgetTester) async { final homeWidget = MultiProvider(providers: [ ChangeNotifierProvider.value( value: mockBottomBarChangeNotifier), @@ -182,14 +182,14 @@ void main() { when(mockSessionModel.acceptedTermsVersion(any)) .thenAnswer((invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); }); when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { + (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, true, null); }, ); @@ -202,41 +202,41 @@ void main() { .thenAnswer((realInvocation) => ValueNotifier(false)); when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); // when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, "", null); }, ); when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { + (realInvocation) { return boolEmptyBuilder(mockBuildContext, false, null); }, ); when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)).thenAnswer(( - realInvocation) { + when(mockEventManager.subscribe(any, any)) + .thenAnswer((realInvocation) { final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation - .positionalArguments[1] as void Function(Event, Map); + final onNewEvent = realInvocation.positionalArguments[1] as void + Function(Event, Map); return () { onNewEvent(event, {}); }; @@ -257,10 +257,10 @@ void main() { // IOS tests group( "Home widget render properly for IOS", - () { + () { testWidgets( "Home widget started with all taps showing", - (widgetTester) async { + (widgetTester) async { final homeWidget = MultiProvider(providers: [ ChangeNotifierProvider.value( value: mockBottomBarChangeNotifier), @@ -273,19 +273,18 @@ void main() { /// stub providers when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); - /// Stub session model when(mockSessionModel.acceptedTermsVersion(any)) .thenAnswer((invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); }); when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { + (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, true, null); }, ); @@ -297,35 +296,34 @@ void main() { when(mockSessionModel.isAuthEnabled) .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, "", null); }, ); when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { + (realInvocation) { return boolEmptyBuilder(mockBuildContext, false, null); }, ); when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)).thenAnswer(( - realInvocation) { + when(mockEventManager.subscribe(any, any)) + .thenAnswer((realInvocation) { final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation - .positionalArguments[1] as void Function(Event, Map); + final onNewEvent = realInvocation.positionalArguments[1] as void + Function(Event, Map); return () { onNewEvent(event, {}); }; @@ -333,21 +331,21 @@ void main() { ///Stub messaging model when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); - ///stub vpn model - when(mockVpnModel.vpnStatus(any, any)).thenAnswer((realInvocation) { - final builder = realInvocation - .positionalArguments[1] as ValueWidgetBuilder; - return builder(mockBuildContext, 'disconnected', null); - },); - + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[1] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + }, + ); await widgetTester.pumpWidget(homeWidget); @@ -355,22 +353,24 @@ void main() { expect(bottombar, findsOneWidget); expect(find.byType(CustomBottomBarItem), findsAtLeast(3)); //replica should be disable on IOS - expect(find.text('discover'.i18n,), findsNothing); + expect( + find.text( + 'discover'.i18n, + ), + findsNothing); }, variant: TargetPlatformVariant.only(TargetPlatform.iOS), ); }, ); - - // Dekstop tests group( - "Home widget render properly for IOS", - () { + "Home widget render properly for Desktop", + () { testWidgets( "Home widget started with all taps showing", - (widgetTester) async { + (widgetTester) async { final homeWidget = MultiProvider(providers: [ ChangeNotifierProvider.value( value: mockBottomBarChangeNotifier), @@ -383,20 +383,19 @@ void main() { /// stub providers when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); - /// Stub session model when(mockSessionModel.acceptedTermsVersion(any)) .thenAnswer((invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); }); when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { + (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, true, null); + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); }, ); @@ -407,35 +406,34 @@ void main() { when(mockSessionModel.isAuthEnabled) .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, "", null); }, ); when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { + (realInvocation) { return boolEmptyBuilder(mockBuildContext, false, null); }, ); when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)).thenAnswer(( - realInvocation) { + when(mockEventManager.subscribe(any, any)) + .thenAnswer((realInvocation) { final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation - .positionalArguments[1] as void Function(Event, Map); + final onNewEvent = realInvocation.positionalArguments[1] as void + Function(Event, Map); return () { onNewEvent(event, {}); }; @@ -443,29 +441,28 @@ void main() { ///Stub messaging model when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); - ///stub vpn model - when(mockVpnModel.vpnStatus(any, any)).thenAnswer((realInvocation) { - final builder = realInvocation - .positionalArguments[1] as ValueWidgetBuilder; - return builder(mockBuildContext, 'disconnected', null); - },); - + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[1] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + }, + ); await widgetTester.pumpWidget(homeWidget); final bottombar = find.byType(BottomNavigationBar); expect(bottombar, findsOneWidget); - expect(find.byType(CustomBottomBarItem), findsAtLeast(3)); - //replica should be disable on IOS - expect(find.text('discover'.i18n,), findsNothing); + expect(find.byType(CustomBottomBarItem), findsAtLeast(2)); + expect(find.text('discover'.i18n), findsNothing); }, variant: TargetPlatformVariant.desktop(), ); diff --git a/test/features/vpn/vpn_tap_test.dart b/test/features/vpn/vpn_tap_test.dart new file mode 100644 index 0000000000..f50dee549d --- /dev/null +++ b/test/features/vpn/vpn_tap_test.dart @@ -0,0 +1,189 @@ +import 'package:fixnum/fixnum.dart'; +import 'package:lantern/common/ui/custom/internet_checker.dart'; +import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; +import 'package:lantern/features/account/split_tunneling.dart'; +import 'package:lantern/features/vpn/vpn_bandwidth.dart'; +import 'package:lantern/features/vpn/vpn_notifier.dart'; +import 'package:lantern/features/vpn/vpn_pro_banner.dart'; +import 'package:lantern/features/vpn/vpn_server_location.dart'; +import 'package:lantern/features/vpn/vpn_switch.dart'; +import 'package:lantern/features/vpn/vpn_tab.dart'; + +import '../../utils/test_common.dart'; +import '../../utils/widgets.dart'; + +void main() { + late MockSessionModel mockSessionModel; + late MockBuildContext mockBuildContext; + late MockMessagingModel mockMessagingModel; + late MockBottomBarChangeNotifier mockBottomBarChangeNotifier; + late MockVPNChangeNotifier mockVPNChangeNotifier; + late MockInternetStatusProvider mockInternetStatusProvider; + late MockReplicaModel mockReplicaModel; + late MockVpnModel mockVpnModel; + late MockEventManager mockEventManager; + + setUpAll( + () { + mockSessionModel = MockSessionModel(); + mockBuildContext = MockBuildContext(); + mockMessagingModel = MockMessagingModel(); + mockBottomBarChangeNotifier = MockBottomBarChangeNotifier(); + mockVPNChangeNotifier = MockVPNChangeNotifier(); + mockInternetStatusProvider = MockInternetStatusProvider(); + mockReplicaModel = MockReplicaModel(); + mockVpnModel = MockVpnModel(); + mockEventManager = MockEventManager(); + + // Injection models + sl.registerLazySingleton(() => mockSessionModel); + // sl.registerLazySingleton(() => mockMessagingModel); + // sl.registerLazySingleton(() => mockReplicaModel); + sl.registerLazySingleton(() => mockVpnModel); + + // mock the providers + mockBottomBarChangeNotifier = MockBottomBarChangeNotifier(); + mockVPNChangeNotifier = MockVPNChangeNotifier(); + mockInternetStatusProvider = MockInternetStatusProvider(); + }, + ); + + tearDownAll( + () { + sl.reset(); + }, + ); + group( + "render VPN tap for mobile", + () { + testWidgets( + 'render VPN tap skeleton for mobile', + (widgetTester) async { + final vpnTapWidget = MultiProvider(providers: [ + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), + ], child: wrapWithMaterialApp(const VPNTab())); + + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.shouldShowAds(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + when(mockSessionModel.proxyAvailable) + .thenAnswer((realInvocation) => ValueNotifier(true)); + + ///stub vpn models + + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[1] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + }, + ); + + await widgetTester.pumpWidget(vpnTapWidget); + + expect(find.byType(VPNTapSkeleton), findsOneWidget); + }, + ); + + testWidgets( + 'render VPN tap for mobile', + (widgetTester) async { + final vpnTapWidget = MultiProvider(providers: [ + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), + ], child: wrapWithMaterialApp(const VPNTab())); + + when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); + + /// Session model stubs + when(mockSessionModel.proxyAvailable) + .thenAnswer((realInvocation) => ValueNotifier(true)); + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + + when(mockSessionModel.shouldShowAds(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + when(mockSessionModel.serverInfo(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, null, null); + }, + ); + + when(mockSessionModel.bandwidth(any)).thenAnswer((realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + var bandwidth = Bandwidth() + ..allowed = Int64(250) + ..remaining = Int64(200) + ..percent = Int64(20); + + return builder(mockBuildContext, bandwidth, null); + }); + + when(mockSessionModel.splitTunneling(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + ///stub vpn models + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[1] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + }, + ); + + await widgetTester.pumpWidget(vpnTapWidget); + + expect(find.byType(VPNTapSkeleton), findsNothing); + expect(find.byType(ProBanner), findsOneWidget); + expect(find.byType(VPNSwitch), findsOneWidget); + expect(find.byType(ServerLocationWidget), findsOneWidget); + expect(find.byType(SplitTunnelingWidget), findsOneWidget); + expect(find.byType(VPNBandwidth), findsNothing); + }, + variant: TargetPlatformVariant.only(TargetPlatform.android), + ); + }, + ); +} diff --git a/test/utils/test_common.dart b/test/utils/test_common.dart index f5d5461c27..af31f8979d 100644 --- a/test/utils/test_common.dart +++ b/test/utils/test_common.dart @@ -4,7 +4,7 @@ import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; import 'package:lantern/features/vpn/vpn_notifier.dart'; import 'package:mockito/mockito.dart'; - +import 'dart:ui' as ui; export 'package:lantern/core/utils/common.dart' hide Verification; export 'test.mocks.mocks.dart'; export 'package:mockito/mockito.dart'; @@ -23,6 +23,9 @@ ValueWidgetBuilder boolNullableEmptyBuilder = (context, value, child) => ValueWidgetBuilder stringEmptyBuilder = (context, value, child) => const SizedBox(); +final desktopWindowSize = const ui.Size(360, 712); + + ///Utils mock From 2194b7de787fb28864cbf24f92a322bb475fbf47 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 10 Oct 2024 15:31:10 +0530 Subject: [PATCH 006/163] Added test for vpn tap. --- lib/common/ui/continue_arrow.dart | 2 +- lib/features/account/split_tunneling.dart | 2 +- lib/features/home/session_model.dart | 2 +- lib/features/vpn/vpn_status.dart | 2 + lib/features/vpn/vpn_tab.dart | 2 +- test/features/vpn/vpn_tap_test.dart | 174 ++++++++++++++++++---- 6 files changed, 155 insertions(+), 29 deletions(-) diff --git a/lib/common/ui/continue_arrow.dart b/lib/common/ui/continue_arrow.dart index 65ba61d8e0..c2420aedf1 100644 --- a/lib/common/ui/continue_arrow.dart +++ b/lib/common/ui/continue_arrow.dart @@ -3,7 +3,7 @@ import 'package:lantern/core/utils/common.dart'; //// An arrow that indicates that clicking on the containing control will continue to a new //// screen. It is sensitive to the current language's directionality. class ContinueArrow extends StatelessWidget { - const ContinueArrow(); + const ContinueArrow({super.key}); @override Widget build(BuildContext context) { diff --git a/lib/features/account/split_tunneling.dart b/lib/features/account/split_tunneling.dart index e340f9b4a1..8140620795 100644 --- a/lib/features/account/split_tunneling.dart +++ b/lib/features/account/split_tunneling.dart @@ -332,7 +332,7 @@ class SplitTunnelingWidget extends StatelessWidget { style: tsSubtitle4, ), ), - mirrorLTR(context: context, child: const ContinueArrow()) + const ContinueArrow(), ], ), ), diff --git a/lib/features/home/session_model.dart b/lib/features/home/session_model.dart index 198f6a0128..ed50b9a484 100644 --- a/lib/features/home/session_model.dart +++ b/lib/features/home/session_model.dart @@ -173,7 +173,7 @@ class SessionModel extends Model { Widget acceptedTermsVersion(ValueWidgetBuilder builder) { if (isMobile()) { return subscribedSingleValueBuilder('accepted_terms_version', - builder: builder); + builder: builder,defaultValue: 0); } return configValueBuilder('accepted_terms_version', builder, (value) => value?.chat.acceptedTermsVersion ?? 0); diff --git a/lib/features/vpn/vpn_status.dart b/lib/features/vpn/vpn_status.dart index ed5acb387d..128b2a4a1a 100644 --- a/lib/features/vpn/vpn_status.dart +++ b/lib/features/vpn/vpn_status.dart @@ -1,6 +1,8 @@ import 'package:lantern/features/vpn/vpn.dart'; class VPNStatus extends StatelessWidget { + const VPNStatus({super.key}); + @override Widget build(BuildContext context) { return vpnModel.vpnStatus(context, diff --git a/lib/features/vpn/vpn_tab.dart b/lib/features/vpn/vpn_tab.dart index 458134e7e5..c5095dd79e 100644 --- a/lib/features/vpn/vpn_tab.dart +++ b/lib/features/vpn/vpn_tab.dart @@ -11,7 +11,7 @@ import 'vpn_status.dart'; import 'vpn_switch.dart'; class VPNTab extends StatelessWidget { - const VPNTab({Key? key}) : super(key: key); + const VPNTab({super.key}); @override Widget build(BuildContext context) { diff --git a/test/features/vpn/vpn_tap_test.dart b/test/features/vpn/vpn_tap_test.dart index f50dee549d..82a7a0d040 100644 --- a/test/features/vpn/vpn_tap_test.dart +++ b/test/features/vpn/vpn_tap_test.dart @@ -53,11 +53,53 @@ void main() { sl.reset(); }, ); + group( - "render VPN tap for mobile", + "render VPN tap", () { testWidgets( - 'render VPN tap skeleton for mobile', + 'render VPN tap for mobile', + (widgetTester) async { + final vpnTapWidget = MultiProvider(providers: [ + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), + ], child: wrapWithMaterialApp(const VPNTab())); + + when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); + + // Session model stubs + stubSessionModel(mockSessionModel: mockSessionModel, mockBuildContext: mockBuildContext,); + + + + + ///stub vpn models + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[1] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + }, + ); + + await widgetTester.pumpWidget(vpnTapWidget); + expect(find.byType(VPNTapSkeleton), findsNothing); + expect(find.byType(ProBanner), findsOneWidget); + expect(find.byType(VPNSwitch), findsOneWidget); + expect(find.byType(ServerLocationWidget), findsOneWidget); + expect(find.byType(SplitTunnelingWidget), findsOneWidget); + expect(find.byType(VPNBandwidth), findsOneWidget); + expect(find.byType(LinearProgressIndicator), findsOneWidget); + }, + variant: TargetPlatformVariant.only(TargetPlatform.android), + ); + + testWidgets( + 'render VPN tap for ios', (widgetTester) async { final vpnTapWidget = MultiProvider(providers: [ ChangeNotifierProvider.value( @@ -68,6 +110,11 @@ void main() { value: mockInternetStatusProvider), ], child: wrapWithMaterialApp(const VPNTab())); + when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); + + /// Session model stubs + when(mockSessionModel.proxyAvailable) + .thenAnswer((realInvocation) => ValueNotifier(true)); when(mockSessionModel.proUser(any)).thenAnswer( (realInvocation) { final builder = realInvocation.positionalArguments[0] @@ -84,11 +131,34 @@ void main() { }, ); - when(mockSessionModel.proxyAvailable) - .thenAnswer((realInvocation) => ValueNotifier(true)); + when(mockSessionModel.serverInfo(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, null, null); + }, + ); - ///stub vpn models + when(mockSessionModel.bandwidth(any)).thenAnswer((realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + var bandwidth = Bandwidth() + ..allowed = Int64(250) + ..remaining = Int64(200) + ..percent = Int64(28); + return builder(mockBuildContext, bandwidth, null); + }); + /// even with spilt tunneling widget should nto show + when(mockSessionModel.splitTunneling(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + ///stub vpn models when(mockVpnModel.vpnStatus(any, any)).thenAnswer( (realInvocation) { final builder = realInvocation.positionalArguments[1] @@ -98,13 +168,19 @@ void main() { ); await widgetTester.pumpWidget(vpnTapWidget); - - expect(find.byType(VPNTapSkeleton), findsOneWidget); + expect(find.byType(VPNTapSkeleton), findsNothing); + expect(find.byType(ProBanner), findsOneWidget); + expect(find.byType(VPNSwitch), findsOneWidget); + expect(find.byType(ServerLocationWidget), findsOneWidget); + expect(find.byType(SplitTunnelingWidget), findsNothing); + expect(find.byType(VPNBandwidth), findsOneWidget); + expect(find.byType(LinearProgressIndicator), findsOneWidget); }, + variant: TargetPlatformVariant.only(TargetPlatform.iOS), ); testWidgets( - 'render VPN tap for mobile', + 'render VPN tap for desktop', (widgetTester) async { final vpnTapWidget = MultiProvider(providers: [ ChangeNotifierProvider.value( @@ -116,6 +192,8 @@ void main() { ], child: wrapWithMaterialApp(const VPNTab())); when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); + when(mockVPNChangeNotifier.vpnStatus) + .thenAnswer((realInvocation) => ValueNotifier('disconnected')); /// Session model stubs when(mockSessionModel.proxyAvailable) @@ -128,15 +206,6 @@ void main() { }, ); - - when(mockSessionModel.shouldShowAds(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, "", null); - }, - ); - when(mockSessionModel.serverInfo(any)).thenAnswer( (realInvocation) { final builder = realInvocation.positionalArguments[0] @@ -151,20 +220,21 @@ void main() { var bandwidth = Bandwidth() ..allowed = Int64(250) ..remaining = Int64(200) - ..percent = Int64(20); - + ..percent = Int64(28); return builder(mockBuildContext, bandwidth, null); }); + /// even with spilt tunneling widget should nto show when(mockSessionModel.splitTunneling(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); ///stub vpn models + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( (realInvocation) { final builder = realInvocation.positionalArguments[1] @@ -174,15 +244,69 @@ void main() { ); await widgetTester.pumpWidget(vpnTapWidget); - expect(find.byType(VPNTapSkeleton), findsNothing); expect(find.byType(ProBanner), findsOneWidget); expect(find.byType(VPNSwitch), findsOneWidget); expect(find.byType(ServerLocationWidget), findsOneWidget); - expect(find.byType(SplitTunnelingWidget), findsOneWidget); - expect(find.byType(VPNBandwidth), findsNothing); + expect(find.byType(SplitTunnelingWidget), findsNothing); + expect(find.byType(VPNBandwidth), findsOneWidget); + expect(find.byType(LinearProgressIndicator), findsOneWidget); }, - variant: TargetPlatformVariant.only(TargetPlatform.android), + variant: TargetPlatformVariant.desktop(), + ); + }, + ); + + group( + "render common widgets properly for al platforms", + () { + testWidgets( + 'render VPN tap skeleton ', + (widgetTester) async { + final vpnTapWidget = MultiProvider(providers: [ + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), + ], child: wrapWithMaterialApp(const VPNTab())); + + if (isDesktop()) { + when(mockVPNChangeNotifier.vpnStatus) + .thenReturn(ValueNotifier('disconnected')); + } + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.shouldShowAds(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + when(mockSessionModel.proxyAvailable) + .thenAnswer((realInvocation) => ValueNotifier(true)); + + ///stub vpn models + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[1] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + }, + ); + await widgetTester.pumpWidget(vpnTapWidget); + expect(find.byType(VPNTapSkeleton), findsOneWidget); + }, + variant: TargetPlatformVariant.all(excluding: {TargetPlatform.fuchsia}), ); }, ); From 01054b303aa62fea6ffdd34370b33f5e430afed7 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 10 Oct 2024 15:39:12 +0530 Subject: [PATCH 007/163] Create reuseable method. --- test/features/vpn/vpn_tap_test.dart | 173 +++++----------------------- test/utils/test_common.dart | 95 ++++++++++++--- 2 files changed, 104 insertions(+), 164 deletions(-) diff --git a/test/features/vpn/vpn_tap_test.dart b/test/features/vpn/vpn_tap_test.dart index 82a7a0d040..1763fb78a7 100644 --- a/test/features/vpn/vpn_tap_test.dart +++ b/test/features/vpn/vpn_tap_test.dart @@ -1,4 +1,3 @@ -import 'package:fixnum/fixnum.dart'; import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; import 'package:lantern/features/account/split_tunneling.dart'; @@ -15,30 +14,23 @@ import '../../utils/widgets.dart'; void main() { late MockSessionModel mockSessionModel; late MockBuildContext mockBuildContext; - late MockMessagingModel mockMessagingModel; late MockBottomBarChangeNotifier mockBottomBarChangeNotifier; late MockVPNChangeNotifier mockVPNChangeNotifier; late MockInternetStatusProvider mockInternetStatusProvider; - late MockReplicaModel mockReplicaModel; late MockVpnModel mockVpnModel; - late MockEventManager mockEventManager; - setUpAll( + setUp( () { mockSessionModel = MockSessionModel(); mockBuildContext = MockBuildContext(); - mockMessagingModel = MockMessagingModel(); + mockBottomBarChangeNotifier = MockBottomBarChangeNotifier(); mockVPNChangeNotifier = MockVPNChangeNotifier(); mockInternetStatusProvider = MockInternetStatusProvider(); - mockReplicaModel = MockReplicaModel(); mockVpnModel = MockVpnModel(); - mockEventManager = MockEventManager(); // Injection models sl.registerLazySingleton(() => mockSessionModel); - // sl.registerLazySingleton(() => mockMessagingModel); - // sl.registerLazySingleton(() => mockReplicaModel); sl.registerLazySingleton(() => mockVpnModel); // mock the providers @@ -48,7 +40,7 @@ void main() { }, ); - tearDownAll( + tearDown( () { sl.reset(); }, @@ -71,21 +63,16 @@ void main() { when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); - // Session model stubs - stubSessionModel(mockSessionModel: mockSessionModel, mockBuildContext: mockBuildContext,); - - - - - ///stub vpn models - when(mockVpnModel.vpnStatus(any, any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[1] - as ValueWidgetBuilder; - return builder(mockBuildContext, 'disconnected', null); - }, + /// session model stubs + stubSessionModel( + mockSessionModel: mockSessionModel, + mockBuildContext: mockBuildContext, ); + ///vpn models stub + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); + await widgetTester.pumpWidget(vpnTapWidget); expect(find.byType(VPNTapSkeleton), findsNothing); expect(find.byType(ProBanner), findsOneWidget); @@ -113,59 +100,13 @@ void main() { when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); /// Session model stubs - when(mockSessionModel.proxyAvailable) - .thenAnswer((realInvocation) => ValueNotifier(true)); - when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.shouldShowAds(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, "", null); - }, - ); - - when(mockSessionModel.serverInfo(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, null, null); - }, - ); - - when(mockSessionModel.bandwidth(any)).thenAnswer((realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - var bandwidth = Bandwidth() - ..allowed = Int64(250) - ..remaining = Int64(200) - ..percent = Int64(28); - return builder(mockBuildContext, bandwidth, null); - }); - - /// even with spilt tunneling widget should nto show - when(mockSessionModel.splitTunneling(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); + stubSessionModel( + mockSessionModel: mockSessionModel, + mockBuildContext: mockBuildContext); ///stub vpn models - when(mockVpnModel.vpnStatus(any, any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[1] - as ValueWidgetBuilder; - return builder(mockBuildContext, 'disconnected', null); - }, - ); + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); await widgetTester.pumpWidget(vpnTapWidget); expect(find.byType(VPNTapSkeleton), findsNothing); @@ -195,53 +136,13 @@ void main() { when(mockVPNChangeNotifier.vpnStatus) .thenAnswer((realInvocation) => ValueNotifier('disconnected')); - /// Session model stubs - when(mockSessionModel.proxyAvailable) - .thenAnswer((realInvocation) => ValueNotifier(true)); - when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.serverInfo(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, null, null); - }, - ); - - when(mockSessionModel.bandwidth(any)).thenAnswer((realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - var bandwidth = Bandwidth() - ..allowed = Int64(250) - ..remaining = Int64(200) - ..percent = Int64(28); - return builder(mockBuildContext, bandwidth, null); - }); - - /// even with spilt tunneling widget should nto show - when(mockSessionModel.splitTunneling(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); + stubSessionModel( + mockSessionModel: mockSessionModel, + mockBuildContext: mockBuildContext); ///stub vpn models - - when(mockVpnModel.vpnStatus(any, any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[1] - as ValueWidgetBuilder; - return builder(mockBuildContext, 'disconnected', null); - }, - ); + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); await widgetTester.pumpWidget(vpnTapWidget); expect(find.byType(VPNTapSkeleton), findsNothing); @@ -276,33 +177,11 @@ void main() { when(mockVPNChangeNotifier.vpnStatus) .thenReturn(ValueNotifier('disconnected')); } - when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.shouldShowAds(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, "", null); - }, - ); - - when(mockSessionModel.proxyAvailable) - .thenAnswer((realInvocation) => ValueNotifier(true)); - - ///stub vpn models - when(mockVpnModel.vpnStatus(any, any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[1] - as ValueWidgetBuilder; - return builder(mockBuildContext, 'disconnected', null); - }, - ); + stubSessionModel( + mockSessionModel: mockSessionModel, + mockBuildContext: mockBuildContext); + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); await widgetTester.pumpWidget(vpnTapWidget); expect(find.byType(VPNTapSkeleton), findsOneWidget); }, diff --git a/test/utils/test_common.dart b/test/utils/test_common.dart index af31f8979d..48a0c733d6 100644 --- a/test/utils/test_common.dart +++ b/test/utils/test_common.dart @@ -1,34 +1,95 @@ +import 'dart:ui' as ui; -import 'package:flutter/material.dart'; -import 'package:lantern/common/ui/custom/internet_checker.dart'; -import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; -import 'package:lantern/features/vpn/vpn_notifier.dart'; +import 'package:fixnum/fixnum.dart'; +import 'package:lantern/core/utils/common.dart'; import 'package:mockito/mockito.dart'; -import 'dart:ui' as ui; -export 'package:lantern/core/utils/common.dart' hide Verification; -export 'test.mocks.mocks.dart'; -export 'package:mockito/mockito.dart'; -export 'package:flutter_test/flutter_test.dart'; +import 'test.mocks.mocks.dart'; -class MockBuildContext extends Mock implements BuildContext {} +export 'package:flutter_test/flutter_test.dart'; +export 'package:lantern/core/utils/common.dart' hide Verification; +export 'package:mockito/mockito.dart'; +export 'test.mocks.mocks.dart'; +class MockBuildContext extends Mock implements BuildContext {} ///Empty builder we can reuse across the test -ValueWidgetBuilder intEmptyBuilder = (context, value, child) => const SizedBox(); -ValueWidgetBuilder doubleEmptyBuilder = (context, value, child) => const SizedBox(); -ValueWidgetBuilder boolEmptyBuilder = (context, value, child) => const SizedBox(); -ValueWidgetBuilder boolNullableEmptyBuilder = (context, value, child) => const SizedBox(); -ValueWidgetBuilder stringEmptyBuilder = (context, value, child) => const SizedBox(); - +ValueWidgetBuilder intEmptyBuilder = + (context, value, child) => const SizedBox(); +ValueWidgetBuilder doubleEmptyBuilder = + (context, value, child) => const SizedBox(); +ValueWidgetBuilder boolEmptyBuilder = + (context, value, child) => const SizedBox(); +ValueWidgetBuilder boolNullableEmptyBuilder = + (context, value, child) => const SizedBox(); +ValueWidgetBuilder stringEmptyBuilder = + (context, value, child) => const SizedBox(); final desktopWindowSize = const ui.Size(360, 712); - ///Utils mock +// Reusable function to stub session model +void stubSessionModel({ + required MockSessionModel mockSessionModel, + required MockBuildContext mockBuildContext, + ValueNotifier? proxyAvailable, + bool proUser = false, + bool splitTunneling = false, + String shouldShowAds = "", + ServerInfo? serverInfo, + Bandwidth? bandwidth, +}) { + when(mockSessionModel.proxyAvailable) + .thenAnswer((_) => proxyAvailable ?? ValueNotifier(true)); + + when(mockSessionModel.proUser(any)).thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, proUser, null); + }); + + when(mockSessionModel.shouldShowAds(any)).thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, shouldShowAds, null); + }); + + when(mockSessionModel.serverInfo(any)).thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, serverInfo, null); + }); + when(mockSessionModel.bandwidth(any)).thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + var usedData = Bandwidth() + ..allowed = Int64(250) + ..remaining = Int64(200) + ..percent = Int64(28); + return builder(mockBuildContext, bandwidth ?? usedData, null); + }); + when(mockSessionModel.splitTunneling(any)).thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, splitTunneling, null); + }); +} +void stubVpnModel({ + required MockVpnModel mockVpnModel, + required MockBuildContext mockBuildContext, + String vpnStatus = 'disconnected', +}) { + when(mockVpnModel.vpnStatus(any, any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[1] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'disconnected', null); + }, + ); +} \ No newline at end of file From 14f0075b743c7f020cb4b3e0ee8829fba377c408 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 10 Oct 2024 18:10:58 +0530 Subject: [PATCH 008/163] Added test for vpn tap and start setting up patrol. --- Makefile | 2 +- integration_test/features/vpn_test.dart | 20 +++++++++ lib/main.dart | 1 - pubspec.yaml | 15 ++++++- test/app_test.dart | 54 ++++--------------------- test/features/vpn/vpn_tap_test.dart | 33 +++++++++++++++ test_driver/integration_driver.dart | 29 ------------- test_driver/integration_test.dart | 29 +++++++++++++ 8 files changed, 104 insertions(+), 79 deletions(-) create mode 100644 integration_test/features/vpn_test.dart delete mode 100644 test_driver/integration_driver.dart create mode 100644 test_driver/integration_test.dart diff --git a/Makefile b/Makefile index 62b51dd403..5c4e7aed61 100644 --- a/Makefile +++ b/Makefile @@ -44,7 +44,7 @@ test: TEST ?= *_test # integration-test: -# @flutter drive --driver test_driver/integration_driver.dart --debug --flavor prod --target `ls integration_test/$(TEST).dart` +# @flutter drive --driver test_driver/integration_test.dart --debug --flavor prod --target `ls integration_test/$(TEST).dart` APP ?= lantern CAPITALIZED_APP := Lantern diff --git a/integration_test/features/vpn_test.dart b/integration_test/features/vpn_test.dart new file mode 100644 index 0000000000..a94746b8b0 --- /dev/null +++ b/integration_test/features/vpn_test.dart @@ -0,0 +1,20 @@ + + +import 'package:flutter_test/flutter_test.dart'; +import 'package:integration_test/integration_test.dart'; +import 'package:lantern/main.dart' as app; + +void main(){ + final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); + + + + testWidgets('', (widgetTester) async{ + + app.main(); + + },); + + + +} \ No newline at end of file diff --git a/lib/main.dart b/lib/main.dart index 3d457af3ae..f2f016713f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -6,7 +6,6 @@ import 'package:lantern/core/utils/common.dart'; import 'package:lantern/core/utils/common_desktop.dart'; import 'package:lantern/features/replica/ui/utils.dart'; import 'package:sentry_flutter/sentry_flutter.dart'; -import 'package:window_manager/window_manager.dart'; // IOS issue // https://github.com/flutter/flutter/issues/133465 diff --git a/pubspec.yaml b/pubspec.yaml index 0b5fcd7d37..5ae02ef708 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -215,4 +215,17 @@ flutter: # weight: 700 # # For details regarding fonts from package dependencies, - # see https://flutter.dev/custom-fonts/#from-packages \ No newline at end of file + # see https://flutter.dev/custom-fonts/#from-packages + + + +# petrol configuration +patrol: + app_name: Lantern + flavor: prod + android: + package_name: org.getlantern.lantern + ios: + bundle_id: org.getlantern.lantern + macos: + bundle_id: org.getlantern.lantern \ No newline at end of file diff --git a/test/app_test.dart b/test/app_test.dart index 7ce530d66a..dda67b197b 100644 --- a/test/app_test.dart +++ b/test/app_test.dart @@ -1,47 +1,7 @@ -// import 'package:lantern/common/ui/custom/internet_checker.dart'; -// import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; -// import 'package:lantern/features/home/home.dart'; -// import 'package:lantern/features/vpn/vpn_notifier.dart'; -// -// import 'utils/test_common.dart'; -// -// -// -// final emptyBuilder = (context, value, child) => Container(); -// -// void main() { -// -// // Mocks the providers also -// group( -// "Home widget test for mobile devices", -// () { -// testWidgets("home widget look okay", (widgetTester) async { -// when(mockSessionModel.acceptedTermsVersion((emptyBuilder))).thenAnswer( -// (invocation) { -// final builder = invocation.namedArguments[const Symbol('builder')] -// as ValueWidgetBuilder; -// return builder(mockBuildContext, 1, null); -// }, -// ); -// -// final myApp = MaterialApp( -// home: MultiProvider( -// providers: [ -// ChangeNotifierProvider( -// create: (context) => BottomBarChangeNotifier()), -// ChangeNotifierProvider(create: (context) => VPNChangeNotifier()), -// ChangeNotifierProvider( -// create: (context) => InternetStatusProvider()) -// ], -// child: HomePage(), -// ), -// ); -// -// await widgetTester.pumpWidget(myApp); -// // await widgetTester.pumpAndSettle(); -// -// expect(find.byType(BottomAppBar), findsOneWidget); -// }, variant: TargetPlatformVariant.only(TargetPlatform.android)); -// }, -// ); -// } + + +/// Looks like patrol does not have native support yet +/// so any test that interact with the native layer will / need to use flutter test +/// patrol feature priority:https://patrol.leancode.co/native/feature-parity +/// finder documentation: https://patrol.leancode.co/finders/usage +void main() {} \ No newline at end of file diff --git a/test/features/vpn/vpn_tap_test.dart b/test/features/vpn/vpn_tap_test.dart index 1763fb78a7..be734c11af 100644 --- a/test/features/vpn/vpn_tap_test.dart +++ b/test/features/vpn/vpn_tap_test.dart @@ -187,6 +187,39 @@ void main() { }, variant: TargetPlatformVariant.all(excluding: {TargetPlatform.fuchsia}), ); + + testWidgets( + 'render VPN tap internet warning', + (widgetTester) async { + final vpnTapWidget = MultiProvider(providers: [ + ChangeNotifierProvider.value( + value: mockBottomBarChangeNotifier), + ChangeNotifierProvider.value( + value: mockVPNChangeNotifier), + ChangeNotifierProvider.value( + value: mockInternetStatusProvider), + ], child: wrapWithMaterialApp(const VPNTab())); + + when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); + when(mockInternetStatusProvider.isConnected).thenReturn(false); + + if (isDesktop()) { + when(mockVPNChangeNotifier.vpnStatus) + .thenReturn(ValueNotifier('disconnected')); + } + stubSessionModel( + mockSessionModel: mockSessionModel, + mockBuildContext: mockBuildContext); + + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); + + await widgetTester.pumpWidget(vpnTapWidget); + + expect(find.byType(InternetChecker), findsOneWidget); + }, + variant: TargetPlatformVariant.all(excluding: {TargetPlatform.fuchsia}), + ); }, ); } diff --git a/test_driver/integration_driver.dart b/test_driver/integration_driver.dart deleted file mode 100644 index ea8a78a86f..0000000000 --- a/test_driver/integration_driver.dart +++ /dev/null @@ -1,29 +0,0 @@ -import 'dart:io'; - -import 'package:integration_test/integration_test_driver.dart'; - -Future main() async { - /// Looks like flutter has a bug where it doesn't properly handle - /// the permissions request. - /// https://github.com/flutter/flutter/issues/12561 - /// we need to execute a custom command with android adb in order to grant - /// permissions to the app. - - final envVars = Platform.environment; - final adbPath = '${envVars['ANDROID_HOME']}/platform-tools/adb'; - await Process.run(adbPath, [ - 'shell', - 'pm', - 'grant', - 'org.getlantern.lantern', - 'android.permission.READ_EXTERNAL_STORAGE' - ]); - await Process.run(adbPath, [ - 'shell', - 'pm', - 'grant', - 'org.getlantern.lantern', - 'android.permission.RECORD_AUDIO' - ]); - await integrationDriver(); -} diff --git a/test_driver/integration_test.dart b/test_driver/integration_test.dart new file mode 100644 index 0000000000..03079e4167 --- /dev/null +++ b/test_driver/integration_test.dart @@ -0,0 +1,29 @@ +import 'dart:io'; + +import 'package:integration_test/integration_test_driver.dart'; + +Future main() async { + /// Looks like flutter has a bug where it doesn't properly handle + /// the permissions request. + /// https://github.com/flutter/flutter/issues/12561 + /// we need to execute a custom command with android adb in order to grant + /// permissions to the app. + + // final envVars = Platform.environment; + // final adbPath = '${envVars['ANDROID_HOME']}/platform-tools/adb'; + // await Process.run(adbPath, [ + // 'shell', + // 'pm', + // 'grant', + // 'org.getlantern.lantern', + // 'android.permission.READ_EXTERNAL_STORAGE' + // ]); + // await Process.run(adbPath, [ + // 'shell', + // 'pm', + // 'grant', + // 'org.getlantern.lantern', + // 'android.permission.RECORD_AUDIO' + // ]); + await integrationDriver(); +} From 88f1d35dbf7590f7f91201937f891b086544f671 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Mon, 14 Oct 2024 15:27:07 +0530 Subject: [PATCH 009/163] added patrol for android and start adding some basic test for integration test. --- android/app/build.gradle | 34 +- .../lantern/test/ApplicationTest.kt | 442 +++++++++--------- .../getlantern/lantern/test/DataUsageTests.kt | 243 +++++----- .../lantern/test/MainActivityTest.java | 34 ++ .../getlantern/lantern/test/RenewalsTest.java | 85 ---- .../getlantern/lantern/test/ReplicaTest.java | 61 --- .../getlantern/lantern/test/UpdateTest.java | 144 +++--- android/build.gradle | 1 + integration_test/app_startup_flow_test.dart | 36 ++ integration_test/features/vpn_test.dart | 27 +- .../0_change_language_test.dart | 4 +- .../10A_contact_info_screen_test.dart | 4 +- .../10B_contact_info_screen_test.dart | 4 +- .../11_rename_contact_test.dart | 4 +- .../12_delete_contact_test.dart | 4 +- .../{ => old_test}/13_block_contact_test.dart | 4 +- .../14A_react_to_message_test.dart | 2 +- .../14B_delete_for_me_test.dart | 2 +- .../14C_delete_for_everyone_test.dart | 2 +- .../15_copy_recovery_key_test.dart | 2 +- .../{ => old_test}/16_recovery_flow_test.dart | 0 .../17_verify_contact_test.dart | 4 +- .../18_disappearing_messages_test.dart | 4 +- .../{ => old_test}/19_become_pro_test.dart | 4 +- .../1A_chat_number_formats_test.dart | 2 +- .../1B_enroll_leave_Me_note_test.dart | 2 +- .../{ => old_test}/20_vpn_test.dart | 2 +- .../21_account_management_test.dart | 2 +- .../{ => old_test}/22_invite_test.dart | 2 +- .../23_download_desktop_test.dart | 2 +- .../24A_authorize_device_pro_PIN_test.dart | 4 +- .../24B_authorize_device_pro_email_test.dart | 4 +- .../{ => old_test}/25_report_issue_test.dart | 2 +- .../26_proxy_all_traffic_test.dart | 4 +- .../27_replica_search_test.dart | 4 +- .../2_add_via_secure_number_test.dart | 4 +- .../3_send_first_message_test.dart | 2 +- .../4_view_attachments_test.dart | 4 +- .../{ => old_test}/5_voice_memo_test.dart | 2 +- .../{ => old_test}/6A_scan_QR_code_test.dart | 2 +- .../{ => old_test}/6B_request_flow_test.dart | 4 +- .../{ => old_test}/6C_introductions_test.dart | 2 +- .../{ => old_test}/7_call_test.dart | 4 +- .../8A_send_single_intro_test.dart | 2 +- .../8B_send_multiple_intros_test.dart | 4 +- .../9A_search_contacts_messages_test.dart | 4 +- .../9B_search_contacts_test.dart | 4 +- .../integration_test_common.dart | 0 .../integration_test_constants.dart | 0 integration_test/test_bundle.dart | 86 ++++ .../helpers/clipboard_test.dart | 20 - integration_test_old/helpers/waiter_test.dart | 7 - lib/app.dart | 7 +- lib/main.dart | 1 + test/app_test.dart | 110 ++++- test/base_screen_test.dart | 2 +- test/utils/test.mocks.dart | 2 +- test/utils/test.mocks.mocks.dart | 9 - 58 files changed, 777 insertions(+), 686 deletions(-) create mode 100644 android/app/src/androidTest/java/org/getlantern/lantern/test/MainActivityTest.java delete mode 100644 android/app/src/androidTest/java/org/getlantern/lantern/test/RenewalsTest.java delete mode 100644 android/app/src/androidTest/java/org/getlantern/lantern/test/ReplicaTest.java create mode 100644 integration_test/app_startup_flow_test.dart rename integration_test/{ => old_test}/0_change_language_test.dart (87%) rename integration_test/{ => old_test}/10A_contact_info_screen_test.dart (91%) rename integration_test/{ => old_test}/10B_contact_info_screen_test.dart (92%) rename integration_test/{ => old_test}/11_rename_contact_test.dart (94%) rename integration_test/{ => old_test}/12_delete_contact_test.dart (92%) rename integration_test/{ => old_test}/13_block_contact_test.dart (93%) rename integration_test/{ => old_test}/14A_react_to_message_test.dart (96%) rename integration_test/{ => old_test}/14B_delete_for_me_test.dart (96%) rename integration_test/{ => old_test}/14C_delete_for_everyone_test.dart (96%) rename integration_test/{ => old_test}/15_copy_recovery_key_test.dart (95%) rename integration_test/{ => old_test}/16_recovery_flow_test.dart (100%) rename integration_test/{ => old_test}/17_verify_contact_test.dart (93%) rename integration_test/{ => old_test}/18_disappearing_messages_test.dart (94%) rename integration_test/{ => old_test}/19_become_pro_test.dart (75%) rename integration_test/{ => old_test}/1A_chat_number_formats_test.dart (87%) rename integration_test/{ => old_test}/1B_enroll_leave_Me_note_test.dart (86%) rename integration_test/{ => old_test}/20_vpn_test.dart (79%) rename integration_test/{ => old_test}/21_account_management_test.dart (80%) rename integration_test/{ => old_test}/22_invite_test.dart (79%) rename integration_test/{ => old_test}/23_download_desktop_test.dart (84%) rename integration_test/{ => old_test}/24A_authorize_device_pro_PIN_test.dart (80%) rename integration_test/{ => old_test}/24B_authorize_device_pro_email_test.dart (87%) rename integration_test/{ => old_test}/25_report_issue_test.dart (87%) rename integration_test/{ => old_test}/26_proxy_all_traffic_test.dart (86%) rename integration_test/{ => old_test}/27_replica_search_test.dart (90%) rename integration_test/{ => old_test}/2_add_via_secure_number_test.dart (90%) rename integration_test/{ => old_test}/3_send_first_message_test.dart (87%) rename integration_test/{ => old_test}/4_view_attachments_test.dart (86%) rename integration_test/{ => old_test}/5_voice_memo_test.dart (96%) rename integration_test/{ => old_test}/6A_scan_QR_code_test.dart (96%) rename integration_test/{ => old_test}/6B_request_flow_test.dart (92%) rename integration_test/{ => old_test}/6C_introductions_test.dart (95%) rename integration_test/{ => old_test}/7_call_test.dart (91%) rename integration_test/{ => old_test}/8A_send_single_intro_test.dart (96%) rename integration_test/{ => old_test}/8B_send_multiple_intros_test.dart (93%) rename integration_test/{ => old_test}/9A_search_contacts_messages_test.dart (92%) rename integration_test/{ => old_test}/9B_search_contacts_test.dart (92%) rename integration_test/{ => old_test}/integration_test_common.dart (100%) rename integration_test/{ => old_test}/integration_test_constants.dart (100%) create mode 100644 integration_test/test_bundle.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 8b606e0f44..3e4bc71b5c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -77,6 +77,9 @@ android { versionName "9999.99.99" } testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + testInstrumentationRunner "pl.leancode.patrol.PatrolJUnitRunner" + testInstrumentationRunnerArguments clearPackageData: "true" + vectorDrawables.useSupportLibrary = true manifestPlaceholders = [ TapsellMediationAppKey : "ad680da7-f319-42cb-bdde-3b6c43486eaa", @@ -198,6 +201,7 @@ android { testOptions { unitTests.returnDefaultValues = true + execution "ANDROIDX_TEST_ORCHESTRATOR" } namespace 'org.getlantern.lantern' } @@ -314,14 +318,16 @@ flutter { source '../..' } -configurations { - debugImplementation.exclude group: "junit", module: "junit" -} +//configurations { +// debugImplementation.exclude group: "junit", module: "junit" +//} dependencies { implementation "org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm:0.3.8" implementation "com.google.protobuf:protobuf-javalite:$protoc_version" implementation 'org.jetbrains.kotlinx:kotlinx-serialization-json:1.7.2' + androidTestImplementation 'junit:junit:4.12' + coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugarJdk" implementation fileTree(dir: "libs", include: "liblantern-${androidArch()}.aar") @@ -352,18 +358,20 @@ dependencies { debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.14' //Test implementation - androidTestImplementation 'androidx.test:rules:1.6.1' - androidTestImplementation 'androidx.annotation:annotation:1.8.2' - androidTestImplementation 'androidx.test:runner:1.6.2' - androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' - androidTestImplementation 'androidx.test.espresso:espresso-idling-resource:3.6.1' - androidTestImplementation 'androidx.test.ext:junit:1.2.1' - - androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.3.0' + androidTestImplementation "androidx.test.espresso:espresso-core:$espressoVersion" androidTestImplementation 'com.squareup.okhttp3:okhttp:4.9.2' +// androidTestImplementation "org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version" testImplementation 'junit:junit:4.13.2' - testImplementation "io.mockk:mockk:1.13.5" + testImplementation "io.mockk:mockk:1.13.12" + androidTestUtil "androidx.test:orchestrator:1.4.2" + // androidTestImplementation 'androidx.test:rules:1.6.1' +// androidTestImplementation 'androidx.annotation:annotation:1.8.2' +// androidTestImplementation 'androidx.test:runner:1.6.2' +// androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1' +// androidTestImplementation 'androidx.test.espresso:espresso-idling-resource:3.6.1' +// androidTestImplementation 'androidx.test.ext:junit:1.2.1' + } @@ -414,3 +422,5 @@ sentry { // Default is disabled. includeNativeSources = false } + + diff --git a/android/app/src/androidTest/java/org/getlantern/lantern/test/ApplicationTest.kt b/android/app/src/androidTest/java/org/getlantern/lantern/test/ApplicationTest.kt index e3b5e4d4ab..3aa7b05464 100644 --- a/android/app/src/androidTest/java/org/getlantern/lantern/test/ApplicationTest.kt +++ b/android/app/src/androidTest/java/org/getlantern/lantern/test/ApplicationTest.kt @@ -1,221 +1,221 @@ -package org.getlantern.lantern.test - -import android.Manifest -import android.graphics.Point -import android.os.AsyncTask -import android.os.SystemClock -import android.util.Log -import android.view.View -import androidx.test.InstrumentationRegistry -import androidx.test.espresso.Espresso -import androidx.test.espresso.ViewInteraction -import androidx.test.espresso.action.ViewActions -import androidx.test.espresso.assertion.ViewAssertions -import androidx.test.espresso.matcher.ViewMatchers -import androidx.test.filters.LargeTest -import androidx.test.filters.SdkSuppress -import androidx.test.rule.GrantPermissionRule -import androidx.test.runner.AndroidJUnit4 -import androidx.test.uiautomator.UiDevice -import com.kyleduo.switchbutton.SwitchButton -import org.getlantern.lantern.MainActivity -import org.getlantern.lantern.R -import org.hamcrest.Description -import org.hamcrest.Matcher -import org.hamcrest.TypeSafeMatcher -import org.json.JSONException -import org.json.JSONObject -import org.junit.Assert -import org.junit.Before -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith -import java.net.HttpURLConnection -import java.net.NetworkInterface -import java.net.SocketException -import java.net.URL -import java.util.concurrent.ExecutionException - -@RunWith(AndroidJUnit4::class) -@SdkSuppress(minSdkVersion = 18) -@LargeTest -class ApplicationTest { - @Rule - @JvmField - var grantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE) - - @Rule - @JvmField - var mActivityRule = MyActivityTestRule(MainActivity::class.java) - - lateinit var mDevice: UiDevice - var mWatchers = UiWatchers() - - fun resetTestUser() { - var url = URL("https://api.getiantem.org/reset-test-user") - val conn = url.openConnection() as HttpURLConnection - conn.connectTimeout = 10000 - conn.readTimeout = 10000 - conn.requestMethod = "POST" - conn.connect() - conn.outputStream.close() - if (conn.responseCode != 200) { - throw Exception("Unexpected response code resetting test user: " + conn.responseCode) - } - } - - @Before - @Throws(Exception::class) - fun setUp() { - resetTestUser() - - mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - SystemClock.sleep(5000) - TestUtils.clickButtonIfPresent(mDevice, "OK") - mWatchers.registerAnrAndCrashWatchers() - val coordinates = arrayOfNulls(4) - coordinates[0] = Point(248, 1520) - coordinates[1] = Point(248, 929) - coordinates[2] = Point(796, 1520) - coordinates[3] = Point(796, 929) - if (!mDevice.isScreenOn) { - mDevice.wakeUp() - mDevice.swipe(coordinates, 10) - } - } - - @Test - fun turnOnAndOffVPN() { - Log.d(TAG, "Testing turning VPN on and off") - clickButton(R.id.powerLantern) - SystemClock.sleep(1000) - TestUtils.clickButtonIfPresent(mDevice, "OK") - verifyVPNOn() - try { - val json = JSONParse().execute().get() - Assert.assertNotNull("should get JSON result", json) - val ip = json.getString("ip") - Log.d(TAG, "Got IP Address! $ip") - } catch (e: ExecutionException) { - e.printStackTrace() - Assert.fail("JSON fetching task failed") - } catch (e: InterruptedException) { - e.printStackTrace() - Assert.fail("JSON fetching task interrupted") - } catch (e: JSONException) { - e.printStackTrace() - Assert.fail("got incorrect JSON result") - } - Log.d(TAG, "Waiting for view powerLantern to turn off VPN") - clickButton(R.id.powerLantern) - verifyVPNOff() - - // TODO: swipeLeft/swipeRight has no effect. Figure out how to test. - // onView(withId(R.id.powerLantern)).perform(swipeRight()); - // verifyVPNOn(); - - // onView(withId(R.id.powerLantern)).perform(swipeLeft()); - // verifyVPNOff(); - } - - @Test - fun upgradeWithStripe() { - Log.d(TAG, "Testing upgrading to Pro with a Stripe purchase") -// clickButton(R.id.upgradeBtn) - clickButton(R.id.oneYearBtn) - fillField(R.id.emailInput, "ox+testuser@gmail.com") - fillField(R.id.cardInput, "4242424242424242") - fillField(R.id.expirationInput, "03/24") - fillField(R.id.cvcInput, "123") - Espresso.onView(ViewMatchers.withId(R.id.cvcInput)).perform(ViewActions.closeSoftKeyboard()) - clickButton(R.id.continueBtn) - clickButton(R.id.continueToProBtn, attempts = 30, sleep = 1000) - } - - private fun verifyVPNOn() { - SystemClock.sleep(1000) - Espresso.onView(ViewMatchers.withId(R.id.powerLantern)).check( - ViewAssertions.matches(withChecked(true)) - ) - Assert.assertTrue("System VPN should be on", isSystemVPNConnected) - } - - private fun verifyVPNOff() { - SystemClock.sleep(1000) - Espresso.onView(ViewMatchers.withId(R.id.powerLantern)).check( - ViewAssertions.matches(withChecked(false)) - ) - Assert.assertFalse("System VPN should be off", isSystemVPNConnected) - } - - private val isSystemVPNConnected: Boolean - private get() = try { - val intf = NetworkInterface.getByName("tun0") - intf != null - } catch (e: SocketException) { - false - } - - private inner class JSONParse : AsyncTask() { - override fun doInBackground(vararg params: String?): JSONObject { - Log.d(TAG, "Fetching json from $url") - return JsonParser.getJSONFromUrl(url) - } - } - - private fun clickButton(id: Int, attempts: Int = 10, sleep: Long = 500) { - doUntilSuccessful( - attempts, sleep, - { - onView(id).perform(ViewActions.click()) - } - ) - } - - private fun fillField(id: Int, text: String, attempts: Int = 10, sleep: Long = 500) { - doUntilSuccessful( - attempts, sleep, - { - onView(id).perform( - ViewActions.clearText(), - ViewActions.typeText(text) - ) - } - ) - } - - private fun onView(id: Int): ViewInteraction { - return Espresso.onView(ViewMatchers.withId(id)) - } - - private fun doUntilSuccessful(attempts: Int = 10, sleep: Long = 500, fn: () -> Unit) { - lateinit var lastException: Throwable - for (x in 0..attempts) { - try { - return fn() - } catch (e: Throwable) { - lastException = e - SystemClock.sleep(sleep) - // continue - } - } - throw lastException - } - - companion object { - private const val TAG = "ApplicationTest" - private const val url = "https://ifconfig.co/json" - private fun withChecked(checked: Boolean): Matcher { - return object : TypeSafeMatcher() { - public override fun matchesSafely(v: View): Boolean { - val button = v as SwitchButton - return button.isChecked == checked - } - - override fun describeTo(description: Description) { - description.appendText("with correct background color") - } - } - } - } -} +//package org.getlantern.lantern.test +// +//import android.Manifest +//import android.graphics.Point +//import android.os.AsyncTask +//import android.os.SystemClock +//import android.util.Log +//import android.view.View +//import androidx.test.InstrumentationRegistry +//import androidx.test.espresso.Espresso +//import androidx.test.espresso.ViewInteraction +//import androidx.test.espresso.action.ViewActions +//import androidx.test.espresso.assertion.ViewAssertions +//import androidx.test.espresso.matcher.ViewMatchers +//import androidx.test.filters.LargeTest +//import androidx.test.filters.SdkSuppress +//import androidx.test.rule.GrantPermissionRule +//import androidx.test.runner.AndroidJUnit4 +//import androidx.test.uiautomator.UiDevice +//import com.kyleduo.switchbutton.SwitchButton +//import org.getlantern.lantern.MainActivity +//import org.getlantern.lantern.R +//import org.hamcrest.Description +//import org.hamcrest.Matcher +//import org.hamcrest.TypeSafeMatcher +//import org.json.JSONException +//import org.json.JSONObject +//import org.junit.Assert +//import org.junit.Before +//import org.junit.Rule +//import org.junit.Test +//import org.junit.runner.RunWith +//import java.net.HttpURLConnection +//import java.net.NetworkInterface +//import java.net.SocketException +//import java.net.URL +//import java.util.concurrent.ExecutionException +// +//@RunWith(AndroidJUnit4::class) +//@SdkSuppress(minSdkVersion = 18) +//@LargeTest +//class ApplicationTest { +// @Rule +// @JvmField +// var grantPermissionRule = GrantPermissionRule.grant(Manifest.permission.WRITE_EXTERNAL_STORAGE) +// +// @Rule +// @JvmField +// var mActivityRule = MyActivityTestRule(MainActivity::class.java) +// +// lateinit var mDevice: UiDevice +// var mWatchers = UiWatchers() +// +// fun resetTestUser() { +// var url = URL("https://api.getiantem.org/reset-test-user") +// val conn = url.openConnection() as HttpURLConnection +// conn.connectTimeout = 10000 +// conn.readTimeout = 10000 +// conn.requestMethod = "POST" +// conn.connect() +// conn.outputStream.close() +// if (conn.responseCode != 200) { +// throw Exception("Unexpected response code resetting test user: " + conn.responseCode) +// } +// } +// +// @Before +// @Throws(Exception::class) +// fun setUp() { +// resetTestUser() +// +// mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) +// SystemClock.sleep(5000) +// TestUtils.clickButtonIfPresent(mDevice, "OK") +// mWatchers.registerAnrAndCrashWatchers() +// val coordinates = arrayOfNulls(4) +// coordinates[0] = Point(248, 1520) +// coordinates[1] = Point(248, 929) +// coordinates[2] = Point(796, 1520) +// coordinates[3] = Point(796, 929) +// if (!mDevice.isScreenOn) { +// mDevice.wakeUp() +// mDevice.swipe(coordinates, 10) +// } +// } +// +// @Test +// fun turnOnAndOffVPN() { +// Log.d(TAG, "Testing turning VPN on and off") +// clickButton(R.id.powerLantern) +// SystemClock.sleep(1000) +// TestUtils.clickButtonIfPresent(mDevice, "OK") +// verifyVPNOn() +// try { +// val json = JSONParse().execute().get() +// Assert.assertNotNull("should get JSON result", json) +// val ip = json.getString("ip") +// Log.d(TAG, "Got IP Address! $ip") +// } catch (e: ExecutionException) { +// e.printStackTrace() +// Assert.fail("JSON fetching task failed") +// } catch (e: InterruptedException) { +// e.printStackTrace() +// Assert.fail("JSON fetching task interrupted") +// } catch (e: JSONException) { +// e.printStackTrace() +// Assert.fail("got incorrect JSON result") +// } +// Log.d(TAG, "Waiting for view powerLantern to turn off VPN") +// clickButton(R.id.powerLantern) +// verifyVPNOff() +// +// // TODO: swipeLeft/swipeRight has no effect. Figure out how to test. +// // onView(withId(R.id.powerLantern)).perform(swipeRight()); +// // verifyVPNOn(); +// +// // onView(withId(R.id.powerLantern)).perform(swipeLeft()); +// // verifyVPNOff(); +// } +// +// @Test +// fun upgradeWithStripe() { +// Log.d(TAG, "Testing upgrading to Pro with a Stripe purchase") +//// clickButton(R.id.upgradeBtn) +// clickButton(R.id.oneYearBtn) +// fillField(R.id.emailInput, "ox+testuser@gmail.com") +// fillField(R.id.cardInput, "4242424242424242") +// fillField(R.id.expirationInput, "03/24") +// fillField(R.id.cvcInput, "123") +// Espresso.onView(ViewMatchers.withId(R.id.cvcInput)).perform(ViewActions.closeSoftKeyboard()) +// clickButton(R.id.continueBtn) +// clickButton(R.id.continueToProBtn, attempts = 30, sleep = 1000) +// } +// +// private fun verifyVPNOn() { +// SystemClock.sleep(1000) +// Espresso.onView(ViewMatchers.withId(R.id.powerLantern)).check( +// ViewAssertions.matches(withChecked(true)) +// ) +// Assert.assertTrue("System VPN should be on", isSystemVPNConnected) +// } +// +// private fun verifyVPNOff() { +// SystemClock.sleep(1000) +// Espresso.onView(ViewMatchers.withId(R.id.powerLantern)).check( +// ViewAssertions.matches(withChecked(false)) +// ) +// Assert.assertFalse("System VPN should be off", isSystemVPNConnected) +// } +// +// private val isSystemVPNConnected: Boolean +// private get() = try { +// val intf = NetworkInterface.getByName("tun0") +// intf != null +// } catch (e: SocketException) { +// false +// } +// +// private inner class JSONParse : AsyncTask() { +// override fun doInBackground(vararg params: String?): JSONObject { +// Log.d(TAG, "Fetching json from $url") +// return JsonParser.getJSONFromUrl(url) +// } +// } +// +// private fun clickButton(id: Int, attempts: Int = 10, sleep: Long = 500) { +// doUntilSuccessful( +// attempts, sleep, +// { +// onView(id).perform(ViewActions.click()) +// } +// ) +// } +// +// private fun fillField(id: Int, text: String, attempts: Int = 10, sleep: Long = 500) { +// doUntilSuccessful( +// attempts, sleep, +// { +// onView(id).perform( +// ViewActions.clearText(), +// ViewActions.typeText(text) +// ) +// } +// ) +// } +// +// private fun onView(id: Int): ViewInteraction { +// return Espresso.onView(ViewMatchers.withId(id)) +// } +// +// private fun doUntilSuccessful(attempts: Int = 10, sleep: Long = 500, fn: () -> Unit) { +// lateinit var lastException: Throwable +// for (x in 0..attempts) { +// try { +// return fn() +// } catch (e: Throwable) { +// lastException = e +// SystemClock.sleep(sleep) +// // continue +// } +// } +// throw lastException +// } +// +// companion object { +// private const val TAG = "ApplicationTest" +// private const val url = "https://ifconfig.co/json" +// private fun withChecked(checked: Boolean): Matcher { +// return object : TypeSafeMatcher() { +// public override fun matchesSafely(v: View): Boolean { +// val button = v as SwitchButton +// return button.isChecked == checked +// } +// +// override fun describeTo(description: Description) { +// description.appendText("with correct background color") +// } +// } +// } +// } +//} diff --git a/android/app/src/androidTest/java/org/getlantern/lantern/test/DataUsageTests.kt b/android/app/src/androidTest/java/org/getlantern/lantern/test/DataUsageTests.kt index 24b8cac186..d0fedfac03 100644 --- a/android/app/src/androidTest/java/org/getlantern/lantern/test/DataUsageTests.kt +++ b/android/app/src/androidTest/java/org/getlantern/lantern/test/DataUsageTests.kt @@ -2,8 +2,7 @@ package org.getlantern.lantern.test import android.app.NotificationManager import android.content.Context.NOTIFICATION_SERVICE -import androidx.test.ext.junit.rules.ActivityScenarioRule -import androidx.test.ext.junit.runners.AndroidJUnit4 + import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.By import androidx.test.uiautomator.UiDevice @@ -20,123 +19,123 @@ import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith -@RunWith(AndroidJUnit4::class) -class DataUsageTests { - - @get:Rule - var mainActivityRule = ActivityScenarioRule(MainActivity::class.java) - - private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) - - private val notificationManager = - LanternApp.getAppContext().getSystemService(NOTIFICATION_SERVICE) as NotificationManager - - @Before - fun setUp() { - LanternApp.getSession().setPaymentTestMode(true) - notificationManager.cancelAll() - } - - @Test - fun WhenDataCapIsHit_AppShouldShowNotification() { - val bandwidth = Bandwidth(100, 0, 1000, 3600) - val title = LanternApp.getAppContext().resources.getString(R.string.lantern_notification) - val content = LanternApp.getAppContext().resources.getString( - R.string.data_cap, - bandwidth.expiresAtString - ) - testDataUsageNotification(bandwidth, title, content) - } - - @Test - fun WhenDataUsageIs50_AppShouldShowNotification() { - val bandwidth = Bandwidth(50, 500, 1000, 3600) - val title = LanternApp.getAppContext().resources.getString(R.string.lantern_notification) - val content = LanternApp.getAppContext().resources.getString( - R.string.data_cap_percent, - bandwidth.remaining, - bandwidth.expiresAtString - ) - testDataUsageNotification(bandwidth, title, content) - } - - @Test - fun WhenDataUsageIs80_AppShouldShowNotification() { - val bandwidth = Bandwidth(80, 200, 1000, 3600) - val title = LanternApp.getAppContext().resources.getString(R.string.lantern_notification) - val content = LanternApp.getAppContext().resources.getString( - R.string.data_cap_percent, - bandwidth.remaining, - bandwidth.expiresAtString - ) - testDataUsageNotification(bandwidth, title, content) - } - - @Test - fun WhenDataUsageIsReset_AppShouldShowNotification() { - val bandwidth = Bandwidth(0, 1000, 1000, 3600) - val title = LanternApp.getAppContext().resources.getString(R.string.lantern_notification) - val content = LanternApp.getAppContext().resources.getString(R.string.data_cap_reset) - testDataUsageNotification(bandwidth, title, content) - } - - @Test - fun WhenDataUsageIsOther_AppShouldNOTShowNotification() { - // given the app is running - val notExpectedTitle = - LanternApp.getAppContext().resources.getString(R.string.lantern_notification) - - // open the notification bar - device.openNotification() - - // send data usage update - val bandwidth = Bandwidth(25, 750, 1000, 3600) - LanternApp.getSession().bandwidthUpdate( - bandwidth.percent, - bandwidth.remaining, - bandwidth.allowed, - bandwidth.ttlSeconds - ) - - // then the user should NOT see a push notification - device.wait(Until.hasObject(By.text(notExpectedTitle)), TIMEOUT) - - // with a correct title - val titleObj: UiObject2? = device.findObject(By.text(notExpectedTitle)) - assertNull(titleObj) - } - - private fun testDataUsageNotification( - bandwidth: Bandwidth, - expectedTitle: String, - expectedContent: String - ) { - // given the app is running - - // open the notification bar - device.openNotification() - - // send data usage update - LanternApp.getSession().bandwidthUpdate( - bandwidth.percent, - bandwidth.remaining, - bandwidth.allowed, - bandwidth.ttlSeconds - ) - - // then the user should see a push notification - device.wait(Until.hasObject(By.text(expectedTitle)), TIMEOUT) - - // with a correct title - val titleObj: UiObject2? = device.findObject(By.text(expectedTitle)) - assertNotNull(titleObj) - - // and a correct content - val contentObj: UiObject2? = device.findObject(By.text(expectedContent)) - assertNotNull(contentObj) - } - - companion object { - private const val TIMEOUT = 3000L // 3 seconds - } -} +//@RunWith(AndroidJUnit4::class) +//class DataUsageTests { +// +// @get:Rule +// var mainActivityRule = ActivityScenarioRule(MainActivity::class.java) +// +// private val device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()) +// +// private val notificationManager = +// LanternApp.getAppContext().getSystemService(NOTIFICATION_SERVICE) as NotificationManager +// +// @Before +// fun setUp() { +// LanternApp.session.setPaymentTestMode(true) +// notificationManager.cancelAll() +// } +// +// @Test +// fun WhenDataCapIsHit_AppShouldShowNotification() { +// val bandwidth = Bandwidth(100, 0, 1000, 3600) +// val title = LanternApp.getAppContext().resources.getString(R.string.lantern_notification) +// val content = LanternApp.getAppContext().resources.getString( +// R.string.data_cap, +// bandwidth.expiresAtString +// ) +// testDataUsageNotification(bandwidth, title, content) +// } +// +// @Test +// fun WhenDataUsageIs50_AppShouldShowNotification() { +// val bandwidth = Bandwidth(50, 500, 1000, 3600) +// val title = LanternApp.getAppContext().resources.getString(R.string.lantern_notification) +// val content = LanternApp.getAppContext().resources.getString( +// R.string.data_cap_percent, +// bandwidth.remaining, +// bandwidth.expiresAtString +// ) +// testDataUsageNotification(bandwidth, title, content) +// } +// +// @Test +// fun WhenDataUsageIs80_AppShouldShowNotification() { +// val bandwidth = Bandwidth(80, 200, 1000, 3600) +// val title = LanternApp.getAppContext().resources.getString(R.string.lantern_notification) +// val content = LanternApp.getAppContext().resources.getString( +// R.string.data_cap_percent, +// bandwidth.remaining, +// bandwidth.expiresAtString +// ) +// testDataUsageNotification(bandwidth, title, content) +// } +// +// @Test +// fun WhenDataUsageIsReset_AppShouldShowNotification() { +// val bandwidth = Bandwidth(0, 1000, 1000, 3600) +// val title = LanternApp.getAppContext().resources.getString(R.string.lantern_notification) +// val content = LanternApp.getAppContext().resources.getString(R.string.data_cap_reset) +// testDataUsageNotification(bandwidth, title, content) +// } +// +// @Test +// fun WhenDataUsageIsOther_AppShouldNOTShowNotification() { +// // given the app is running +// val notExpectedTitle = +// LanternApp.getAppContext().resources.getString(R.string.lantern_notification) +// +// // open the notification bar +// device.openNotification() +// +// // send data usage update +// val bandwidth = Bandwidth(25, 750, 1000, 3600) +// LanternApp.getSession().bandwidthUpdate( +// bandwidth.percent, +// bandwidth.remaining, +// bandwidth.allowed, +// bandwidth.ttlSeconds +// ) +// +// // then the user should NOT see a push notification +// device.wait(Until.hasObject(By.text(notExpectedTitle)), TIMEOUT) +// +// // with a correct title +// val titleObj: UiObject2? = device.findObject(By.text(notExpectedTitle)) +// assertNull(titleObj) +// } +// +// private fun testDataUsageNotification( +// bandwidth: Bandwidth, +// expectedTitle: String, +// expectedContent: String +// ) { +// // given the app is running +// +// // open the notification bar +// device.openNotification() +// +// // send data usage update +// LanternApp.getSession().bandwidthUpdate( +// bandwidth.percent, +// bandwidth.remaining, +// bandwidth.allowed, +// bandwidth.ttlSeconds +// ) +// +// // then the user should see a push notification +// device.wait(Until.hasObject(By.text(expectedTitle)), TIMEOUT) +// +// // with a correct title +// val titleObj: UiObject2? = device.findObject(By.text(expectedTitle)) +// assertNotNull(titleObj) +// +// // and a correct content +// val contentObj: UiObject2? = device.findObject(By.text(expectedContent)) +// assertNotNull(contentObj) +// } +// +// companion object { +// private const val TIMEOUT = 3000L // 3 seconds +// } +//} diff --git a/android/app/src/androidTest/java/org/getlantern/lantern/test/MainActivityTest.java b/android/app/src/androidTest/java/org/getlantern/lantern/test/MainActivityTest.java new file mode 100644 index 0000000000..c115bd068c --- /dev/null +++ b/android/app/src/androidTest/java/org/getlantern/lantern/test/MainActivityTest.java @@ -0,0 +1,34 @@ +package org.getlantern.lantern.test; + +import androidx.test.platform.app.InstrumentationRegistry; + +import org.getlantern.lantern.MainActivity; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import pl.leancode.patrol.PatrolJUnitRunner; + +@RunWith(Parameterized.class) +public class MainActivityTest { + @Parameters(name = "{0}") + public static Object[] testCases() { + PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation(); + instrumentation.setUp(MainActivity.class); + instrumentation.waitForPatrolAppService(); + return instrumentation.listDartTests(); + } + + public MainActivityTest(String dartTestName) { + this.dartTestName = dartTestName; + } + + private final String dartTestName; + + @Test + public void runDartTest() { + PatrolJUnitRunner instrumentation = (PatrolJUnitRunner) InstrumentationRegistry.getInstrumentation(); + instrumentation.runDartTest(dartTestName); + } +} diff --git a/android/app/src/androidTest/java/org/getlantern/lantern/test/RenewalsTest.java b/android/app/src/androidTest/java/org/getlantern/lantern/test/RenewalsTest.java deleted file mode 100644 index 3b2925442e..0000000000 --- a/android/app/src/androidTest/java/org/getlantern/lantern/test/RenewalsTest.java +++ /dev/null @@ -1,85 +0,0 @@ -package org.getlantern.lantern.test; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.assertion.ViewAssertions.matches; -import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed; -import static androidx.test.espresso.matcher.ViewMatchers.withId; - -import android.content.Intent; -import android.os.SystemClock; -import android.util.Log; - -import androidx.test.InstrumentationRegistry; -import androidx.test.espresso.NoMatchingViewException; -import androidx.test.rule.ActivityTestRule; -import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject; -import androidx.test.uiautomator.UiSelector; - -import org.getlantern.lantern.MainActivity; -import org.getlantern.lantern.R; -import org.joda.time.DateTime; -import org.joda.time.DateTimeConstants; -import org.joda.time.DateTimeZone; -import org.joda.time.LocalDateTime; -import org.junit.Assert; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -public class RenewalsTest { - - private static final String TAG = RenewalsTest.class.getName(); - private UiDevice device; - - private String minDatafile = "{\"groups\": [], \"projectId\": \"8504447126\", \"variables\": [{\"defaultValue\": \"true\", \"type\": \"boolean\", \"id\": \"8516291943\", \"key\": \"test_variable\"}], \"version\": \"3\", \"experiments\": [{\"status\": \"Running\", \"key\": \"android_experiment_key\", \"layerId\": \"8499056327\", \"trafficAllocation\": [{\"entityId\": \"8509854340\", \"endOfRange\": 5000}, {\"entityId\": \"8505434669\", \"endOfRange\": 10000}], \"audienceIds\": [], \"variations\": [{\"variables\": [], \"id\": \"8509854340\", \"key\": \"var_1\"}, {\"variables\": [], \"id\": \"8505434669\", \"key\": \"var_2\"}], \"forcedVariations\": {}, \"id\": \"8509139139\"}], \"audiences\": [], \"anonymizeIP\": true, \"attributes\": [], \"revision\": \"7\", \"events\": [{\"experimentIds\": [\"8509139139\"], \"id\": \"8505434668\", \"key\": \"test_event\"}], \"accountId\": \"8362480420\"}"; - - @Rule - public ActivityTestRule mActivityRule = new MyActivityTestRule(MainActivity.class); - - @Before - public void setUp() throws Exception { - device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - } - - private void checkTextDisplayed(final int id, final String text) { - try { - onView(withId(id)).check(matches(isDisplayed())); - UiObject o = device.findObject(new UiSelector().text(text)); - Assert.assertTrue(o.exists()); - } catch (NoMatchingViewException e) { - Log.e(TAG, "No matching view found", e); - } - } - - private void testRenewal(final LocalDateTime expires, - final String text) { - final boolean isProUser = true; - final boolean expired = false; - final DateTime utc = expires.toDateTime(DateTimeZone.UTC); - final long expiration = utc.getMillis() / DateTimeConstants.MILLIS_PER_SECOND; - mActivityRule.launchActivity(new Intent()); - SystemClock.sleep(5000); - checkTextDisplayed(R.id.renewalHeader, text); - } - - @Test - public void testProExpiresTomorrowRenewal() { - testRenewal(LocalDateTime.now().plusDays(1), - "tomorrow"); - } - - @Test - public void testProExpiresToday() { - testRenewal(LocalDateTime.now(), "today"); - } - - @Test - public void testProExpired() { - testRenewal(LocalDateTime.now().minusDays(1), - "Limited time offer"); - } -} diff --git a/android/app/src/androidTest/java/org/getlantern/lantern/test/ReplicaTest.java b/android/app/src/androidTest/java/org/getlantern/lantern/test/ReplicaTest.java deleted file mode 100644 index d7e9a9f95a..0000000000 --- a/android/app/src/androidTest/java/org/getlantern/lantern/test/ReplicaTest.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.getlantern.lantern.test; - -import android.content.Context; -import android.util.Log; - -import androidx.test.filters.LargeTest; -import androidx.test.platform.app.InstrumentationRegistry; -import androidx.test.runner.AndroidJUnit4; - -import org.getlantern.lantern.LanternApp; -import org.getlantern.mobilesdk.Settings; -import org.getlantern.mobilesdk.StartResult; -import org.getlantern.mobilesdk.embedded.EmbeddedLantern; -import org.getlantern.mobilesdk.model.SessionManager; -import org.junit.Assert; -import org.junit.Test; -import org.junit.runner.RunWith; - -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.Map; - -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; - -@RunWith(AndroidJUnit4.class) -@LargeTest -public class ReplicaTest extends BaseTest { - // Tests if Replica is initialized properly. - @Test - public void testReplicaIsInitialized() throws Exception { - // Initialize internalsdk - Context context = InstrumentationRegistry.getInstrumentation().getTargetContext(); - Settings settings = Settings.init(InstrumentationRegistry.getInstrumentation().getTargetContext()); - settings.shouldRunReplica = true; - SessionManager session = LanternApp.getSession(); - session.setReplicaAddr(""); - StartResult result = new EmbeddedLantern().start( - Paths.get(context.getFilesDir().getAbsolutePath(), ".lantern").toString(), - "en_US", settings, session); - - // Wait for replica to start - Thread.sleep(5000); - Log.d("PINEAPPLE", "testReplicaIsInitialized: " + session.getReplicaAddr()); - Assert.assertNotEquals("", session.getReplicaAddr()); - - // Assert that /replica routes yield a 200 - for (Map.Entry entry : new HashMap() {{ - put("replica/heartbeat", "http://" + session.getReplicaAddr() + "/replica/heartbeat"); - put("replica/search", "http://" + session.getReplicaAddr() + "/replica/search?s=hello&page=1&orderBy=relevance&type=web"); - }}.entrySet()) { - OkHttpClient client = new OkHttpClient(); - Request req = new Request.Builder() - .url(entry.getValue()) - .build(); - Response resp = client.newCall(req).execute(); - Assert.assertEquals(200, resp.code()); - } - } -} diff --git a/android/app/src/androidTest/java/org/getlantern/lantern/test/UpdateTest.java b/android/app/src/androidTest/java/org/getlantern/lantern/test/UpdateTest.java index bd44e0dacd..ff34fd2106 100644 --- a/android/app/src/androidTest/java/org/getlantern/lantern/test/UpdateTest.java +++ b/android/app/src/androidTest/java/org/getlantern/lantern/test/UpdateTest.java @@ -1,72 +1,72 @@ -package org.getlantern.lantern.test; - -import static androidx.test.espresso.Espresso.onView; -import static androidx.test.espresso.action.ViewActions.click; - -import android.content.Intent; -import android.os.SystemClock; - -import androidx.test.InstrumentationRegistry; -import androidx.test.filters.LargeTest; -import androidx.test.filters.SdkSuppress; -import androidx.test.rule.ActivityTestRule; -import androidx.test.runner.AndroidJUnit4; -import androidx.test.uiautomator.UiDevice; -import androidx.test.uiautomator.UiObject; -import androidx.test.uiautomator.UiSelector; - -import org.getlantern.lantern.MainActivity; -import org.getlantern.lantern.R; -import org.getlantern.lantern.activity.UpdateActivity_; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; -import org.junit.runner.RunWith; - -@RunWith(AndroidJUnit4.class) -@SdkSuppress(minSdkVersion = 18) -@LargeTest -public class UpdateTest { - @Rule - public ActivityTestRule mMainActivityRule = new MyActivityTestRule(MainActivity.class); - @Rule - public ActivityTestRule mUpdateActivityRule = - new MyActivityTestRule(UpdateActivity_.class, - false /* initialTouchMode */, - false /* launchActivity */); - - UiDevice mDevice; - UiWatchers mWatchers = new UiWatchers(); - String updateURL = "https://github.com/getlantern/lantern/releases/download/3.6.1/update_android_arm.bz2"; - - @Before - public void setUp() throws Exception { - mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); - TestUtils.clickButtonIfPresent(mDevice, "OK"); - mWatchers.registerAnrAndCrashWatchers(); - } - - @Test - public void testUpdate() { - Intent intent = new Intent(); - intent.putExtra("updateUrl", updateURL); - mUpdateActivityRule.launchActivity(intent); - SystemClock.sleep(1000); // leave some time for flashlight to run - onView(withId(R.id.installUpdate)).perform(click()); - waitForDownloadingAPK(); - SystemClock.sleep(1000); - TestUtils.clickButtonIfPresent(mDevice, "OK"); - TestUtils.clickButtonIfPresent(mDevice, "Install"); - SystemClock.sleep(5000); - } - - private void waitForDownloadingAPK() { - String text = mUpdateActivityRule.getActivity(). - getResources().getString(R.string.updating_lantern); - UiObject dialog; - do { - SystemClock.sleep(1000); - dialog = mDevice.findObject(new UiSelector().text(text)); - } while (dialog.exists()); - } -} +//package org.getlantern.lantern.test; +// +//import static androidx.test.espresso.Espresso.onView; +//import static androidx.test.espresso.action.ViewActions.click; +// +//import android.content.Intent; +//import android.os.SystemClock; +// +//import androidx.test.InstrumentationRegistry; +//import androidx.test.filters.LargeTest; +//import androidx.test.filters.SdkSuppress; +//import androidx.test.rule.ActivityTestRule; +//import androidx.test.runner.AndroidJUnit4; +//import androidx.test.uiautomator.UiDevice; +//import androidx.test.uiautomator.UiObject; +//import androidx.test.uiautomator.UiSelector; +// +//import org.getlantern.lantern.MainActivity; +//import org.getlantern.lantern.R; +//import org.getlantern.lantern.activity.UpdateActivity_; +//import org.junit.Before; +//import org.junit.Rule; +//import org.junit.Test; +//import org.junit.runner.RunWith; +// +//@RunWith(AndroidJUnit4.class) +//@SdkSuppress(minSdkVersion = 18) +//@LargeTest +//public class UpdateTest { +// @Rule +// public ActivityTestRule mMainActivityRule = new MyActivityTestRule(MainActivity.class); +// @Rule +// public ActivityTestRule mUpdateActivityRule = +// new MyActivityTestRule(UpdateActivity_.class, +// false /* initialTouchMode */, +// false /* launchActivity */); +// +// UiDevice mDevice; +// UiWatchers mWatchers = new UiWatchers(); +// String updateURL = "https://github.com/getlantern/lantern/releases/download/3.6.1/update_android_arm.bz2"; +// +// @Before +// public void setUp() throws Exception { +// mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation()); +// TestUtils.clickButtonIfPresent(mDevice, "OK"); +// mWatchers.registerAnrAndCrashWatchers(); +// } +// +// @Test +// public void testUpdate() { +// Intent intent = new Intent(); +// intent.putExtra("updateUrl", updateURL); +// mUpdateActivityRule.launchActivity(intent); +// SystemClock.sleep(1000); // leave some time for flashlight to run +// onView(withId(R.id.installUpdate)).perform(click()); +// waitForDownloadingAPK(); +// SystemClock.sleep(1000); +// TestUtils.clickButtonIfPresent(mDevice, "OK"); +// TestUtils.clickButtonIfPresent(mDevice, "Install"); +// SystemClock.sleep(5000); +// } +// +// private void waitForDownloadingAPK() { +// String text = mUpdateActivityRule.getActivity(). +// getResources().getString(R.string.updating_lantern); +// UiObject dialog; +// do { +// SystemClock.sleep(1000); +// dialog = mDevice.findObject(new UiSelector().text(text)); +// } while (dialog.exists()); +// } +//} diff --git a/android/build.gradle b/android/build.gradle index 510737ce54..b11964f957 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -92,3 +92,4 @@ subprojects { tasks.register("clean", Delete) { delete rootProject.buildDir } + diff --git a/integration_test/app_startup_flow_test.dart b/integration_test/app_startup_flow_test.dart new file mode 100644 index 0000000000..764399483c --- /dev/null +++ b/integration_test/app_startup_flow_test.dart @@ -0,0 +1,36 @@ +import 'package:lantern/features/home/home.dart'; +import 'package:lantern/main.dart' as app; +import 'package:patrol/patrol.dart'; + +import '../test/utils/test_common.dart'; + +/// Looks like patrol does not have native support yet for Linux and Windows +/// so any test that interact with the native layer will / need to use flutter test +/// patrol feature priority:https://patrol.leancode.co/native/feature-parity +/// finder documentation: https://patrol.leancode.co/finders/usage + +///1 +/// if you want to interact with the native layer, then user patrolTest +/// For running patrolTest, you need to run the patrol test --target integration_test/$fileName +///Make sure using this test will run as native test and not as a flutter test +///So use this only when you need to interact with the native layer + +/// 2 +/// if you do not want not to interact with the native layer, then use patrolWidgetTest +/// For running patrolWidgetTest, you need to run the patrol test or flutter test both will work +void main() { + patrolWidgetTest( + "app start up sequence", + ($) async { + await app.main(); + + await $.pumpAndSettle(); + + await $(HomePage).waitUntilVisible(); + + final bottombar = find.byType(BottomNavigationBar); + expect(bottombar, findsOneWidget); + }, + ); +} + diff --git a/integration_test/features/vpn_test.dart b/integration_test/features/vpn_test.dart index a94746b8b0..15fe99afa9 100644 --- a/integration_test/features/vpn_test.dart +++ b/integration_test/features/vpn_test.dart @@ -1,20 +1,25 @@ - - +import 'package:flutter/cupertino.dart'; import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; +import 'package:lantern/features/vpn/vpn_switch.dart'; import 'package:lantern/main.dart' as app; +import 'package:patrol/patrol.dart'; -void main(){ - final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - - +void main() { + patrolTest( + "VPN test end to end", + (petrolTester) async { + await app.main(); - testWidgets('', (widgetTester) async{ + await petrolTester.pumpAndSettle(); - app.main(); - },); + expect(petrolTester(VPNSwitch()), findsOneWidget); + }, + variant: TargetPlatformVariant.only(TargetPlatform.android), + config: PatrolTesterConfig( -} \ No newline at end of file + ), + ); +} diff --git a/integration_test/0_change_language_test.dart b/integration_test/old_test/0_change_language_test.dart similarity index 87% rename from integration_test/0_change_language_test.dart rename to integration_test/old_test/0_change_language_test.dart index d5a8899831..a2e0d8fb4e 100644 --- a/integration_test/0_change_language_test.dart +++ b/integration_test/old_test/0_change_language_test.dart @@ -1,8 +1,8 @@ import 'package:intl/intl.dart'; import 'package:lantern/i18n/localization_constants.dart'; -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { final lang = toBeginningOfSentenceCase(displayLanguage(simulatedLocale))!; diff --git a/integration_test/10A_contact_info_screen_test.dart b/integration_test/old_test/10A_contact_info_screen_test.dart similarity index 91% rename from integration_test/10A_contact_info_screen_test.dart rename to integration_test/old_test/10A_contact_info_screen_test.dart index dd205c930d..afeb2fb93e 100644 --- a/integration_test/10A_contact_info_screen_test.dart +++ b/integration_test/old_test/10A_contact_info_screen_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/10B_contact_info_screen_test.dart b/integration_test/old_test/10B_contact_info_screen_test.dart similarity index 92% rename from integration_test/10B_contact_info_screen_test.dart rename to integration_test/old_test/10B_contact_info_screen_test.dart index 101ef40c10..cf4e05a706 100644 --- a/integration_test/10B_contact_info_screen_test.dart +++ b/integration_test/old_test/10B_contact_info_screen_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/11_rename_contact_test.dart b/integration_test/old_test/11_rename_contact_test.dart similarity index 94% rename from integration_test/11_rename_contact_test.dart rename to integration_test/old_test/11_rename_contact_test.dart index 3d30fb591a..17e2e90cb5 100644 --- a/integration_test/11_rename_contact_test.dart +++ b/integration_test/old_test/11_rename_contact_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/12_delete_contact_test.dart b/integration_test/old_test/12_delete_contact_test.dart similarity index 92% rename from integration_test/12_delete_contact_test.dart rename to integration_test/old_test/12_delete_contact_test.dart index d230a13b90..728416bf0b 100644 --- a/integration_test/12_delete_contact_test.dart +++ b/integration_test/old_test/12_delete_contact_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/13_block_contact_test.dart b/integration_test/old_test/13_block_contact_test.dart similarity index 93% rename from integration_test/13_block_contact_test.dart rename to integration_test/old_test/13_block_contact_test.dart index 219858597f..6d561ab1f2 100644 --- a/integration_test/13_block_contact_test.dart +++ b/integration_test/old_test/13_block_contact_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/14A_react_to_message_test.dart b/integration_test/old_test/14A_react_to_message_test.dart similarity index 96% rename from integration_test/14A_react_to_message_test.dart rename to integration_test/old_test/14A_react_to_message_test.dart index 02828b7a69..72d98cac15 100644 --- a/integration_test/14A_react_to_message_test.dart +++ b/integration_test/old_test/14A_react_to_message_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/14B_delete_for_me_test.dart b/integration_test/old_test/14B_delete_for_me_test.dart similarity index 96% rename from integration_test/14B_delete_for_me_test.dart rename to integration_test/old_test/14B_delete_for_me_test.dart index 4f0cedb798..031e90ca2b 100644 --- a/integration_test/14B_delete_for_me_test.dart +++ b/integration_test/old_test/14B_delete_for_me_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/14C_delete_for_everyone_test.dart b/integration_test/old_test/14C_delete_for_everyone_test.dart similarity index 96% rename from integration_test/14C_delete_for_everyone_test.dart rename to integration_test/old_test/14C_delete_for_everyone_test.dart index 55a301090b..d5c76ece28 100644 --- a/integration_test/14C_delete_for_everyone_test.dart +++ b/integration_test/old_test/14C_delete_for_everyone_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/15_copy_recovery_key_test.dart b/integration_test/old_test/15_copy_recovery_key_test.dart similarity index 95% rename from integration_test/15_copy_recovery_key_test.dart rename to integration_test/old_test/15_copy_recovery_key_test.dart index 5dbdc5cf40..34ddc1b6a7 100644 --- a/integration_test/15_copy_recovery_key_test.dart +++ b/integration_test/old_test/15_copy_recovery_key_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/16_recovery_flow_test.dart b/integration_test/old_test/16_recovery_flow_test.dart similarity index 100% rename from integration_test/16_recovery_flow_test.dart rename to integration_test/old_test/16_recovery_flow_test.dart diff --git a/integration_test/17_verify_contact_test.dart b/integration_test/old_test/17_verify_contact_test.dart similarity index 93% rename from integration_test/17_verify_contact_test.dart rename to integration_test/old_test/17_verify_contact_test.dart index 803173fb2a..79c1366cb2 100644 --- a/integration_test/17_verify_contact_test.dart +++ b/integration_test/old_test/17_verify_contact_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/18_disappearing_messages_test.dart b/integration_test/old_test/18_disappearing_messages_test.dart similarity index 94% rename from integration_test/18_disappearing_messages_test.dart rename to integration_test/old_test/18_disappearing_messages_test.dart index 35b7372449..05f6e5e52a 100644 --- a/integration_test/18_disappearing_messages_test.dart +++ b/integration_test/old_test/18_disappearing_messages_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/19_become_pro_test.dart b/integration_test/old_test/19_become_pro_test.dart similarity index 75% rename from integration_test/19_become_pro_test.dart rename to integration_test/old_test/19_become_pro_test.dart index 8cd9bd2525..99a4f44faa 100644 --- a/integration_test/19_become_pro_test.dart +++ b/integration_test/old_test/19_become_pro_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { await runTest( diff --git a/integration_test/1A_chat_number_formats_test.dart b/integration_test/old_test/1A_chat_number_formats_test.dart similarity index 87% rename from integration_test/1A_chat_number_formats_test.dart rename to integration_test/old_test/1A_chat_number_formats_test.dart index d168d1a40a..099bbfc088 100644 --- a/integration_test/1A_chat_number_formats_test.dart +++ b/integration_test/old_test/1A_chat_number_formats_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/1B_enroll_leave_Me_note_test.dart b/integration_test/old_test/1B_enroll_leave_Me_note_test.dart similarity index 86% rename from integration_test/1B_enroll_leave_Me_note_test.dart rename to integration_test/old_test/1B_enroll_leave_Me_note_test.dart index 76fc58e080..9fe5f22edb 100644 --- a/integration_test/1B_enroll_leave_Me_note_test.dart +++ b/integration_test/old_test/1B_enroll_leave_Me_note_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/20_vpn_test.dart b/integration_test/old_test/20_vpn_test.dart similarity index 79% rename from integration_test/20_vpn_test.dart rename to integration_test/old_test/20_vpn_test.dart index 5aa438d63a..eeb71fc2d7 100644 --- a/integration_test/20_vpn_test.dart +++ b/integration_test/old_test/20_vpn_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/21_account_management_test.dart b/integration_test/old_test/21_account_management_test.dart similarity index 80% rename from integration_test/21_account_management_test.dart rename to integration_test/old_test/21_account_management_test.dart index b3544307d7..c5a739cc47 100644 --- a/integration_test/21_account_management_test.dart +++ b/integration_test/old_test/21_account_management_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/22_invite_test.dart b/integration_test/old_test/22_invite_test.dart similarity index 79% rename from integration_test/22_invite_test.dart rename to integration_test/old_test/22_invite_test.dart index 7f98fe8d7e..a447a4265a 100644 --- a/integration_test/22_invite_test.dart +++ b/integration_test/old_test/22_invite_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/23_download_desktop_test.dart b/integration_test/old_test/23_download_desktop_test.dart similarity index 84% rename from integration_test/23_download_desktop_test.dart rename to integration_test/old_test/23_download_desktop_test.dart index 68d88194e2..6ef011401a 100644 --- a/integration_test/23_download_desktop_test.dart +++ b/integration_test/old_test/23_download_desktop_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/24A_authorize_device_pro_PIN_test.dart b/integration_test/old_test/24A_authorize_device_pro_PIN_test.dart similarity index 80% rename from integration_test/24A_authorize_device_pro_PIN_test.dart rename to integration_test/old_test/24A_authorize_device_pro_PIN_test.dart index 4e8275d647..d37417990b 100644 --- a/integration_test/24A_authorize_device_pro_PIN_test.dart +++ b/integration_test/old_test/24A_authorize_device_pro_PIN_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { await runTest( diff --git a/integration_test/24B_authorize_device_pro_email_test.dart b/integration_test/old_test/24B_authorize_device_pro_email_test.dart similarity index 87% rename from integration_test/24B_authorize_device_pro_email_test.dart rename to integration_test/old_test/24B_authorize_device_pro_email_test.dart index 3eeb778861..e57ebe9706 100644 --- a/integration_test/24B_authorize_device_pro_email_test.dart +++ b/integration_test/old_test/24B_authorize_device_pro_email_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { await runTest( diff --git a/integration_test/25_report_issue_test.dart b/integration_test/old_test/25_report_issue_test.dart similarity index 87% rename from integration_test/25_report_issue_test.dart rename to integration_test/old_test/25_report_issue_test.dart index 78b57ee303..ee26094f15 100644 --- a/integration_test/25_report_issue_test.dart +++ b/integration_test/old_test/25_report_issue_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/26_proxy_all_traffic_test.dart b/integration_test/old_test/26_proxy_all_traffic_test.dart similarity index 86% rename from integration_test/26_proxy_all_traffic_test.dart rename to integration_test/old_test/26_proxy_all_traffic_test.dart index a4c9a61ea2..ec3d50fc38 100644 --- a/integration_test/26_proxy_all_traffic_test.dart +++ b/integration_test/old_test/26_proxy_all_traffic_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { await runTest( diff --git a/integration_test/27_replica_search_test.dart b/integration_test/old_test/27_replica_search_test.dart similarity index 90% rename from integration_test/27_replica_search_test.dart rename to integration_test/old_test/27_replica_search_test.dart index cbed06d2b3..662481c0b4 100644 --- a/integration_test/27_replica_search_test.dart +++ b/integration_test/old_test/27_replica_search_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { await runTest( diff --git a/integration_test/2_add_via_secure_number_test.dart b/integration_test/old_test/2_add_via_secure_number_test.dart similarity index 90% rename from integration_test/2_add_via_secure_number_test.dart rename to integration_test/old_test/2_add_via_secure_number_test.dart index 07c537e3ac..d1f88766a4 100644 --- a/integration_test/2_add_via_secure_number_test.dart +++ b/integration_test/old_test/2_add_via_secure_number_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { await runTest((driver) async { diff --git a/integration_test/3_send_first_message_test.dart b/integration_test/old_test/3_send_first_message_test.dart similarity index 87% rename from integration_test/3_send_first_message_test.dart rename to integration_test/old_test/3_send_first_message_test.dart index 8a02d503f7..24ec54cf24 100644 --- a/integration_test/3_send_first_message_test.dart +++ b/integration_test/old_test/3_send_first_message_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/4_view_attachments_test.dart b/integration_test/old_test/4_view_attachments_test.dart similarity index 86% rename from integration_test/4_view_attachments_test.dart rename to integration_test/old_test/4_view_attachments_test.dart index 22954c85bf..e5f6f4260e 100644 --- a/integration_test/4_view_attachments_test.dart +++ b/integration_test/old_test/4_view_attachments_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { await runTest( diff --git a/integration_test/5_voice_memo_test.dart b/integration_test/old_test/5_voice_memo_test.dart similarity index 96% rename from integration_test/5_voice_memo_test.dart rename to integration_test/old_test/5_voice_memo_test.dart index e747dd5882..95f44eeb31 100644 --- a/integration_test/5_voice_memo_test.dart +++ b/integration_test/old_test/5_voice_memo_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { await runTest( diff --git a/integration_test/6A_scan_QR_code_test.dart b/integration_test/old_test/6A_scan_QR_code_test.dart similarity index 96% rename from integration_test/6A_scan_QR_code_test.dart rename to integration_test/old_test/6A_scan_QR_code_test.dart index 15aeab2854..55b5264623 100644 --- a/integration_test/6A_scan_QR_code_test.dart +++ b/integration_test/old_test/6A_scan_QR_code_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/6B_request_flow_test.dart b/integration_test/old_test/6B_request_flow_test.dart similarity index 92% rename from integration_test/6B_request_flow_test.dart rename to integration_test/old_test/6B_request_flow_test.dart index 881b259f24..d5c2e1cc31 100644 --- a/integration_test/6B_request_flow_test.dart +++ b/integration_test/old_test/6B_request_flow_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/6C_introductions_test.dart b/integration_test/old_test/6C_introductions_test.dart similarity index 95% rename from integration_test/6C_introductions_test.dart rename to integration_test/old_test/6C_introductions_test.dart index 4af8a784ef..4c0a82cbf8 100644 --- a/integration_test/6C_introductions_test.dart +++ b/integration_test/old_test/6C_introductions_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/7_call_test.dart b/integration_test/old_test/7_call_test.dart similarity index 91% rename from integration_test/7_call_test.dart rename to integration_test/old_test/7_call_test.dart index 2713989aa5..6b104ffb7b 100644 --- a/integration_test/7_call_test.dart +++ b/integration_test/old_test/7_call_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/8A_send_single_intro_test.dart b/integration_test/old_test/8A_send_single_intro_test.dart similarity index 96% rename from integration_test/8A_send_single_intro_test.dart rename to integration_test/old_test/8A_send_single_intro_test.dart index c233b7be09..18f15e7f03 100644 --- a/integration_test/8A_send_single_intro_test.dart +++ b/integration_test/old_test/8A_send_single_intro_test.dart @@ -1,4 +1,4 @@ -import 'integration_test_common.dart'; +import '../integration_test_common.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/8B_send_multiple_intros_test.dart b/integration_test/old_test/8B_send_multiple_intros_test.dart similarity index 93% rename from integration_test/8B_send_multiple_intros_test.dart rename to integration_test/old_test/8B_send_multiple_intros_test.dart index 0862ab9fd1..f23ac254f7 100644 --- a/integration_test/8B_send_multiple_intros_test.dart +++ b/integration_test/old_test/8B_send_multiple_intros_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/9A_search_contacts_messages_test.dart b/integration_test/old_test/9A_search_contacts_messages_test.dart similarity index 92% rename from integration_test/9A_search_contacts_messages_test.dart rename to integration_test/old_test/9A_search_contacts_messages_test.dart index 07b62c86bb..5622bce694 100644 --- a/integration_test/9A_search_contacts_messages_test.dart +++ b/integration_test/old_test/9A_search_contacts_messages_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/9B_search_contacts_test.dart b/integration_test/old_test/9B_search_contacts_test.dart similarity index 92% rename from integration_test/9B_search_contacts_test.dart rename to integration_test/old_test/9B_search_contacts_test.dart index 6eb6bf8915..27f91e0209 100644 --- a/integration_test/9B_search_contacts_test.dart +++ b/integration_test/old_test/9B_search_contacts_test.dart @@ -1,5 +1,5 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; +import '../integration_test_common.dart'; +import '../integration_test_constants.dart'; Future main() async { late FlutterDriver driver; diff --git a/integration_test/integration_test_common.dart b/integration_test/old_test/integration_test_common.dart similarity index 100% rename from integration_test/integration_test_common.dart rename to integration_test/old_test/integration_test_common.dart diff --git a/integration_test/integration_test_constants.dart b/integration_test/old_test/integration_test_constants.dart similarity index 100% rename from integration_test/integration_test_constants.dart rename to integration_test/old_test/integration_test_constants.dart diff --git a/integration_test/test_bundle.dart b/integration_test/test_bundle.dart new file mode 100644 index 0000000000..afe08c23fb --- /dev/null +++ b/integration_test/test_bundle.dart @@ -0,0 +1,86 @@ +// GENERATED CODE - DO NOT MODIFY BY HAND AND DO NOT COMMIT TO VERSION CONTROL +// ignore_for_file: type=lint, invalid_use_of_internal_member + +import 'dart:async'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:patrol/patrol.dart'; +import 'package:patrol/src/native/contracts/contracts.dart'; +import 'package:test_api/src/backend/invoker.dart'; + +// START: GENERATED TEST IMPORTS +import 'features/vpn_test.dart' as features__vpn_test; +// END: GENERATED TEST IMPORTS + +Future main() async { + // This is the entrypoint of the bundled Dart test. + // + // Its responsibilies are: + // * Running a special Dart test that runs before all the other tests and + // explores the hierarchy of groups and tests. + // * Hosting a PatrolAppService, which the native side of Patrol uses to get + // the Dart tests, and to request execution of a specific Dart test. + // + // When running on Android, the Android Test Orchestrator, before running the + // tests, makes an initial run to gather the tests that it will later run. The + // native side of Patrol (specifically: PatrolJUnitRunner class) is hooked + // into the Android Test Orchestrator lifecycle and knows when that initial + // run happens. When it does, PatrolJUnitRunner makes an RPC call to + // PatrolAppService and asks it for Dart tests. + // + // When running on iOS, the native side of Patrol (specifically: the + // PATROL_INTEGRATION_TEST_IOS_RUNNER macro) makes an initial run to gather + // the tests that it will later run (same as the Android). During that initial + // run, it makes an RPC call to PatrolAppSevice and asks it for Dart tests. + // + // Once the native runner has the list of Dart tests, it dynamically creates + // native test cases from them. On Android, this is done using the + // Parametrized JUnit runner. On iOS, new test case methods are swizzled into + // the RunnerUITests class, taking advantage of the very dynamic nature of + // Objective-C runtime. + // + // Execution of these dynamically created native test cases is then fully + // managed by the underlying native test framework (JUnit on Android, XCTest + // on iOS). The native test cases do only one thing - request execution of the + // Dart test (out of which they had been created) and wait for it to complete. + // The result of running the Dart test is the result of the native test case. + + final nativeAutomator = NativeAutomator(config: NativeAutomatorConfig()); + await nativeAutomator.initialize(); + final binding = PatrolBinding.ensureInitialized(NativeAutomatorConfig()); + final testExplorationCompleter = Completer(); + + // A special test to explore the hierarchy of groups and tests. This is a hack + // around https://github.com/dart-lang/test/issues/1998. + // + // This test must be the first to run. If not, the native side likely won't + // receive any tests, and everything will fall apart. + test('patrol_test_explorer', () { + // Maybe somewhat counterintuitively, this callback runs *after* the calls + // to group() below. + final topLevelGroup = Invoker.current!.liveTest.groups.first; + final dartTestGroup = createDartTestGroup(topLevelGroup, + tags: null, + excludeTags: null, + ); + testExplorationCompleter.complete(dartTestGroup); + print('patrol_test_explorer: obtained Dart-side test hierarchy:'); + reportGroupStructure(dartTestGroup); + }); + + // START: GENERATED TEST GROUPS + group('features.vpn_test', features__vpn_test.main); + // END: GENERATED TEST GROUPS + + final dartTestGroup = await testExplorationCompleter.future; + final appService = PatrolAppService(topLevelDartTestGroup: dartTestGroup); + binding.patrolAppService = appService; + await runAppService(appService); + + // Until now, the native test runner was waiting for us, the Dart side, to + // come alive. Now that we did, let's tell it that we're ready to be asked + // about Dart tests. + await nativeAutomator.markPatrolAppServiceReady(); + + await appService.testExecutionCompleted; +} diff --git a/integration_test_old/helpers/clipboard_test.dart b/integration_test_old/helpers/clipboard_test.dart index 88454b98f1..e69de29bb2 100644 --- a/integration_test_old/helpers/clipboard_test.dart +++ b/integration_test_old/helpers/clipboard_test.dart @@ -1,20 +0,0 @@ -import 'dart:async'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:flutter/services.dart'; - -class ClipboardMock { - Object get clipboardData => _clipboardData; - Object _clipboardData = { - 'text': null, - }; - - Future handleMethodCall(MethodCall methodCall) async { - switch (methodCall.method) { - case 'Clipboard.getData': - return _clipboardData; - case 'Clipboard.setData': - _clipboardData = methodCall.arguments as Object; - break; - } - } -} diff --git a/integration_test_old/helpers/waiter_test.dart b/integration_test_old/helpers/waiter_test.dart index 1e42b62089..e69de29bb2 100644 --- a/integration_test_old/helpers/waiter_test.dart +++ b/integration_test_old/helpers/waiter_test.dart @@ -1,7 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; - -Future awaitFor( - WidgetTester tester, { - Duration duration = const Duration(seconds: 1), -}) async => - await tester.pump(duration); diff --git a/lib/app.dart b/lib/app.dart index 8878c40808..5a7dd6f911 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -40,6 +40,7 @@ class _LanternAppState extends State @override void initState() { + print("initState"); _animateNetworkWarning(); WidgetsBinding.instance.addPostFrameCallback((_) { initDeepLinks(); @@ -115,8 +116,7 @@ class _LanternAppState extends State } } - Widget _buildMaterialApp(BuildContext context, String lang) { - final currentLocal = View.of(context).platformDispatcher.locale; + Widget _buildMaterialApp(BuildContext context, String lang,Locale currentLocal) { final app = MaterialApp.router( locale: currentLocale(lang), debugShowCheckedModeBanner: false, @@ -175,6 +175,7 @@ class _LanternAppState extends State ], child: sessionModel.language( (context, lang, child) { + print("selected language: $lang"); Localization.locale = lang.startsWith('en') ? "en_us" : lang; return GlobalLoaderOverlay( useDefaultLoading: false, @@ -192,7 +193,7 @@ class _LanternAppState extends State child: I18n( initialLocale: currentLocale(lang), child: ScaffoldMessenger( - child: _buildMaterialApp(context, lang), + child: _buildMaterialApp(context, lang,currentLocal), ), ), ); diff --git a/lib/main.dart b/lib/main.dart index f2f016713f..8b85844cde 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -42,6 +42,7 @@ Future main() async { await Localization.ensureInitialized(); await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); + SentryFlutter.init((options) { // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring. // We recommend adjusting this value in production. diff --git a/test/app_test.dart b/test/app_test.dart index dda67b197b..0515691287 100644 --- a/test/app_test.dart +++ b/test/app_test.dart @@ -1,7 +1,107 @@ +import 'package:lantern/app.dart'; +import 'package:lantern/common/ui/custom/internet_checker.dart'; +import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; +import 'package:lantern/features/messaging/messaging_model.dart'; +import 'package:lantern/features/replica/models/replica_model.dart'; +import 'package:lantern/features/vpn/vpn_notifier.dart'; +import 'utils/test_common.dart'; -/// Looks like patrol does not have native support yet -/// so any test that interact with the native layer will / need to use flutter test -/// patrol feature priority:https://patrol.leancode.co/native/feature-parity -/// finder documentation: https://patrol.leancode.co/finders/usage -void main() {} \ No newline at end of file +void main() { + late MockSessionModel mockSessionModel; + late MockBuildContext mockBuildContext; + late MockMessagingModel mockMessagingModel; + late MockReplicaModel mockReplicaModel; + late MockVpnModel mockVpnModel; + + setUp( + () { + mockSessionModel = MockSessionModel(); + mockBuildContext = MockBuildContext(); + mockMessagingModel = MockMessagingModel(); + + mockReplicaModel = MockReplicaModel(); + mockVpnModel = MockVpnModel(); + + // Injection models + sl.registerLazySingleton(() => mockSessionModel); + sl.registerLazySingleton(() => mockMessagingModel); + sl.registerLazySingleton(() => mockReplicaModel); + sl.registerLazySingleton(() => mockVpnModel); + }, + ); + + tearDown( + () { + sl.reset(); + }, + ); + + // testWidgets( + // 'Providers are correctly initialized', + // (WidgetTester tester) async { + // tester.view.devicePixelRatio = 2.0; + // tester.platformDispatcher.localesTestValue = [const Locale('en-us'), const Locale('ar-jo')]; + // tester.platformDispatcher.localeTestValue = const Locale('en-us'); + // + // + // when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); + // + // when(mockSessionModel.language(any)).thenAnswer( + // (realInvocation) { + // final builder = realInvocation.positionalArguments[0] + // as ValueWidgetBuilder; + // return builder(mockBuildContext, 'en_in', null); + // }, + // ); + // + // when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + // (realInvocation) { + // final builder = realInvocation.positionalArguments[0] + // as ValueWidgetBuilder; + // return builder(mockBuildContext, 0, null); + // }, + // ); + // + // when(mockSessionModel.developmentMode(any)).thenAnswer( + // (realInvocation) { + // final builder = realInvocation.positionalArguments[0] + // as ValueWidgetBuilder; + // return builder(mockBuildContext, false, null); + // }, + // ); + // + // await tester.pumpWidget(const LanternApp()); + // // await tester.pumpAndSettle(); + // + // final bottomBarProvider = Provider.of( + // tester.element(find.byType(MaterialApp)),listen: false); + // expect(bottomBarProvider, isNotNull); + // + // final vpnProvider = Provider.of( + // tester.element(find.byType(MaterialApp)),listen: false); + // + // expect(vpnProvider, isNotNull); + // + // final internetStatusProvider = Provider.of( + // tester.element(find.byType(MaterialApp))); + // expect(internetStatusProvider, isNotNull); + // }, + // ); + // + // testWidgets('App applies correct localization and language', + // (WidgetTester tester) async { + // await tester.pumpWidget(const LanternApp()); + // + // await tester.pumpAndSettle(); + // expect(find.text('app_name'.i18n), findsOneWidget); + // + // // Change the language and verify the locale change is applied + // final sessionModel = + // Provider.of(tester.element(find.byType(MaterialApp))); + // sessionModel.setLanguage('fr'); + // await tester.pumpAndSettle(); + // + // expect(Localization.locale, 'fr'); + // }); +} diff --git a/test/base_screen_test.dart b/test/base_screen_test.dart index 4b6788fcea..af4a999d1b 100644 --- a/test/base_screen_test.dart +++ b/test/base_screen_test.dart @@ -1,6 +1,6 @@ // import 'package:audioplayers/audioplayers.dart'; // import 'package:flutter_test/flutter_test.dart'; -// import 'package:lantern/app_test.dart'; +// import 'package:lantern/app_startup_flow_test.dart'; // import 'package:lantern/core/utils/common.dart'; // import 'package:lantern/features/home/home.dart'; // import 'package:lantern/features/messaging/messaging_model.dart'; diff --git a/test/utils/test.mocks.dart b/test/utils/test.mocks.dart index d238ed0905..3a74ab7964 100644 --- a/test/utils/test.mocks.dart +++ b/test/utils/test.mocks.dart @@ -7,7 +7,7 @@ import 'package:lantern/features/vpn/vpn_notifier.dart'; import 'package:mockito/annotations.dart'; /// All generate mock should happened or add here -/// For generate mock run dart run run build_runner build --delete-conflicting-outputs +/// For generate mock run dart run build_runner build --delete-conflicting-outputs /// So for most other test cases we need import only one class @GenerateNiceMocks([ MockSpec(), diff --git a/test/utils/test.mocks.mocks.dart b/test/utils/test.mocks.mocks.dart index 067902f87a..b77c282006 100644 --- a/test/utils/test.mocks.mocks.dart +++ b/test/utils/test.mocks.mocks.dart @@ -193,15 +193,6 @@ class MockSessionModel extends _i1.Mock implements _i2.SessionModel { ), ) as _i2.EventManager); - @override - set eventManager(_i2.EventManager? _eventManager) => super.noSuchMethod( - Invocation.setter( - #eventManager, - _eventManager, - ), - returnValueForMissingStub: null, - ); - @override _i2.ValueNotifier get networkAvailable => (super.noSuchMethod( Invocation.getter(#networkAvailable), From 62391853d673a07017b9f8a5a0f1c72f68cac187 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Tue, 15 Oct 2024 13:04:17 +0530 Subject: [PATCH 010/163] add more test for app.dart. --- test/app_test.dart | 222 +++++++++++++++++++++++++++++++-------------- 1 file changed, 153 insertions(+), 69 deletions(-) diff --git a/test/app_test.dart b/test/app_test.dart index 0515691287..42839840b7 100644 --- a/test/app_test.dart +++ b/test/app_test.dart @@ -3,7 +3,9 @@ import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; import 'package:lantern/features/messaging/messaging_model.dart'; import 'package:lantern/features/replica/models/replica_model.dart'; +import 'package:lantern/features/tray/tray_container.dart'; import 'package:lantern/features/vpn/vpn_notifier.dart'; +import 'package:lantern/features/window/window_container.dart'; import 'utils/test_common.dart'; @@ -13,8 +15,10 @@ void main() { late MockMessagingModel mockMessagingModel; late MockReplicaModel mockReplicaModel; late MockVpnModel mockVpnModel; + late MockEventManager mockEventManager; + late MockVPNChangeNotifier mockVPNChangeNotifier; - setUp( + setUpAll( () { mockSessionModel = MockSessionModel(); mockBuildContext = MockBuildContext(); @@ -22,6 +26,9 @@ void main() { mockReplicaModel = MockReplicaModel(); mockVpnModel = MockVpnModel(); + mockEventManager = MockEventManager(); + + mockVPNChangeNotifier = MockVPNChangeNotifier(); // Injection models sl.registerLazySingleton(() => mockSessionModel); @@ -31,77 +38,154 @@ void main() { }, ); - tearDown( + tearDownAll( () { sl.reset(); }, ); - // testWidgets( - // 'Providers are correctly initialized', - // (WidgetTester tester) async { - // tester.view.devicePixelRatio = 2.0; - // tester.platformDispatcher.localesTestValue = [const Locale('en-us'), const Locale('ar-jo')]; - // tester.platformDispatcher.localeTestValue = const Locale('en-us'); - // - // - // when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); - // - // when(mockSessionModel.language(any)).thenAnswer( - // (realInvocation) { - // final builder = realInvocation.positionalArguments[0] - // as ValueWidgetBuilder; - // return builder(mockBuildContext, 'en_in', null); - // }, - // ); - // - // when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( - // (realInvocation) { - // final builder = realInvocation.positionalArguments[0] - // as ValueWidgetBuilder; - // return builder(mockBuildContext, 0, null); - // }, - // ); - // - // when(mockSessionModel.developmentMode(any)).thenAnswer( - // (realInvocation) { - // final builder = realInvocation.positionalArguments[0] - // as ValueWidgetBuilder; - // return builder(mockBuildContext, false, null); - // }, - // ); - // - // await tester.pumpWidget(const LanternApp()); - // // await tester.pumpAndSettle(); - // - // final bottomBarProvider = Provider.of( - // tester.element(find.byType(MaterialApp)),listen: false); - // expect(bottomBarProvider, isNotNull); - // - // final vpnProvider = Provider.of( - // tester.element(find.byType(MaterialApp)),listen: false); - // - // expect(vpnProvider, isNotNull); - // - // final internetStatusProvider = Provider.of( - // tester.element(find.byType(MaterialApp))); - // expect(internetStatusProvider, isNotNull); - // }, - // ); - // - // testWidgets('App applies correct localization and language', - // (WidgetTester tester) async { - // await tester.pumpWidget(const LanternApp()); - // - // await tester.pumpAndSettle(); - // expect(find.text('app_name'.i18n), findsOneWidget); - // - // // Change the language and verify the locale change is applied - // final sessionModel = - // Provider.of(tester.element(find.byType(MaterialApp))); - // sessionModel.setLanguage('fr'); - // await tester.pumpAndSettle(); - // - // expect(Localization.locale, 'fr'); - // }); + group( + 'app widget', + () { + testWidgets( + 'Providers are correctly initialized', + (WidgetTester tester) async { + tester.view.devicePixelRatio = 2.0; + tester.platformDispatcher.localesTestValue = [ + const Locale('en-us'), + const Locale('ar-jo') + ]; + tester.platformDispatcher.localeTestValue = const Locale('en-us'); + + when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); + when(mockSessionModel.pathValueNotifier(any, false)) + .thenReturn(ValueNotifier(true)); + + when(mockSessionModel.language(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_in', null); + }, + ); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }, + ); + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + await tester.pumpWidget(const LanternApp()); + + final bottomBarProvider = Provider.of( + tester.element(find.byType(MaterialApp)), + listen: false); + expect(bottomBarProvider, isNotNull); + + final vpnProvider = Provider.of( + tester.element(find.byType(MaterialApp)), + listen: false); + + expect(vpnProvider, isNotNull); + + final internetStatusProvider = Provider.of( + tester.element(find.byType(MaterialApp)), + listen: false); + expect(internetStatusProvider, isNotNull); + }, + ); + + testWidgets('Desktop-specific widgets are used on desktop platforms', + (WidgetTester tester) async { + when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); + when(mockSessionModel.isTestPlayVersion) + .thenReturn(ValueNotifier(false)); + when(mockSessionModel.isStoreVersion).thenReturn(ValueNotifier(false)); + when(mockSessionModel.isAuthEnabled).thenReturn(ValueNotifier(false)); + + when(mockSessionModel.language(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_us', null); + }, + ); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }, + ); + + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, '', null); + }, + ); + + when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, null, null); + }, + ); + + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); + + await tester.pumpWidget(const LanternApp()); + + // Verify that WindowContainer and TrayContainer are used + expect(find.byType(WindowContainer), findsOneWidget); + expect(find.byType(TrayContainer), findsOneWidget); + }, variant: TargetPlatformVariant.desktop()); + }, + ); } From 9b512f2209bcb3a07a2ec3a89938c1c53476169e Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 16 Oct 2024 16:18:16 +0530 Subject: [PATCH 011/163] more test changes. --- .../features/home/homepage_flow_test.dart | 58 +++ .../features/{ => vpn}/vpn_test.dart | 17 +- integration_test/test_bundle.dart | 4 +- integration_test/utils/test_utils.dart | 8 + integration_test_old/action/input_test.dart | 247 ------------ .../action/navigation_action_test.dart | 57 --- .../conversation_page_audio_test.dart | 157 -------- .../conversation_page_test.dart | 361 ------------------ .../enums/disappearing_test.dart | 4 - integration_test_old/enums/screens_test.dart | 13 - .../helpers/clipboard_test.dart | 0 integration_test_old/helpers/waiter_test.dart | 0 integration_test_old/messages_page_test.dart | 87 ----- .../new_message_page_test.dart | 91 ----- lib/features/account/account.dart | 1 - lib/features/home/home.dart | 5 +- lib/main.dart | 25 +- pubspec.lock | 12 +- pubspec.yaml | 5 +- 19 files changed, 91 insertions(+), 1061 deletions(-) create mode 100644 integration_test/features/home/homepage_flow_test.dart rename integration_test/features/{ => vpn}/vpn_test.dart (54%) create mode 100644 integration_test/utils/test_utils.dart delete mode 100644 integration_test_old/action/input_test.dart delete mode 100644 integration_test_old/action/navigation_action_test.dart delete mode 100644 integration_test_old/conversation_page_audio_test.dart delete mode 100644 integration_test_old/conversation_page_test.dart delete mode 100644 integration_test_old/enums/disappearing_test.dart delete mode 100644 integration_test_old/enums/screens_test.dart delete mode 100644 integration_test_old/helpers/clipboard_test.dart delete mode 100644 integration_test_old/helpers/waiter_test.dart delete mode 100644 integration_test_old/messages_page_test.dart delete mode 100644 integration_test_old/new_message_page_test.dart diff --git a/integration_test/features/home/homepage_flow_test.dart b/integration_test/features/home/homepage_flow_test.dart new file mode 100644 index 0000000000..9c7e182c8d --- /dev/null +++ b/integration_test/features/home/homepage_flow_test.dart @@ -0,0 +1,58 @@ +import 'package:flutter/material.dart'; +import 'package:lantern/app.dart'; +import 'package:lantern/features/account/account_tab.dart'; +import 'package:lantern/features/home/home.dart'; +import 'package:lantern/main.dart' as app; + +import '../../utils/test_utils.dart'; + +void main() { + late MockSessionModel mockSessionModel; + + setUp( + () { + mockSessionModel = MockSessionModel(); + + sl.registerSingleton(mockSessionModel); + }, + ); + + group( + 'home page flow end to end test', + () { + patrolWidgetTest( + 'app initializes and navigates to homepage', + ($) async { + await app.main(); + + await $.pumpAndSettle(); + + await $(HomePage).waitUntilVisible(); + + expect($(BottomNavigationBar), findsOneWidget); + expect($('VPN'.i18n), findsOne); + expect($('Account'.i18n), findsOne); + expect($('VPN'.i18n).visible, equals(true)); + expect($('Account'.i18n).visible, equals(true)); + + await $('Account').tap(); + await $.pumpAndSettle(); + + expect($(AccountTab), findsOneWidget); + expect($(AppBar), findsOneWidget); + }, + ); + + patrolWidgetTest( + 'home widget for appstore and play store ', + ($) async { + await $.pumpWidget(const LanternApp()); + await $.pumpAndSettle(); + await $(HomePage).waitUntilVisible(); + + expect($(BottomNavigationBar), findsOneWidget); + }, + ); + }, + ); +} diff --git a/integration_test/features/vpn_test.dart b/integration_test/features/vpn/vpn_test.dart similarity index 54% rename from integration_test/features/vpn_test.dart rename to integration_test/features/vpn/vpn_test.dart index 15fe99afa9..1c19b972f7 100644 --- a/integration_test/features/vpn_test.dart +++ b/integration_test/features/vpn/vpn_test.dart @@ -1,25 +1,16 @@ import 'package:flutter/cupertino.dart'; -import 'package:flutter_test/flutter_test.dart'; import 'package:lantern/features/vpn/vpn_switch.dart'; -import 'package:lantern/main.dart' as app; -import 'package:patrol/patrol.dart'; +import '../../utils/test_utils.dart'; +import 'package:lantern/main.dart' as app; void main() { patrolTest( "VPN test end to end", (petrolTester) async { - await app.main(); - await petrolTester.pumpAndSettle(); - - - - - expect(petrolTester(VPNSwitch()), findsOneWidget); + expect(petrolTester(const VPNSwitch()), findsOneWidget); }, variant: TargetPlatformVariant.only(TargetPlatform.android), - config: PatrolTesterConfig( - - ), + config: const PatrolTesterConfig(), ); } diff --git a/integration_test/test_bundle.dart b/integration_test/test_bundle.dart index afe08c23fb..4458993213 100644 --- a/integration_test/test_bundle.dart +++ b/integration_test/test_bundle.dart @@ -9,7 +9,7 @@ import 'package:patrol/src/native/contracts/contracts.dart'; import 'package:test_api/src/backend/invoker.dart'; // START: GENERATED TEST IMPORTS -import 'features/vpn_test.dart' as features__vpn_test; +import 'features/home/homepage_flow_test.dart' as features__home__homepage_flow_test; // END: GENERATED TEST IMPORTS Future main() async { @@ -69,7 +69,7 @@ Future main() async { }); // START: GENERATED TEST GROUPS - group('features.vpn_test', features__vpn_test.main); + group('features.home.homepage_flow_test', features__home__homepage_flow_test.main); // END: GENERATED TEST GROUPS final dartTestGroup = await testExplorationCompleter.future; diff --git a/integration_test/utils/test_utils.dart b/integration_test/utils/test_utils.dart new file mode 100644 index 0000000000..24e9b6b459 --- /dev/null +++ b/integration_test/utils/test_utils.dart @@ -0,0 +1,8 @@ +export '../../test/utils/test.mocks.mocks.dart'; + +export 'package:flutter_test/flutter_test.dart'; +export 'package:patrol/patrol.dart'; +export 'package:i18n_extension/default.i18n.dart'; +export 'package:lantern/core/service/injection_container.dart'; +export 'package:lantern/features/home/session_model.dart'; + diff --git a/integration_test_old/action/input_test.dart b/integration_test_old/action/input_test.dart deleted file mode 100644 index 25cbb92566..0000000000 --- a/integration_test_old/action/input_test.dart +++ /dev/null @@ -1,247 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:lantern/features/messaging/conversation/audio/audio_widget.dart'; -import 'package:lantern/features/messaging/conversation/audio/waveform.dart'; -import 'package:lantern/features/messaging/conversation/message_bubble.dart'; -import 'package:lantern/features/messaging/conversation/status_row.dart'; - -import '../enums/disappearing_test.dart'; -import '../helpers/clipboard_test.dart'; -import '../helpers/waiter_test.dart'; - -class Input { - static Future setTextMessage( - WidgetTester tester, - CommonFinders find, { - String text = '', - String? emojiSelection, - bool visualize = false, - int seconds = 0, - }) async { - var textformfield = - tester.widget(find.byType(TextFormField)); - if (emojiSelection == null) { - await tester.enterText(find.byType(TextFormField), text); - } else { - // TODO: This is outdated, needs fix - await tester.tap(find.byIcon(Icons.sentiment_very_satisfied)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - await tester.tap(find.text(emojiSelection)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - } - await tester.pump(); - if (visualize) { - await awaitFor(tester, duration: Duration(seconds: seconds)); - } - expect(textformfield.controller?.text, equals(emojiSelection ?? text)); - } - - static Future checkRemovedTextMessageWithDelay( - WidgetTester tester, - CommonFinders find, { - String text = '', - Duration delay = Duration.zero, - }) async { - await Future.delayed(delay); - await awaitFor(tester, duration: const Duration(seconds: 1)); - expect(find.widgetWithText(MessageBubble, text), findsNothing); - } - - static Future setDisappearingMessage( - WidgetTester tester, - CommonFinders find, { - String key = '', - DISAPPEARING disappearing = DISAPPEARING.SECONDS_5, - bool checkDurationStatus = false, - }) async { - await tester.tap(find.byKey(Key(key))); - await awaitFor(tester, duration: const Duration(seconds: 1)); - await tester - .tap(find.widgetWithText(ListTile, disappearMap[disappearing]!)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - if (checkDurationStatus) { - expect(find.text(disappearMapReduced[disappearing]!), findsOneWidget); - } - } - - static Future sendMessage( - WidgetTester tester, - CommonFinders find, { - bool checkForBubble = false, - isAudio = false, - String? text, - }) async { - if (isAudio) { - await tester.tap(find.widgetWithIcon(GestureDetector, Icons.send)); - } else { - final sendButtonFinder = find.byKey(const ValueKey('send_message')); - await tester.tap(sendButtonFinder); - } - await awaitFor(tester, duration: const Duration(seconds: 1)); - if (checkForBubble) { - if (text != null) { - expect(find.widgetWithText(MessageBubble, text), findsOneWidget); - } - if (isAudio) { - print('await for the audio to be sended'); - await awaitFor(tester, duration: const Duration(seconds: 2)); - print('await for the audio to be playable'); - await awaitFor(tester, duration: const Duration(seconds: 2)); - await awaitFor(tester, duration: const Duration(seconds: 2)); - expect(find.byType(Waveform), findsOneWidget); - } - } - } - - static Future removeMessageForMe( - WidgetTester tester, - CommonFinders find, { - String text = '', - optionTitle = '', - removeBtnTitle = '', - bool checkDialog = false, - }) async { - await tester.longPress(find.widgetWithText(MessageBubble, text)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - await tester.tap(find.text(optionTitle)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - if (checkDialog) { - expect(find.byType(AlertDialog), findsOneWidget); - } - await tester.tap(find.text(removeBtnTitle)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - expect(find.widgetWithText(MessageBubble, text), findsNothing); - } - - static Future copyTextMessage( - WidgetTester tester, - CommonFinders find, { - String text = '', - optionTitle = '', - }) async { - await tester.longPress(find.widgetWithText(MessageBubble, text)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - await tester.tap(find.text(optionTitle)); - await tester.pump(); - } - - static Future setReply( - WidgetTester tester, - CommonFinders find, { - String text = '', - optionTitle = '', - bool checkReply = false, - }) async { - await tester.longPress(find.widgetWithText(MessageBubble, text)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - await tester.tap(find.text(optionTitle)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - if (checkReply) { - expect(find.widgetWithText(Row, 'Replying to me'), findsOneWidget); - } - } - - static Future closeReply( - WidgetTester tester, - CommonFinders find, { - String key = '', - }) async { - await tester.tap(find.byKey(Key(key))); - await awaitFor(tester, duration: const Duration(seconds: 1)); - expect(find.widgetWithText(Row, 'Replying to me'), findsNothing); - } - - static Future setReaction( - WidgetTester tester, - CommonFinders find, { - String text = '', - reaction = '', - customReaction, - bool isCustomReaction = false, - }) async { - await tester.longPress(find.widgetWithText(MessageBubble, text)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - await tester.tap(find.byKey(Key(reaction))); - await awaitFor(tester, duration: const Duration(seconds: 1)); - if (isCustomReaction) { - await tester.tap(find.text(customReaction)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - } - expect( - find.widgetWithText(StatusRow, customReaction ?? reaction), - findsOneWidget, - ); - } - - static Future startRecording( - WidgetTester tester, - CommonFinders find, { - String key = '', - Duration recordFor = Duration.zero, - bool checkAudioPreviewComponents = false, - }) async { - await tester.timedDrag( - find.byKey( - Key(key), - ), - tester.getCenter(find.byKey(Key(key))), - recordFor, - ); - await tester.pumpAndSettle(); - if (checkAudioPreviewComponents) { - expect( - find.widgetWithIcon(GestureDetector, Icons.delete), - findsOneWidget, - ); - expect(find.widgetWithIcon(GestureDetector, Icons.send), findsOneWidget); - expect(find.byType(AudioWidget), findsOneWidget); - } - } - - static Future playAudioPreview( - WidgetTester tester, - CommonFinders find, { - Duration playFor = Duration.zero, - bool checkAudioPreviewComponents = false, - int expectedAudioCompletion = 0, - }) async { - await tester.tap(find.widgetWithIcon(TextButton, Icons.play_arrow)); - await tester.pump(playFor); - await tester.tap(find.widgetWithIcon(TextButton, Icons.pause)); - await awaitFor(tester, duration: const Duration(seconds: 1)); - if (checkAudioPreviewComponents) { - expect(find.byType(Slider), findsOneWidget); - var slider = tester.widget(find.byType(Slider)); - var waveform = tester.widget(find.byType(Waveform)); - expect((waveform).progressPercentage, greaterThanOrEqualTo(33)); - expect((slider).value, greaterThanOrEqualTo(expectedAudioCompletion)); - } - } - - static Future deleteAudioPreview( - WidgetTester tester, - CommonFinders find, { - bool checkAudioPreviewComponents = false, - }) async { - await tester.tap(find.widgetWithIcon(GestureDetector, Icons.delete)); - await tester.pumpAndSettle(); - if (checkAudioPreviewComponents) { - expect(find.byType(Slider), findsNothing); - expect(find.byType(Waveform), findsNothing); - } - } - - static Future checkClipboard( - WidgetTester tester, - CommonFinders find, - ClipboardMock clipboard, { - String text = '', - }) async { - expect( - clipboard.clipboardData, - equals( - {'text': text}, - ), - ); - } -} diff --git a/integration_test_old/action/navigation_action_test.dart b/integration_test_old/action/navigation_action_test.dart deleted file mode 100644 index 2dcbae4b6c..0000000000 --- a/integration_test_old/action/navigation_action_test.dart +++ /dev/null @@ -1,57 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:lantern/app.dart'; -import 'package:lantern/core/utils/common.dart'; - -import '../enums/screens_test.dart'; -import '../helpers/waiter_test.dart'; - -class GoTo { - /// NavigateTo is used to handle the navigation flow from one screen to another. - /// the navigation is based on the Origin and Destination screens. - /// So we can have more than 1 scenario were we can navigate from one screen to another by more than 1 way. - static Future navigateTo( - WidgetTester tester, { - SCREENS from = SCREENS.MAIN, - to = SCREENS.MESSAGES, - }) async { - switch (to) { - case SCREENS.MAIN: - case SCREENS.MESSAGES: - await tester.pumpWidget(LanternApp()); - await tester.pumpAndSettle(); - print('This pump and settle is used for 2 reasons'); - print('1. The settle happens after the main has been completed'); - print( - '2. Autoroute is still building so the second settle is just to await for that process to be completed', - ); - await tester.pumpAndSettle(); - break; - case SCREENS.CONTACTS: - if (from == SCREENS.MESSAGES) { - await tester.tap(find.byType(FloatingActionButton)); - await tester.pumpAndSettle(); - } - break; - case SCREENS.CONVERSATION: - if (from == SCREENS.MESSAGES) { - await tester - .tap(find.widgetWithText(ListItemFactory, 'Note to self')); - await awaitFor(tester, duration: const Duration(seconds: 1)); - find.widgetWithText(ListItemFactory, 'Note to self'); - } - if (from == SCREENS.CONTACTS) { - await tester - .tap(find.widgetWithText(ListItemFactory, 'Note to self')); - await awaitFor(tester, duration: const Duration(seconds: 1)); - } - break; - default: - return; - } - } - - static Future navigateBack(WidgetTester tester) async { - await tester.pageBack(); - await tester.pumpAndSettle(); - } -} diff --git a/integration_test_old/conversation_page_audio_test.dart b/integration_test_old/conversation_page_audio_test.dart deleted file mode 100644 index 9f27ab54bd..0000000000 --- a/integration_test_old/conversation_page_audio_test.dart +++ /dev/null @@ -1,157 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; - -import 'action/input_test.dart'; -import 'action/navigation_action_test.dart'; -import 'enums/screens_test.dart'; -import 'helpers/clipboard_test.dart'; - -export 'package:flutter_localizations/flutter_localizations.dart'; -export 'package:i18n_extension/i18n_widget.dart'; -export 'package:lantern/core/localization/i18n.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - WidgetsFlutterBinding.ensureInitialized(); - - group('Conversation Page Test', () { - ClipboardMock? mockClipboard; - - setUp(() { - mockClipboard = ClipboardMock(); - SystemChannels.platform - .setMockMethodCallHandler(mockClipboard?.handleMethodCall); - }); - - testWidgets('Check for an audio preview being rendered', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.startRecording( - tester, - find, - checkAudioPreviewComponents: true, - key: 'btnRecord', - recordFor: const Duration(seconds: 6), - ); - }); - - testWidgets( - 'Check if the audio can be played and the wave components should match with the slider', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.startRecording( - tester, - find, - checkAudioPreviewComponents: true, - key: 'btnRecord', - recordFor: const Duration(seconds: 6), - ); - await Input.playAudioPreview( - tester, - find, - checkAudioPreviewComponents: true, - expectedAudioCompletion: 33, - playFor: const Duration(seconds: 3), - ); - }); - - testWidgets('Delete an AudioPreview', (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.startRecording( - tester, - find, - checkAudioPreviewComponents: true, - key: 'btnRecord', - recordFor: const Duration(seconds: 6), - ); - await Input.playAudioPreview( - tester, - find, - checkAudioPreviewComponents: true, - expectedAudioCompletion: 33, - playFor: const Duration(seconds: 3), - ); - await Input.deleteAudioPreview( - tester, - find, - checkAudioPreviewComponents: true, - ); - }); - - testWidgets('Send an AudioPreview', (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.startRecording( - tester, - find, - checkAudioPreviewComponents: true, - key: 'btnRecord', - recordFor: const Duration(seconds: 5), - ); - await Input.sendMessage( - tester, - find, - checkForBubble: true, - isAudio: true, - ); - }); - - testWidgets('Play the audio bubble', (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await tester.pump(const Duration(seconds: 20)); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONVERSATION, - ); - await Input.playAudioPreview( - tester, - find, - checkAudioPreviewComponents: true, - expectedAudioCompletion: 33, - playFor: const Duration(seconds: 3), - ); - }); - }); -} diff --git a/integration_test_old/conversation_page_test.dart b/integration_test_old/conversation_page_test.dart deleted file mode 100644 index 769bd91372..0000000000 --- a/integration_test_old/conversation_page_test.dart +++ /dev/null @@ -1,361 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:lantern/features/messaging/contacts/new_chat.dart'; -import 'package:lantern/features/messaging/conversation/conversation.dart'; - -import 'action/input_test.dart'; -import 'action/navigation_action_test.dart'; -import 'enums/disappearing_test.dart'; -import 'enums/screens_test.dart'; -import 'helpers/clipboard_test.dart'; - -export 'package:flutter_localizations/flutter_localizations.dart'; -export 'package:i18n_extension/i18n_widget.dart'; -export 'package:lantern/core/localization/i18n.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - WidgetsFlutterBinding.ensureInitialized(); - - group('Conversation Page Test', () { - ClipboardMock? mockClipboard; - - setUp(() { - mockClipboard = ClipboardMock(); - SystemChannels.platform - .setMockMethodCallHandler(mockClipboard?.handleMethodCall); - }); - - testWidgets( - 'Navigate from Messages Page into New Message Page into Conversation Page', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - expect(find.byType(Conversation), findsOneWidget); - }); - - testWidgets('Input message to send and evaluate if the text was received', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setTextMessage( - tester, - find, - text: 'hello this is a message from Flutter Test', - seconds: 2, - visualize: true, - ); - await Input.sendMessage( - tester, - find, - checkForBubble: true, - text: 'hello this is a message from Flutter Test', - ); - }); - - testWidgets('Remove the message sended just for me', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.removeMessageForMe( - tester, - find, - text: 'hello this is a message from Flutter Test', - checkDialog: true, - optionTitle: 'Delete for me', - removeBtnTitle: 'delete'.toUpperCase(), - ); - }); - - testWidgets('Copy the content into the Clipboard event', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setTextMessage( - tester, - find, - text: 'hello this is a message from Flutter Test', - seconds: 2, - visualize: true, - ); - await Input.sendMessage( - tester, - find, - checkForBubble: true, - text: 'hello this is a message from Flutter Test', - ); - await Input.copyTextMessage( - tester, - find, - text: 'hello this is a message from Flutter Test', - optionTitle: 'Copy Text', - ); - await Input.checkClipboard( - tester, - find, - mockClipboard!, - text: 'hello this is a message from Flutter Test', - ); - }); - - testWidgets('Test preset reactions', (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setReaction( - tester, - find, - text: 'hello this is a message from Flutter Test', - reaction: '👍', - ); - await GoTo.navigateBack(tester); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setReaction( - tester, - find, - text: 'hello this is a message from Flutter Test', - reaction: '👎', - ); - await GoTo.navigateBack(tester); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setReaction( - tester, - find, - text: 'hello this is a message from Flutter Test', - reaction: '😄', - ); - await GoTo.navigateBack(tester); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setReaction( - tester, - find, - text: 'hello this is a message from Flutter Test', - reaction: '❤', - ); - await GoTo.navigateBack(tester); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setReaction( - tester, - find, - text: 'hello this is a message from Flutter Test', - reaction: '😢', - ); - await GoTo.navigateBack(tester); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setReaction( - tester, - find, - text: 'hello this is a message from Flutter Test', - customReaction: '😉', - reaction: '•••', - isCustomReaction: true, - ); - }); - - testWidgets( - 'Remove a reply intent for the message "hello this is a message send from Flutter Test"', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setReply( - tester, - find, - text: 'hello this is a message from Flutter Test', - checkReply: true, - optionTitle: 'Reply', - ); - await Input.closeReply(tester, find, key: 'close_reply'); - }); - - testWidgets( - 'Send a reply intent for the message "hello this is a message send from Flutter Test"', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setReply( - tester, - find, - text: 'hello this is a message from Flutter Test', - checkReply: true, - optionTitle: 'Reply', - ); - await Input.setTextMessage( - tester, - find, - text: 'Replying: TERI TERI! DAISHORI!! :D', - seconds: 2, - visualize: true, - ); - await Input.sendMessage( - tester, - find, - checkForBubble: true, - text: 'Replying: TERI TERI! DAISHORI!! :D', - ); - }); - - testWidgets('Set a disappearing time and wait till is gone', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setDisappearingMessage( - tester, - find, - key: 'disappearingSelect', - checkDurationStatus: true, - disappearing: DISAPPEARING.SECONDS_5, - ); - await Input.setTextMessage( - tester, - find, - text: 'This will be shortly gone', - seconds: 1, - visualize: true, - ); - await Input.sendMessage( - tester, - find, - checkForBubble: true, - text: 'This will be shortly gone', - ); - await Input.checkRemovedTextMessageWithDelay( - tester, - find, - delay: const Duration(seconds: 5), - text: 'This will be shortly gone', - ); - }); - - testWidgets('Send a custom emoji', (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await Input.setTextMessage( - tester, - find, - emojiSelection: '😇', - seconds: 2, - visualize: true, - ); - await Input.sendMessage(tester, find, checkForBubble: true, text: '😇'); - }); - - testWidgets('Go back using physical button to New Message Page', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateTo( - tester, - from: SCREENS.CONTACTS, - to: SCREENS.CONVERSATION, - ); - await GoTo.navigateBack(tester); - expect(find.byType(NewChat), findsOneWidget); - }); - }); -} diff --git a/integration_test_old/enums/disappearing_test.dart b/integration_test_old/enums/disappearing_test.dart deleted file mode 100644 index 468d15c1aa..0000000000 --- a/integration_test_old/enums/disappearing_test.dart +++ /dev/null @@ -1,4 +0,0 @@ -enum DISAPPEARING { SECONDS_5, MINUTES_1, DAYS_1 } - -Map disappearMap = {DISAPPEARING.SECONDS_5: '5 seconds'}; -Map disappearMapReduced = {DISAPPEARING.SECONDS_5: '5S'}; diff --git a/integration_test_old/enums/screens_test.dart b/integration_test_old/enums/screens_test.dart deleted file mode 100644 index b491bae7a9..0000000000 --- a/integration_test_old/enums/screens_test.dart +++ /dev/null @@ -1,13 +0,0 @@ -enum SCREENS { - MAIN, - MESSAGES, - CONTACTS, - VPN, - ACCOUNT, - DEVELOPER, - ADD_CONTACT_USERNAME, - ADD_CONTACT_QR, - CONVERSATION, - CONTACT_INFORMATION, - SETTINGS, -} diff --git a/integration_test_old/helpers/clipboard_test.dart b/integration_test_old/helpers/clipboard_test.dart deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/integration_test_old/helpers/waiter_test.dart b/integration_test_old/helpers/waiter_test.dart deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/integration_test_old/messages_page_test.dart b/integration_test_old/messages_page_test.dart deleted file mode 100644 index 77299efdda..0000000000 --- a/integration_test_old/messages_page_test.dart +++ /dev/null @@ -1,87 +0,0 @@ -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; -import 'package:lantern/features/home/home.dart'; -import 'package:lantern/features/messaging/chats.dart'; -import 'package:lantern/features/messaging/messaging.dart'; - -import 'action/navigation_action_test.dart'; -import 'enums/screens_test.dart'; - -export 'package:flutter_localizations/flutter_localizations.dart'; -export 'package:i18n_extension/i18n_widget.dart'; -export 'package:lantern/core/localization/i18n.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - WidgetsFlutterBinding.ensureInitialized(); - - group('Messaging Page Integration Test', () { - testWidgets('Initial page should be HomePage', (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MAIN); - expect(find.byType(HomePage), findsOneWidget); - }); - - testWidgets('Check if MessagesPages is loaded', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - expect(find.byType(Chats), findsOneWidget); - }); - - testWidgets('Check for components loaded correctly on MessagesPages AppBar', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - expect( - find.byType(AppBar), - findsOneWidget, - reason: 'Search for a CustomAppBar', - ); - expect(find.widgetWithText(AppBar, 'Messages'), findsOneWidget); - // TODO: This is outdated, needs fix - expect( - find.byIcon(Icons.search), - findsOneWidget, - reason: 'Search should be displayed', - ); - // TODO: This is outdated, needs fix - expect( - find.byIcon(Icons.qr_code), - findsOneWidget, - reason: 'QR should be displayed', - ); - }); - - testWidgets('Check for BottomBar being loaded with all their components', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - expect( - find.byType(AutoTabsScaffold), - findsOneWidget, - reason: 'Check if the AutoTabsScaffold is already on stage', - ); - var autoTabsScaffold = - tester.widget(find.byType(AutoTabsScaffold)); - expect( - autoTabsScaffold.routes, - isNotEmpty, - reason: 'The available routes should not be empty', - ); - expect( - autoTabsScaffold.routes.length, - 4, - reason: 'The available routes should be 4', - ); - expect( - find.byType(CustomBottomBar), - findsOneWidget, - reason: 'Check if the CustomBottomBar is displayed', - ); - var cb = tester.widget(find.byType(CustomBottomBar)); - expect( - cb.selectedTab, - TAB_CHATS, - reason: 'Check if the current page is 0, which correspond to Messaging', - ); - }); - }); -} diff --git a/integration_test_old/new_message_page_test.dart b/integration_test_old/new_message_page_test.dart deleted file mode 100644 index 3deb10e74c..0000000000 --- a/integration_test_old/new_message_page_test.dart +++ /dev/null @@ -1,91 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:integration_test/integration_test.dart'; -import 'package:lantern/core/utils/common.dart' as common; -import 'package:lantern/features/messaging/chats.dart'; -import 'package:lantern/features/messaging/contacts/new_chat.dart'; - -import 'action/navigation_action_test.dart'; -import 'enums/screens_test.dart'; - -export 'package:flutter_localizations/flutter_localizations.dart'; -export 'package:i18n_extension/i18n_widget.dart'; -export 'package:lantern/core/localization/i18n.dart'; - -void main() { - IntegrationTestWidgetsFlutterBinding.ensureInitialized(); - WidgetsFlutterBinding.ensureInitialized(); - - group('New Message Test', () { - testWidgets('Navigate from Messages Page into New Message Page', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - expect(find.byType(NewChat), findsOneWidget); - }); - - testWidgets( - 'Check for components loaded correctly on New Message Page AppBar', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - print('Search for a CustomAppBar'); - expect(find.byType(AppBar), findsOneWidget); - expect(find.widgetWithText(AppBar, 'New Message'), findsOneWidget); - print('Search and QR should be displayed'); - // TODO: This is outdated, needs fix - expect(find.byIcon(Icons.search), findsWidgets); - // TODO: This is outdated, needs fix - expect(find.byIcon(Icons.qr_code), findsWidgets); - }); - - testWidgets('Check for the body components on New Message Page', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - expect( - find.text('Recent contacts'.toUpperCase()), - findsOneWidget, - reason: 'We have 1 contact registered', - ); - var contactList = tester.widget(find.byType(ListView)); - expect( - contactList.semanticChildCount, - equals(1), - reason: 'The list should have 1 element', - ); - print('We check if the ListView can be scrollable'); - print('Manually scrolling 200 pixels down'); - await tester.drag(find.byType(ListView), const Offset(0.0, -300.0)); - print('Flush the widget tree'); - await tester.pumpAndSettle(); - var contactElement = - find.widgetWithText(common.ListItemFactory, 'Note to self'); - expect(contactElement, findsOneWidget); - }); - - testWidgets('Go back using physical button to Messages Page', - (WidgetTester tester) async { - await GoTo.navigateTo(tester, to: SCREENS.MESSAGES); - await GoTo.navigateTo( - tester, - from: SCREENS.MESSAGES, - to: SCREENS.CONTACTS, - ); - await GoTo.navigateBack(tester); - expect(find.byType(Chats), findsOneWidget); - }); - }); -} diff --git a/lib/features/account/account.dart b/lib/features/account/account.dart index 9b0ad869a8..5a22bbad57 100644 --- a/lib/features/account/account.dart +++ b/lib/features/account/account.dart @@ -225,7 +225,6 @@ class _AccountMenuState extends State { @override Widget build(BuildContext context) { - print("Auth vaule ${sessionModel.isAuthEnabled.value}"); return BaseScreen( title: 'Account'.i18n, automaticallyImplyLeading: false, diff --git a/lib/features/home/home.dart b/lib/features/home/home.dart index 2706063895..ec7b187ca7 100644 --- a/lib/features/home/home.dart +++ b/lib/features/home/home.dart @@ -141,17 +141,16 @@ class _HomePageState extends State { final tabModel = context.watch(); return sessionModel.acceptedTermsVersion( (BuildContext context, int version, Widget? child) { - print("accepted terms version $version"); return sessionModel.developmentMode( (BuildContext context, bool developmentMode, Widget? child) { - print("Development mode $developmentMode"); if (developmentMode) { Logger.level = Level.trace; } else { Logger.level = Level.error; } - bool isPlayVersion = (sessionModel.isTestPlayVersion.value ?? false); + bool isPlayVersion = + (sessionModel.isTestPlayVersion.value ?? false); bool isStoreVersion = (sessionModel.isStoreVersion.value ?? false); if ((isStoreVersion || isPlayVersion) && version == 0) { diff --git a/lib/main.dart b/lib/main.dart index 8b85844cde..5680628f30 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -43,18 +43,19 @@ Future main() async { await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - SentryFlutter.init((options) { - // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring. - // We recommend adjusting this value in production. - options.tracesSampleRate = 1.0; - // The sampling rate for profiling is relative to tracesSampleRate - // Setting to 1.0 will profile 100% of sampled transactions: - options.profilesSampleRate = 1.0; - options.environment = kReleaseMode ? "production" : "development"; - options.dsn = kReleaseMode ? AppSecret.dnsConfig() : ""; - options.enableNativeCrashHandling = true; - options.attachStacktrace = true; - }, appRunner: () => runApp(const LanternApp())); + runApp(LanternApp()); + // SentryFlutter.init((options) { + // // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring. + // // We recommend adjusting this value in production. + // options.tracesSampleRate = 1.0; + // // The sampling rate for profiling is relative to tracesSampleRate + // // Setting to 1.0 will profile 100% of sampled transactions: + // options.profilesSampleRate = 1.0; + // options.environment = kReleaseMode ? "production" : "development"; + // options.dsn = kReleaseMode ? AppSecret.dnsConfig() : ""; + // options.enableNativeCrashHandling = true; + // options.attachStacktrace = true; + // }, appRunner: () => runApp(const LanternApp())); } Future _initGoogleMobileAds() async { diff --git a/pubspec.lock b/pubspec.lock index e936967148..bd9dc57bb0 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -14,14 +14,6 @@ packages: description: dart source: sdk version: "0.3.2" - alchemist: - dependency: "direct dev" - description: - name: alchemist - sha256: "2fcd35d7422070cdf7f276bc04dd2d38c9164adb7d58bd00b7bebd8b6927d112" - url: "https://pub.dev" - source: hosted - version: "0.10.0" analyzer: dependency: "direct dev" description: @@ -2002,10 +1994,10 @@ packages: dependency: "direct main" description: name: uuid - sha256: "83d37c7ad7aaf9aa8e275490669535c8080377cfa7a7004c24dfac53afffaa90" + sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff url: "https://pub.dev" source: hosted - version: "4.4.2" + version: "4.5.1" vector_graphics: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5ae02ef708..6dc5928825 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -29,9 +29,9 @@ dependencies: sdk: flutter # State management and Data handling - provider: ^6.0.5 + provider: ^6.1.2 protobuf: any - uuid: ^4.4.0 + uuid: ^4.5.1 sliver_tools: ^0.2.12 @@ -155,7 +155,6 @@ dev_dependencies: analyzer: ^6.7.0 golden_toolkit: ^0.15.0 # Testing - alchemist: ^0.10.0 patrol: ^3.11.0 From 05b37abaf534a56e993b7431b8b4b5daf551401b Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 16 Oct 2024 17:19:49 +0530 Subject: [PATCH 012/163] Mock home test --- .../home/homepage_flow_mock_test.dart | 154 ++++++++++++++++++ .../features/home/homepage_flow_test.dart | 19 +-- integration_test/utils/test_utils.dart | 11 +- lib/features/home/home.dart | 1 + 4 files changed, 169 insertions(+), 16 deletions(-) create mode 100644 integration_test/features/home/homepage_flow_mock_test.dart diff --git a/integration_test/features/home/homepage_flow_mock_test.dart b/integration_test/features/home/homepage_flow_mock_test.dart new file mode 100644 index 0000000000..e598b4e798 --- /dev/null +++ b/integration_test/features/home/homepage_flow_mock_test.dart @@ -0,0 +1,154 @@ +import 'package:lantern/app.dart'; +import 'package:lantern/features/account/privacy_disclosure.dart'; +import 'package:lantern/features/messaging/messaging_model.dart'; + +import '../../../test/utils/test_common.dart'; +import '../../utils/test_utils.dart'; + +/// This is a mock test file for the home page flow. +/// The file exists to mock data for certain scenarios. +/// This ensures the homepage works as expected. +/// Examples include: +/// 1. Showing the privacy policy if the user is using the app from the Play Store. +/// 2. Showing a local plan if the user is using the app from the App Store. +/// Using mocks, we can test these scenarios effectively. +void main() { + late MockSessionModel mockSessionModel; + late MockBuildContext mockBuildContext; + late MockMessagingModel mockMessagingModel; + late MockBottomBarChangeNotifier mockBottomBarChangeNotifier; + late MockVPNChangeNotifier mockVPNChangeNotifier; + late MockInternetStatusProvider mockInternetStatusProvider; + late MockReplicaModel mockReplicaModel; + late MockVpnModel mockVpnModel; + late MockEventManager mockEventManager; + + setUpAll( + () async { + await Localization.ensureInitialized(); + + mockSessionModel = MockSessionModel(); + mockBuildContext = MockBuildContext(); + mockMessagingModel = MockMessagingModel(); + mockBottomBarChangeNotifier = MockBottomBarChangeNotifier(); + mockVPNChangeNotifier = MockVPNChangeNotifier(); + mockInternetStatusProvider = MockInternetStatusProvider(); + mockReplicaModel = MockReplicaModel(); + mockVpnModel = MockVpnModel(); + mockEventManager = MockEventManager(); + + // Injection models + sl.registerLazySingleton(() => mockSessionModel); + sl.registerLazySingleton(() => mockMessagingModel); + sl.registerLazySingleton(() => mockReplicaModel); + sl.registerLazySingleton(() => mockVpnModel); + + // mock the providers + mockBottomBarChangeNotifier = MockBottomBarChangeNotifier(); + mockVPNChangeNotifier = MockVPNChangeNotifier(); + mockInternetStatusProvider = MockInternetStatusProvider(); + }, + ); + + tearDownAll( + () { + sl.reset(); + }, + ); + + group( + 'home widget with mock', + () { + patrolWidgetTest( + 'home widget show privacy policy', + ($) async { + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + + /// Stub session model + when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); + + when(mockSessionModel.language(any)).thenAnswer( + (invocation) { + final builder = invocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_us', null); + }, + ); + + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) => ValueNotifier(true)); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(false)); + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "test", null); + }, + ); + + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)) + .thenAnswer((realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation.positionalArguments[1] as void + Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }, + ); + + await $.pumpWidget(const LanternApp()); + await $.pumpAndSettle(); + + await $.pump(const Duration(seconds: 1)); + + expect($(PrivacyDisclosure).visible, true); + + expect($(Button), findsOneWidget); + expect($(BottomNavigationBar), findsNothing); + }, + ); + }, + ); +} diff --git a/integration_test/features/home/homepage_flow_test.dart b/integration_test/features/home/homepage_flow_test.dart index 9c7e182c8d..655332d709 100644 --- a/integration_test/features/home/homepage_flow_test.dart +++ b/integration_test/features/home/homepage_flow_test.dart @@ -1,22 +1,17 @@ import 'package:flutter/material.dart'; +import 'package:i18n_extension/default.i18n.dart'; import 'package:lantern/app.dart'; import 'package:lantern/features/account/account_tab.dart'; import 'package:lantern/features/home/home.dart'; import 'package:lantern/main.dart' as app; - import '../../utils/test_utils.dart'; -void main() { - late MockSessionModel mockSessionModel; - - setUp( - () { - mockSessionModel = MockSessionModel(); - - sl.registerSingleton(mockSessionModel); - }, - ); +/// This file contains end-to-end tests for the home page flow. +/// These tests should not use mocks to ensure the app works as expected in production. +/// This helps verify that the app functions correctly in real-world scenarios. +/// For mock tests, refer to [homepage_flow_mock_test.dart]. +void main() { group( 'home page flow end to end test', () { @@ -46,6 +41,8 @@ void main() { patrolWidgetTest( 'home widget for appstore and play store ', ($) async { + when(sessionModel.isAuthEnabled).thenReturn(ValueNotifier(true)); + await $.pumpWidget(const LanternApp()); await $.pumpAndSettle(); await $(HomePage).waitUntilVisible(); diff --git a/integration_test/utils/test_utils.dart b/integration_test/utils/test_utils.dart index 24e9b6b459..11524157cf 100644 --- a/integration_test/utils/test_utils.dart +++ b/integration_test/utils/test_utils.dart @@ -1,8 +1,9 @@ -export '../../test/utils/test.mocks.mocks.dart'; - export 'package:flutter_test/flutter_test.dart'; -export 'package:patrol/patrol.dart'; -export 'package:i18n_extension/default.i18n.dart'; -export 'package:lantern/core/service/injection_container.dart'; + +export 'package:lantern/core/service/injection_container.dart'; export 'package:lantern/features/home/session_model.dart'; +export 'package:lantern/features/replica/common.dart'; +export 'package:mockito/mockito.dart'; +export 'package:patrol/patrol.dart'; +export '../../test/utils/test.mocks.mocks.dart'; diff --git a/lib/features/home/home.dart b/lib/features/home/home.dart index ec7b187ca7..b2835a0c23 100644 --- a/lib/features/home/home.dart +++ b/lib/features/home/home.dart @@ -160,6 +160,7 @@ class _HomePageState extends State { } if (sessionModel.isAuthEnabled.value!) { + print('userNew'); userNew(() { _checkForFirstTimeVisit(); }); From 4eb177569799d486e6b7857b549aa8e05c88e631 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 18 Oct 2024 11:52:08 +0530 Subject: [PATCH 013/163] Routing without context and some other test cases fixes. --- .../home/homepage_flow_mock_test.dart | 323 +++++++++++++----- integration_test/test_bundle.dart | 77 +---- lib/app.dart | 15 +- lib/core/service/injection_container.dart | 11 + lib/core/utils/common.dart | 5 + lib/features/auth/auth_landing.dart | 8 +- lib/features/home/home.dart | 2 +- lib/main.dart | 2 +- 8 files changed, 270 insertions(+), 173 deletions(-) diff --git a/integration_test/features/home/homepage_flow_mock_test.dart b/integration_test/features/home/homepage_flow_mock_test.dart index e598b4e798..cf91038612 100644 --- a/integration_test/features/home/homepage_flow_mock_test.dart +++ b/integration_test/features/home/homepage_flow_mock_test.dart @@ -1,6 +1,10 @@ import 'package:lantern/app.dart'; +import 'package:lantern/common/ui/custom/internet_checker.dart'; +import 'package:lantern/core/router/router.dart'; +import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; import 'package:lantern/features/account/privacy_disclosure.dart'; import 'package:lantern/features/messaging/messaging_model.dart'; +import 'package:lantern/features/vpn/vpn_notifier.dart'; import '../../../test/utils/test_common.dart'; import '../../utils/test_utils.dart'; @@ -24,7 +28,7 @@ void main() { late MockEventManager mockEventManager; setUpAll( - () async { + () async { await Localization.ensureInitialized(); mockSessionModel = MockSessionModel(); @@ -42,113 +46,254 @@ void main() { sl.registerLazySingleton(() => mockMessagingModel); sl.registerLazySingleton(() => mockReplicaModel); sl.registerLazySingleton(() => mockVpnModel); + sl.registerLazySingleton(() => AppRouter()); // mock the providers mockBottomBarChangeNotifier = MockBottomBarChangeNotifier(); mockVPNChangeNotifier = MockVPNChangeNotifier(); mockInternetStatusProvider = MockInternetStatusProvider(); + + // Injection models + sl.registerLazySingleton( + () => mockBottomBarChangeNotifier); + sl.registerLazySingleton(() => mockVPNChangeNotifier); + sl.registerLazySingleton( + () => mockInternetStatusProvider); }, ); tearDownAll( - () { + () { sl.reset(); }, ); group( - 'home widget with mock', - () { - patrolWidgetTest( - 'home widget show privacy policy', - ($) async { - when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); - - /// Stub session model - when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); - - when(mockSessionModel.language(any)).thenAnswer( - (invocation) { - final builder = invocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, 'en_us', null); - }, - ); - - when(mockSessionModel.acceptedTermsVersion(any)) - .thenAnswer((invocation) { - final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, 0, null); - }); - - when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { + 'home widget with mock', + () { + patrolWidgetTest( + 'home widget show privacy policy', + ($) async { + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + + /// Stub session model + when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); + + when(mockSessionModel.language(any)).thenAnswer( + (invocation) { + final builder = invocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_us', null); + }, + ); + + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, true, null); - }, - ); - - when(mockSessionModel.isTestPlayVersion) - .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.isStoreVersion) - .thenAnswer((realInvocation) => ValueNotifier(true)); - when(mockSessionModel.isAuthEnabled) - .thenAnswer((realInvocation) => ValueNotifier(false)); - - when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, "test", null); - }, - ); - - when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { - return boolEmptyBuilder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)) - .thenAnswer((realInvocation) { - final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation.positionalArguments[1] as void - Function(Event, Map); - return () { - onNewEvent(event, {}); - }; - }); - - when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); - }, - ); + }); - await $.pumpWidget(const LanternApp()); - await $.pumpAndSettle(); + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); - await $.pump(const Duration(seconds: 1)); + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) => ValueNotifier(true)); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(false)); - expect($(PrivacyDisclosure).visible, true); + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); - expect($(Button), findsOneWidget); - expect($(BottomNavigationBar), findsNothing); - }, - ); - }, + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "test", null); + }, + ); + + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)) + .thenAnswer((realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation.positionalArguments[1] as void + Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }, + ); + + await $.pumpWidget(const LanternApp()); + await $.pumpAndSettle(); + + await $.pump(const Duration(seconds: 1)); + + expect($(PrivacyDisclosure).visible, true); + + expect($(Button), findsOneWidget); + expect($(BottomNavigationBar), findsNothing); + }, + ); + + patrolWidgetTest( + 'home widget auth enable show first time visit screen', + ($) async { + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn( + true); + + /// Stub session model + when(mockSessionModel.proxyAvailable).thenReturn( + ValueNotifier(true)); + + when(mockSessionModel.pathValueNotifier(any, false)) + .thenReturn(ValueNotifier(true)); + + when(mockSessionModel.language(any)).thenAnswer( + (invocation) { + final builder = invocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_us', null); + }, + ); + + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + + when(mockSessionModel.shouldShowAds(any)).thenAnswer( + (invocation) { + final builder = invocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.proUserNotifier) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(true)); + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + stubSessionModel( + mockSessionModel: mockSessionModel, + mockBuildContext: mockBuildContext); + + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)) + .thenAnswer((realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation.positionalArguments[1] as void + Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }, + ); + + when(sessionModel.isUserFirstTimeVisit()) + .thenAnswer((realInvocation) => Future.value(true)); + + /// messageing model + when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, null, null); + }, + ); + + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); + + await $.pumpWidget(const LanternApp()); + await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 2)); + + final signInFinder = $(Button).$('sign_in'.i18n.toUpperCase()); + final lanternProFinder = $(Button).$( + 'get_lantern_pro'.i18n.toUpperCase()); + + expect($(Button), findsExactly(2)); + expect(lanternProFinder, findsOneWidget); + expect(signInFinder, findsOneWidget); + + + when(mockSessionModel.hasUserSignedInNotifier).thenReturn(ValueNotifier(false)); + when(mockSessionModel.userEmail).thenReturn(ValueNotifier("")); + + await signInFinder.tap(); + await $.pumpAndSettle(); + + expect($(AuthLanding), findsNothing); + expect($(AppBarProHeader), findsOneWidget); + expect($('sign_in'.i18n), findsOneWidget); + }, + ); + } ); } diff --git a/integration_test/test_bundle.dart b/integration_test/test_bundle.dart index 4458993213..4b5ece9a71 100644 --- a/integration_test/test_bundle.dart +++ b/integration_test/test_bundle.dart @@ -1,86 +1,21 @@ -// GENERATED CODE - DO NOT MODIFY BY HAND AND DO NOT COMMIT TO VERSION CONTROL // ignore_for_file: type=lint, invalid_use_of_internal_member -import 'dart:async'; - +import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/patrol.dart'; -import 'package:patrol/src/native/contracts/contracts.dart'; -import 'package:test_api/src/backend/invoker.dart'; // START: GENERATED TEST IMPORTS -import 'features/home/homepage_flow_test.dart' as features__home__homepage_flow_test; +import 'features/home/homepage_flow_mock_test.dart' as features__home__homepage_flow_mock_test; // END: GENERATED TEST IMPORTS Future main() async { - // This is the entrypoint of the bundled Dart test. - // - // Its responsibilies are: - // * Running a special Dart test that runs before all the other tests and - // explores the hierarchy of groups and tests. - // * Hosting a PatrolAppService, which the native side of Patrol uses to get - // the Dart tests, and to request execution of a specific Dart test. - // - // When running on Android, the Android Test Orchestrator, before running the - // tests, makes an initial run to gather the tests that it will later run. The - // native side of Patrol (specifically: PatrolJUnitRunner class) is hooked - // into the Android Test Orchestrator lifecycle and knows when that initial - // run happens. When it does, PatrolJUnitRunner makes an RPC call to - // PatrolAppService and asks it for Dart tests. - // - // When running on iOS, the native side of Patrol (specifically: the - // PATROL_INTEGRATION_TEST_IOS_RUNNER macro) makes an initial run to gather - // the tests that it will later run (same as the Android). During that initial - // run, it makes an RPC call to PatrolAppSevice and asks it for Dart tests. - // - // Once the native runner has the list of Dart tests, it dynamically creates - // native test cases from them. On Android, this is done using the - // Parametrized JUnit runner. On iOS, new test case methods are swizzled into - // the RunnerUITests class, taking advantage of the very dynamic nature of - // Objective-C runtime. - // - // Execution of these dynamically created native test cases is then fully - // managed by the underlying native test framework (JUnit on Android, XCTest - // on iOS). The native test cases do only one thing - request execution of the - // Dart test (out of which they had been created) and wait for it to complete. - // The result of running the Dart test is the result of the native test case. - final nativeAutomator = NativeAutomator(config: NativeAutomatorConfig()); await nativeAutomator.initialize(); - final binding = PatrolBinding.ensureInitialized(NativeAutomatorConfig()); - final testExplorationCompleter = Completer(); - - // A special test to explore the hierarchy of groups and tests. This is a hack - // around https://github.com/dart-lang/test/issues/1998. - // - // This test must be the first to run. If not, the native side likely won't - // receive any tests, and everything will fall apart. - test('patrol_test_explorer', () { - // Maybe somewhat counterintuitively, this callback runs *after* the calls - // to group() below. - final topLevelGroup = Invoker.current!.liveTest.groups.first; - final dartTestGroup = createDartTestGroup(topLevelGroup, - tags: null, - excludeTags: null, - ); - testExplorationCompleter.complete(dartTestGroup); - print('patrol_test_explorer: obtained Dart-side test hierarchy:'); - reportGroupStructure(dartTestGroup); - }); + PatrolBinding.ensureInitialized(NativeAutomatorConfig()) + ..workaroundDebugDefaultTargetPlatformOverride = + debugDefaultTargetPlatformOverride; // START: GENERATED TEST GROUPS - group('features.home.homepage_flow_test', features__home__homepage_flow_test.main); + group('features.home.homepage_flow_mock_test', features__home__homepage_flow_mock_test.main); // END: GENERATED TEST GROUPS - - final dartTestGroup = await testExplorationCompleter.future; - final appService = PatrolAppService(topLevelDartTestGroup: dartTestGroup); - binding.patrolAppService = appService; - await runAppService(appService); - - // Until now, the native test runner was waiting for us, the Dart side, to - // come alive. Now that we did, let's tell it that we're ready to be asked - // about Dart tests. - await nativeAutomator.markPatrolAppServiceReady(); - - await appService.testExecutionCompleted; } diff --git a/lib/app.dart b/lib/app.dart index 5a7dd6f911..5e87bb295e 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -11,7 +11,7 @@ import 'package:lantern/features/window/window_container.dart'; import 'common/ui/custom/internet_checker.dart'; final navigatorKey = GlobalKey(); -final globalRouter = AppRouter(); +final globalRouter = sl(); final networkWarningBarHeightRatio = ValueNotifier(0.0); var showConnectivityWarning = false; @@ -116,7 +116,8 @@ class _LanternAppState extends State } } - Widget _buildMaterialApp(BuildContext context, String lang,Locale currentLocal) { + Widget _buildMaterialApp( + BuildContext context, String lang, Locale currentLocal) { final app = MaterialApp.router( locale: currentLocale(lang), debugShowCheckedModeBanner: false, @@ -166,12 +167,12 @@ class _LanternAppState extends State @override Widget build(BuildContext context) { final currentLocal = View.of(context).platformDispatcher.locale; - print('selected local: ${currentLocal.languageCode}'); + return MultiProvider( providers: [ - ChangeNotifierProvider(create: (context) => BottomBarChangeNotifier()), - ChangeNotifierProvider(create: (context) => VPNChangeNotifier()), - ChangeNotifierProvider(create: (context) => InternetStatusProvider()), + ChangeNotifierProvider(create: (context) => sl()), + ChangeNotifierProvider(create: (context) => sl()), + ChangeNotifierProvider(create: (context) => sl()), ], child: sessionModel.language( (context, lang, child) { @@ -193,7 +194,7 @@ class _LanternAppState extends State child: I18n( initialLocale: currentLocale(lang), child: ScaffoldMessenger( - child: _buildMaterialApp(context, lang,currentLocal), + child: _buildMaterialApp(context, lang, currentLocal), ), ), ); diff --git a/lib/core/service/injection_container.dart b/lib/core/service/injection_container.dart index fc55b7fe90..4908e6625d 100644 --- a/lib/core/service/injection_container.dart +++ b/lib/core/service/injection_container.dart @@ -1,8 +1,12 @@ import 'package:get_it/get_it.dart'; +import 'package:lantern/common/ui/custom/internet_checker.dart'; +import 'package:lantern/core/router/router.dart'; import 'package:lantern/core/service/app_purchase.dart'; import 'package:lantern/core/utils/common.dart'; +import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; import 'package:lantern/features/messaging/messaging_model.dart'; import 'package:lantern/features/replica/models/replica_model.dart'; +import 'package:lantern/features/vpn/vpn_notifier.dart'; final GetIt sl = GetIt.instance; @@ -13,8 +17,15 @@ void initServices() { sl.registerLazySingleton(() => AppPurchase()); sl().init(); } + sl.registerLazySingleton(() => AppRouter()); sl.registerLazySingleton(() => SessionModel()); sl.registerLazySingleton(() => MessagingModel()); sl.registerLazySingleton(() => ReplicaModel()); sl.registerLazySingleton(() => VpnModel()); + + /// Notifiers + sl.registerLazySingleton(() => BottomBarChangeNotifier()); + sl.registerLazySingleton(() => VPNChangeNotifier()); + sl.registerLazySingleton(() => InternetStatusProvider()); + } diff --git a/lib/core/utils/common.dart b/lib/core/utils/common.dart index c0bc531ca8..d48ea3c526 100644 --- a/lib/core/utils/common.dart +++ b/lib/core/utils/common.dart @@ -3,6 +3,7 @@ import 'dart:io'; import 'package:lantern/features/replica/common.dart'; import 'package:logger/logger.dart'; +import '../router/router.dart'; import 'common.dart'; export 'dart:async'; @@ -170,3 +171,7 @@ final mainLogger = Logger( printEmojis: true, printTime: true, ), filter: DevelopmentFilter(), level: Level.debug); + + + +AppRouter get appRouter => sl(); diff --git a/lib/features/auth/auth_landing.dart b/lib/features/auth/auth_landing.dart index 2102a3f8dd..3bf816e344 100644 --- a/lib/features/auth/auth_landing.dart +++ b/lib/features/auth/auth_landing.dart @@ -72,17 +72,17 @@ class AuthLanding extends StatelessWidget { void openSignIn(BuildContext context, bool proUser) { if (proUser) { - context.router.popAndPush(SignIn(authFlow: AuthFlow.updateAccount)); + appRouter.popAndPush(SignIn(authFlow: AuthFlow.updateAccount)); } else { - context.router.popAndPush(SignIn(authFlow: AuthFlow.signIn)); + appRouter.popAndPush(SignIn(authFlow: AuthFlow.signIn)); } } void openPlans(BuildContext context, bool proUser) { if (proUser) { - context.router.popAndPush(SignIn(authFlow: AuthFlow.signIn)); + appRouter.popAndPush(SignIn(authFlow: AuthFlow.signIn)); } else { - context.router.popAndPush(const PlansPage()); + appRouter.popAndPush(const PlansPage()); } } } diff --git a/lib/features/home/home.dart b/lib/features/home/home.dart index b2835a0c23..b22f4cad86 100644 --- a/lib/features/home/home.dart +++ b/lib/features/home/home.dart @@ -100,6 +100,7 @@ class _HomePageState extends State { } final isFirstTime = await sessionModel.isUserFirstTimeVisit(); if (isFirstTime) { + print('User is not a pro user'); context.router.push(const AuthLanding()); sessionModel.setFirstTimeVisit(); if (sessionModel.proUserNotifier.hasListeners) { @@ -160,7 +161,6 @@ class _HomePageState extends State { } if (sessionModel.isAuthEnabled.value!) { - print('userNew'); userNew(() { _checkForFirstTimeVisit(); }); diff --git a/lib/main.dart b/lib/main.dart index 5680628f30..d273b31f7f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -43,7 +43,7 @@ Future main() async { await SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); - runApp(LanternApp()); + runApp(const LanternApp()); // SentryFlutter.init((options) { // // Set tracesSampleRate to 1.0 to capture 100% of transactions for performance monitoring. // // We recommend adjusting this value in production. From 95aea940a89e08de9417b08744f20a2399439eda Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 18 Oct 2024 18:00:03 +0530 Subject: [PATCH 014/163] Added test case for VPN tap. --- .../features/home/homepage_flow_test.dart | 29 ++++---- .../features/vpn/vpn_flow_test.dart | 71 +++++++++++++++++++ integration_test/features/vpn/vpn_test.dart | 16 ----- integration_test/test_bundle.dart | 4 +- integration_test/utils/test_utils.dart | 9 ++- ios/Podfile.lock | 11 +++ lib/core/widgtes/split_tunnel_widget.dart | 37 ++++++++++ lib/features/account/split_tunneling.dart | 41 ----------- lib/features/vpn/vpn_tab.dart | 1 + test/features/vpn/vpn_tap_test.dart | 3 +- 10 files changed, 144 insertions(+), 78 deletions(-) create mode 100644 integration_test/features/vpn/vpn_flow_test.dart delete mode 100644 integration_test/features/vpn/vpn_test.dart create mode 100644 lib/core/widgtes/split_tunnel_widget.dart diff --git a/integration_test/features/home/homepage_flow_test.dart b/integration_test/features/home/homepage_flow_test.dart index 655332d709..b86ee43ada 100644 --- a/integration_test/features/home/homepage_flow_test.dart +++ b/integration_test/features/home/homepage_flow_test.dart @@ -1,22 +1,32 @@ import 'package:flutter/material.dart'; import 'package:i18n_extension/default.i18n.dart'; -import 'package:lantern/app.dart'; import 'package:lantern/features/account/account_tab.dart'; import 'package:lantern/features/home/home.dart'; import 'package:lantern/main.dart' as app; -import '../../utils/test_utils.dart'; +import '../../utils/test_utils.dart'; /// This file contains end-to-end tests for the home page flow. /// These tests should not use mocks to ensure the app works as expected in production. /// This helps verify that the app functions correctly in real-world scenarios. /// For mock tests, refer to [homepage_flow_mock_test.dart]. void main() { + setUp( + () {}, + ); + + tearDown( + () { + sl.reset(); + }, + ); + group( 'home page flow end to end test', () { patrolWidgetTest( 'app initializes and navigates to homepage', + tags: ['android,ios,desktop'], ($) async { await app.main(); @@ -25,8 +35,6 @@ void main() { await $(HomePage).waitUntilVisible(); expect($(BottomNavigationBar), findsOneWidget); - expect($('VPN'.i18n), findsOne); - expect($('Account'.i18n), findsOne); expect($('VPN'.i18n).visible, equals(true)); expect($('Account'.i18n).visible, equals(true)); @@ -37,19 +45,6 @@ void main() { expect($(AppBar), findsOneWidget); }, ); - - patrolWidgetTest( - 'home widget for appstore and play store ', - ($) async { - when(sessionModel.isAuthEnabled).thenReturn(ValueNotifier(true)); - - await $.pumpWidget(const LanternApp()); - await $.pumpAndSettle(); - await $(HomePage).waitUntilVisible(); - - expect($(BottomNavigationBar), findsOneWidget); - }, - ); }, ); } diff --git a/integration_test/features/vpn/vpn_flow_test.dart b/integration_test/features/vpn/vpn_flow_test.dart new file mode 100644 index 0000000000..9c20bdb04f --- /dev/null +++ b/integration_test/features/vpn/vpn_flow_test.dart @@ -0,0 +1,71 @@ +import 'package:lantern/core/utils/common.dart'; +import 'package:lantern/core/widgtes/split_tunnel_widget.dart'; +import 'package:lantern/features/vpn/vpn_bandwidth.dart'; +import 'package:lantern/features/vpn/vpn_pro_banner.dart'; +import 'package:lantern/features/vpn/vpn_server_location.dart'; +import 'package:lantern/features/vpn/vpn_status.dart'; +import 'package:lantern/features/vpn/vpn_switch.dart'; +import 'package:lantern/features/vpn/vpn_tab.dart'; +import 'package:lantern/main.dart' as app; +import '../../utils/test_utils.dart'; + +void main() { + setUp( + () {}, + ); + + tearDown( + () { + sl.reset(); + }, + ); + + group( + 'vpn tap end to end test', + () { + patrolWidgetTest( + 'renders VPN tap properly', + ($) async { + await app.main(); + await $.pumpAndSettle(); + await $(VPNTab).waitUntilVisible(); + await $.pump(const Duration(seconds: 6)); + + expect($(ProBanner), findsOneWidget); + expect($(VPNSwitch), findsOneWidget); + expect($(VPNStatus), findsOneWidget); + expect($(ServerLocationWidget), findsOneWidget); + expect($(VPNBandwidth), findsOneWidget); + if (isAndroid()) { + expect($(SplitTunnelingWidget), findsOneWidget); + }else{ + expect($(SplitTunnelingWidget), findsNothing); + } + + }, + ); + + patrolWidgetTest( + 'renders VPN tap navigation work properly', + ($) async { + await app.main(); + await $.pumpAndSettle(); + await $(VPNTab).waitUntilVisible(); + await $.pump(const Duration(seconds: 6)); + await $(ProBanner).tap(); + await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 1)); + expect($(FullScreenDialog), findsOneWidget); + if (isAndroid()) { + //go back + await $(IconButton).tap(); + await $.pumpAndSettle(); + await $('split_tunneling'.i18n).tap(); + await $.pumpAndSettle(); + expect($('split_tunneling'.i18n), findsOneWidget); + } + }, + ); + }, + ); +} diff --git a/integration_test/features/vpn/vpn_test.dart b/integration_test/features/vpn/vpn_test.dart deleted file mode 100644 index 1c19b972f7..0000000000 --- a/integration_test/features/vpn/vpn_test.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:flutter/cupertino.dart'; -import 'package:lantern/features/vpn/vpn_switch.dart'; -import '../../utils/test_utils.dart'; -import 'package:lantern/main.dart' as app; - -void main() { - patrolTest( - "VPN test end to end", - (petrolTester) async { - await petrolTester.pumpAndSettle(); - expect(petrolTester(const VPNSwitch()), findsOneWidget); - }, - variant: TargetPlatformVariant.only(TargetPlatform.android), - config: const PatrolTesterConfig(), - ); -} diff --git a/integration_test/test_bundle.dart b/integration_test/test_bundle.dart index 4b5ece9a71..a93e2daf0b 100644 --- a/integration_test/test_bundle.dart +++ b/integration_test/test_bundle.dart @@ -5,7 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/patrol.dart'; // START: GENERATED TEST IMPORTS -import 'features/home/homepage_flow_mock_test.dart' as features__home__homepage_flow_mock_test; +import 'features/vpn/vpn_flow_test.dart' as features__vpn__vpn_flow_test; // END: GENERATED TEST IMPORTS Future main() async { @@ -16,6 +16,6 @@ Future main() async { debugDefaultTargetPlatformOverride; // START: GENERATED TEST GROUPS - group('features.home.homepage_flow_mock_test', features__home__homepage_flow_mock_test.main); + group('features.vpn.vpn_flow_test', features__vpn__vpn_flow_test.main); // END: GENERATED TEST GROUPS } diff --git a/integration_test/utils/test_utils.dart b/integration_test/utils/test_utils.dart index 11524157cf..55ca65c442 100644 --- a/integration_test/utils/test_utils.dart +++ b/integration_test/utils/test_utils.dart @@ -1,5 +1,7 @@ -export 'package:flutter_test/flutter_test.dart'; +import 'package:flutter/foundation.dart'; +import 'package:flutter_test/flutter_test.dart'; +export 'package:flutter_test/flutter_test.dart'; export 'package:lantern/core/service/injection_container.dart'; export 'package:lantern/features/home/session_model.dart'; export 'package:lantern/features/replica/common.dart'; @@ -7,3 +9,8 @@ export 'package:mockito/mockito.dart'; export 'package:patrol/patrol.dart'; export '../../test/utils/test.mocks.mocks.dart'; + +TestVariant isMobile() { + return const TargetPlatformVariant( + {TargetPlatform.android, TargetPlatform.iOS}); +} \ No newline at end of file diff --git a/ios/Podfile.lock b/ios/Podfile.lock index 2f58eb22d9..8a4317d8dd 100644 --- a/ios/Podfile.lock +++ b/ios/Podfile.lock @@ -4,6 +4,7 @@ PODS: - Flutter - audioplayers_darwin (0.0.1): - Flutter + - CocoaAsyncSocket (7.6.5) - connectivity_plus (0.0.1): - Flutter - FlutterMacOS @@ -104,6 +105,10 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS + - patrol (0.0.1): + - CocoaAsyncSocket (~> 7.6) + - Flutter + - FlutterMacOS - permission_handler_apple (9.3.0): - Flutter - qr_code_scanner (0.2.0): @@ -162,6 +167,7 @@ DEPENDENCIES: - integration_test (from `.symlinks/plugins/integration_test/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) + - patrol (from `.symlinks/plugins/patrol/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`) - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) @@ -177,6 +183,7 @@ DEPENDENCIES: SPEC REPOS: https://github.com/CocoaPods/Specs.git: - Alamofire + - CocoaAsyncSocket - DKImagePickerController - DKPhotoGallery - Google-Mobile-Ads-SDK @@ -231,6 +238,8 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: :path: ".symlinks/plugins/path_provider_foundation/darwin" + patrol: + :path: ".symlinks/plugins/patrol/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" qr_code_scanner: @@ -256,6 +265,7 @@ SPEC CHECKSUMS: Alamofire: 814429acc853c6c54ff123fc3d2ef66803823ce0 app_links: e7a6750a915a9e161c58d91bc610e8cd1d4d0ad0 audioplayers_darwin: 877d9a4d06331c5c374595e46e16453ac7eafa40 + CocoaAsyncSocket: 065fd1e645c7abab64f7a6a2007a48038fdc6a99 connectivity_plus: ddd7f30999e1faaef5967c23d5b6d503d10434db device_info_plus: 97af1d7e84681a90d0693e63169a5d50e0839a0d DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c @@ -281,6 +291,7 @@ SPEC CHECKSUMS: OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94 package_info_plus: 58f0028419748fad15bf008b270aaa8e54380b1c path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46 + patrol: 0564cee315ff6c86fb802b3647db05cc2d3d0624 permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2 qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e SDWebImage: 8a6b7b160b4d710e2a22b6900e25301075c34cb3 diff --git a/lib/core/widgtes/split_tunnel_widget.dart b/lib/core/widgtes/split_tunnel_widget.dart new file mode 100644 index 0000000000..ab293d54ec --- /dev/null +++ b/lib/core/widgtes/split_tunnel_widget.dart @@ -0,0 +1,37 @@ +// SplitTunnelingWidget is the split tunneling widget that appears on the main VPN screen +import '../utils/common.dart'; + +class SplitTunnelingWidget extends StatelessWidget { + const SplitTunnelingWidget({super.key}); + + @override + Widget build(BuildContext context) { + return sessionModel.splitTunneling( + (BuildContext context, bool value, Widget? child) => InkWell( + onTap: () { + appRouter.push(const SplitTunneling()); + }, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + CText( + 'split_tunneling'.i18n, + style: tsSubtitle3.copiedWith( + color: unselectedTabIconColor, + ), + ), + const Spacer(), + Padding( + padding: const EdgeInsetsDirectional.only(end: 8), + child: CText( + value ? 'on'.i18n : 'off'.i18n, + style: tsSubtitle4, + ), + ), + const ContinueArrow(), + ], + ), + ), + ); + } +} diff --git a/lib/features/account/split_tunneling.dart b/lib/features/account/split_tunneling.dart index 8140620795..e6eea93e1a 100644 --- a/lib/features/account/split_tunneling.dart +++ b/lib/features/account/split_tunneling.dart @@ -298,44 +298,3 @@ class _SplitTunnelingAppsListState extends State { showRestartVPNSnackBar(context); } } - -// SplitTunnelingWidget is the split tunneling widget that appears on the main VPN screen -class SplitTunnelingWidget extends StatelessWidget { - const SplitTunnelingWidget({super.key}); - - @override - Widget build(BuildContext context) { - return sessionModel.splitTunneling( - (BuildContext context, bool value, Widget? child) => InkWell( - onTap: () { - Navigator.push( - context, - MaterialPageRoute( - builder: (context) => const SplitTunneling(), - ), - ); - }, - child: Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - CText( - 'split_tunneling'.i18n, - style: tsSubtitle3.copiedWith( - color: unselectedTabIconColor, - ), - ), - const Spacer(), - Padding( - padding: const EdgeInsetsDirectional.only(end: 8), - child: CText( - value ? 'on'.i18n : 'off'.i18n, - style: tsSubtitle4, - ), - ), - const ContinueArrow(), - ], - ), - ), - ); - } -} diff --git a/lib/features/vpn/vpn_tab.dart b/lib/features/vpn/vpn_tab.dart index c5095dd79e..c217895190 100644 --- a/lib/features/vpn/vpn_tab.dart +++ b/lib/features/vpn/vpn_tab.dart @@ -1,3 +1,4 @@ +import 'package:lantern/core/widgtes/split_tunnel_widget.dart'; import 'package:lantern/features/account/split_tunneling.dart'; import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/features/messaging/messaging.dart'; diff --git a/test/features/vpn/vpn_tap_test.dart b/test/features/vpn/vpn_tap_test.dart index be734c11af..6b163aaff3 100644 --- a/test/features/vpn/vpn_tap_test.dart +++ b/test/features/vpn/vpn_tap_test.dart @@ -1,6 +1,7 @@ import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; -import 'package:lantern/features/account/split_tunneling.dart'; +import 'package:lantern/core/widgtes/split_tunnel_widget.dart'; + import 'package:lantern/features/vpn/vpn_bandwidth.dart'; import 'package:lantern/features/vpn/vpn_notifier.dart'; import 'package:lantern/features/vpn/vpn_pro_banner.dart'; From 16461ad599477f4fb645101bed65e21eaa433a36 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 18 Oct 2024 18:44:28 +0530 Subject: [PATCH 015/163] Fix android session model issue. --- .../main/kotlin/io/lantern/model/SessionModel.kt | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt b/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt index 5824629cb9..7bc029feb8 100644 --- a/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt +++ b/android/app/src/main/kotlin/io/lantern/model/SessionModel.kt @@ -31,6 +31,7 @@ import org.getlantern.lantern.LanternApp import org.getlantern.lantern.activity.WebViewActivity import org.getlantern.lantern.model.InAppBilling import org.getlantern.lantern.model.Utils +import org.getlantern.lantern.plausible.Plausible import org.getlantern.lantern.util.AutoUpdater import org.getlantern.lantern.util.LanternProxySelector import org.getlantern.lantern.util.PaymentsUtil @@ -123,11 +124,16 @@ class SessionModel internal constructor( result ) } - "checkForUpdates" -> { autoUpdater.checkForUpdates(result) } + "trackUserAction" -> { + val props: Map = mapOf("title" to call.argument("title")!!) + Plausible.event( + call.argument("name")!!, url = call.argument("url")!!, props = props + ) + } "proxyAddr" -> result.success(LanternApp.session.hTTPAddr) @@ -199,7 +205,10 @@ class SessionModel internal constructor( } fun setUserIdAndToken(userId: Long, token: String) { - model.invokeMethod("setUserIdAndToken", Arguments(mapOf("userId" to userId, "token" to token))) + model.invokeMethod( + "setUserIdAndToken", + Arguments(mapOf("userId" to userId, "token" to token)) + ) } fun setUserPro(isPro: Boolean) { From 1a1cd493754630cbc71ba48914a122384cc929f7 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 18 Oct 2024 18:44:47 +0530 Subject: [PATCH 016/163] Added macos setup and vpn tap test for dekstop. --- .../features/account/account_flow_test.dart | 0 .../features/vpn/vpn_flow_test.dart | 33 +++++++++++++++++-- integration_test/utils/test_utils.dart | 2 +- lib/features/vpn/vpn_switch.dart | 2 ++ 4 files changed, 33 insertions(+), 4 deletions(-) create mode 100644 integration_test/features/account/account_flow_test.dart diff --git a/integration_test/features/account/account_flow_test.dart b/integration_test/features/account/account_flow_test.dart new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration_test/features/vpn/vpn_flow_test.dart b/integration_test/features/vpn/vpn_flow_test.dart index 9c20bdb04f..ec164d10b7 100644 --- a/integration_test/features/vpn/vpn_flow_test.dart +++ b/integration_test/features/vpn/vpn_flow_test.dart @@ -1,3 +1,4 @@ +import 'package:animated_toggle_switch/animated_toggle_switch.dart'; import 'package:lantern/core/utils/common.dart'; import 'package:lantern/core/widgtes/split_tunnel_widget.dart'; import 'package:lantern/features/vpn/vpn_bandwidth.dart'; @@ -7,6 +8,7 @@ import 'package:lantern/features/vpn/vpn_status.dart'; import 'package:lantern/features/vpn/vpn_switch.dart'; import 'package:lantern/features/vpn/vpn_tab.dart'; import 'package:lantern/main.dart' as app; + import '../../utils/test_utils.dart'; void main() { @@ -32,16 +34,19 @@ void main() { await $.pump(const Duration(seconds: 6)); expect($(ProBanner), findsOneWidget); - expect($(VPNSwitch), findsOneWidget); + if (isMobile()) { + expect($(VPNSwitch), findsOneWidget); + } else { + expect($(CustomAnimatedToggleSwitch), findsOneWidget); + } expect($(VPNStatus), findsOneWidget); expect($(ServerLocationWidget), findsOneWidget); expect($(VPNBandwidth), findsOneWidget); if (isAndroid()) { expect($(SplitTunnelingWidget), findsOneWidget); - }else{ + } else { expect($(SplitTunnelingWidget), findsNothing); } - }, ); @@ -66,6 +71,28 @@ void main() { } }, ); + + patrolWidgetTest( + 'toggles VPN switch on and off for desktop platforms', + skip: isMobile(), + ($) async { + await app.main(); + await $.pumpAndSettle(); + await $(VPNTab).waitUntilVisible(); + await $.pump(const Duration(seconds: 3)); + await $(CustomAnimatedToggleSwitch).tap(); + await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 1)); + expect($('connected'.i18n), findsOneWidget); + await $.pump(const Duration(seconds: 2)); + + await $(CustomAnimatedToggleSwitch).tap(settlePolicy: SettlePolicy.settle); + await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 1)); + expect($('connected'.i18n), findsNothing); + expect($('Disconnected'.i18n), findsOneWidget); + }, + ); }, ); } diff --git a/integration_test/utils/test_utils.dart b/integration_test/utils/test_utils.dart index 55ca65c442..950df1e687 100644 --- a/integration_test/utils/test_utils.dart +++ b/integration_test/utils/test_utils.dart @@ -10,7 +10,7 @@ export 'package:patrol/patrol.dart'; export '../../test/utils/test.mocks.mocks.dart'; -TestVariant isMobile() { +TestVariant mobileVariant() { return const TargetPlatformVariant( {TargetPlatform.android, TargetPlatform.iOS}); } \ No newline at end of file diff --git a/lib/features/vpn/vpn_switch.dart b/lib/features/vpn/vpn_switch.dart index a9ad3b1413..f94420d8fc 100644 --- a/lib/features/vpn/vpn_switch.dart +++ b/lib/features/vpn/vpn_switch.dart @@ -51,6 +51,7 @@ class _VPNSwitchState extends State { }); }); } else { + print("Using desktop switch;"); return CustomAnimatedToggleSwitch( current: vpnNotifier.vpnStatus.value, values: const ['disconnected', 'connected'], @@ -89,6 +90,7 @@ class _VPNSwitchState extends State { }, onChanged: (newValue) {}, onTap: (props) { + print("Tapped on switch $props"); if (vpnNotifier.vpnStatus.value == 'connected') { vpnProcessForDesktop('disconnected'); } else { From 2006a5e105c42f9eaaa99a34749e2049a2fbefe3 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 23 Oct 2024 17:48:32 +0530 Subject: [PATCH 017/163] More stuff --- Makefile | 15 + integration_test/app_startup_flow_test.dart | 9 +- .../features/account/account_flow_test.dart | 3 + .../home/homepage_flow_mock_test.dart | 436 +++++++++-------- .../features/home/homepage_flow_test.dart | 2 - .../features/vpn/vpn_flow_test.dart | 31 +- .../old_test/0_change_language_test.dart | 25 - .../10A_contact_info_screen_test.dart | 39 -- .../10B_contact_info_screen_test.dart | 41 -- .../old_test/11_rename_contact_test.dart | 60 --- .../old_test/12_delete_contact_test.dart | 43 -- .../old_test/13_block_contact_test.dart | 51 -- .../old_test/14A_react_to_message_test.dart | 48 -- .../old_test/14B_delete_for_me_test.dart | 42 -- .../14C_delete_for_everyone_test.dart | 43 -- .../old_test/15_copy_recovery_key_test.dart | 36 -- .../old_test/16_recovery_flow_test.dart | 51 -- .../old_test/17_verify_contact_test.dart | 56 --- .../18_disappearing_messages_test.dart | 61 --- .../old_test/19_become_pro_test.dart | 15 - .../old_test/1A_chat_number_formats_test.dart | 12 - .../1B_enroll_leave_Me_note_test.dart | 12 - integration_test/old_test/20_vpn_test.dart | 10 - .../old_test/21_account_management_test.dart | 10 - integration_test/old_test/22_invite_test.dart | 10 - .../old_test/23_download_desktop_test.dart | 11 - .../24A_authorize_device_pro_PIN_test.dart | 18 - .../24B_authorize_device_pro_email_test.dart | 26 - .../old_test/25_report_issue_test.dart | 14 - .../old_test/26_proxy_all_traffic_test.dart | 26 - .../old_test/27_replica_search_test.dart | 31 -- .../2_add_via_secure_number_test.dart | 38 -- .../old_test/3_send_first_message_test.dart | 16 - .../old_test/4_view_attachments_test.dart | 29 -- .../old_test/5_voice_memo_test.dart | 34 -- .../old_test/6A_scan_QR_code_test.dart | 44 -- .../old_test/6B_request_flow_test.dart | 43 -- .../old_test/6C_introductions_test.dart | 33 -- integration_test/old_test/7_call_test.dart | 44 -- .../old_test/8A_send_single_intro_test.dart | 48 -- .../8B_send_multiple_intros_test.dart | 50 -- .../9A_search_contacts_messages_test.dart | 43 -- .../old_test/9B_search_contacts_test.dart | 45 -- .../old_test/integration_test_common.dart | 449 ------------------ .../old_test/integration_test_constants.dart | 11 - integration_test/test_bundle.dart | 4 +- integration_test/utils/test_utils.dart | 57 ++- lib/app.dart | 3 +- lib/features/home/home.dart | 4 +- pubspec.lock | 4 +- pubspec.yaml | 2 +- 51 files changed, 336 insertions(+), 1952 deletions(-) delete mode 100644 integration_test/old_test/0_change_language_test.dart delete mode 100644 integration_test/old_test/10A_contact_info_screen_test.dart delete mode 100644 integration_test/old_test/10B_contact_info_screen_test.dart delete mode 100644 integration_test/old_test/11_rename_contact_test.dart delete mode 100644 integration_test/old_test/12_delete_contact_test.dart delete mode 100644 integration_test/old_test/13_block_contact_test.dart delete mode 100644 integration_test/old_test/14A_react_to_message_test.dart delete mode 100644 integration_test/old_test/14B_delete_for_me_test.dart delete mode 100644 integration_test/old_test/14C_delete_for_everyone_test.dart delete mode 100644 integration_test/old_test/15_copy_recovery_key_test.dart delete mode 100644 integration_test/old_test/16_recovery_flow_test.dart delete mode 100644 integration_test/old_test/17_verify_contact_test.dart delete mode 100644 integration_test/old_test/18_disappearing_messages_test.dart delete mode 100644 integration_test/old_test/19_become_pro_test.dart delete mode 100644 integration_test/old_test/1A_chat_number_formats_test.dart delete mode 100644 integration_test/old_test/1B_enroll_leave_Me_note_test.dart delete mode 100644 integration_test/old_test/20_vpn_test.dart delete mode 100644 integration_test/old_test/21_account_management_test.dart delete mode 100644 integration_test/old_test/22_invite_test.dart delete mode 100644 integration_test/old_test/23_download_desktop_test.dart delete mode 100644 integration_test/old_test/24A_authorize_device_pro_PIN_test.dart delete mode 100644 integration_test/old_test/24B_authorize_device_pro_email_test.dart delete mode 100644 integration_test/old_test/25_report_issue_test.dart delete mode 100644 integration_test/old_test/26_proxy_all_traffic_test.dart delete mode 100644 integration_test/old_test/27_replica_search_test.dart delete mode 100644 integration_test/old_test/2_add_via_secure_number_test.dart delete mode 100644 integration_test/old_test/3_send_first_message_test.dart delete mode 100644 integration_test/old_test/4_view_attachments_test.dart delete mode 100644 integration_test/old_test/5_voice_memo_test.dart delete mode 100644 integration_test/old_test/6A_scan_QR_code_test.dart delete mode 100644 integration_test/old_test/6B_request_flow_test.dart delete mode 100644 integration_test/old_test/6C_introductions_test.dart delete mode 100644 integration_test/old_test/7_call_test.dart delete mode 100644 integration_test/old_test/8A_send_single_intro_test.dart delete mode 100644 integration_test/old_test/8B_send_multiple_intros_test.dart delete mode 100644 integration_test/old_test/9A_search_contacts_messages_test.dart delete mode 100644 integration_test/old_test/9B_search_contacts_test.dart delete mode 100644 integration_test/old_test/integration_test_common.dart delete mode 100644 integration_test/old_test/integration_test_constants.dart diff --git a/Makefile b/Makefile index 8f4f9e1a67..079fb66fe7 100644 --- a/Makefile +++ b/Makefile @@ -712,3 +712,18 @@ clean: rm -f `which gomobile` && \ rm -f `which gobind` rm -Rf "$(FLASHLIGHT_FRAMEWORK_PATH)" "$(INTERMEDIATE_FLASHLIGHT_FRAMEWORK_PATH)" + + +# Test environment scripts and other utilities + +guard-patrol-cli: + @command -v patrol > /dev/null || (echo "Patrol CLI is not installed. Please install it before running this target." && exit 1) + +nativeTest: guard-patrol-cli + @echo "Running native tests..." + patrol test --target integration_test/features/vpn/vpn_flow_test.dart --dart-define native=true --flavor=prod --verbose + +appWorkflowTest: + @echo "Running all integration tests..." + flutter test integration_test/ --flavor=prod + diff --git a/integration_test/app_startup_flow_test.dart b/integration_test/app_startup_flow_test.dart index 764399483c..f676c5a8b2 100644 --- a/integration_test/app_startup_flow_test.dart +++ b/integration_test/app_startup_flow_test.dart @@ -1,9 +1,8 @@ import 'package:lantern/features/home/home.dart'; -import 'package:lantern/main.dart' as app; -import 'package:patrol/patrol.dart'; import '../test/utils/test_common.dart'; - +import 'utils/test_utils.dart'; +import 'package:lantern/main.dart' as app; /// Looks like patrol does not have native support yet for Linux and Windows /// so any test that interact with the native layer will / need to use flutter test /// patrol feature priority:https://patrol.leancode.co/native/feature-parity @@ -23,14 +22,10 @@ void main() { "app start up sequence", ($) async { await app.main(); - await $.pumpAndSettle(); - await $(HomePage).waitUntilVisible(); - final bottombar = find.byType(BottomNavigationBar); expect(bottombar, findsOneWidget); }, ); } - diff --git a/integration_test/features/account/account_flow_test.dart b/integration_test/features/account/account_flow_test.dart index e69de29bb2..52794b155c 100644 --- a/integration_test/features/account/account_flow_test.dart +++ b/integration_test/features/account/account_flow_test.dart @@ -0,0 +1,3 @@ +void main() { + +} \ No newline at end of file diff --git a/integration_test/features/home/homepage_flow_mock_test.dart b/integration_test/features/home/homepage_flow_mock_test.dart index cf91038612..80e942e18e 100644 --- a/integration_test/features/home/homepage_flow_mock_test.dart +++ b/integration_test/features/home/homepage_flow_mock_test.dart @@ -26,9 +26,12 @@ void main() { late MockReplicaModel mockReplicaModel; late MockVpnModel mockVpnModel; late MockEventManager mockEventManager; + late ValueNotifier proxyNotifier; + + setUp( + () async { + print("setting up mock and sl"); - setUpAll( - () async { await Localization.ensureInitialized(); mockSessionModel = MockSessionModel(); @@ -55,245 +58,256 @@ void main() { // Injection models sl.registerLazySingleton( - () => mockBottomBarChangeNotifier); + () => mockBottomBarChangeNotifier); sl.registerLazySingleton(() => mockVPNChangeNotifier); sl.registerLazySingleton( - () => mockInternetStatusProvider); - }, + () => mockInternetStatusProvider); + proxyNotifier = ValueNotifier(true); + + }, ); - tearDownAll( - () { - sl.reset(); + + + tearDown( + () async { + print("resetting mock and sl"); + resetMockitoState(); + reset(mockSessionModel); + await sl.reset(); }, ); - group( - 'home widget with mock', - () { - patrolWidgetTest( - 'home widget show privacy policy', - ($) async { - when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); - - /// Stub session model - when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); - - when(mockSessionModel.language(any)).thenAnswer( - (invocation) { - final builder = invocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, 'en_us', null); - }, - ); - - when(mockSessionModel.acceptedTermsVersion(any)) - .thenAnswer((invocation) { - final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, 0, null); - }); - - when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { - final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, true, null); - }, - ); - - when(mockSessionModel.isTestPlayVersion) - .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.isStoreVersion) - .thenAnswer((realInvocation) => ValueNotifier(true)); - when(mockSessionModel.isAuthEnabled) - .thenAnswer((realInvocation) => ValueNotifier(false)); - - when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, "test", null); - }, - ); - - when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { - return boolEmptyBuilder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)) - .thenAnswer((realInvocation) { - final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation.positionalArguments[1] as void - Function(Event, Map); - return () { - onNewEvent(event, {}); - }; - }); - - when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, 0, null); - }, - ); - - await $.pumpWidget(const LanternApp()); - await $.pumpAndSettle(); - - await $.pump(const Duration(seconds: 1)); - - expect($(PrivacyDisclosure).visible, true); - - expect($(Button), findsOneWidget); - expect($(BottomNavigationBar), findsNothing); - }, - ); + group('home widget with mock', () { + patrolWidgetTest( + 'home widget auth enable show first time visit screen', + ($) async { + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); - patrolWidgetTest( - 'home widget auth enable show first time visit screen', - ($) async { - when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); - when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn( - true); + /// Stub session model + when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); - /// Stub session model - when(mockSessionModel.proxyAvailable).thenReturn( - ValueNotifier(true)); + when(mockSessionModel.pathValueNotifier(any, false)) + .thenReturn(ValueNotifier(true)); - when(mockSessionModel.pathValueNotifier(any, false)) - .thenReturn(ValueNotifier(true)); - - when(mockSessionModel.language(any)).thenAnswer( - (invocation) { - final builder = invocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, 'en_us', null); - }, - ); + when(mockSessionModel.language(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_us', null); + }, + ); - when(mockSessionModel.acceptedTermsVersion(any)) - .thenAnswer((invocation) { - final builder = + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { + final builder = invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, 0, null); - }); + return builder(mockBuildContext, 0, null); + }); - when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { - final builder = + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, true, null); - }, - ); + return builder(mockBuildContext, true, null); + }, + ); - when(mockSessionModel.shouldShowAds(any)).thenAnswer( - (invocation) { - final builder = invocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, "", null); - }, - ); - - when(mockSessionModel.isTestPlayVersion) - .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.proUserNotifier) - .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.isStoreVersion) - .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.isAuthEnabled) - .thenAnswer((realInvocation) => ValueNotifier(true)); - - when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] + when(mockSessionModel.shouldShowAds(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.proUserNotifier) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) { + print("auth enable show"); + return ValueNotifier(false); + }); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(true)); + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); + return builder(mockBuildContext, false, null); + }, + ); - when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, "", null); - }, - ); - - stubSessionModel( - mockSessionModel: mockSessionModel, - mockBuildContext: mockBuildContext); - - when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)) - .thenAnswer((realInvocation) { - final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation.positionalArguments[1] as void + return builder(mockBuildContext, "", null); + }, + ); + + stubSessionModel( + mockSessionModel: mockSessionModel, + mockBuildContext: mockBuildContext); + + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)).thenAnswer((realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation.positionalArguments[1] as void Function(Event, Map); - return () { - onNewEvent(event, {}); - }; - }); - - when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] + return () { + onNewEvent(event, {}); + }; + }); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, 0, null); - }, - ); + return builder(mockBuildContext, 0, null); + }, + ); - when(sessionModel.isUserFirstTimeVisit()) - .thenAnswer((realInvocation) => Future.value(true)); + when(sessionModel.isUserFirstTimeVisit()) + .thenAnswer((realInvocation) => Future.value(true)); - /// messageing model - when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] + /// messageing model + when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, null, null); - }, - ); - - stubVpnModel( - mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); + return builder(mockBuildContext, null, null); + }, + ); - await $.pumpWidget(const LanternApp()); - await $.pumpAndSettle(); - await $.pump(const Duration(seconds: 2)); + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); + + await $.pumpWidget(const LanternApp()); + await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 2)); + + final signInFinder = $(Button).$('sign_in'.i18n.toUpperCase()); + final lanternProFinder = + $(Button).$('get_lantern_pro'.i18n.toUpperCase()); + + expect($(Button), findsExactly(2)); + expect(lanternProFinder, findsOneWidget); + expect(signInFinder, findsOneWidget); + + when(mockSessionModel.hasUserSignedInNotifier) + .thenReturn(ValueNotifier(false)); + when(mockSessionModel.userEmail).thenReturn(ValueNotifier("")); + + await signInFinder.tap(); + await $.pumpAndSettle(); + + expect($(AuthLanding), findsNothing); + expect($(AppBarProHeader), findsOneWidget); + expect($('sign_in'.i18n), findsOneWidget); + }, + ); + + patrolWidgetTest( + 'home widget show privacy policy', + ($) async { + print("privacy policy test"); + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + + /// Stub session model + when(mockSessionModel.proxyAvailable).thenReturn(proxyNotifier); + when(mockSessionModel.pathValueNotifier(any, false)) + .thenReturn(ValueNotifier(true)); + + when(mockSessionModel.language(any)).thenAnswer( + (invocation) { + print("language called"); + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_us', null); + }, + ); - final signInFinder = $(Button).$('sign_in'.i18n.toUpperCase()); - final lanternProFinder = $(Button).$( - 'get_lantern_pro'.i18n.toUpperCase()); + when(mockSessionModel.acceptedTermsVersion(any)) + .thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }); - expect($(Button), findsExactly(2)); - expect(lanternProFinder, findsOneWidget); - expect(signInFinder, findsOneWidget); + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion) + .thenAnswer((realInvocation) { + print("privacy policy "); + return ValueNotifier(true); + }); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(false)); + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); - when(mockSessionModel.hasUserSignedInNotifier).thenReturn(ValueNotifier(false)); - when(mockSessionModel.userEmail).thenReturn(ValueNotifier("")); + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "test", null); + }, + ); - await signInFinder.tap(); - await $.pumpAndSettle(); + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, false, null); + }, + ); - expect($(AuthLanding), findsNothing); - expect($(AppBarProHeader), findsOneWidget); - expect($('sign_in'.i18n), findsOneWidget); + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)).thenAnswer((realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = realInvocation.positionalArguments[1] as void + Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); }, ); - } - ); + + print("privacy policy pushing"); + await $.pumpWidget(const LanternApp()); + await $.pumpAndSettle(); + + await $.pump(const Duration(seconds: 1)); + + expect($(PrivacyDisclosure).visible, true); + + expect($(Button), findsOneWidget); + expect($(BottomNavigationBar), findsNothing); + }, + ); + }); } diff --git a/integration_test/features/home/homepage_flow_test.dart b/integration_test/features/home/homepage_flow_test.dart index b86ee43ada..720d5452d7 100644 --- a/integration_test/features/home/homepage_flow_test.dart +++ b/integration_test/features/home/homepage_flow_test.dart @@ -1,5 +1,4 @@ import 'package:flutter/material.dart'; -import 'package:i18n_extension/default.i18n.dart'; import 'package:lantern/features/account/account_tab.dart'; import 'package:lantern/features/home/home.dart'; import 'package:lantern/main.dart' as app; @@ -26,7 +25,6 @@ void main() { () { patrolWidgetTest( 'app initializes and navigates to homepage', - tags: ['android,ios,desktop'], ($) async { await app.main(); diff --git a/integration_test/features/vpn/vpn_flow_test.dart b/integration_test/features/vpn/vpn_flow_test.dart index ec164d10b7..bb5a0f9adc 100644 --- a/integration_test/features/vpn/vpn_flow_test.dart +++ b/integration_test/features/vpn/vpn_flow_test.dart @@ -1,5 +1,4 @@ import 'package:animated_toggle_switch/animated_toggle_switch.dart'; -import 'package:lantern/core/utils/common.dart'; import 'package:lantern/core/widgtes/split_tunnel_widget.dart'; import 'package:lantern/features/vpn/vpn_bandwidth.dart'; import 'package:lantern/features/vpn/vpn_pro_banner.dart'; @@ -64,9 +63,9 @@ void main() { if (isAndroid()) { //go back await $(IconButton).tap(); - await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 1)); await $('split_tunneling'.i18n).tap(); - await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 1)); expect($('split_tunneling'.i18n), findsOneWidget); } }, @@ -86,13 +85,37 @@ void main() { expect($('connected'.i18n), findsOneWidget); await $.pump(const Duration(seconds: 2)); - await $(CustomAnimatedToggleSwitch).tap(settlePolicy: SettlePolicy.settle); + await $(CustomAnimatedToggleSwitch) + .tap(settlePolicy: SettlePolicy.settle); await $.pumpAndSettle(); await $.pump(const Duration(seconds: 1)); expect($('connected'.i18n), findsNothing); expect($('Disconnected'.i18n), findsOneWidget); }, ); + + patrolNative( + 'VPN turn off/on on mobile platforms', + skip: isDesktop(), + ($) async { + await createApp($); + await $(VPNTab).waitUntilVisible(); + await $.pump(const Duration(seconds: 3)); + expect($('Disconnected'.i18n), findsOneWidget); + await $(const AdvancedSwitch()).tap(); + await $.pump(const Duration(seconds: 1)); + //Turn on + $.native.tap(Selector(text: 'OK')); + await $.pumpAndSettle(); + expect($('connected'.i18n), findsOneWidget); + expect($('Disconnected'.i18n), findsNothing); + //Turn off + await $.pump(const Duration(seconds: 1)); + await $(const AdvancedSwitch()).tap(); + expect($('connected'.i18n), findsNothing); + expect($('Disconnected'.i18n), findsOneWidget); + }, + ); }, ); } diff --git a/integration_test/old_test/0_change_language_test.dart b/integration_test/old_test/0_change_language_test.dart deleted file mode 100644 index a2e0d8fb4e..0000000000 --- a/integration_test/old_test/0_change_language_test.dart +++ /dev/null @@ -1,25 +0,0 @@ -import 'package:intl/intl.dart'; -import 'package:lantern/i18n/localization_constants.dart'; - -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - final lang = toBeginningOfSentenceCase(displayLanguage(simulatedLocale))!; - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText( - 'settings', - overwriteTimeout: defaultWaitTimeout, - ); - await driver.tapText( - 'language', - overwriteTimeout: defaultWaitTimeout, - ); - await driver.scrollTextUntilVisible(lang); - await driver.tap(find.text(lang)); - await driver.waitForSeconds(2); - }, - ); -} diff --git a/integration_test/old_test/10A_contact_info_screen_test.dart b/integration_test/old_test/10A_contact_info_screen_test.dart deleted file mode 100644 index afeb2fb93e..0000000000 --- a/integration_test/old_test/10A_contact_info_screen_test.dart +++ /dev/null @@ -1,39 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'contact_info_screen_1'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Access a contact info screen via long tap', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - await driver.longPressFirstItemInList( - 'chats_messages_list', - ); - - await driver.tapText( - 'view_contact_info', - overwriteTimeout: longWaitTimeout, - ); - - await driver.waitForSeconds(5); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/10B_contact_info_screen_test.dart b/integration_test/old_test/10B_contact_info_screen_test.dart deleted file mode 100644 index cf4e05a706..0000000000 --- a/integration_test/old_test/10B_contact_info_screen_test.dart +++ /dev/null @@ -1,41 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'contact_info_screen_2'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Access a contact info screen via top right menu', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - await driver.tapFirstItemInList('chats_messages_list'); - - print('tap on top right menu bar'); - await driver.tapKey( - 'conversation_topbar_more_menu', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText( - 'view_contact_info', - overwriteTimeout: longWaitTimeout, - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/11_rename_contact_test.dart b/integration_test/old_test/11_rename_contact_test.dart deleted file mode 100644 index 17e2e90cb5..0000000000 --- a/integration_test/old_test/11_rename_contact_test.dart +++ /dev/null @@ -1,60 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'rename_contact'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Rename a contact', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - await driver.tapFirstItemInList('chats_messages_list'); - - print('tap on top right menu bar'); - await driver.tapKey( - 'conversation_topbar_more_menu', - overwriteTimeout: defaultWaitTimeout, - ); - - print('tap on View Contact Info'); - await driver.tapText( - 'view_contact_info', - overwriteTimeout: defaultWaitTimeout, - ); - - print('click on EDIT'); - await driver.tapText('edit', capitalize: true); - - print('enter new contact name'); - await driver.enterText( - renameContact, - timeout: longWaitTimeout, - ); - - await driver.waitForSeconds(2); - - print('tap SAVE'); - await driver.tapText( - 'save', - capitalize: true, - overwriteTimeout: longWaitTimeout, - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/12_delete_contact_test.dart b/integration_test/old_test/12_delete_contact_test.dart deleted file mode 100644 index 728416bf0b..0000000000 --- a/integration_test/old_test/12_delete_contact_test.dart +++ /dev/null @@ -1,43 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'delete_contact'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Delete a contact', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - await driver.tapFirstItemInList('chats_messages_list'); - - print('tap on top right menu bar'); - await driver.tapKey( - 'conversation_topbar_more_menu', - overwriteTimeout: defaultWaitTimeout, - ); - - await driver.tapText( - 'view_contact_info', - overwriteTimeout: defaultWaitTimeout, - ); - - await driver.tapText('delete_contact', capitalize: true); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/13_block_contact_test.dart b/integration_test/old_test/13_block_contact_test.dart deleted file mode 100644 index 6d561ab1f2..0000000000 --- a/integration_test/old_test/13_block_contact_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'block_contact'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Block a contact', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - await driver.tapFirstItemInList('chats_messages_list'); - - await driver.tapKey( - 'conversation_topbar_more_menu', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText( - 'view_contact_info', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText( - 'block', - capitalize: true, - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapType( - 'Checkbox', - overwriteTimeout: longWaitTimeout, - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/14A_react_to_message_test.dart b/integration_test/old_test/14A_react_to_message_test.dart deleted file mode 100644 index 72d98cac15..0000000000 --- a/integration_test/old_test/14A_react_to_message_test.dart +++ /dev/null @@ -1,48 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'react_to_message'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'React to message', - () async { - print( - 'This test relies on only one message having the _just now_ timestamp, so lets wait a bit in case other conversations were active recently', - ); - await driver.waitForSeconds(60); - - await driver.openTab('chats', homeFirst: true); - - await driver.screenshotCurrentView(); - - await driver.tapFAB(); - - await driver.tapFirstItemInList('grouped_contact_list'); - - await driver.typeAndSend('test_text'); - - print('long press message we just shared'); - await driver.longPressText('just_now'); - - await driver.tapText('copy_text'); - - await driver.tapText('reply'); - - await driver.typeAndSend('test_reply'); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/14B_delete_for_me_test.dart b/integration_test/old_test/14B_delete_for_me_test.dart deleted file mode 100644 index 031e90ca2b..0000000000 --- a/integration_test/old_test/14B_delete_for_me_test.dart +++ /dev/null @@ -1,42 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'delete_for_me'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Delete for me', - () async { - print( - 'This test relies on only one message having the _just now_ timestamp, so lets wait a bit in case other conversations were active recently', - ); - await driver.waitForSeconds(60); - - await driver.openTab('chats', homeFirst: true); - - await driver.tapFAB(); - - await driver.tapFirstItemInList('grouped_contact_list'); - - await driver.typeAndSend('test_text'); - print('long press message we just shared'); - await driver.longPressText('just_now'); - - print('delete for me'); - await driver.tapText('delete_for_me'); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/14C_delete_for_everyone_test.dart b/integration_test/old_test/14C_delete_for_everyone_test.dart deleted file mode 100644 index d5c76ece28..0000000000 --- a/integration_test/old_test/14C_delete_for_everyone_test.dart +++ /dev/null @@ -1,43 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'delete_for_everyone'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Delete for everyone', - () async { - print( - 'This test relies on only one message having the _just now_ timestamp, so lets wait a bit in case other conversations were active recently', - ); - await driver.waitForSeconds(60); - - await driver.openTab('chats', homeFirst: true); - - await driver.screenshotCurrentView(); - - await driver.tapFAB(); - - await driver.tapFirstItemInList('grouped_contact_list'); - - await driver.typeAndSend('test_text'); - print('long press message we just shared'); - await driver.longPressText('just_now'); - - await driver.tapText('delete_for_everyone'); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/15_copy_recovery_key_test.dart b/integration_test/old_test/15_copy_recovery_key_test.dart deleted file mode 100644 index 34ddc1b6a7..0000000000 --- a/integration_test/old_test/15_copy_recovery_key_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'copy_recovery_key'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Copy Recovery Key', - () async { - await driver.resetFlagsAndEnrollAgain(skipScreenshot: true); - await driver.openTab('Account'); - await driver.tapText('account_management'); - await driver.tapText('backup_recovery_key'); - await driver.captureScreenshotDuringFuture( - futureToScreenshot: driver.tapText( - 'copy_recovery_key', - capitalize: true, - ), - screenshotTitle: testName, - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/16_recovery_flow_test.dart b/integration_test/old_test/16_recovery_flow_test.dart deleted file mode 100644 index b3a80231c3..0000000000 --- a/integration_test/old_test/16_recovery_flow_test.dart +++ /dev/null @@ -1,51 +0,0 @@ -import 'integration_test_common.dart'; -import 'integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'recovery_flow_test'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Recovery_flow_test', - () async { - await driver.resetFlags(); - await driver.tapText( - 'chats', - waitText: 'welcome_title', - ); - - await driver.tapText( - 'recover', - capitalize: true, - overwriteTimeout: defaultWaitTimeout, - ); - - print('entering recovery key'); - await driver.captureScreenshotDuringFuture( - futureToScreenshot: driver.enterText( - recoveryKey, - ), - screenshotTitle: testName, - ); - - await driver.tapText( - 'Submit', - capitalize: true, - overwriteTimeout: defaultWaitTimeout, - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/17_verify_contact_test.dart b/integration_test/old_test/17_verify_contact_test.dart deleted file mode 100644 index 79c1366cb2..0000000000 --- a/integration_test/old_test/17_verify_contact_test.dart +++ /dev/null @@ -1,56 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'verify_contact'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - // Test requirements - // * First message needs to be an unverified contact - group(testName, () { - test( - 'Verify a contact via voice call', - () async { - await driver.openTab('chats', homeFirst: true); - - await driver.addDummyContacts(); - - await driver.tapFAB(); - - await driver.tapFirstItemInList('grouped_contact_list'); - - await driver.tapKey( - 'verification_badge', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText( - 'verify_via_call', - overwriteTimeout: defaultWaitTimeout, - ); - - await driver.tapKey( - 'call_verify_button', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText( - 'mark_as_verified', - capitalize: true, - overwriteTimeout: longWaitTimeout, - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/18_disappearing_messages_test.dart b/integration_test/old_test/18_disappearing_messages_test.dart deleted file mode 100644 index 05f6e5e52a..0000000000 --- a/integration_test/old_test/18_disappearing_messages_test.dart +++ /dev/null @@ -1,61 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'disappearing_messages'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Test disappearing messages settings', - () async { - await driver.openTab('chats', homeFirst: true); - - await driver.tapFAB(); - - await driver.tapFirstItemInList('grouped_contact_list'); - - await driver.typeAndSend( - 'test_disappearing_messages_1', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapKey( - 'conversation_topbar_more_menu', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText( - 'disappearing_messages', - overwriteTimeout: longWaitTimeout, - ); - - final five = await driver.requestData('5'); - final seconds = - (await driver.requestData('longform_seconds')).split(' ')[1]; - await driver.tapText( - '$five $seconds', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText('set', capitalize: true); - - await driver.typeAndSend('test_disappearing_messages_2'); - - await driver.waitForSeconds(5); - await driver.saveScreenshot('final'); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/19_become_pro_test.dart b/integration_test/old_test/19_become_pro_test.dart deleted file mode 100644 index 99a4f44faa..0000000000 --- a/integration_test/old_test/19_become_pro_test.dart +++ /dev/null @@ -1,15 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText( - 'Upgrade to Lantern Pro', - overwriteTimeout: defaultWaitTimeout, - ); - await driver.waitForSeconds(2); - }, - ); -} diff --git a/integration_test/old_test/1A_chat_number_formats_test.dart b/integration_test/old_test/1A_chat_number_formats_test.dart deleted file mode 100644 index 099bbfc088..0000000000 --- a/integration_test/old_test/1A_chat_number_formats_test.dart +++ /dev/null @@ -1,12 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.resetFlagsAndEnrollAgain(); - await driver.openTab('Account'); - await driver.tapText('account_management'); - await driver.tapFirstItemInList('account_management_free_list'); - }, - ); -} diff --git a/integration_test/old_test/1B_enroll_leave_Me_note_test.dart b/integration_test/old_test/1B_enroll_leave_Me_note_test.dart deleted file mode 100644 index 9fe5f22edb..0000000000 --- a/integration_test/old_test/1B_enroll_leave_Me_note_test.dart +++ /dev/null @@ -1,12 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('chats', homeFirst: true); - await driver.tapFAB(); - await driver.tapFirstItemInList('grouped_contact_list'); - await driver.typeAndSend('test_text'); - }, - ); -} diff --git a/integration_test/old_test/20_vpn_test.dart b/integration_test/old_test/20_vpn_test.dart deleted file mode 100644 index eeb71fc2d7..0000000000 --- a/integration_test/old_test/20_vpn_test.dart +++ /dev/null @@ -1,10 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('VPN'); - await driver.tapType('FlutterSwitch'); - }, - ); -} diff --git a/integration_test/old_test/21_account_management_test.dart b/integration_test/old_test/21_account_management_test.dart deleted file mode 100644 index c5a739cc47..0000000000 --- a/integration_test/old_test/21_account_management_test.dart +++ /dev/null @@ -1,10 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText('account_management'); - }, - ); -} diff --git a/integration_test/old_test/22_invite_test.dart b/integration_test/old_test/22_invite_test.dart deleted file mode 100644 index a447a4265a..0000000000 --- a/integration_test/old_test/22_invite_test.dart +++ /dev/null @@ -1,10 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText('Invite Friends'); - }, - ); -} diff --git a/integration_test/old_test/23_download_desktop_test.dart b/integration_test/old_test/23_download_desktop_test.dart deleted file mode 100644 index 6ef011401a..0000000000 --- a/integration_test/old_test/23_download_desktop_test.dart +++ /dev/null @@ -1,11 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText('desktop_version'); - await driver.tapText('Share link', capitalize: true); - }, - ); -} diff --git a/integration_test/old_test/24A_authorize_device_pro_PIN_test.dart b/integration_test/old_test/24A_authorize_device_pro_PIN_test.dart deleted file mode 100644 index d37417990b..0000000000 --- a/integration_test/old_test/24A_authorize_device_pro_PIN_test.dart +++ /dev/null @@ -1,18 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText('Authorize Device for Pro', - overwriteTimeout: defaultWaitTimeout, - ); - await driver.tapText( - 'Link with PIN', - capitalize: true, - overwriteTimeout: defaultWaitTimeout, - ); - }, - ); -} diff --git a/integration_test/old_test/24B_authorize_device_pro_email_test.dart b/integration_test/old_test/24B_authorize_device_pro_email_test.dart deleted file mode 100644 index e57ebe9706..0000000000 --- a/integration_test/old_test/24B_authorize_device_pro_email_test.dart +++ /dev/null @@ -1,26 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText( - 'Authorize Device for Pro', - overwriteTimeout: defaultWaitTimeout, - ); - await driver.tapText( - 'Link via Email', - capitalize: true, - overwriteTimeout: defaultWaitTimeout, - ); - await driver.typeAndSend('youremail@email.com'); - await driver.tapText( - 'Submit', - capitalize: true, - overwriteTimeout: defaultWaitTimeout, - ); - await driver.screenshotCurrentView(); - }, - ); -} diff --git a/integration_test/old_test/25_report_issue_test.dart b/integration_test/old_test/25_report_issue_test.dart deleted file mode 100644 index ee26094f15..0000000000 --- a/integration_test/old_test/25_report_issue_test.dart +++ /dev/null @@ -1,14 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText('Settings'); - await driver.tapText('report_issue'); - // TODO: select issue from dropdown - // TODO: enter text - // TODO: test submit - }, - ); -} diff --git a/integration_test/old_test/26_proxy_all_traffic_test.dart b/integration_test/old_test/26_proxy_all_traffic_test.dart deleted file mode 100644 index ec3d50fc38..0000000000 --- a/integration_test/old_test/26_proxy_all_traffic_test.dart +++ /dev/null @@ -1,26 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Account'); - await driver.tapText( - 'Settings', - overwriteTimeout: defaultWaitTimeout, - ); - await driver.tapKey( - 'proxy_all_icon', - overwriteTimeout: defaultWaitTimeout, - ); - await driver.tapText( - 'OK', - overwriteTimeout: defaultWaitTimeout, - ); - await driver.tapType( - 'FlutterSwitch', - overwriteTimeout: defaultWaitTimeout, - ); - }, - ); -} diff --git a/integration_test/old_test/27_replica_search_test.dart b/integration_test/old_test/27_replica_search_test.dart deleted file mode 100644 index 662481c0b4..0000000000 --- a/integration_test/old_test/27_replica_search_test.dart +++ /dev/null @@ -1,31 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('Discover'); - final messageContent = await driver.requestData('test_replica_search'); - await driver.waitForSeconds(2); - await driver.tapType( - 'SearchField', - overwriteTimeout: longWaitTimeout, - ); - await driver.captureScreenshotDuringFuture( - futureToScreenshot: driver.enterText( - messageContent, - timeout: longWaitTimeout, - ), - screenshotTitle: 'sending_message', - ); - await driver.tapKey('submit_text_field'); - - print('viewing a Replica video'); - await driver.waitForSeconds(5); - await driver.screenshotCurrentView(); - await driver.tapFirstItemInList('replica_tab_view'); - await driver.waitForSeconds(5); - await driver.screenshotCurrentView(); - }, - ); -} diff --git a/integration_test/old_test/2_add_via_secure_number_test.dart b/integration_test/old_test/2_add_via_secure_number_test.dart deleted file mode 100644 index d1f88766a4..0000000000 --- a/integration_test/old_test/2_add_via_secure_number_test.dart +++ /dev/null @@ -1,38 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - await runTest((driver) async { - await driver.openTab('chats', homeFirst: true); - - await driver.tapFAB(); - - await driver.tapText( - 'add_via_chat_number', - overwriteTimeout: longWaitTimeout, - ); - - await driver.waitForSeconds(2); - - print('entering secure chat number'); - await driver.captureScreenshotDuringFuture( - futureToScreenshot: driver.enterText( - textThisNumber, - timeout: longWaitTimeout, - ), - screenshotTitle: 'entering_secure_number', - ); - - await driver.tapText( - 'start_chat', - capitalize: true, - overwriteTimeout: longWaitTimeout, - ); - - await driver.enterText(contactNewName); - - await driver.waitForSeconds(2); - - await driver.tapText('Done', capitalize: true); - }); -} diff --git a/integration_test/old_test/3_send_first_message_test.dart b/integration_test/old_test/3_send_first_message_test.dart deleted file mode 100644 index 24ec54cf24..0000000000 --- a/integration_test/old_test/3_send_first_message_test.dart +++ /dev/null @@ -1,16 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('chats', homeFirst: true); - - await driver.tapFAB(); - - await driver.tapFirstItemInList('grouped_contact_list'); - - print('typing text'); - await driver.typeAndSend('test_hello'); - }, - ); -} diff --git a/integration_test/old_test/4_view_attachments_test.dart b/integration_test/old_test/4_view_attachments_test.dart deleted file mode 100644 index e5f6f4260e..0000000000 --- a/integration_test/old_test/4_view_attachments_test.dart +++ /dev/null @@ -1,29 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('chats', homeFirst: true); - - await driver.addDummyContacts(); - - await driver.sendDummyFiles(); - - await driver.tapFirstItemInList('chats_messages_list'); - - print('tapping on image attachment'); - await driver.tapType( - 'ImageAttachment', - overwriteTimeout: longWaitTimeout, - ); - - await driver.goBack(); - - await driver.tapType( - 'VideoAttachment', - overwriteTimeout: longWaitTimeout, - ); - }, - ); -} diff --git a/integration_test/old_test/5_voice_memo_test.dart b/integration_test/old_test/5_voice_memo_test.dart deleted file mode 100644 index 95f44eeb31..0000000000 --- a/integration_test/old_test/5_voice_memo_test.dart +++ /dev/null @@ -1,34 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - await runTest( - (driver) async { - await driver.openTab('chats', homeFirst: true); - - await driver.tapFAB(); - - // Looking for "Me" works for most languages. - // However in some cases (Chinese), "Me" is a single character, and so is the alphabetic marker we use to separate contacts, which confuses the test driver. - // In that case, we can just find the first element of the 'grouped_contact_list' ListBody. - try { - await driver.tapText('me'); - } catch (_) { - print( - 'there was an issue tapping on Me conversation, will find another contact', - ); - await driver.tapFirstItemInList('grouped_contact_list'); - } - - final recorderButtonFinder = find.byValueKey('recorder_button'); - - print('tapping on start record button'); - await driver.longPress(target: recorderButtonFinder); - - print('tapping on stop record button'); - await driver.longPress(target: recorderButtonFinder); - - print('tapping on send'); - await driver.tapKey('send_message'); - }, - ); -} diff --git a/integration_test/old_test/6A_scan_QR_code_test.dart b/integration_test/old_test/6A_scan_QR_code_test.dart deleted file mode 100644 index 55b5264623..0000000000 --- a/integration_test/old_test/6A_scan_QR_code_test.dart +++ /dev/null @@ -1,44 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'scan_QR_code'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - // Test requirements - // * Needs another phone to scan the QR code with - group(testName, () { - test( - 'Scan QR code', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.tapFAB(); - - // click on Scan QR Code - await driver.tapText('scan_qr_code'); - - // screenshot and wait - await driver.screenshotCurrentView(); - await driver.waitForSeconds(1); - - // screenshot and wait - await driver.screenshotCurrentView(); - await driver.waitForSeconds(1); - - // screenshot and wait - await driver.screenshotCurrentView(); - await driver.waitForSeconds(1); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/6B_request_flow_test.dart b/integration_test/old_test/6B_request_flow_test.dart deleted file mode 100644 index d5c2e1cc31..0000000000 --- a/integration_test/old_test/6B_request_flow_test.dart +++ /dev/null @@ -1,43 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'request_flow'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - // Test requirements - // * Needs to have _just_ received a message request from another user - // TODO (not immediate): we can set up a second test driver so that one message requests the other - group(testName, () { - test( - 'Accept via message request', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - print('open message request'); - await driver.tapText('just_now'); - - await driver.captureScreenshotDuringFuture( - futureToScreenshot: driver.tapText( - 'accept', - capitalize: true, - overwriteTimeout: longWaitTimeout, - ), - screenshotTitle: 'naming new contact', - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/6C_introductions_test.dart b/integration_test/old_test/6C_introductions_test.dart deleted file mode 100644 index 4c0a82cbf8..0000000000 --- a/integration_test/old_test/6C_introductions_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'introductions'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - -// * Test requirements -// * Needs to have received a _single_ introduction from another user - // TODO (not immediate): we can set up a second test driver so that one sends an intro to the other - group(testName, () { - test( - 'Accept an introduction', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.tapText('introductions'); - await driver.tapText('reject', capitalize: true); - await driver.tapText('cancel', capitalize: true); - await driver.tapText('accept', capitalize: true); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/7_call_test.dart b/integration_test/old_test/7_call_test.dart deleted file mode 100644 index 6b104ffb7b..0000000000 --- a/integration_test/old_test/7_call_test.dart +++ /dev/null @@ -1,44 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'call'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Call a contact', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - await driver.tapFAB(); - - await driver.tapFirstItemInList('grouped_contact_list'); - - await driver.tapType( - 'CallAction', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText( - 'call', - overwriteTimeout: longWaitTimeout, - ); - - await driver.waitForSeconds(2); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/8A_send_single_intro_test.dart b/integration_test/old_test/8A_send_single_intro_test.dart deleted file mode 100644 index 18f15e7f03..0000000000 --- a/integration_test/old_test/8A_send_single_intro_test.dart +++ /dev/null @@ -1,48 +0,0 @@ -import '../integration_test_common.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'send_single_intro'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Send introduction to a single contact', - () async { - await driver.openTab('chats', homeFirst: true); - - await driver.addDummyContacts(); - - print('navigating to New Chat'); - await driver.tapFAB(); - - print('long pressing first contact in list'); - await driver.longPressFirstItemInList('grouped_contact_list'); - - await driver.tapText('introduce_contact'); - - print('select first contact from Introduce list'); - await driver.captureScreenshotDuringFuture( - futureToScreenshot: driver.tapFirstItemInList('grouped_contact_list'), - screenshotTitle: 'sending_intros', - ); - - // go back to Chats - await driver.goBack(); - - // tap first item which will display the sent introduction - await driver.tapFirstItemInList('chats_messages_list'); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/8B_send_multiple_intros_test.dart b/integration_test/old_test/8B_send_multiple_intros_test.dart deleted file mode 100644 index f23ac254f7..0000000000 --- a/integration_test/old_test/8B_send_multiple_intros_test.dart +++ /dev/null @@ -1,50 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'send_multiple_intros'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Send introductions to multiple contacts', - () async { - await driver.openTab( - 'chats', - homeFirst: true, - ); - await driver.addDummyContacts(); - - await driver.tapKey( - 'chats_topbar_more_menu', - ); - - // click on Introduce contacts - await driver.tapText('introduce_contacts'); - - // HACK - print('tap secret select all intros key'); - await driver.tapKey( - 'select_all_intros', - overwriteTimeout: longWaitTimeout, - ); - - await driver.tapText('send_introductions', capitalize: true); - - // we are now back in Chats, go to first message to see our sent invitation - await driver.tapFirstItemInList('chats_messages_list'); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/9A_search_contacts_messages_test.dart b/integration_test/old_test/9A_search_contacts_messages_test.dart deleted file mode 100644 index 5622bce694..0000000000 --- a/integration_test/old_test/9A_search_contacts_messages_test.dart +++ /dev/null @@ -1,43 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'search_contacts_messages'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Search in Messages and Contacts', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - print('tap search icon'); - await driver.tapKey( - 'search_icon', - overwriteTimeout: defaultWaitTimeout, - ); - - print('enter search term'); - await driver.captureScreenshotDuringFuture( - futureToScreenshot: driver.enterText( - 'test_text', - timeout: longWaitTimeout, - ), - screenshotTitle: testName, - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/9B_search_contacts_test.dart b/integration_test/old_test/9B_search_contacts_test.dart deleted file mode 100644 index 27f91e0209..0000000000 --- a/integration_test/old_test/9B_search_contacts_test.dart +++ /dev/null @@ -1,45 +0,0 @@ -import '../integration_test_common.dart'; -import '../integration_test_constants.dart'; - -Future main() async { - late FlutterDriver driver; - final testName = 'search_contacts'; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await FlutterDriver.connect(timeout: const Duration(seconds: 30)); - await driver.initScreenshotsDirectory(testName); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(testName, () { - test( - 'Search in Contacts', - () async { - await driver.openTab('chats', homeFirst: true); - await driver.screenshotCurrentView(); - - await driver.tapFAB(); - - print('tap search icon'); - await driver.tapType( - 'RoundButton', - overwriteTimeout: defaultWaitTimeout, - ); - - print('enter search term'); - await driver.captureScreenshotDuringFuture( - futureToScreenshot: driver.enterText( - contactNewName.split(' ')[0], - timeout: longWaitTimeout, - ), - screenshotTitle: testName, - ); - }, - timeout: const Timeout(Duration(minutes: 5)), - ); - }); -} diff --git a/integration_test/old_test/integration_test_common.dart b/integration_test/old_test/integration_test_common.dart deleted file mode 100644 index d5f616471e..0000000000 --- a/integration_test/old_test/integration_test_common.dart +++ /dev/null @@ -1,449 +0,0 @@ -import 'dart:io'; - -import 'package:flutter_driver/flutter_driver.dart'; -import 'package:lantern/core/utils/add_nonbreaking_spaces.dart'; -import 'package:lantern/flutter_driver_extensions/add_dummy_contacts_command.dart'; -import 'package:lantern/flutter_driver_extensions/navigate_command.dart'; -import 'package:lantern/flutter_driver_extensions/reset_flags_command.dart'; -import 'package:lantern/flutter_driver_extensions/send_dummy_files_command.dart'; -import 'package:lantern/i18n/localization_constants.dart'; -import 'package:path/path.dart'; -import 'package:test/test.dart'; - -import 'integration_test_constants.dart'; - -export 'package:flutter_driver/flutter_driver.dart'; -export 'package:test/test.dart'; - -Future connect({int port = 8888}) async { - return await FlutterDriver.connect( - dartVmServiceUrl: 'http://127.0.0.1:$port', - timeout: const Duration(seconds: 15), - ); -} - -/// Scaffolds the test workflow -Future runTest( - Future Function(FlutterDriver driver) doTest, { - Timeout timeout = const Timeout(Duration(minutes: 5)), -}) async { - final name = Platform.script.pathSegments.last.replaceAll('_test.dart', ''); - - late FlutterDriver driver; - - setUpAll(() async { - // Connect to a running Flutter application instance. - driver = await connect(); - await driver.initScreenshotsDirectory(name); - }); - - tearDownAll(() async { - await driver.close(); - }); - - group(name, () { - test( - name, - () async { - await doTest(driver); - }, - timeout: timeout, - ); - }); -} - -extension DriverExtension on FlutterDriver { - static var screenshotSequence = 0; - - // screenshots for a given test are saved here - static var currentTestDirPath = ''; - - /// Custom flutter driver command for navigating to Home - /// pops router all the way behind the scenes navigatorKey.currentContext?.router.popUntilRoot() - Future home() async { - await sendCommand(NavigateCommand(NavigateCommand.home)); - } - - /// Custom flutter driver command for adding dummy contacts - Future addDummyContacts() async { - print('adding dummy contacts'); - await sendCommand(AddDummyContactsCommand()); - } - - /// Custom flutter driver command for downloading and sharing an image and a video file in a conversation with one of the dummy contacts - Future sendDummyFiles() async { - print('downloading and sharing dummy files'); - await sendCommand(SendDummyFilesCommand()); - } - - /// Custom flutter driver command that resets Chat flags and timestamps - Future resetFlags() async { - print('resetting flags and timestamps'); - await sendCommand(ResetFlagsCommand()); - } - - /// Opens specified tab - Future openTab( - String label, { - bool homeFirst = false, - bool skipScreenshot = false, - }) async { - if (homeFirst) { - print('navigating to home'); - await home(); - } - await tapText( - label, - skipScreenshot: skipScreenshot, - parent: find.byType('CustomBottomBarItem'), - ); - } - - /// iterates through our array of available locales and creates a folder for each locale - Future initLocaleFolders() async { - languages.forEach((lang) async { - final directory = Directory('screenshots/$lang'); - if (await directory.exists()) return; - await directory.create(); - }); - } - - /// deletes and re-creates currentTestDirPath each time the test is run - Future initScreenshotsDirectory(String testName) async { - currentTestDirPath = 'screenshots/$simulatedLocale/$testName'; - final directory = Directory(currentTestDirPath); - if (await directory.exists()) await directory.delete(recursive: true); - await directory.create(recursive: true); - } - - /// saves screenshots to currentTestDirPath directory - Future saveScreenshot(String name) async { - try { - final png = await screenshot(); - final screenshotName = join( - currentTestDirPath, - '${++screenshotSequence}_$name.png', - ); - final file = File(screenshotName); - await file.writeAsBytes(png); - } catch (e) { - print(e); - } - } - - /// finds and clicks the top left back button - Future goBack() async { - final backButton = const PageBack(); - print('going back'); - try { - await tap(backButton, timeout: defaultTapTimeout); - } catch (e) { - print('Hit home, will wait'); - } - } - - /// Handles non-breaking text wrapping - Future doWaitForText(String waitText) async { - final localizedWaitText = waitText; - try { - await waitFor( - find.text(localizedWaitText), - timeout: defaultWaitTimeout, - ); - } catch (_) { - // try it with non-breaking spaces like those added by CText - try { - await waitFor( - find.text(addNonBreakingSpaces(localizedWaitText)), - timeout: defaultWaitTimeout, - ); - } catch (e) { - print(e); - } - } - } - - /// Taps on widget after it localizes it via find.text() - Future tapText( - String tapText, { - String? waitText, - bool? skipScreenshot, - Duration? overwriteTimeout, - SerializableFinder? parent, - bool capitalize = false, - }) async { - tapText = await requestData(tapText); - if (capitalize) { - tapText = tapText.toUpperCase(); - } - waitText = waitText == null ? null : await requestData(waitText); - print('tapping on text: $tapText'); - try { - await tapFinder( - find.text(tapText), - waitText: waitText, - skipScreenshot: skipScreenshot, - overwriteTimeout: overwriteTimeout, - parent: parent, - ); - } catch (_) { - // try it with non-breaking spaces like those added by CText - await tapFinder( - find.text(addNonBreakingSpaces(tapText)), - waitText: waitText, - skipScreenshot: skipScreenshot, - overwriteTimeout: overwriteTimeout, - parent: parent, - ); - } - } - - /// Taps on Floating Action Button in Chats - Future tapFAB({ - String? waitText, - bool? skipScreenshot, - }) async { - print('tapping on FAB'); - await tapType( - 'FloatingActionButton', - waitText: waitText, - skipScreenshot: skipScreenshot, - ); - } - - /// Taps on widget after it localizes it via find.byType(type) - Future tapType( - String type, { - String? waitText, - bool? skipScreenshot, - Duration? overwriteTimeout, - }) async { - print('tapping on type: $type'); - await tapFinder( - find.byType(type), - waitText: waitText, - skipScreenshot: skipScreenshot, - overwriteTimeout: overwriteTimeout, - ); - } - - /// Taps on widget after it localizes it via find.byValueKey(key) - Future tapKey( - String key, { - String? waitText, - bool? skipScreenshot, - Duration? overwriteTimeout, - }) async { - print('tapping on key: $key'); - try { - await tapFinder( - find.byValueKey(key), - waitText: waitText, - skipScreenshot: skipScreenshot, - overwriteTimeout: overwriteTimeout, - ); - } catch (e) { - print(e); - } - } - - /// Finds and longpresses a specific string - Future longPressText(String text) async { - await longPress(target: find.text(await requestData(text))); - } - - /// Simulates a long press is simulated, and screenshots labeled as 'long_press' are saved. - /// It receives either a SerializableFinder or a text to look for using find.text() - Future longPress({required dynamic target}) async { - SerializableFinder finder; - if (target is SerializableFinder) { - finder = target; - print( - 'simulating long press at ${target.serialize()}, times out after $longWaitTimeout', - ); - } else if (target is String) { - // we have a String text we will use to find the widget with - take into consideration we have to handle the breaking spaces case - print( - 'simulating long press at text $target, times out after $longWaitTimeout', - ); - try { - finder = find.text(target); - } catch (_) { - finder = find.text(addNonBreakingSpaces(target)); - } - } else { - return; - } - try { - // running this as chained futures in order to be able to capture a screenshot of the long press state before it completes - await captureScreenshotDuringFuture( - futureToScreenshot: scroll( - finder, - 0, - 0, - longWaitTimeout, - timeout: longWaitTimeout, - ), - screenshotTitle: 'long_press', - ); - } catch (e) { - print(e); - } - } - - /// receives a SerializableFinder finder and taps at the center of the widget located by it. It handles text wrapping in case the finder can't locate the target. - /// It saves a screenshot of the viewport unless skipScreenshot = true - Future tapFinder( - SerializableFinder finder, { - String? waitText, - bool? skipScreenshot, - Duration? overwriteTimeout, - SerializableFinder? parent, - }) async { - if (parent != null) { - finder = find.descendant(of: parent, matching: finder); - } - try { - await tap( - finder, - timeout: overwriteTimeout ?? defaultTapTimeout, - ); - if (waitText != null) { - await doWaitForText(waitText); - } - } catch (e) { - print(e); - rethrow; - } finally { - if (skipScreenshot == true) return; - await saveScreenshot( - 'tap_${finder}_wait_for_$waitText', - ); - } - } - - /// Controls scrolling inside a ListView widget - Future scrollTextUntilVisible(String text) async { - try { - print('scrolling until $text is visible'); - final scrollable = find.byType('ListView'); - await waitFor( - scrollable, - timeout: defaultWaitTimeout, - ); - await scrollUntilVisible( - scrollable, - find.text(await requestData(text)), - dyScroll: -500, - timeout: const Duration( - seconds: 600, - ), - ); - } catch (e) { - print(e); - } - } - - /// Simple delay for [seconds] - Future waitForSeconds(int seconds) async { - print('will now wait for $seconds seconds'); - await Future.delayed(Duration(seconds: seconds)); - } - - /// Automates the Developer → RESET FLAGS → Chats → GET STARTED → NEXT flow - Future resetFlagsAndEnrollAgain({bool? skipScreenshot}) async { - print('do the whole reset -> enroll thing'); - await openTab('Developer', homeFirst: true, skipScreenshot: true); - await scrollTextUntilVisible('RESET FLAGS'); - await tapText( - 'RESET FLAGS', - skipScreenshot: true, - ); - await tapText( - await requestData('chats'), - skipScreenshot: skipScreenshot, - ); - await tapText( - (await requestData('get_started')).toUpperCase(), - skipScreenshot: skipScreenshot, - ); - await tapText( - 'next', - capitalize: true, - skipScreenshot: skipScreenshot, - ); - } - - /// Locates message bar, types a message and sends it, also saves screenshot with title "sending_message" - Future typeAndSend( - String messageContent, { - Duration? overwriteTimeout, - }) async { - messageContent = await requestData(messageContent); - await waitForSeconds(2); - await tapType( - 'TextFormField', - overwriteTimeout: overwriteTimeout, - ); - // running this as chained futures in order to be able to capture a screenshot of the state before the first future completes - await captureScreenshotDuringFuture( - futureToScreenshot: enterText( - messageContent, - timeout: overwriteTimeout ?? const Duration(seconds: 1), - ), - screenshotTitle: 'sending_message', - ); - await tapKey( - 'send_message', - overwriteTimeout: overwriteTimeout, - ); - } - - /// creates an array for Futures, one of which is what we want to save a screenshot of, and the other is the saveScreenshot() function - Future captureScreenshotDuringFuture({ - required Future futureToScreenshot, - required String screenshotTitle, - }) async { - print('making sure we screenshot the future that is currently running'); - await Future.wait([ - futureToScreenshot, - saveScreenshot( - screenshotTitle, - ) - ]); - } - - /// saves a screenshot of the current view and labels it "current_screen" - Future screenshotCurrentView() async { - print('screenshotting current view'); - await captureScreenshotDuringFuture( - futureToScreenshot: waitForSeconds(1), - screenshotTitle: 'current_screen', - ); - } - - /// finds first item of descendants in list with given list_key - Future fistItemFinder(String list_key) async { - final list = find.byValueKey(list_key); - final firstItem = find.descendant( - of: list, - matching: find.byType('ListItemFactory'), - firstMatchOnly: true, - ); - return firstItem; - } - - /// taps first item of descendants in list with given list_key - Future tapFirstItemInList(String list_key) async { - print('access first message of list with $list_key key'); - await tapFinder( - await fistItemFinder(list_key), - overwriteTimeout: defaultWaitTimeout, - ); - } - - /// long presses first item of descendants in list with given list_key - Future longPressFirstItemInList(String list_key) async { - await longPress(target: await fistItemFinder(list_key)); - } -} diff --git a/integration_test/old_test/integration_test_constants.dart b/integration_test/old_test/integration_test_constants.dart deleted file mode 100644 index 7b3bf00b0c..0000000000 --- a/integration_test/old_test/integration_test_constants.dart +++ /dev/null @@ -1,11 +0,0 @@ -const defaultTapTimeout = Duration(seconds: 1); -const defaultWaitTimeout = Duration(seconds: 5); -const longWaitTimeout = Duration(seconds: 20); -const veryLongWaitTimeout = Duration(seconds: 40); -const textThisNumber = '601 601 640 606'; -const contactNewName = 'Layla B'; -const renameContact = 'Benjamin D'; -const recoveryKey = '8k171bwe866wd4apxyxtu9e2r92tmcdbgar35ymjx6b4ymhymugy'; - -// * SET LOCALE WE WANT TO TEST HERE -const simulatedLocale = 'en_US'; diff --git a/integration_test/test_bundle.dart b/integration_test/test_bundle.dart index a93e2daf0b..4b5ece9a71 100644 --- a/integration_test/test_bundle.dart +++ b/integration_test/test_bundle.dart @@ -5,7 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/patrol.dart'; // START: GENERATED TEST IMPORTS -import 'features/vpn/vpn_flow_test.dart' as features__vpn__vpn_flow_test; +import 'features/home/homepage_flow_mock_test.dart' as features__home__homepage_flow_mock_test; // END: GENERATED TEST IMPORTS Future main() async { @@ -16,6 +16,6 @@ Future main() async { debugDefaultTargetPlatformOverride; // START: GENERATED TEST GROUPS - group('features.vpn.vpn_flow_test', features__vpn__vpn_flow_test.main); + group('features.home.homepage_flow_mock_test', features__home__homepage_flow_mock_test.main); // END: GENERATED TEST GROUPS } diff --git a/integration_test/utils/test_utils.dart b/integration_test/utils/test_utils.dart index 950df1e687..b3af2d67c2 100644 --- a/integration_test/utils/test_utils.dart +++ b/integration_test/utils/test_utils.dart @@ -1,5 +1,8 @@ import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; +import 'package:lantern/core/utils/common.dart'; +import 'package:lantern/main.dart' as app; +import 'package:patrol/patrol.dart'; export 'package:flutter_test/flutter_test.dart'; export 'package:lantern/core/service/injection_container.dart'; @@ -8,9 +11,61 @@ export 'package:lantern/features/replica/common.dart'; export 'package:mockito/mockito.dart'; export 'package:patrol/patrol.dart'; +export 'package:lantern/core/utils/common.dart' hide Notification, Selector, Verification; + export '../../test/utils/test.mocks.mocks.dart'; +final _patrolTesterConfig = const PatrolTesterConfig(); +final _nativeAutomatorConfig = const NativeAutomatorConfig( + findTimeout: Duration(seconds: 20), +); + TestVariant mobileVariant() { return const TargetPlatformVariant( {TargetPlatform.android, TargetPlatform.iOS}); -} \ No newline at end of file +} +bool shouldSkipNative(){ + if(isMobile()){ + final bool isNative = const String.fromEnvironment('native', defaultValue: 'false') == 'true'; + print("isNative: $isNative"); + print("isNative return value: ${!isNative}"); + return !isNative; + } + return true; +} +bool isNative(){ + return const String.fromEnvironment('native', defaultValue: 'false') == 'true'; +} + +/// Use this function to interact with the native layer. +/// Avoid using it on desktop platforms and most parts of mobile platforms. +/// Should be used only on when VPN turn on/off +void patrolNative( + String description, + Future Function(PatrolIntegrationTester $) callback, { + bool? skip, + List tags = const [], + NativeAutomatorConfig? nativeAutomatorConfig, + LiveTestWidgetsFlutterBindingFramePolicy framePolicy = + LiveTestWidgetsFlutterBindingFramePolicy.fadePointers, +}) { + + /// if we are not running native test then return + if(!isNative()){ + return; + } + patrolTest( + description, + config: _patrolTesterConfig, + nativeAutomatorConfig: nativeAutomatorConfig ?? _nativeAutomatorConfig, + framePolicy: framePolicy, + skip: skip, + callback, + tags: tags, + ); +} + +Future createApp(PatrolIntegrationTester $) async { + await app.main(); + await $.pumpAndSettle(); +} diff --git a/lib/app.dart b/lib/app.dart index 5e87bb295e..eb60ffe153 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -50,8 +50,7 @@ class _LanternAppState extends State void _animateNetworkWarning() { if (isMobile()) { - sessionModel.proxyAvailable - .addListener(toggleConnectivityWarningIfNecessary); + // sessionModel.proxyAvailable.addListener(toggleConnectivityWarningIfNecessary); networkWarningAnimationController = AnimationController( duration: shortAnimationDuration, vsync: this, diff --git a/lib/features/home/home.dart b/lib/features/home/home.dart index b22f4cad86..36a0cffd81 100644 --- a/lib/features/home/home.dart +++ b/lib/features/home/home.dart @@ -100,7 +100,6 @@ class _HomePageState extends State { } final isFirstTime = await sessionModel.isUserFirstTimeVisit(); if (isFirstTime) { - print('User is not a pro user'); context.router.push(const AuthLanding()); sessionModel.setFirstTimeVisit(); if (sessionModel.proUserNotifier.hasListeners) { @@ -153,10 +152,11 @@ class _HomePageState extends State { bool isPlayVersion = (sessionModel.isTestPlayVersion.value ?? false); bool isStoreVersion = (sessionModel.isStoreVersion.value ?? false); - + print("store version: $isStoreVersion"); if ((isStoreVersion || isPlayVersion) && version == 0) { // show privacy disclosure if it's a Play build and the terms have // not already been accepted + print("showing privacy disclosure"); return const PrivacyDisclosure(); } diff --git a/pubspec.lock b/pubspec.lock index bd9dc57bb0..fe383602db 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -1421,10 +1421,10 @@ packages: dependency: "direct dev" description: name: patrol - sha256: "6ac5e239384282da389a6b78d68aa86d8760ad8d5481d665937c4eb586c0b3dc" + sha256: "7d4c57bcf16fa1da7d52449ec63b8c85dbbc9f8e951afa93594634047f56eb3d" url: "https://pub.dev" source: hosted - version: "3.11.0" + version: "3.11.2" patrol_finders: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 6dc5928825..7b70bbb2bb 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -155,7 +155,7 @@ dev_dependencies: analyzer: ^6.7.0 golden_toolkit: ^0.15.0 # Testing - patrol: ^3.11.0 + patrol: ^3.11.2 From fa6111b172e1c2a5336dfca5f20003425186d9d2 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 23 Oct 2024 20:01:19 +0530 Subject: [PATCH 018/163] Update homepage_flow_mock_test.dart --- .../home/homepage_flow_mock_test.dart | 468 +++++++++--------- 1 file changed, 232 insertions(+), 236 deletions(-) diff --git a/integration_test/features/home/homepage_flow_mock_test.dart b/integration_test/features/home/homepage_flow_mock_test.dart index 80e942e18e..32b7746f12 100644 --- a/integration_test/features/home/homepage_flow_mock_test.dart +++ b/integration_test/features/home/homepage_flow_mock_test.dart @@ -28,7 +28,7 @@ void main() { late MockEventManager mockEventManager; late ValueNotifier proxyNotifier; - setUp( + setUpAll( () async { print("setting up mock and sl"); @@ -63,251 +63,247 @@ void main() { sl.registerLazySingleton( () => mockInternetStatusProvider); proxyNotifier = ValueNotifier(true); - - }, + }, ); - - tearDown( + () { + clearInteractions(mockSessionModel); + }, + ); + + tearDownAll( () async { - print("resetting mock and sl"); - resetMockitoState(); - reset(mockSessionModel); await sl.reset(); }, ); - group('home widget with mock', () { - patrolWidgetTest( - 'home widget auth enable show first time visit screen', - ($) async { - when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); - when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); - - /// Stub session model - when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); - - when(mockSessionModel.pathValueNotifier(any, false)) - .thenReturn(ValueNotifier(true)); - - when(mockSessionModel.language(any)).thenAnswer( - (invocation) { - final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, 'en_us', null); - }, - ); - - when(mockSessionModel.acceptedTermsVersion(any)) - .thenAnswer((invocation) { + + patrolWidgetTest( + 'home widget show privacy policy', + ($) async { + print("privacy policy test"); + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + + /// Stub session model + when(mockSessionModel.proxyAvailable).thenReturn(proxyNotifier); + when(mockSessionModel.pathValueNotifier(any, false)) + .thenReturn(ValueNotifier(true)); + + when(mockSessionModel.language(any)).thenAnswer( + (invocation) { + print("language called"); + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_us', null); + }, + ); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion).thenAnswer((realInvocation) { + print("privacy policy "); + return ValueNotifier(true); + }); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(false)); + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + realInvocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "test", null); + }, + ); + + when(mockSessionModel.proUser(any)).thenAnswer( + (realInvocation) { + return boolEmptyBuilder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)).thenAnswer((realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = + realInvocation.positionalArguments[1] as void Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = + realInvocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); - }); - - when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { - final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, true, null); - }, - ); - - when(mockSessionModel.shouldShowAds(any)).thenAnswer( - (invocation) { - final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, "", null); - }, - ); - - when(mockSessionModel.isTestPlayVersion) - .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.proUserNotifier) - .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.isStoreVersion) - .thenAnswer((realInvocation) { - print("auth enable show"); - return ValueNotifier(false); - }); - when(mockSessionModel.isAuthEnabled) - .thenAnswer((realInvocation) => ValueNotifier(true)); - - when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, "", null); - }, - ); - - stubSessionModel( - mockSessionModel: mockSessionModel, - mockBuildContext: mockBuildContext); - - when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)).thenAnswer((realInvocation) { - final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation.positionalArguments[1] as void - Function(Event, Map); - return () { - onNewEvent(event, {}); - }; - }); - - when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, 0, null); - }, - ); - - when(sessionModel.isUserFirstTimeVisit()) - .thenAnswer((realInvocation) => Future.value(true)); - - /// messageing model - when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, null, null); - }, - ); - - stubVpnModel( - mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); - - await $.pumpWidget(const LanternApp()); - await $.pumpAndSettle(); - await $.pump(const Duration(seconds: 2)); - - final signInFinder = $(Button).$('sign_in'.i18n.toUpperCase()); - final lanternProFinder = - $(Button).$('get_lantern_pro'.i18n.toUpperCase()); - - expect($(Button), findsExactly(2)); - expect(lanternProFinder, findsOneWidget); - expect(signInFinder, findsOneWidget); - - when(mockSessionModel.hasUserSignedInNotifier) - .thenReturn(ValueNotifier(false)); - when(mockSessionModel.userEmail).thenReturn(ValueNotifier("")); - - await signInFinder.tap(); - await $.pumpAndSettle(); - - expect($(AuthLanding), findsNothing); - expect($(AppBarProHeader), findsOneWidget); - expect($('sign_in'.i18n), findsOneWidget); - }, - ); - - patrolWidgetTest( - 'home widget show privacy policy', - ($) async { - print("privacy policy test"); - when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); - - /// Stub session model - when(mockSessionModel.proxyAvailable).thenReturn(proxyNotifier); - when(mockSessionModel.pathValueNotifier(any, false)) - .thenReturn(ValueNotifier(true)); - - when(mockSessionModel.language(any)).thenAnswer( - (invocation) { - print("language called"); - final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, 'en_us', null); - }, - ); - - when(mockSessionModel.acceptedTermsVersion(any)) - .thenAnswer((invocation) { + }, + ); + + print("privacy policy pushing"); + await $.pumpWidget(const LanternApp()); + await $.pumpAndSettle(); + + await $.pump(const Duration(seconds: 5)); + await $(FullScreenDialog).waitUntilVisible(timeout: Duration(seconds: 20)); + + + expect($('privacy_disclosure_accept'.i18n.toUpperCase()), findsOneWidget); + expect($(BottomNavigationBar), findsNothing); + }, + ); + + patrolWidgetTest( + 'home widget auth enable show first time visit screen', + ($) async { + when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); + when(mockVPNChangeNotifier.isFlashlightInitialized).thenReturn(true); + + /// Stub session model + when(mockSessionModel.proxyAvailable).thenReturn(ValueNotifier(true)); + + when(mockSessionModel.pathValueNotifier(any, false)) + .thenReturn(ValueNotifier(true)); + + when(mockSessionModel.language(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 'en_us', null); + }, + ); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer((invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, 0, null); + }); + + when(mockSessionModel.developmentMode(any)).thenAnswer( + (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, true, null); + }, + ); + + when(mockSessionModel.shouldShowAds(any)).thenAnswer( + (invocation) { + final builder = + invocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + when(mockSessionModel.isTestPlayVersion) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.proUserNotifier) + .thenAnswer((realInvocation) => ValueNotifier(false)); + when(mockSessionModel.isStoreVersion).thenAnswer((realInvocation) { + print("auth enable show"); + return ValueNotifier(false); + }); + when(mockSessionModel.isAuthEnabled) + .thenAnswer((realInvocation) => ValueNotifier(true)); + + when(mockSessionModel.chatEnabled(any)).thenAnswer( + (realInvocation) { + final builder = + realInvocation.positionalArguments[0] as ValueWidgetBuilder; + return builder(mockBuildContext, false, null); + }, + ); + + when(mockSessionModel.replicaAddr(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, "", null); + }, + ); + + stubSessionModel( + mockSessionModel: mockSessionModel, + mockBuildContext: mockBuildContext); + + when(mockSessionModel.eventManager).thenReturn(mockEventManager); + when(mockEventManager.subscribe(any, any)).thenAnswer((realInvocation) { + final event = realInvocation.positionalArguments[0] as Event; + final onNewEvent = + realInvocation.positionalArguments[1] as void Function(Event, Map); + return () { + onNewEvent(event, {}); + }; + }); + + when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( + (realInvocation) { + final builder = + realInvocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); - }); - - when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { - final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, true, null); - }, - ); - - when(mockSessionModel.isTestPlayVersion) - .thenAnswer((realInvocation) => ValueNotifier(false)); - when(mockSessionModel.isStoreVersion) - .thenAnswer((realInvocation) { - print("privacy policy "); - return ValueNotifier(true); - }); - when(mockSessionModel.isAuthEnabled) - .thenAnswer((realInvocation) => ValueNotifier(false)); - - when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, "test", null); - }, - ); - - when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { - return boolEmptyBuilder(mockBuildContext, false, null); - }, - ); - - when(mockSessionModel.eventManager).thenReturn(mockEventManager); - when(mockEventManager.subscribe(any, any)).thenAnswer((realInvocation) { - final event = realInvocation.positionalArguments[0] as Event; - final onNewEvent = realInvocation.positionalArguments[1] as void - Function(Event, Map); - return () { - onNewEvent(event, {}); - }; - }); - - when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( - (realInvocation) { - final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; - return builder(mockBuildContext, 0, null); - }, - ); - - print("privacy policy pushing"); - await $.pumpWidget(const LanternApp()); - await $.pumpAndSettle(); - - await $.pump(const Duration(seconds: 1)); - - expect($(PrivacyDisclosure).visible, true); - - expect($(Button), findsOneWidget); - expect($(BottomNavigationBar), findsNothing); - }, - ); - }); + }, + ); + + when(sessionModel.isUserFirstTimeVisit()) + .thenAnswer((realInvocation) => Future.value(true)); + + /// messageing model + when(mockMessagingModel.getOnBoardingStatus(any)).thenAnswer( + (realInvocation) { + final builder = realInvocation.positionalArguments[0] + as ValueWidgetBuilder; + return builder(mockBuildContext, null, null); + }, + ); + + stubVpnModel( + mockVpnModel: mockVpnModel, mockBuildContext: mockBuildContext); + + await $.pumpWidget(const LanternApp()); + await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 2)); + + final signInFinder = $(Button).$('sign_in'.i18n.toUpperCase()); + final lanternProFinder = + $(Button).$('get_lantern_pro'.i18n.toUpperCase()); + + expect($(Button), findsExactly(2)); + expect(lanternProFinder, findsOneWidget); + expect(signInFinder, findsOneWidget); + + when(mockSessionModel.hasUserSignedInNotifier) + .thenReturn(ValueNotifier(false)); + when(mockSessionModel.userEmail).thenReturn(ValueNotifier("")); + + await signInFinder.tap(); + await $.pumpAndSettle(); + + expect($(AuthLanding), findsNothing); + expect($(AppBarProHeader), findsOneWidget); + expect($('sign_in'.i18n), findsOneWidget); + }, + ); + } From 63d6a49d3db02d0e7a5a5149f3f841d7a078045d Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 23 Oct 2024 20:01:22 +0530 Subject: [PATCH 019/163] Update .gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index c6e8f7d371..65bd7304ff 100644 --- a/.gitignore +++ b/.gitignore @@ -85,3 +85,5 @@ lib/generated_bindings.dart *.env ExportOptions.plist +integration_test/test_bundle.dart +integration_test/test_bundle.dart From cfe0bee080a1cfbfc16f43b6c6ed031bb931d744 Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Wed, 23 Oct 2024 23:35:33 +0530 Subject: [PATCH 020/163] test improvements. --- integration_test/app_startup_flow_test.dart | 4 ++ .../home/homepage_flow_mock_test.dart | 40 ++++++++----------- .../features/home/homepage_flow_test.dart | 4 +- 3 files changed, 23 insertions(+), 25 deletions(-) diff --git a/integration_test/app_startup_flow_test.dart b/integration_test/app_startup_flow_test.dart index f676c5a8b2..b75e932843 100644 --- a/integration_test/app_startup_flow_test.dart +++ b/integration_test/app_startup_flow_test.dart @@ -18,6 +18,10 @@ import 'package:lantern/main.dart' as app; /// if you do not want not to interact with the native layer, then use patrolWidgetTest /// For running patrolWidgetTest, you need to run the patrol test or flutter test both will work void main() { + tearDownAll(() async { + await sl.reset(); + },); + patrolWidgetTest( "app start up sequence", ($) async { diff --git a/integration_test/features/home/homepage_flow_mock_test.dart b/integration_test/features/home/homepage_flow_mock_test.dart index 32b7746f12..bc9d571e59 100644 --- a/integration_test/features/home/homepage_flow_mock_test.dart +++ b/integration_test/features/home/homepage_flow_mock_test.dart @@ -2,7 +2,6 @@ import 'package:lantern/app.dart'; import 'package:lantern/common/ui/custom/internet_checker.dart'; import 'package:lantern/core/router/router.dart'; import 'package:lantern/core/widgtes/custom_bottom_bar.dart'; -import 'package:lantern/features/account/privacy_disclosure.dart'; import 'package:lantern/features/messaging/messaging_model.dart'; import 'package:lantern/features/vpn/vpn_notifier.dart'; @@ -30,8 +29,6 @@ void main() { setUpAll( () async { - print("setting up mock and sl"); - await Localization.ensureInitialized(); mockSessionModel = MockSessionModel(); @@ -67,7 +64,7 @@ void main() { ); tearDown( - () { + () async { clearInteractions(mockSessionModel); }, ); @@ -78,10 +75,9 @@ void main() { }, ); - patrolWidgetTest( 'home widget show privacy policy', - ($) async { + ($) async { print("privacy policy test"); when(mockBottomBarChangeNotifier.currentIndex).thenReturn(TAB_VPN); @@ -91,24 +87,24 @@ void main() { .thenReturn(ValueNotifier(true)); when(mockSessionModel.language(any)).thenAnswer( - (invocation) { + (invocation) { print("language called"); final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 'en_us', null); }, ); when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer((invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); }); when(mockSessionModel.developmentMode(any)).thenAnswer( - (invocation) { + (invocation) { final builder = - invocation.positionalArguments[0] as ValueWidgetBuilder; + invocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, true, null); }, ); @@ -123,23 +119,23 @@ void main() { .thenAnswer((realInvocation) => ValueNotifier(false)); when(mockSessionModel.chatEnabled(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = - realInvocation.positionalArguments[0] as ValueWidgetBuilder; + realInvocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, false, null); }, ); when(mockSessionModel.replicaAddr(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = realInvocation.positionalArguments[0] - as ValueWidgetBuilder; + as ValueWidgetBuilder; return builder(mockBuildContext, "test", null); }, ); when(mockSessionModel.proUser(any)).thenAnswer( - (realInvocation) { + (realInvocation) { return boolEmptyBuilder(mockBuildContext, false, null); }, ); @@ -148,16 +144,16 @@ void main() { when(mockEventManager.subscribe(any, any)).thenAnswer((realInvocation) { final event = realInvocation.positionalArguments[0] as Event; final onNewEvent = - realInvocation.positionalArguments[1] as void Function(Event, Map); + realInvocation.positionalArguments[1] as void Function(Event, Map); return () { onNewEvent(event, {}); }; }); when(mockSessionModel.acceptedTermsVersion(any)).thenAnswer( - (realInvocation) { + (realInvocation) { final builder = - realInvocation.positionalArguments[0] as ValueWidgetBuilder; + realInvocation.positionalArguments[0] as ValueWidgetBuilder; return builder(mockBuildContext, 0, null); }, ); @@ -167,8 +163,8 @@ void main() { await $.pumpAndSettle(); await $.pump(const Duration(seconds: 5)); - await $(FullScreenDialog).waitUntilVisible(timeout: Duration(seconds: 20)); - + await $(FullScreenDialog) + .waitUntilVisible(timeout: Duration(seconds: 20)); expect($('privacy_disclosure_accept'.i18n.toUpperCase()), findsOneWidget); expect($(BottomNavigationBar), findsNothing); @@ -222,7 +218,6 @@ void main() { when(mockSessionModel.proUserNotifier) .thenAnswer((realInvocation) => ValueNotifier(false)); when(mockSessionModel.isStoreVersion).thenAnswer((realInvocation) { - print("auth enable show"); return ValueNotifier(false); }); when(mockSessionModel.isAuthEnabled) @@ -305,5 +300,4 @@ void main() { expect($('sign_in'.i18n), findsOneWidget); }, ); - } diff --git a/integration_test/features/home/homepage_flow_test.dart b/integration_test/features/home/homepage_flow_test.dart index 720d5452d7..341acbd94b 100644 --- a/integration_test/features/home/homepage_flow_test.dart +++ b/integration_test/features/home/homepage_flow_test.dart @@ -14,8 +14,8 @@ void main() { () {}, ); - tearDown( - () { + tearDownAll( + () async { sl.reset(); }, ); From 9f66bf9637f644d0927e45800c9b1f75b73578eb Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 24 Oct 2024 18:23:21 +0530 Subject: [PATCH 021/163] Implemented account flow test --- Makefile | 18 ++- ...kotlin-compiler-3070100366162913447.salive | 0 assets/locales/en-us.po | 2 + .../features/account/account_flow_test.dart | 103 +++++++++++++++++- .../home/homepage_flow_mock_test.dart | 6 +- integration_test/test_bundle.dart | 4 +- integration_test/utils/test_utils.dart | 50 +++++++-- lib/core/app/app_keys.dart | 10 ++ lib/features/account/account.dart | 7 ++ lib/features/account/report_issue.dart | 2 +- lib/features/account/support.dart | 2 + lib/features/checkout/feature_list.dart | 1 - lib/features/checkout/plans.dart | 2 +- 13 files changed, 186 insertions(+), 21 deletions(-) create mode 100644 android/.kotlin/sessions/kotlin-compiler-3070100366162913447.salive diff --git a/Makefile b/Makefile index 079fb66fe7..9ad0d8553a 100644 --- a/Makefile +++ b/Makefile @@ -716,10 +716,11 @@ clean: # Test environment scripts and other utilities -guard-patrol-cli: - @command -v patrol > /dev/null || (echo "Patrol CLI is not installed. Please install it before running this target." && exit 1) +require-patrol-cli: + @if [[ -z "$(patrol)" ]]; then echo 'Patrol CLI is not installed. Please install it before running this target'; exit 1; fi -nativeTest: guard-patrol-cli + +nativeTest: require-patrol-cli @echo "Running native tests..." patrol test --target integration_test/features/vpn/vpn_flow_test.dart --dart-define native=true --flavor=prod --verbose @@ -727,3 +728,14 @@ appWorkflowTest: @echo "Running all integration tests..." flutter test integration_test/ --flavor=prod +# Target to run tests on a specific file +runTest: + @ARGUMENTS=$(filter-out $@,$(MAKECMDGOALS)); \ + echo "Running tests on: $$ARGUMENTS" && \ + flutter test $$ARGUMENTS --flavor=prod + + +runNativeTest: require-patrol-cli + @ARGUMENTS=$(filter-out $@,$(MAKECMDGOALS)); \ + echo "Running patrol native tests on: $$ARGUMENTS" && \ + patrol test --target $$ARGUMENTS --flavor=prod \ No newline at end of file diff --git a/android/.kotlin/sessions/kotlin-compiler-3070100366162913447.salive b/android/.kotlin/sessions/kotlin-compiler-3070100366162913447.salive new file mode 100644 index 0000000000..e69de29bb2 diff --git a/assets/locales/en-us.po b/assets/locales/en-us.po index a878daf59b..053afe0918 100644 --- a/assets/locales/en-us.po +++ b/assets/locales/en-us.po @@ -1833,5 +1833,7 @@ msgstr "Socks Proxy copied" msgid "proxy_everything" msgstr "Proxy Everything" +msgid "activation_lantern_pro_code" +msgstr "Have a Lantern Pro activation code? Click here." diff --git a/integration_test/features/account/account_flow_test.dart b/integration_test/features/account/account_flow_test.dart index 52794b155c..d454ee13a2 100644 --- a/integration_test/features/account/account_flow_test.dart +++ b/integration_test/features/account/account_flow_test.dart @@ -1,3 +1,104 @@ +import 'package:lantern/features/account/follow_us.dart'; +import 'package:lantern/features/checkout/feature_list.dart'; +import 'package:lantern/features/checkout/plan_details.dart'; + +import '../../utils/test_utils.dart'; + void main() { + group( + "account page end to end", + () { + patrolWidget( + 'render account screen and navigation test [free user]', + ($) async { + await $('Account'.i18n).tap(); + await $.pumpAndSettle(); + await $.pump(const Duration(seconds: 2)); + + expect($(AppKeys.upgrade_lantern_pro).visible, equals(true)); + expect($(AppKeys.inviteFriends).visible, equals(true)); + expect($(AppKeys.devices).visible, equals(true)); + expect($(AppKeys.desktopVersion).visible, equals(true)); + expect($(AppKeys.followUs).visible, equals(true)); + expect($(AppKeys.support).visible, equals(true)); + expect($(AppKeys.setting).visible, equals(true)); + + //check for navigation + await $(AppKeys.upgrade_lantern_pro).tap(); + await $.pumpAndSettle(); + + //plans page + expect($(IconButton).visible, true); + expect($(FullScreenDialog).visible, true); + expect($(PlanCard), findsAtLeast(2)); + expect($(PlanCard).at(0).visible, true); + expect($(PlanCard).at(1).visible, true); + expect($(FeatureList), findsOneWidget); + expect($('activation_lantern_pro_code'.i18n).visible, true); + + // go back + await $(IconButton).tap(); + await $.pumpAndSettle(); + await $(AppKeys.inviteFriends).tap(); + await $.pumpAndSettle(); + + //invite friends page + expect($(ListItemFactory), findsOneWidget); + expect($('share_lantern_pro'.i18n).visible, equals(true)); + expect($('share_referral_code'.i18n.toUpperCase()).visible, equals(true)); + expect($(Button).visible, equals(true)); + + await $(IconButton).tap(); + + // approve device page + await $(AppKeys.devices).tap(); + await $.pumpAndSettle(); + expect($(Button), findsExactly(2)); + expect($('Link with PIN'.i18n.toUpperCase()).visible, true); + expect($('Link via Email'.i18n.toUpperCase()).visible, true); + + await $(IconButton).tap(); + + // desktop version + await $(AppKeys.desktopVersion).tap(); + await $.pumpAndSettle(); + expect($(Button).visible, true); + expect($(CAssetImage).visible, true); + expect($('most_recent_lantern_apps'.i18n), findsOneWidget); + expect($('most_recent_lantern_apps'.i18n).visible, true); + expect($('share_link'.i18n.toUpperCase()).visible, true); + + await $(IconButton).tap(); + + // follow us + await $(AppKeys.followUs).tap(); + await $.pumpAndSettle(); + expect($(FollowUs).visible, true); + await $.tester.tapAt(const Offset(10, 10)); + await $.pumpAndSettle(); + + //support + await $(AppKeys.support).tap(); + await $.pumpAndSettle(); + + expect($(AppKeys.reportIssue).visible, true); + expect($(AppKeys.userForum).visible, true); + expect($(AppKeys.faq).visible, true); + + //report issue + await $(AppKeys.reportIssue).tap(); + await $.pumpAndSettle(); -} \ No newline at end of file + expect($(CTextField).at(0).visible, true); + expect($(CTextField).at(1).visible, true); + expect($(CTextField), findsExactly(2)); + expect($('email'.i18n), findsOneWidget); + expect($('select_an_issue'.i18n), findsOneWidget); + expect($(DropdownButtonFormField), findsOneWidget); + expect($(DropdownButtonFormField).visible, true); + expect($('send_report'.i18n.toUpperCase()).visible, true); + }, + ); + }, + ); +} diff --git a/integration_test/features/home/homepage_flow_mock_test.dart b/integration_test/features/home/homepage_flow_mock_test.dart index bc9d571e59..7ff138bf21 100644 --- a/integration_test/features/home/homepage_flow_mock_test.dart +++ b/integration_test/features/home/homepage_flow_mock_test.dart @@ -66,6 +66,8 @@ void main() { tearDown( () async { clearInteractions(mockSessionModel); + clearInteractions(mockReplicaModel); + clearInteractions(mockVpnModel); }, ); @@ -130,7 +132,7 @@ void main() { (realInvocation) { final builder = realInvocation.positionalArguments[0] as ValueWidgetBuilder; - return builder(mockBuildContext, "test", null); + return builder(mockBuildContext, "", null); }, ); @@ -261,7 +263,7 @@ void main() { }, ); - when(sessionModel.isUserFirstTimeVisit()) + when(mockSessionModel.isUserFirstTimeVisit()) .thenAnswer((realInvocation) => Future.value(true)); /// messageing model diff --git a/integration_test/test_bundle.dart b/integration_test/test_bundle.dart index 4b5ece9a71..bad43f8612 100644 --- a/integration_test/test_bundle.dart +++ b/integration_test/test_bundle.dart @@ -5,7 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:patrol/patrol.dart'; // START: GENERATED TEST IMPORTS -import 'features/home/homepage_flow_mock_test.dart' as features__home__homepage_flow_mock_test; +import 'features/account/account_flow_test.dart' as features__account__account_flow_test; // END: GENERATED TEST IMPORTS Future main() async { @@ -16,6 +16,6 @@ Future main() async { debugDefaultTargetPlatformOverride; // START: GENERATED TEST GROUPS - group('features.home.homepage_flow_mock_test', features__home__homepage_flow_mock_test.main); + group('features.account.account_flow_test', features__account__account_flow_test.main); // END: GENERATED TEST GROUPS } diff --git a/integration_test/utils/test_utils.dart b/integration_test/utils/test_utils.dart index b3af2d67c2..4331ad7e14 100644 --- a/integration_test/utils/test_utils.dart +++ b/integration_test/utils/test_utils.dart @@ -1,18 +1,18 @@ -import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:lantern/core/utils/common.dart'; import 'package:lantern/main.dart' as app; import 'package:patrol/patrol.dart'; +import 'package:patrol_finders/src/custom_finders/patrol_tester.dart'; export 'package:flutter_test/flutter_test.dart'; export 'package:lantern/core/service/injection_container.dart'; +export 'package:lantern/core/utils/common.dart' + hide Notification, Selector, Verification; export 'package:lantern/features/home/session_model.dart'; export 'package:lantern/features/replica/common.dart'; export 'package:mockito/mockito.dart'; export 'package:patrol/patrol.dart'; -export 'package:lantern/core/utils/common.dart' hide Notification, Selector, Verification; - export '../../test/utils/test.mocks.mocks.dart'; final _patrolTesterConfig = const PatrolTesterConfig(); @@ -24,17 +24,21 @@ TestVariant mobileVariant() { return const TargetPlatformVariant( {TargetPlatform.android, TargetPlatform.iOS}); } -bool shouldSkipNative(){ - if(isMobile()){ - final bool isNative = const String.fromEnvironment('native', defaultValue: 'false') == 'true'; + +bool shouldSkipNative() { + if (isMobile()) { + final bool isNative = + const String.fromEnvironment('native', defaultValue: 'false') == 'true'; print("isNative: $isNative"); print("isNative return value: ${!isNative}"); return !isNative; } return true; } -bool isNative(){ - return const String.fromEnvironment('native', defaultValue: 'false') == 'true'; + +bool isNative() { + return const String.fromEnvironment('native', defaultValue: 'false') == + 'true'; } /// Use this function to interact with the native layer. @@ -49,9 +53,8 @@ void patrolNative( LiveTestWidgetsFlutterBindingFramePolicy framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fadePointers, }) { - /// if we are not running native test then return - if(!isNative()){ + if (!isNative()) { return; } patrolTest( @@ -69,3 +72,30 @@ Future createApp(PatrolIntegrationTester $) async { await app.main(); await $.pumpAndSettle(); } + +/// Use this function to interact with the widget layer. +/// Should be used on desktop platforms and most parts of mobile platforms. +/// Should be used only when testing the UI. +/// Avoid using it on when VPN turn on/off for mobile since it needs to interact with the native layer. +/// App is already created in this function. +void patrolWidget( + String description, Future Function(PatrolTester $) callback, + {bool? skip, + List tags = const [], + NativeAutomatorConfig? nativeAutomatorConfig}) { + patrolWidgetTest( + description, + config: _patrolTesterConfig, + skip: skip, + ($) async { + await createWidgetApp($); + await callback($); + }, + tags: tags, + ); +} + +Future createWidgetApp(PatrolTester $) async { + await app.main(); + await $.pumpAndSettle(); +} diff --git a/lib/core/app/app_keys.dart b/lib/core/app/app_keys.dart index 2b593676cc..238d9a1c30 100644 --- a/lib/core/app/app_keys.dart +++ b/lib/core/app/app_keys.dart @@ -11,6 +11,14 @@ class AppKeys { //Account Widget static const upgrade_lantern_pro = ValueKey('upgrade_lantern_pro'); + static const inviteFriends = ValueKey('invite_friends'); + static const devices = ValueKey('devices'); + static const signIn = ValueKey('sign_in'); + static const desktopVersion = ValueKey('desktop_version'); + static const followUs = ValueKey('follow_us'); + static const setting = ValueKey('setting'); + static const signOut = ValueKey('sign_out'); + static const account_management = ValueKey('account_management'); static const account_renew = ValueKey('account_renew'); static const support = ValueKey('support'); @@ -29,6 +37,8 @@ class AppKeys { //Support static const reportIssue = ValueKey('report_issue'); + static const userForum = ValueKey('lantern_user_forum'); + static const faq = ValueKey('faq'); static const reportDescription = 'report_description'; static const sendReport = 'send_report'; } diff --git a/lib/features/account/account.dart b/lib/features/account/account.dart index 5a22bbad57..35fc365095 100644 --- a/lib/features/account/account.dart +++ b/lib/features/account/account.dart @@ -92,6 +92,7 @@ class _AccountMenuState extends State { return [ if (authEnabled && !hasUserLoggedIn) ListItemFactory.settingsItem( + key: AppKeys.signIn, icon: ImagePaths.signIn, content: 'sign_in'.i18n, onTap: () => openSignIn(context), @@ -129,6 +130,7 @@ class _AccountMenuState extends State { }, ), ListItemFactory.settingsItem( + key: AppKeys.inviteFriends, icon: ImagePaths.star, content: 'Invite Friends'.i18n, onTap: () { @@ -136,6 +138,7 @@ class _AccountMenuState extends State { }, ), ListItemFactory.settingsItem( + key: AppKeys.devices, icon: ImagePaths.devices, content: 'Authorize Device for Pro'.i18n, onTap: () => authorizeDeviceForPro(context), @@ -186,6 +189,7 @@ class _AccountMenuState extends State { return [ if (isMobile()) ListItemFactory.settingsItem( + key: AppKeys.desktopVersion, icon: ImagePaths.desktop, content: 'desktop_version'.i18n, onTap: () { @@ -193,6 +197,7 @@ class _AccountMenuState extends State { }, ), ListItemFactory.settingsItem( + key: AppKeys.followUs, icon: ImagePaths.thumbUp, content: 'follow_us'.i18n, onTap: () { @@ -208,6 +213,7 @@ class _AccountMenuState extends State { }, ), ListItemFactory.settingsItem( + key: AppKeys.setting, icon: ImagePaths.settings, content: 'settings'.i18n, onTap: () { @@ -216,6 +222,7 @@ class _AccountMenuState extends State { ), if (authEnabled && hasUserLoggedIn) ListItemFactory.settingsItem( + key: AppKeys.signOut, icon: ImagePaths.signOut, content: 'sign_out'.i18n, onTap: () => showSingOutDialog(context), diff --git a/lib/features/account/report_issue.dart b/lib/features/account/report_issue.dart index a44d803806..5d3d191a95 100644 --- a/lib/features/account/report_issue.dart +++ b/lib/features/account/report_issue.dart @@ -91,7 +91,7 @@ class _ReportIssueState extends State { ), child: Form( key: issueFieldKey, - child: DropdownButtonFormField( + child: DropdownButtonFormField( decoration: InputDecoration( border: OutlineInputBorder( borderSide: BorderSide( diff --git a/lib/features/account/support.dart b/lib/features/account/support.dart index d73864751f..71f488f7d6 100644 --- a/lib/features/account/support.dart +++ b/lib/features/account/support.dart @@ -34,6 +34,7 @@ class Support extends StatelessWidget { }, ), ListItemFactory.settingsItem( + key: AppKeys.userForum, content: 'lantern_user_forum'.i18n, icon: ImagePaths.forum, trailingArray: [ @@ -50,6 +51,7 @@ class Support extends StatelessWidget { onTap: () => forumTap(context), ), ListItemFactory.settingsItem( + key: AppKeys.faq, content: 'faq'.i18n, icon: ImagePaths.info, trailingArray: [ diff --git a/lib/features/checkout/feature_list.dart b/lib/features/checkout/feature_list.dart index 4aaf2d12fb..aba380dedd 100644 --- a/lib/features/checkout/feature_list.dart +++ b/lib/features/checkout/feature_list.dart @@ -1,5 +1,4 @@ - import '../../core/utils/common.dart'; class FeatureList extends StatelessWidget { diff --git a/lib/features/checkout/plans.dart b/lib/features/checkout/plans.dart index 79832f7c15..9fe0c91846 100644 --- a/lib/features/checkout/plans.dart +++ b/lib/features/checkout/plans.dart @@ -174,7 +174,7 @@ class PlansPage extends StatelessWidget { child: GestureDetector( onTap: () => _onPromoCodeTap(context, proUser), child: Text( - 'Have a Lantern Pro activation code? Click here.', + 'activation_lantern_pro_code'.i18n, style: tsBody1.copiedWith(color: grey5), ), ), // Translations From 1c07351a51e595dbb4e4045de7d2dc08c60dea6b Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Thu, 24 Oct 2024 18:43:03 +0530 Subject: [PATCH 022/163] Updated account flow test cases to support desktop --- Makefile | 32 ++++++++++++++++--- .../features/account/account_flow_test.dart | 29 +++++++++-------- 2 files changed, 44 insertions(+), 17 deletions(-) diff --git a/Makefile b/Makefile index 9ad0d8553a..77c318ca4e 100644 --- a/Makefile +++ b/Makefile @@ -719,23 +719,47 @@ clean: require-patrol-cli: @if [[ -z "$(patrol)" ]]; then echo 'Patrol CLI is not installed. Please install it before running this target'; exit 1; fi +# Application test cases +# Run all native tests nativeTest: require-patrol-cli @echo "Running native tests..." patrol test --target integration_test/features/vpn/vpn_flow_test.dart --dart-define native=true --flavor=prod --verbose +# Run specific native tests + +runNativeTest: require-patrol-cli + @ARGUMENTS=$(filter-out $@,$(MAKECMDGOALS)); \ + echo "Running patrol native tests on: $$ARGUMENTS" && \ + patrol test --target $$ARGUMENTS --flavor=prod + + +#Runs all integration tests appWorkflowTest: @echo "Running all integration tests..." flutter test integration_test/ --flavor=prod -# Target to run tests on a specific file + +# Run specific integration tests +# Usage: make run-specific-integration-tests integration_test/app_startup_flow_test.dart runTest: @ARGUMENTS=$(filter-out $@,$(MAKECMDGOALS)); \ echo "Running tests on: $$ARGUMENTS" && \ flutter test $$ARGUMENTS --flavor=prod -runNativeTest: require-patrol-cli +#------- Desktop test and utils ------------ + +# Run all workflow tests on desktop +desktopWorkflowTest: + @echo "Running all integration tests..." + flutter test integration_test/ + + +# Run specific tests on desktop +runDesktopTest: @ARGUMENTS=$(filter-out $@,$(MAKECMDGOALS)); \ - echo "Running patrol native tests on: $$ARGUMENTS" && \ - patrol test --target $$ARGUMENTS --flavor=prod \ No newline at end of file + echo "Running tests on: $$ARGUMENTS" && \ + flutter test $$ARGUMENTS -d macOS + + diff --git a/integration_test/features/account/account_flow_test.dart b/integration_test/features/account/account_flow_test.dart index d454ee13a2..ee2e1f659f 100644 --- a/integration_test/features/account/account_flow_test.dart +++ b/integration_test/features/account/account_flow_test.dart @@ -18,7 +18,9 @@ void main() { expect($(AppKeys.upgrade_lantern_pro).visible, equals(true)); expect($(AppKeys.inviteFriends).visible, equals(true)); expect($(AppKeys.devices).visible, equals(true)); - expect($(AppKeys.desktopVersion).visible, equals(true)); + if (isMobile()) { + expect($(AppKeys.desktopVersion).visible, equals(true)); + } expect($(AppKeys.followUs).visible, equals(true)); expect($(AppKeys.support).visible, equals(true)); expect($(AppKeys.setting).visible, equals(true)); @@ -45,7 +47,8 @@ void main() { //invite friends page expect($(ListItemFactory), findsOneWidget); expect($('share_lantern_pro'.i18n).visible, equals(true)); - expect($('share_referral_code'.i18n.toUpperCase()).visible, equals(true)); + expect($('share_referral_code'.i18n.toUpperCase()).visible, + equals(true)); expect($(Button).visible, equals(true)); await $(IconButton).tap(); @@ -59,16 +62,18 @@ void main() { await $(IconButton).tap(); - // desktop version - await $(AppKeys.desktopVersion).tap(); - await $.pumpAndSettle(); - expect($(Button).visible, true); - expect($(CAssetImage).visible, true); - expect($('most_recent_lantern_apps'.i18n), findsOneWidget); - expect($('most_recent_lantern_apps'.i18n).visible, true); - expect($('share_link'.i18n.toUpperCase()).visible, true); + if (isMobile()) { + // desktop version + await $(AppKeys.desktopVersion).tap(); + await $.pumpAndSettle(); + expect($(Button).visible, true); + expect($(CAssetImage).visible, true); + expect($('most_recent_lantern_apps'.i18n), findsOneWidget); + expect($('most_recent_lantern_apps'.i18n).visible, true); + expect($('share_link'.i18n.toUpperCase()).visible, true); - await $(IconButton).tap(); + await $(IconButton).tap(); + } // follow us await $(AppKeys.followUs).tap(); @@ -92,8 +97,6 @@ void main() { expect($(CTextField).at(0).visible, true); expect($(CTextField).at(1).visible, true); expect($(CTextField), findsExactly(2)); - expect($('email'.i18n), findsOneWidget); - expect($('select_an_issue'.i18n), findsOneWidget); expect($(DropdownButtonFormField), findsOneWidget); expect($(DropdownButtonFormField).visible, true); expect($('send_report'.i18n.toUpperCase()).visible, true); From cc61496ed3a9e298745d8a2772a893b0aa8100ab Mon Sep 17 00:00:00 2001 From: Jigar-f Date: Fri, 25 Oct 2024 16:34:36 +0530 Subject: [PATCH 023/163] Setting flow, report issue flow test cases. --- .../account/report_issue_flow_test.dart | 43 +++++++++ .../features/account/setting_flow_test.dart | 95 +++++++++++++++++++ integration_test/test_bundle.dart | 4 +- integration_test/utils/test_utils.dart | 22 +++-- lib/common/ui/custom/list_item_factory.dart | 2 + lib/core/app/app_keys.dart | 15 +++ lib/core/widgtes/version_footer.dart | 50 ++++++++++ lib/features/account/proxies_settings.dart | 2 + lib/features/account/settings.dart | 69 +++----------- lib/features/account/split_tunneling.dart | 3 +- test/utils/stubs.dart | 70 ++++++++++++++ test/utils/test_common.dart | 74 +-------------- 12 files changed, 310 insertions(+), 139 deletions(-) create mode 100644 integration_test/features/account/report_issue_flow_test.dart create mode 100644 integration_test/features/account/setting_flow_test.dart create mode 100644 lib/core/widgtes/version_footer.dart create mode 100644 test/utils/stubs.dart diff --git a/integration_test/features/account/report_issue_flow_test.dart b/integration_test/features/account/report_issue_flow_test.dart new file mode 100644 index 0000000000..43c7fd8b8b --- /dev/null +++ b/integration_test/features/account/report_issue_flow_test.dart @@ -0,0 +1,43 @@ +import '../../utils/test_utils.dart'; + +void main() { + patrolWidget( + "report issue end to end test", + ($) async { + await $('Account'.i18n).tap(); + await $.pumpAndSettle(); + await $(AppKeys.support).tap(); + await $.pumpAndSettle(); + await $(AppKeys.reportIssue).tap(); + await $.pumpAndSettle(); + + expect($(CTextField), findsExactly(2)); + expect($(DropdownButtonFormField), findsOneWidget); + expect($('send_report'.i18n.toUpperCase()), findsOneWidget); + expect($(Button), findsOneWidget); + + final email = $.tester.widget($(CTextField).at(0)); + final desc = $.tester.widget($(CTextField).at(1)); + final sendReport = $.tester.widget