You've already forked immich
							
							
				mirror of
				https://github.com/immich-app/immich.git
				synced 2025-10-31 00:18:28 +02:00 
			
		
		
		
	test(app): fix integration test and improve reliability and speed (#1792)
This commit is contained in:
		
							
								
								
									
										62
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										62
									
								
								.github/workflows/test.yml
									
									
									
									
										vendored
									
									
								
							| @@ -86,8 +86,9 @@ jobs: | ||||
|       - uses: actions/checkout@v3 | ||||
|       - uses: actions/setup-java@v3 | ||||
|         with: | ||||
|           distribution: 'adopt' | ||||
|           java-version: '11' | ||||
|           distribution: 'zulu' | ||||
|           java-version: '12.x' | ||||
|           cache: 'gradle' | ||||
|       - name: Cache android SDK | ||||
|         uses: actions/cache@v3 | ||||
|         id: android-sdk | ||||
| @@ -96,24 +97,59 @@ jobs: | ||||
|           path: | | ||||
|             /usr/local/lib/android/ | ||||
|             ~/.android | ||||
|       - name: Cache Gradle | ||||
|         uses: actions/cache@v3 | ||||
|         with: | ||||
|           path: | | ||||
|             ./mobile/build/ | ||||
|             ./mobile/android/.gradle/ | ||||
|           key: ${{ runner.os }}-flutter-${{ hashFiles('**/*.gradle*', 'pubspec.lock') }} | ||||
|       - name: Setup Android SDK | ||||
|         if: steps.android-sdk.outputs.cache-hit != 'true' | ||||
|         uses: android-actions/setup-android@v2 | ||||
|       - name: AVD cache | ||||
|         uses: actions/cache@v3 | ||||
|         id: avd-cache | ||||
|         with: | ||||
|           path: | | ||||
|             ~/.android/avd/* | ||||
|             ~/.android/adb* | ||||
|           key: avd-29 | ||||
|       - name: create AVD and generate snapshot for caching | ||||
|         if: steps.avd-cache.outputs.cache-hit != 'true' | ||||
|         uses: reactivecircus/android-emulator-runner@v2.27.0 | ||||
|         with: | ||||
|           working-directory: ./mobile | ||||
|           cores: 2 | ||||
|           api-level: 29 | ||||
|           arch: x86_64 | ||||
|           profile: pixel | ||||
|           target: default | ||||
|           force-avd-creation: false | ||||
|           emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | ||||
|           disable-animations: false | ||||
|           script: echo "Generated AVD snapshot for caching." | ||||
|       - name: Setup Flutter SDK | ||||
|         uses: subosito/flutter-action@v2 | ||||
|         with: | ||||
|           channel: 'stable' | ||||
|           flutter-version: '3.7.3' | ||||
|           cache: true | ||||
|       - name: Run integration tests | ||||
|         uses: reactivecircus/android-emulator-runner@v2.27.0 | ||||
|         uses: Wandalen/wretry.action@master | ||||
|         with: | ||||
|           working-directory: ./mobile | ||||
|           api-level: 29 | ||||
|           arch: x86_64 | ||||
|           profile: pixel | ||||
|           target: default | ||||
|           emulator-options: -no-window -gpu swiftshader_indirect -no-snapshot -noaudio -no-boot-anim | ||||
|           disable-linux-hw-accel: false | ||||
|           script: | | ||||
|             flutter pub get | ||||
|             flutter test integration_test | ||||
|           action: reactivecircus/android-emulator-runner@v2.27.0 | ||||
|           with: | | ||||
|             working-directory: ./mobile | ||||
|             cores: 2 | ||||
|             api-level: 29 | ||||
|             arch: x86_64 | ||||
|             profile: pixel | ||||
|             target: default | ||||
|             force-avd-creation: false | ||||
|             emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none | ||||
|             disable-animations: true | ||||
|             script: | | ||||
|               flutter pub get | ||||
|               flutter test integration_test | ||||
|           attempt_limit: 3 | ||||
|   | ||||
| @@ -1,3 +1,5 @@ | ||||
| import 'dart:async'; | ||||
|  | ||||
| import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter_test/flutter_test.dart'; | ||||
| import 'package:hive/hive.dart'; | ||||
| @@ -43,7 +45,6 @@ class ImmichTestHelper { | ||||
|     // Load main Widget | ||||
|     await tester.pumpWidget(app.getMainWidget(db)); | ||||
|     // Post run tasks | ||||
|     await tester.pumpAndSettle(); | ||||
|     await EasyLocalization.ensureInitialized(); | ||||
|   } | ||||
| } | ||||
| @@ -62,3 +63,17 @@ void immichWidgetTest( | ||||
|     semanticsEnabled: false, | ||||
|   ); | ||||
| } | ||||
|  | ||||
| Future<void> pumpUntilFound( | ||||
|     WidgetTester tester, | ||||
|     Finder finder, { | ||||
|       Duration timeout = const Duration(seconds: 120), | ||||
|     }) async { | ||||
|   bool found = false; | ||||
|   final timer = Timer(timeout, () => throw TimeoutException("Pump until has timed out")); | ||||
|   while (found != true) { | ||||
|     await tester.pump(); | ||||
|     found = tester.any(finder); | ||||
|   } | ||||
|   timer.cancel(); | ||||
| } | ||||
|   | ||||
| @@ -2,33 +2,20 @@ import 'package:easy_localization/easy_localization.dart'; | ||||
| import 'package:flutter/material.dart'; | ||||
| import 'package:flutter_test/flutter_test.dart'; | ||||
|  | ||||
| import 'general_helper.dart'; | ||||
|  | ||||
| class ImmichTestLoginHelper { | ||||
|   final WidgetTester tester; | ||||
|  | ||||
|   ImmichTestLoginHelper(this.tester); | ||||
|  | ||||
|   Future<void> waitForLoginScreen({int timeoutSeconds = 20}) async { | ||||
|     for (var i = 0; i < timeoutSeconds; i++) { | ||||
|       // Search for "IMMICH" test in the app bar | ||||
|       final result = find.text("IMMICH"); | ||||
|       if (tester.any(result)) { | ||||
|         // Wait 5s until everything settled | ||||
|         await tester.pump(const Duration(seconds: 5)); | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       // Wait 1s before trying again | ||||
|       await Future.delayed(const Duration(seconds: 1)); | ||||
|     } | ||||
|  | ||||
|     fail("Timeout while waiting for login screen"); | ||||
|   Future<void> waitForLoginScreen() async { | ||||
|     await pumpUntilFound(tester, find.text("Login")); | ||||
|   } | ||||
|  | ||||
|   Future<bool> acknowledgeNewServerVersion() async { | ||||
|     await pumpUntilFound(tester, find.text("Acknowledge")); | ||||
|     final result = find.text("Acknowledge"); | ||||
|     if (!tester.any(result)) { | ||||
|       return false; | ||||
|     } | ||||
|  | ||||
|     await tester.tap(result); | ||||
|     await tester.pump(); | ||||
| @@ -43,17 +30,17 @@ class ImmichTestLoginHelper { | ||||
|   }) async { | ||||
|     final loginForms = find.byType(TextFormField); | ||||
|  | ||||
|     await tester.pump(const Duration(milliseconds: 500)); | ||||
|     await tester.enterText(loginForms.at(0), email); | ||||
|     await tester.pump(); | ||||
|  | ||||
|     await tester.pump(const Duration(milliseconds: 500)); | ||||
|     await tester.enterText(loginForms.at(1), password); | ||||
|     await tester.pump(); | ||||
|  | ||||
|     await tester.pump(const Duration(milliseconds: 500)); | ||||
|     await tester.enterText(loginForms.at(2), server); | ||||
|     await tester.pump(); | ||||
|  | ||||
|     await tester.testTextInput.receiveAction(TextInputAction.done); | ||||
|     await tester.pumpAndSettle(); | ||||
|     await tester.pump(); | ||||
|   } | ||||
|  | ||||
|   Future<void> enterCredentialsOf(LoginCredentials credentials) async { | ||||
| @@ -65,32 +52,17 @@ class ImmichTestLoginHelper { | ||||
|   } | ||||
|  | ||||
|   Future<void> pressLoginButton() async { | ||||
|     await pumpUntilFound(tester, find.textContaining("login_form_button_text".tr())); | ||||
|     final button = find.textContaining("login_form_button_text".tr()); | ||||
|     await tester.tap(button); | ||||
|   } | ||||
|  | ||||
|   Future<void> assertLoginSuccess({int timeoutSeconds = 15}) async { | ||||
|     for (var i = 0; i < timeoutSeconds * 2; i++) { | ||||
|       if (tester.any(find.text("home_page_building_timeline".tr()))) { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       await tester.pump(const Duration(milliseconds: 500)); | ||||
|     } | ||||
|  | ||||
|     fail("Login failed."); | ||||
|     await pumpUntilFound(tester, find.text("home_page_building_timeline".tr())); | ||||
|   } | ||||
|  | ||||
|   Future<void> assertLoginFailed({int timeoutSeconds = 15}) async { | ||||
|     for (var i = 0; i < timeoutSeconds * 2; i++) { | ||||
|       if (tester.any(find.text("login_form_failed_login".tr()))) { | ||||
|         return; | ||||
|       } | ||||
|  | ||||
|       await tester.pump(const Duration(milliseconds: 500)); | ||||
|     } | ||||
|  | ||||
|     fail("Timeout."); | ||||
|       await pumpUntilFound(tester, find.text("login_form_failed_login".tr())); | ||||
|   } | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user