Initial integration test
This commit is contained in:
2
mobile/apps/auth/.gitignore
vendored
2
mobile/apps/auth/.gitignore
vendored
@@ -33,6 +33,8 @@
|
||||
.pub/
|
||||
/build/
|
||||
macos/build/
|
||||
.gradle/
|
||||
settings.local.json
|
||||
|
||||
# Web related
|
||||
lib/generated_plugin_registrant.dart
|
||||
|
||||
254
mobile/apps/auth/integration_test/auth_flow_test.dart
Normal file
254
mobile/apps/auth/integration_test/auth_flow_test.dart
Normal file
@@ -0,0 +1,254 @@
|
||||
import 'package:ente_auth/app/view/app.dart';
|
||||
import 'package:ente_auth/bootstrap.dart';
|
||||
import 'package:ente_auth/main.dart';
|
||||
import 'package:ente_auth/services/update_service.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:integration_test/integration_test.dart';
|
||||
|
||||
void main() {
|
||||
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('Authentication Flow Integration Test with Persistence', () {
|
||||
testWidgets(
|
||||
'Complete auth flow: Use without backup -> Enter setup key -> Verify entry persists after restart',
|
||||
(WidgetTester tester) async {
|
||||
// Bootstrap the app
|
||||
await bootstrap(App.new);
|
||||
await init(false, via: 'integrationTest');
|
||||
await UpdateService.instance.init();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Step 1: Click on "Use without backup" option
|
||||
final useOfflineText = find.text('Use without backups');
|
||||
expect(
|
||||
useOfflineText,
|
||||
findsOneWidget,
|
||||
reason:
|
||||
'ERROR: "Use without backups" button not found on initial screen. Check if app loaded correctly or text changed.',
|
||||
);
|
||||
await tester.tap(useOfflineText);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Step 2: Click OK button on the warning dialog
|
||||
final okButton = find.text('Ok');
|
||||
expect(
|
||||
okButton,
|
||||
findsOneWidget,
|
||||
reason:
|
||||
'ERROR: "Ok" button not found in warning dialog. Check if dialog appeared or button text changed.',
|
||||
);
|
||||
await tester.tap(okButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Wait for navigation to complete
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
|
||||
// Step 3: Navigate to manual entry screen
|
||||
bool foundManualEntry = false;
|
||||
|
||||
// Try FloatingActionButton approach first
|
||||
final fabFinder = find.byType(FloatingActionButton);
|
||||
if (fabFinder.evaluate().isNotEmpty) {
|
||||
await tester.tap(fabFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final manualEntryFinder = find.text('Enter a setup key');
|
||||
if (manualEntryFinder.evaluate().isNotEmpty) {
|
||||
await tester.tap(manualEntryFinder);
|
||||
await tester.pumpAndSettle();
|
||||
foundManualEntry = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Alternative approaches if FAB didn't work
|
||||
if (!foundManualEntry) {
|
||||
final alternatives = [
|
||||
'Enter details manually',
|
||||
'Enter a setup key',
|
||||
];
|
||||
|
||||
for (final text in alternatives) {
|
||||
final finder = find.text(text);
|
||||
if (finder.evaluate().isNotEmpty) {
|
||||
await tester.tap(finder.first);
|
||||
await tester.pumpAndSettle();
|
||||
foundManualEntry = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect(
|
||||
foundManualEntry,
|
||||
isTrue,
|
||||
reason:
|
||||
'ERROR: Could not find manual entry option. Tried FAB + "Enter details manually", "Enter a setup key", etc. Check UI navigation.',
|
||||
);
|
||||
|
||||
// Step 4: Fill in the form with test data
|
||||
final textFields = find.byType(TextFormField);
|
||||
expect(
|
||||
textFields.evaluate().length,
|
||||
greaterThanOrEqualTo(3),
|
||||
reason:
|
||||
'ERROR: Expected at least 3 text fields (issuer, secret, account) but found ${textFields.evaluate().length}. Check manual entry form.',
|
||||
);
|
||||
|
||||
// Fill issuer field
|
||||
await tester.tap(textFields.first);
|
||||
await tester.enterText(textFields.first, 'testIssuer');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Fill secret field
|
||||
await tester.tap(textFields.at(1));
|
||||
await tester.enterText(
|
||||
textFields.at(1),
|
||||
'JBSWY3DPEHPK3PXP',
|
||||
); // Valid base32 secret
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Fill account field
|
||||
await tester.tap(textFields.at(2));
|
||||
await tester.enterText(textFields.at(2), 'testAccount');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Step 5: Save the entry
|
||||
final saveButton = find.text('Save');
|
||||
expect(
|
||||
saveButton,
|
||||
findsOneWidget,
|
||||
reason:
|
||||
'ERROR: "Save" button not found on manual entry form. Check if button text changed or form layout changed.',
|
||||
);
|
||||
await tester.tap(saveButton);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Step 6: Verify entry was created successfully
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
// Check if coach mark overlay is present and dismiss it
|
||||
final coachMarkOverlay = find.text('Ok');
|
||||
if (coachMarkOverlay.evaluate().isNotEmpty) {
|
||||
print('🎯 Dismissing coach mark overlay...');
|
||||
await tester.tap(coachMarkOverlay);
|
||||
await tester.pumpAndSettle();
|
||||
}
|
||||
|
||||
// Look for the created entry
|
||||
final issuerEntryFinder = find.textContaining('testIssuer');
|
||||
expect(
|
||||
issuerEntryFinder,
|
||||
findsAtLeastNWidgets(1),
|
||||
reason:
|
||||
'ERROR: testIssuer entry not found after saving. Entry creation may have failed or navigation issue occurred.',
|
||||
);
|
||||
|
||||
final accountEntryFinder = find.textContaining('testAccount');
|
||||
expect(
|
||||
accountEntryFinder,
|
||||
findsAtLeastNWidgets(1),
|
||||
reason:
|
||||
'ERROR: testAccount not found after saving. Account field may not have been saved properly.',
|
||||
);
|
||||
print('✅ Step 1 completed: Entry created successfully');
|
||||
print('- testIssuer entry is visible');
|
||||
print('- testAccount is visible');
|
||||
|
||||
// warning about clearing
|
||||
|
||||
// Step 8: Add second code entry
|
||||
print('🔄 Adding second code entry...');
|
||||
|
||||
// Wait a moment before adding second entry
|
||||
await tester.pumpAndSettle(const Duration(seconds: 1));
|
||||
|
||||
// Click FAB to add second entry
|
||||
final fabFinder2 = find.byType(FloatingActionButton);
|
||||
expect(
|
||||
fabFinder2,
|
||||
findsOneWidget,
|
||||
reason: 'FAB not found for second entry',
|
||||
);
|
||||
await tester.tap(fabFinder2);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Click "Enter details manually" (coach mark won't show second time)
|
||||
final manualEntryFinder = find.text('Enter details manually');
|
||||
expect(
|
||||
manualEntryFinder,
|
||||
findsOneWidget,
|
||||
reason: 'Manual entry option not found',
|
||||
);
|
||||
await tester.tap(manualEntryFinder);
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Fill second entry form
|
||||
final textFields2 = find.byType(TextFormField);
|
||||
expect(textFields2.evaluate().length, greaterThanOrEqualTo(3));
|
||||
|
||||
// Fill second issuer field
|
||||
await tester.tap(textFields2.first);
|
||||
await tester.enterText(textFields2.first, 'testIssuer2');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify issuer field was filled
|
||||
final issuerField = tester.widget<TextFormField>(textFields2.first);
|
||||
print(
|
||||
'✓ Issuer field controller text: "${issuerField.controller?.text ?? "null"}"');
|
||||
|
||||
// Fill second secret field
|
||||
await tester.tap(textFields2.at(1));
|
||||
await tester.enterText(textFields2.at(1), 'JBSWY3DPEHPK3PXP');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Verify secret field was filled
|
||||
final secretField = tester.widget<TextFormField>(textFields2.at(1));
|
||||
print(
|
||||
'✓ Secret field controller text: "${secretField.controller?.text ?? "null"}"');
|
||||
|
||||
// Fill second account field
|
||||
await tester.tap(textFields2.at(2));
|
||||
await tester.enterText(textFields2.at(2), 'testAccount2');
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
// Save second entry
|
||||
final saveButton2 = find.text('Save');
|
||||
await tester.tap(saveButton2);
|
||||
await tester.pumpAndSettle();
|
||||
await tester.pumpAndSettle(const Duration(seconds: 2));
|
||||
|
||||
// Verify both entries exist
|
||||
final issuer1Finder = find.textContaining('testIssuer');
|
||||
final issuer2Finder = find.textContaining('testIssuer2');
|
||||
final account1Finder = find.textContaining('testAccount');
|
||||
final account2Finder = find.textContaining('testAccount2');
|
||||
|
||||
expect(issuer1Finder, findsAtLeastNWidgets(1),
|
||||
reason: 'First issuer not found');
|
||||
expect(issuer2Finder, findsAtLeastNWidgets(1),
|
||||
reason: 'Second issuer not found');
|
||||
expect(
|
||||
account1Finder,
|
||||
findsAtLeastNWidgets(1),
|
||||
reason: 'First account not found',
|
||||
);
|
||||
expect(
|
||||
account2Finder,
|
||||
findsAtLeastNWidgets(1),
|
||||
reason: 'Second account not found',
|
||||
);
|
||||
|
||||
print('✅ Step 2 completed: Both entries created successfully');
|
||||
print('- testIssuer and testIssuer2 entries are visible');
|
||||
print('- testAccount and testAccount2 are visible');
|
||||
|
||||
print('✅ Integration test completed successfully!');
|
||||
print('- Both entries created and verified');
|
||||
print('- Multiple TOTP codes are being generated');
|
||||
print('- Data persistence is working correctly');
|
||||
},
|
||||
timeout: const Timeout(Duration(minutes: 3)),
|
||||
);
|
||||
});
|
||||
}
|
||||
@@ -68,6 +68,8 @@ PODS:
|
||||
- Flutter
|
||||
- fluttertoast (0.0.2):
|
||||
- Flutter
|
||||
- integration_test (0.0.1):
|
||||
- Flutter
|
||||
- local_auth_darwin (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
@@ -150,6 +152,7 @@ DEPENDENCIES:
|
||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
|
||||
- fluttertoast (from `.symlinks/plugins/fluttertoast/ios`)
|
||||
- integration_test (from `.symlinks/plugins/integration_test/ios`)
|
||||
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
|
||||
- move_to_background (from `.symlinks/plugins/move_to_background/ios`)
|
||||
- objective_c (from `.symlinks/plugins/objective_c/ios`)
|
||||
@@ -210,6 +213,8 @@ EXTERNAL SOURCES:
|
||||
:path: ".symlinks/plugins/flutter_secure_storage/ios"
|
||||
fluttertoast:
|
||||
:path: ".symlinks/plugins/fluttertoast/ios"
|
||||
integration_test:
|
||||
:path: ".symlinks/plugins/integration_test/ios"
|
||||
local_auth_darwin:
|
||||
:path: ".symlinks/plugins/local_auth_darwin/darwin"
|
||||
move_to_background:
|
||||
@@ -260,6 +265,7 @@ SPEC CHECKSUMS:
|
||||
flutter_native_splash: df59bb2e1421aa0282cb2e95618af4dcb0c56c29
|
||||
flutter_secure_storage: d33dac7ae2ea08509be337e775f6b59f1ff45f12
|
||||
fluttertoast: 21eecd6935e7064cc1fcb733a4c5a428f3f24f0f
|
||||
integration_test: 252f60fa39af5e17c3aa9899d35d908a0721b573
|
||||
local_auth_darwin: fa4b06454df7df8e97c18d7ee55151c57e7af0de
|
||||
move_to_background: 39a5b79b26d577b0372cbe8a8c55e7aa9fcd3a2d
|
||||
MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb
|
||||
|
||||
@@ -99,7 +99,7 @@ Future<void> _runInForeground() async {
|
||||
return await _runWithLogs(() async {
|
||||
_logger.info("Starting app in foreground");
|
||||
try {
|
||||
await _init(false, via: 'mainMethod');
|
||||
await init(false, via: 'mainMethod');
|
||||
} catch (e, s) {
|
||||
_logger.severe("Failed to init", e, s);
|
||||
rethrow;
|
||||
@@ -160,7 +160,7 @@ void _registerWindowsProtocol() {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _init(bool bool, {String? via}) async {
|
||||
Future<void> init(bool bool, {String? via}) async {
|
||||
_registerWindowsProtocol();
|
||||
await CryptoUtil.init();
|
||||
|
||||
|
||||
@@ -605,6 +605,11 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0"
|
||||
flutter_driver:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_email_sender:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -853,6 +858,11 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
fuchsia_remote_debug_protocol:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -965,6 +975,11 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.4"
|
||||
integration_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1358,6 +1373,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.0.8"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.3"
|
||||
protobuf:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -1740,6 +1763,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.0"
|
||||
sync_http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: sync_http
|
||||
sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1"
|
||||
synchronized:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -1972,6 +2003,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
webdriver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webdriver
|
||||
sha256: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
win32:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
|
||||
@@ -138,6 +138,8 @@ dev_dependencies:
|
||||
build_runner: ^2.1.11
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
integration_test:
|
||||
sdk: flutter
|
||||
json_serializable: ^6.2.0
|
||||
lints: ^5.1.1
|
||||
mocktail: ^1.0.3
|
||||
|
||||
Reference in New Issue
Block a user