diff --git a/integration_test/e2e_mnemonic_test.dart b/integration_test/e2e_mnemonic_test.dart new file mode 100644 index 00000000..9ab3d200 --- /dev/null +++ b/integration_test/e2e_mnemonic_test.dart @@ -0,0 +1,119 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:my_wit_wallet/screens/create_wallet/generate_mnemonic_card.dart'; +import 'package:my_wit_wallet/screens/dashboard/view/dashboard_screen.dart'; +import 'package:my_wit_wallet/util/storage/database/wallet.dart'; +import 'package:my_wit_wallet/widgets/PaddedButton.dart'; +import 'package:my_wit_wallet/widgets/labeled_checkbox.dart'; +import 'test_utils.dart'; + +Future e2eImportMnemonicTest(WidgetTester tester) async { + await initializeTest(tester); + + /// Assess what is on the screen + walletsExist = isTextOnScreen("Unlock wallet"); + bool biometricsActive = isTextOnScreen("CANCEL"); + + /// Cancel the Biometrics popup + if (walletsExist && biometricsActive) await tapButton(tester, "CANCEL"); + + if (walletsExist) { + /// Login Screen + await enterText(tester, TextFormField, password); + await tapButton(tester, "Unlock wallet"); + + /// Dashboard + /// Tap on the first PaddedButton on the screen, which is the identicon + /// and brings up the wallet list. + await tapButton(tester, PaddedButton, index: 0); + await tapButton(tester, FontAwesomeIcons.circlePlus); + } + + /// Create or Import Wallet + await tapButton(tester, "Import wallet"); + await tapButton(tester, "Import from secret security phrase"); + + /// Wallet Security + await scrollUntilVisible( + tester, widgetByLabel("I will be careful, I promise!")); + await tapButton(tester, LabeledCheckbox); + await tapButton(tester, "Continue"); + + /// Enter Mnemonic + await enterText(tester, TextField, mnemonic); + await tapButton(tester, "Continue"); + + /// Enter Wallet Name + await enterText(tester, TextField, "Test Wallet"); + await tapButton(tester, "Continue"); + + /// If the wallet database does not exist we need to enter the password. + if (!walletsExist) { + await enterText(tester, TextFormField, password, index: 0); + await enterText(tester, TextFormField, password, index: 1); + await tapButton(tester, "Continue"); + } + + /// Get the currentWallet loaded in the dashboard + final DashboardScreenState dashboardScreenState = + tester.state(widgetByType(DashboardScreen)); + dashboardScreenState.currentWallet!.printDebug(); + Wallet? currentWallet = dashboardScreenState.currentWallet; + /// Verify the imported wallet and the current address + expect(currentWallet!.externalAccounts[0]!.address, + "wit174la8pevl74hczcpfepgmt036zkmjen4hu8zzs"); +} + +Future e2eCreateMnemonicTest(WidgetTester tester) async { + await initializeTest(tester); + + /// Assess what is on the screen + walletsExist = isTextOnScreen("Unlock wallet"); + bool biometricsActive = isTextOnScreen("CANCEL"); + + /// Cancel the Biometrics popup + if (walletsExist && biometricsActive) await tapButton(tester, "CANCEL"); + + if (walletsExist) { + /// Login Screen + await enterText(tester, TextFormField, password); + await tapButton(tester, "Unlock wallet"); + + /// Dashboard + /// Tap on the first PaddedButton on the screen, which is the identicon + /// and brings up the wallet list. + await tapButton(tester, PaddedButton, index: 0); + await tapButton(tester, FontAwesomeIcons.circlePlus); + } + + /// Create or Import Wallet + await tapButton(tester, "Create new wallet"); + + /// Wallet Security + await scrollUntilVisible( + tester, widgetByLabel("I will be careful, I promise!")); + await tapButton(tester, LabeledCheckbox); + await tapButton(tester, "Continue"); + + /// Get the generated mnemonic + final GenerateMnemonicCardState generateMnemonicCardState = + tester.state(widgetByType(GenerateMnemonicCard)); + String generatedMnemonic = generateMnemonicCardState.mnemonic; + await tapButton(tester, "Continue"); + + /// Enter Mnemonic + await enterText(tester, TextField, generatedMnemonic); + await tapButton(tester, "Continue"); + + /// Enter Wallet Name + await enterText(tester, TextField, "Test Wallet"); + await tapButton(tester, "Continue"); + + /// If the wallet database does not exist we need to enter the password. + if (!walletsExist) { + await enterText(tester, TextFormField, password, index: 0); + await enterText(tester, TextFormField, password, index: 1); + await tapButton(tester, "Continue"); + } +} diff --git a/integration_test/import_mnemonic_test.dart b/integration_test/import_mnemonic_test.dart deleted file mode 100644 index 8235f5cc..00000000 --- a/integration_test/import_mnemonic_test.dart +++ /dev/null @@ -1,64 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_dotenv/flutter_dotenv.dart'; -import 'package:flutter_test/flutter_test.dart'; -import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:my_wit_wallet/main.dart' as myWitWallet; -import 'package:my_wit_wallet/util/integration_test_utils.dart'; -import 'package:my_wit_wallet/widgets/PaddedButton.dart'; -import 'package:my_wit_wallet/widgets/labeled_checkbox.dart'; - -bool walletsExist = false; -String password = dotenv.env['PASSWORD'] ?? "password"; -String mnemonic = dotenv.env['MNEMONIC'] ?? - "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; - -void main() async { - testWidgets("Import Mnemonic Test", (WidgetTester tester) async { - myWitWallet.main(); - await tester.pumpAndSettle(); - - /// Assess what is on the screen - walletsExist = isTextOnScreen("Unlock wallet"); - bool biometricsActive = isTextOnScreen("CANCEL"); - - /// Cancel the Biometrics popup - if (walletsExist && biometricsActive) await tapButton(tester, "CANCEL"); - - if (walletsExist) { - /// Login Screen - await enterText(tester, TextFormField, password); - await tapButton(tester, "Unlock wallet"); - - /// Dashboard - /// Tap on the first PaddedButton on the screen, which is the identicon - /// and brings up the wallet list. - await tapButton(tester, PaddedButton, index: 0); - await tapButton(tester, FontAwesomeIcons.circlePlus); - } - - /// Create or Import Wallet - await tapButton(tester, "Import wallet"); - await tapButton(tester, "Import from secret security phrase"); - - /// Wallet Security - await scrollUntilVisible( - tester, widgetByLabel("I will be careful, I promise!")); - await tapButton(tester, LabeledCheckbox); - await tapButton(tester, "Continue"); - - /// Enter Mnemonic - await enterText(tester, TextField, mnemonic); - await tapButton(tester, "Continue"); - - /// Enter Wallet Name - await enterText(tester, TextField, "Test Wallet"); - await tapButton(tester, "Continue"); - - /// If the wallet database does not exist we need to enter the password. - if (!walletsExist) { - await enterText(tester, TextFormField, password, index: 0); - await enterText(tester, TextFormField, password, index: 1); - await tapButton(tester, "Continue"); - } - }); -} diff --git a/integration_test/main.dart b/integration_test/main.dart new file mode 100644 index 00000000..a1b76eca --- /dev/null +++ b/integration_test/main.dart @@ -0,0 +1,9 @@ +import "e2e_mnemonic_test.dart"; +import 'package:flutter_test/flutter_test.dart'; + +void main() async { + group("End To End Mnemonic Tests", () { + testWidgets("Create Mnemonic Test", e2eCreateMnemonicTest); + testWidgets("Import Mnemonic Test", e2eImportMnemonicTest); + }); +} diff --git a/lib/util/integration_test_utils.dart b/integration_test/test_utils.dart similarity index 55% rename from lib/util/integration_test_utils.dart rename to integration_test/test_utils.dart index 4d1c866a..b72a1f88 100644 --- a/lib/util/integration_test_utils.dart +++ b/integration_test/test_utils.dart @@ -1,16 +1,36 @@ import 'package:flutter/material.dart'; +import 'package:flutter_dotenv/flutter_dotenv.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:my_wit_wallet/main.dart' as myWitWallet; import 'package:my_wit_wallet/widgets/PaddedButton.dart'; +bool walletsExist = false; +int defaultDelay = int.parse(dotenv.env['DELAY']!); +String password = dotenv.env['PASSWORD'] ?? "password"; +String mnemonic = dotenv.env['MNEMONIC'] ?? + "abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon abandon about"; + Finder widgetByType(Type type) => find.byType(type); + Finder widgetByText(String text) => find.text(text); + Finder widgetByIcon(IconData icon) => find.byIcon(icon); + Finder widgetByLabel(String label) => find.bySemanticsLabel(label); -const int defaultDelay = 1000; -Future tapButton(WidgetTester tester, dynamic value, - {int? index, bool delay = true, int milliseconds = defaultDelay}) async { +Future initializeTest(WidgetTester tester) async { + myWitWallet.main(); + await tester.pumpAndSettle(); +} + +Future tapButton( + WidgetTester tester, + dynamic value, { + int? index, + bool delay = true, + int? milliseconds, +}) async { Finder finder; switch (value.runtimeType) { case Type: @@ -35,19 +55,21 @@ Future tapButton(WidgetTester tester, dynamic value, break; } } - await tester.tap(index != null ? finder.at(index) : finder); await tester.pumpAndSettle(); if (delay) { - await Future.delayed(Duration(milliseconds: milliseconds)); + await Future.delayed(Duration(milliseconds: milliseconds ?? defaultDelay)); } return true; } -Future tapButtonByName(WidgetTester tester, String text, - {int index = 0, - bool delay = true, - int milliseconds = defaultDelay}) async => +Future tapButtonByName( + WidgetTester tester, + String text, { + int index = 0, + bool delay = true, + int? milliseconds, +}) async => await tapButton( tester, text, @@ -56,10 +78,13 @@ Future tapButtonByName(WidgetTester tester, String text, milliseconds: milliseconds, ); -Future tapButtonByType(WidgetTester tester, Type type, - {int index = 0, - bool delay = true, - int milliseconds = defaultDelay}) async => +Future tapButtonByType( + WidgetTester tester, + Type type, { + int index = 0, + bool delay = true, + int? milliseconds, +}) async => await tapButton( tester, type, @@ -68,10 +93,13 @@ Future tapButtonByType(WidgetTester tester, Type type, milliseconds: milliseconds, ); -Future tapButtonByIndex(WidgetTester tester, dynamic data, - {int index = 0, - bool delay = true, - int milliseconds = defaultDelay}) async => +Future tapButtonByIndex( + WidgetTester tester, + dynamic data, { + int index = 0, + bool delay = true, + int? milliseconds, +}) async => await tapButton( tester, data, @@ -80,10 +108,13 @@ Future tapButtonByIndex(WidgetTester tester, dynamic data, milliseconds: milliseconds, ); -Future tapButtonByIcon(WidgetTester tester, IconData icon, - {int index = 0, - bool delay = true, - int milliseconds = defaultDelay}) async => +Future tapButtonByIcon( + WidgetTester tester, + IconData icon, { + int index = 0, + bool delay = true, + int? milliseconds, +}) async => await tapButton( tester, icon, @@ -92,10 +123,13 @@ Future tapButtonByIcon(WidgetTester tester, IconData icon, milliseconds: milliseconds, ); -Future tapButtonByLabel(WidgetTester tester, String label, - {int index = 0, - bool delay = true, - int milliseconds = defaultDelay}) async => +Future tapButtonByLabel( + WidgetTester tester, + String label, { + int index = 0, + bool delay = true, + int? milliseconds, +}) async => await tapButton( tester, label, @@ -104,27 +138,38 @@ Future tapButtonByLabel(WidgetTester tester, String label, milliseconds: milliseconds, ); -Future enterText(WidgetTester tester, Type type, String text, - {int? index, bool delay = true, int milliseconds = defaultDelay}) async { +Future enterText( + WidgetTester tester, + Type type, + String text, { + int? index, + bool delay = true, + int? milliseconds, +}) async { index != null ? await tester.enterText(widgetByType(type).at(index), text) : await tester.enterText(widgetByType(type), text); await tester.pumpAndSettle(); if (delay) { - await Future.delayed(Duration(milliseconds: milliseconds)); + await Future.delayed(Duration(milliseconds: milliseconds ?? defaultDelay)); } return true; } enum ScrollDirection { Up, Down, Left, Right } -Future scrollUntilVisible(WidgetTester tester, Finder finder, - {int index = 0, bool delay = true, int milliseconds = defaultDelay}) async { +Future scrollUntilVisible( + WidgetTester tester, + Finder finder, { + int index = 0, + bool delay = true, + int? milliseconds, +}) async { await tester.scrollUntilVisible(finder, -100.0, duration: Duration(milliseconds: 500), maxScrolls: 100); await tester.pumpAndSettle(); if (delay) { - await Future.delayed(Duration(milliseconds: milliseconds)); + await Future.delayed(Duration(milliseconds: milliseconds ?? defaultDelay)); } return true; } diff --git a/lib/app.dart b/lib/app.dart index b19fa501..d0f08ae9 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -51,6 +51,7 @@ Widget _buildWithTheme(BuildContext context, ThemeState state) { parent: RangeMaintainingScrollPhysics())), title: 'myWitWallet', home: LoginScreen(), + initialRoute: LoginScreen.route, theme: state.themeData, routes: { CreateWalletScreen.route: (context) => CreateWalletScreen(), diff --git a/lib/shared/api_theme.dart b/lib/shared/api_theme.dart index d97ed32b..ad9cb9c4 100644 --- a/lib/shared/api_theme.dart +++ b/lib/shared/api_theme.dart @@ -11,9 +11,9 @@ class ApiTheme { Brightness _brightness = Brightness.dark; bool _shouldLoadBrightness = true; - ApiTheme({ - Key? key, - }); + static final ApiTheme _apiTheme = ApiTheme._internal(); + ApiTheme._internal(); + factory ApiTheme.instance() => _apiTheme; Future setBrightness(Brightness brightness) async { _brightness = brightness; diff --git a/lib/shared/locator.dart b/lib/shared/locator.dart index b0d17048..db8793fe 100644 --- a/lib/shared/locator.dart +++ b/lib/shared/locator.dart @@ -15,14 +15,18 @@ class Locator { Locator.setup() { _i = GetIt.I; - _i.registerSingleton(ApiTheme()); - _i.registerSingleton(ApiDatabase()); - _i.registerSingleton(ApiExplorer()); - _i.registerSingleton(ApiPreferences()); - _i.registerSingleton(ApiCreateWallet()); - _i.registerSingleton(ApiCrypto()); - _i.registerSingleton(CryptoIsolate.instance()); - _i.registerSingleton(DatabaseIsolate.instance()); + /// check if things are already registered, if they are skip it. + /// if they are already registered it is because of end-to-end testing. + if(!_i.isRegistered()){ + _i.registerSingleton(ApiTheme.instance()); + _i.registerSingleton(ApiDatabase()); + _i.registerSingleton(ApiExplorer()); + _i.registerSingleton(ApiPreferences()); + _i.registerSingleton(ApiCreateWallet()); + _i.registerSingleton(ApiCrypto()); + _i.registerSingleton(CryptoIsolate.instance()); + _i.registerSingleton(DatabaseIsolate.instance()); + } } Future initialize() async {