Преглед изворни кода

Made a working integration test from login to scrolling home gallery + optimized image for thumbnail

ashilkn пре 2 година
родитељ
комит
3648444e58

+ 99 - 0
integration_test/app_test.dart

@@ -0,0 +1,99 @@
+import "package:flutter/material.dart";
+import "package:flutter_test/flutter_test.dart";
+import "package:integration_test/integration_test.dart";
+import 'package:photos/main.dart' as app;
+import "package:scrollable_positioned_list/scrollable_positioned_list.dart";
+
+void main() {
+  group("App test", () {
+    final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized();
+    // binding.framePolicy = LiveTestWidgetsFlutterBindingFramePolicy.fullyLive;
+    testWidgets("Demo test", (tester) async {
+      app.main();
+      try {
+        await tester.pumpAndSettle(const Duration(seconds: 5));
+
+        await dismissUpdateAppDialog(tester);
+
+        //Click the sign in button on the landing page
+        final signInButton = find.byKey(const ValueKey("signInButton"));
+        await tester.tap(signInButton);
+        await tester.pumpAndSettle();
+
+        //Enter the email address and click the login button
+        final emailInputField = find.byKey(const ValueKey("emailInputField"));
+        final logInButton = find.byKey(const ValueKey("logInButton"));
+        await tester.enterText(emailInputField, "enter email here");
+        await tester.pumpAndSettle(const Duration(seconds: 3));
+        await findAndTapFAB(tester, logInButton);
+
+        //Enter OTT and click the verify button
+        final ottVerificationInputField =
+            find.byKey(const ValueKey("ottVerificationInputField"));
+        final verifyOttButton = find.byKey(const ValueKey("verifyOttButton"));
+        await tester.tap(ottVerificationInputField);
+        await tester.pumpAndSettle();
+        await tester.enterText(ottVerificationInputField, "enter otp here");
+        await tester.pumpAndSettle();
+        await findAndTapFAB(tester, verifyOttButton);
+
+        //Enter password and click the verify button
+        final passwordInputField =
+            find.byKey(const ValueKey("passwordInputField"));
+        final verifyPasswordButton =
+            find.byKey(const ValueKey("verifyPasswordButton"));
+        await tester.enterText(passwordInputField, "ente password here");
+        await tester.pumpAndSettle();
+        await findAndTapFAB(tester, verifyPasswordButton);
+
+        await tester.pumpAndSettle(const Duration(seconds: 2));
+        await dismissUpdateAppDialog(tester);
+
+        //Grant permission to access photos
+        final grantPermissionButton =
+            find.byKey(const ValueKey("grantPermissionButton"));
+        await tester.tap(grantPermissionButton);
+        await tester.pumpAndSettle(const Duration(seconds: 1));
+
+        //Manually grant permission to access photos within 3 seconds
+        await tester.pumpAndSettle(const Duration(seconds: 3));
+
+        //Skip backup
+        final skipBackupButton = find.byKey(const ValueKey("skipBackupButton"));
+        await tester.tap(skipBackupButton);
+        await tester.pumpAndSettle(const Duration(seconds: 3));
+
+        //scroll gallery
+        final scrollablePositionedList = find.byType(ScrollablePositionedList);
+        await tester.fling(
+          scrollablePositionedList,
+          const Offset(0, -4000),
+          4000,
+        );
+        await tester.pumpAndSettle();
+        await tester.fling(
+          scrollablePositionedList,
+          const Offset(0, 4000),
+          4000,
+        );
+        await tester.pumpAndSettle();
+      } catch (e) {
+        print("\n$e\n");
+      }
+    });
+  });
+}
+
+Future<void> findAndTapFAB(WidgetTester tester, Finder finder) async {
+  final RenderBox box = tester.renderObject(finder);
+  final Offset desiredOffset = Offset(box.size.width - 10, box.size.height / 2);
+  // Calculate the global position of the desired offset within the widget.
+  final Offset globalPosition = box.localToGlobal(desiredOffset);
+  await tester.tapAt(globalPosition);
+  await tester.pumpAndSettle(const Duration(seconds: 3));
+}
+
+Future<void> dismissUpdateAppDialog(WidgetTester tester) async {
+  await tester.tapAt(const Offset(0, 0));
+  await tester.pumpAndSettle();
+}

+ 6 - 0
ios/Podfile.lock

@@ -99,6 +99,8 @@ PODS:
   - in_app_purchase_storekit (0.0.1):
     - Flutter
     - FlutterMacOS
+  - integration_test (0.0.1):
+    - Flutter
   - libwebp (1.2.4):
     - libwebp/demux (= 1.2.4)
     - libwebp/mux (= 1.2.4)
@@ -195,6 +197,7 @@ DEPENDENCIES:
   - fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
   - image_editor_common (from `.symlinks/plugins/image_editor_common/ios`)
   - in_app_purchase_storekit (from `.symlinks/plugins/in_app_purchase_storekit/ios`)
+  - integration_test (from `.symlinks/plugins/integration_test/ios`)
   - local_auth_ios (from `.symlinks/plugins/local_auth_ios/ios`)
   - media_extension (from `.symlinks/plugins/media_extension/ios`)
   - motionphoto (from `.symlinks/plugins/motionphoto/ios`)
@@ -276,6 +279,8 @@ EXTERNAL SOURCES:
     :path: ".symlinks/plugins/image_editor_common/ios"
   in_app_purchase_storekit:
     :path: ".symlinks/plugins/in_app_purchase_storekit/ios"
+  integration_test:
+    :path: ".symlinks/plugins/integration_test/ios"
   local_auth_ios:
     :path: ".symlinks/plugins/local_auth_ios/ios"
   media_extension:
@@ -345,6 +350,7 @@ SPEC CHECKSUMS:
   GoogleUtilities: c2bdc4cf2ce786c4d2e6b3bcfd599a25ca78f06f
   image_editor_common: d6f6644ae4a6de80481e89fe6d0a8c49e30b4b43
   in_app_purchase_storekit: 6b297e2b5eab9fa3251a492d57301722e4132a71
+  integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5
   libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef
   local_auth_ios: c6cf091ded637a88f24f86a8875d8b0f526e2605
   Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d

+ 2 - 0
ios/Runner.xcodeproj/project.pbxproj

@@ -289,6 +289,7 @@
 				"${BUILT_PRODUCTS_DIR}/fluttertoast/fluttertoast.framework",
 				"${BUILT_PRODUCTS_DIR}/image_editor_common/image_editor_common.framework",
 				"${BUILT_PRODUCTS_DIR}/in_app_purchase_storekit/in_app_purchase_storekit.framework",
+				"${BUILT_PRODUCTS_DIR}/integration_test/integration_test.framework",
 				"${BUILT_PRODUCTS_DIR}/libwebp/libwebp.framework",
 				"${BUILT_PRODUCTS_DIR}/local_auth_ios/local_auth_ios.framework",
 				"${BUILT_PRODUCTS_DIR}/media_extension/media_extension.framework",
@@ -345,6 +346,7 @@
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/fluttertoast.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/image_editor_common.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/in_app_purchase_storekit.framework",
+				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/integration_test.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/libwebp.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/local_auth_ios.framework",
 				"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/media_extension.framework",

+ 4 - 2
lib/ui/account/login_page.dart

@@ -53,6 +53,7 @@ class _LoginPageState extends State<LoginPage> {
       ),
       body: _getBody(),
       floatingActionButton: DynamicFAB(
+        key: const ValueKey("logInButton"),
         isKeypadOpen: isKeypadOpen,
         isFormValid: _emailIsValid,
         buttonText: S.of(context).logInLabel,
@@ -87,6 +88,7 @@ class _LoginPageState extends State<LoginPage> {
                 Padding(
                   padding: const EdgeInsets.fromLTRB(20, 24, 20, 0),
                   child: TextFormField(
+                    key: const ValueKey("emailInputField"),
                     autofillHints: const [AutofillHints.email],
                     decoration: InputDecoration(
                       fillColor: _emailInputFieldColor,
@@ -186,9 +188,9 @@ class _LoginPageState extends State<LoginPage> {
                           },
                         ),
                       ),
-                      Expanded(
+                      const Expanded(
                         flex: 1,
-                        child: Container(),
+                        child: SizedBox.shrink(),
                       )
                     ],
                   ),

+ 2 - 0
lib/ui/account/ott_verification_page.dart

@@ -64,6 +64,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
       ),
       body: _getBody(),
       floatingActionButton: DynamicFAB(
+        key: const ValueKey("verifyOttButton"),
         isKeypadOpen: isKeypadOpen,
         isFormValid: _verificationCodeController.text.isNotEmpty,
         buttonText: S.of(context).verify,
@@ -146,6 +147,7 @@ class _OTTVerificationPageState extends State<OTTVerificationPage> {
             Padding(
               padding: const EdgeInsets.fromLTRB(20, 16, 20, 16),
               child: TextFormField(
+                key: const ValueKey("ottVerificationInputField"),
                 style: Theme.of(context).textTheme.subtitle1,
                 decoration: InputDecoration(
                   filled: true,

+ 2 - 0
lib/ui/account/password_reentry_page.dart

@@ -66,6 +66,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
       ),
       body: _getBody(),
       floatingActionButton: DynamicFAB(
+        key: const ValueKey("verifyPasswordButton"),
         isKeypadOpen: isKeypadOpen,
         isFormValid: _passwordController.text.isNotEmpty,
         buttonText: S.of(context).verifyPassword,
@@ -169,6 +170,7 @@ class _PasswordReentryPageState extends State<PasswordReentryPage> {
                 Padding(
                   padding: const EdgeInsets.fromLTRB(20, 24, 20, 0),
                   child: TextFormField(
+                    key: const ValueKey("passwordInputField"),
                     autofillHints: const [AutofillHints.password],
                     decoration: InputDecoration(
                       hintText: S.of(context).enterYourPassword,

+ 1 - 0
lib/ui/backup_folder_selection_page.dart

@@ -185,6 +185,7 @@ class _BackupFolderSelectionPageState extends State<BackupFolderSelectionPage> {
                         bottom: Platform.isIOS ? 48 : 32,
                       ),
                       child: GestureDetector(
+                        key: const ValueKey("skipBackupButton"),
                         onTap: () {
                           Navigator.of(context).pop();
                         },

+ 1 - 0
lib/ui/home/grant_permissions_widget.dart

@@ -92,6 +92,7 @@ class GrantPermissionsWidget extends StatelessWidget {
           bottom: 16,
         ),
         child: OutlinedButton(
+          key: const ValueKey("grantPermissionButton"),
           child: Text(S.of(context).grantPermission),
           onPressed: () async {
             final state = await PhotoManager.requestPermissionExtend();

+ 1 - 0
lib/ui/home/landing_page_widget.dart

@@ -116,6 +116,7 @@ class _LandingPageWidgetState extends State<LandingPageWidget> {
               child: Hero(
                 tag: "log_in",
                 child: ElevatedButton(
+                  key: const ValueKey("signInButton"),
                   style:
                       Theme.of(context).colorScheme.optionalActionButtonStyle,
                   onPressed: _navigateToSignInPage,

+ 1 - 0
lib/ui/settings/app_update_dialog.dart

@@ -112,6 +112,7 @@ class _AppUpdateDialogState extends State<AppUpdateDialog> {
     return WillPopScope(
       onWillPop: () async => !shouldForceUpdate,
       child: AlertDialog(
+        key: const ValueKey("updateAppDialog"),
         title: Column(
           crossAxisAlignment: CrossAxisAlignment.start,
           children: [

+ 21 - 1
lib/ui/viewer/file/thumbnail_widget.dart

@@ -58,9 +58,12 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
   bool _isLoadingRemoteThumbnail = false;
   bool _errorLoadingRemoteThumbnail = false;
   ImageProvider? _imageProvider;
+  int? optimizedImageHeight;
+  int? optimizedImageWidth;
 
   @override
   void initState() {
+    changeSmallestSideToThumbnailSize();
     super.initState();
   }
 
@@ -83,6 +86,17 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
     }
   }
 
+  void changeSmallestSideToThumbnailSize() {
+    if (widget.file!.width == 0 || widget.file!.height == 0) {
+      return;
+    }
+    if (widget.file!.width < widget.file!.height) {
+      optimizedImageWidth = widget.thumbnailSize;
+    } else {
+      optimizedImageHeight = widget.thumbnailSize;
+    }
+  }
+
   @override
   Widget build(BuildContext context) {
     if (widget.file!.isRemoteFile) {
@@ -93,7 +107,13 @@ class _ThumbnailWidgetState extends State<ThumbnailWidget> {
     Widget? image;
     if (_imageProvider != null) {
       image = Image(
-        image: _imageProvider!,
+        image: optimizedImageHeight != null || optimizedImageWidth != null
+            ? ResizeImage(
+                _imageProvider!,
+                width: optimizedImageWidth,
+                height: optimizedImageHeight,
+              )
+            : _imageProvider!,
         fit: widget.fit,
       );
     }

+ 33 - 2
pubspec.lock

@@ -53,10 +53,10 @@ packages:
     dependency: "direct main"
     description:
       name: archive
-      sha256: d6347d54a2d8028e0437e3c099f66fdb8ae02c4720c1e7534c9f24c10351f85d
+      sha256: "80e5141fafcb3361653ce308776cfd7d45e6e9fbb429e14eec571382c0c5fecb"
       url: "https://pub.dev"
     source: hosted
-    version: "3.3.6"
+    version: "3.3.2"
   args:
     dependency: transitive
     description:
@@ -614,6 +614,11 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "1.5.1"
+  flutter_driver:
+    dependency: "direct dev"
+    description: flutter
+    source: sdk
+    version: "0.0.0"
   flutter_easyloading:
     dependency: "direct main"
     description:
@@ -877,6 +882,11 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "3.2.0"
+  fuchsia_remote_debug_protocol:
+    dependency: transitive
+    description: flutter
+    source: sdk
+    version: "0.0.0"
   glob:
     dependency: transitive
     description:
@@ -1013,6 +1023,11 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "0.3.5+2"
+  integration_test:
+    dependency: "direct dev"
+    description: flutter
+    source: sdk
+    version: "0.0.0"
   intl:
     dependency: "direct main"
     description:
@@ -1804,6 +1819,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "7.0.0"
+  sync_http:
+    dependency: transitive
+    description:
+      name: sync_http
+      sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
+      url: "https://pub.dev"
+    source: hosted
+    version: "0.3.1"
   syncfusion_flutter_core:
     dependency: "direct main"
     description:
@@ -2130,6 +2153,14 @@ packages:
       url: "https://pub.dev"
     source: hosted
     version: "2.3.0"
+  webdriver:
+    dependency: transitive
+    description:
+      name: webdriver
+      sha256: ef67178f0cc7e32c1494645b11639dd1335f1d18814aa8435113a92e9ef9d841
+      url: "https://pub.dev"
+    source: hosted
+    version: "3.0.1"
   webkit_inspection_protocol:
     dependency: transitive
     description:

+ 4 - 0
pubspec.yaml

@@ -144,8 +144,12 @@ dev_dependencies:
   flutter_test:
     sdk: flutter
   freezed: ^2.3.2
+  integration_test:
+    sdk: flutter
   json_serializable: ^6.6.1
   test:
+  flutter_driver:
+    sdk: flutter
 
 flutter_icons:
   android: "launcher_icon"