diff --git a/.github/workflows/pullrequest_android.yml b/.github/workflows/pullrequest_android.yml
index ff8e3c61..867bc5c8 100644
--- a/.github/workflows/pullrequest_android.yml
+++ b/.github/workflows/pullrequest_android.yml
@@ -13,17 +13,26 @@ env:
jobs:
# MAUI Android Build
build-android:
- runs-on: windows-2022
+ #runs-on: windows-2022
+ runs-on: macos-12
name: Android Build
steps:
- name: Checkout
uses: actions/checkout@v3
- - name: Setup MSBuild
- uses: microsoft/setup-msbuild@v1.1
+ - uses: malinskiy/action-android/install-sdk@release/0.1.1
+ - run: sdkmanager "platform-tools" "platforms;android-31"
+ - run: sdkmanager "build-tools;30.0.2"
+ - run: adb devices
+
+ - name: Set up Node.js
+ uses: actions/setup-node@v1
with:
- vs-prerelease: true
- msbuild-architecture: x64
+ node-version: '12.12.0'
+
+ - name: Set up Appium
+ run: |
+ npm install -g appium --unsafe-perm=true --allow-root
- name: Setup .NET 6
uses: actions/setup-dotnet@v2
@@ -49,6 +58,15 @@ jobs:
- name: Run Unit Tests
run: dotnet test TransactionMobile.Maui.BusinessLogic.Tests/TransactionMobile.Maui.BusinessLogic.Tests.csproj
+
+ - name: Run Integration Tests - Android
+ uses: malinskiy/action-android/emulator-run-cmd@release/0.1.1
+ with:
+ cmd: dotnet test TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj --filter (Category=PRTest)&(Category=Android)
+ api: 28
+ tag: default
+ abi: x86
+ verbose: true
#- name: Upload Android Artifact
# uses: actions/upload-artifact@v2.3.1
diff --git a/.github/workflows/pullrequest_ios.yml b/.github/workflows/pullrequest_ios.yml
index 75969523..4173a5ab 100644
--- a/.github/workflows/pullrequest_ios.yml
+++ b/.github/workflows/pullrequest_ios.yml
@@ -23,6 +23,15 @@ jobs:
with:
xcode-version: '13.3'
+ - name: Set up Node.js
+ uses: actions/setup-node@v1
+ with:
+ node-version: '12.12.0'
+
+ - name: Set up Appium
+ run: |
+ npm install -g appium --unsafe-perm=true --allow-root
+
- name: Setup .NET 6
uses: actions/setup-dotnet@v2
with:
@@ -43,6 +52,9 @@ jobs:
- name: Run Unit Tests
run: dotnet test TransactionMobile.Maui.BusinessLogic.Tests/TransactionMobile.Maui.BusinessLogic.Tests.csproj
+ - name: Run Integration Tests - iOS
+ run: dotnet test TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj --filter "Category=PRTest&Category=iOS"
+
#- name: Upload iOS Artifact
# uses: actions/upload-artifact@v2.3.1
# with:
diff --git a/.github/workflows/pullrequest_maccatalyst.yml b/.github/workflows/pullrequest_maccatalyst.yml
index 433d6dbe..fd706dcf 100644
--- a/.github/workflows/pullrequest_maccatalyst.yml
+++ b/.github/workflows/pullrequest_maccatalyst.yml
@@ -13,7 +13,7 @@ env:
jobs:
# MAUI MacCatalyst Build
build-mac:
- runs-on: macos-11
+ runs-on: macos-12
name: MacCatalyst Build
steps:
- name: Checkout
diff --git a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj
index 3501235e..cf0bf6ca 100644
--- a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj
+++ b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj
@@ -24,9 +24,9 @@
-
-
-
-
+
+
+
+
diff --git a/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs b/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs
index 515bd74f..5275ec54 100644
--- a/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs
+++ b/TransactionMobile.Maui.BusinessLogic/UIServices/INavigationService.cs
@@ -36,6 +36,5 @@ Task GoToVoucherIssueVoucherPage(String operatorIdentifier,
Guid productId,
Decimal voucherAmount);
-
#endregion
}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs
index 6a303ee6..39f9430c 100644
--- a/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs
+++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/LoginPageViewModel.cs
@@ -122,6 +122,8 @@ private async Task LoginCommandExecute()
// TODO: Need to set the application as in training mode somehow
+ this.MemoryCacheService.Set("IsLoggedIn", true);
+
await this.NavigationService.GoToHome();
}
diff --git a/TransactionMobile.Maui.UITests - Copy/Common/BaseTestFixture.cs b/TransactionMobile.Maui.UITests - Copy/Common/BaseTestFixture.cs
new file mode 100644
index 00000000..dfc69fc6
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Common/BaseTestFixture.cs
@@ -0,0 +1,16 @@
+namespace TransactionMobile.Maui.UITests.Common
+{
+ using Drivers;
+
+ public abstract class BaseTestFixture
+ {
+ #region Constructors
+
+ protected BaseTestFixture(MobileTestPlatform mobileTestPlatform)
+ {
+ AppiumDriver.MobileTestPlatform = mobileTestPlatform;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Common/MobileTestPlatform.cs b/TransactionMobile.Maui.UITests - Copy/Common/MobileTestPlatform.cs
new file mode 100644
index 00000000..8465ee22
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Common/MobileTestPlatform.cs
@@ -0,0 +1,9 @@
+namespace TransactionMobile.Maui.UITests.Common;
+
+public enum MobileTestPlatform
+{
+ iOS,
+ Android,
+ Windows,
+ MacCatalyst
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Common/Retry.cs b/TransactionMobile.Maui.UITests - Copy/Common/Retry.cs
new file mode 100644
index 00000000..c82c362a
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Common/Retry.cs
@@ -0,0 +1,68 @@
+namespace TransactionMobile.Maui.UITests.Common;
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+public static class Retry
+{
+ #region Fields
+
+ ///
+ /// The default retry for
+ ///
+ private static readonly TimeSpan DefaultRetryFor = TimeSpan.FromSeconds(60);
+
+ ///
+ /// The default retry interval
+ ///
+ private static readonly TimeSpan DefaultRetryInterval = TimeSpan.FromSeconds(5);
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Fors the specified action.
+ ///
+ /// The action.
+ /// The retry for.
+ /// The retry interval.
+ ///
+ public static async Task For(Func action,
+ TimeSpan? retryFor = null,
+ TimeSpan? retryInterval = null)
+ {
+ DateTime startTime = DateTime.Now;
+ Exception lastException = null;
+
+ if (retryFor == null)
+ {
+ retryFor = Retry.DefaultRetryFor;
+ }
+
+ while (DateTime.Now.Subtract(startTime).TotalMilliseconds < retryFor.Value.TotalMilliseconds)
+ {
+ try
+ {
+ await action().ConfigureAwait(false);
+ lastException = null;
+ break;
+ }
+ catch (Exception e)
+ {
+ lastException = e;
+
+ // wait before retrying
+ Thread.Sleep(retryInterval ?? Retry.DefaultRetryInterval);
+ }
+ }
+
+ if (lastException != null)
+ {
+ throw lastException;
+ }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Common/SpecflowTableHelper.cs b/TransactionMobile.Maui.UITests - Copy/Common/SpecflowTableHelper.cs
new file mode 100644
index 00000000..2e5e2846
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Common/SpecflowTableHelper.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TransactionMobile.Maui.UITests.Common
+{
+ using TechTalk.SpecFlow;
+
+ public static class SpecflowTableHelper
+ {
+ #region Methods
+
+ ///
+ /// Gets the enum value.
+ ///
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static T GetEnumValue(TableRow row,
+ String key) where T : struct
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ Enum.TryParse(field, out T myEnum);
+
+ return myEnum;
+ }
+
+ ///
+ /// Gets the boolean value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static Boolean GetBooleanValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return bool.TryParse(field, out Boolean value) && value;
+ }
+
+ ///
+ /// Gets the date for date string.
+ ///
+ /// The date string.
+ /// The today.
+ ///
+ public static DateTime GetDateForDateString(String dateString,
+ DateTime today)
+ {
+ switch (dateString.ToUpper())
+ {
+ case "TODAY":
+ return today.Date;
+ case "YESTERDAY":
+ return today.AddDays(-1).Date;
+ case "LASTWEEK":
+ return today.AddDays(-7).Date;
+ case "LASTMONTH":
+ return today.AddMonths(-1).Date;
+ case "LASTYEAR":
+ return today.AddYears(-1).Date;
+ case "TOMORROW":
+ return today.AddDays(1).Date;
+ default:
+ return DateTime.Parse(dateString);
+ }
+ }
+
+ ///
+ /// Gets the decimal value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static Decimal GetDecimalValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return decimal.TryParse(field, out Decimal value) ? value : 0;
+ }
+
+ ///
+ /// Gets the int value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static Int32 GetIntValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return int.TryParse(field, out Int32 value) ? value : -1;
+ }
+
+ ///
+ /// Gets the short value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static Int16 GetShortValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ if (short.TryParse(field, out Int16 value))
+ {
+ return value;
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Gets the string row value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static String GetStringRowValue(TableRow row,
+ String key)
+ {
+ return row.TryGetValue(key, out String value) ? value : "";
+ }
+
+ #endregion
+ }
+}
diff --git a/TransactionMobile.Maui.UITests - Copy/Drivers/AppiumDriver.cs b/TransactionMobile.Maui.UITests - Copy/Drivers/AppiumDriver.cs
new file mode 100644
index 00000000..262c8a8d
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Drivers/AppiumDriver.cs
@@ -0,0 +1,111 @@
+namespace TransactionMobile.Maui.UITests.Drivers
+{
+ using System;
+ using System.IO;
+ using System.Reflection;
+ using Common;
+ using OpenQA.Selenium.Appium;
+ using OpenQA.Selenium.Appium.Android;
+ using OpenQA.Selenium.Appium.Enums;
+ using OpenQA.Selenium.Appium.iOS;
+ using OpenQA.Selenium.Appium.Mac;
+ using OpenQA.Selenium.Appium.Service;
+ using OpenQA.Selenium.Appium.Windows;
+
+ public class AppiumDriver
+ {
+ #region Fields
+
+ public static AndroidDriver AndroidDriver;
+
+ public static IOSDriver iOSDriver;
+
+ public static MacDriver MacDriver;
+
+ public static MobileTestPlatform MobileTestPlatform;
+
+ public static WindowsDriver WindowsDriver;
+
+ #endregion
+
+ #region Methods
+
+ public void StartApp()
+ {
+ AppiumLocalService appiumService = new AppiumServiceBuilder().UsingPort(4723).Build();
+
+ if (appiumService.IsRunning == false)
+ {
+ appiumService.Start();
+ }
+
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ var driverOptions = new AppiumOptions();
+ driverOptions.AddAdditionalCapability("adbExecTimeout", TimeSpan.FromMinutes(5).Milliseconds);
+ driverOptions.AddAdditionalCapability(MobileCapabilityType.AutomationName, "Espresso");
+ // TODO: Only do this locally
+ driverOptions.AddAdditionalCapability(MobileCapabilityType.FullReset, true);
+ driverOptions.AddAdditionalCapability("forceEspressoRebuild", true);
+ driverOptions.AddAdditionalCapability("enforceAppInstall", true);
+ driverOptions.AddAdditionalCapability("noSign", true);
+ driverOptions.AddAdditionalCapability(MobileCapabilityType.PlatformName, "Android");
+ driverOptions.AddAdditionalCapability(MobileCapabilityType.PlatformVersion, "9.0");
+ driverOptions.AddAdditionalCapability(MobileCapabilityType.DeviceName, "emulator-5554");
+
+ String assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ String binariesFolder = Path.Combine(assemblyFolder, "..", "..", "..", "..", @"TransactionMobile.Maui/bin/Release/net6.0-android/");
+ var apkPath = Path.Combine(binariesFolder, "com.transactionprocessing.pos-Signed.apk");
+ driverOptions.AddAdditionalCapability(MobileCapabilityType.App, apkPath);
+ driverOptions.AddAdditionalCapability("espressoBuildConfig",
+ "{ \"additionalAppDependencies\": [ \"com.google.android.material:material:1.0.0\", \"androidx.lifecycle:lifecycle-extensions:2.1.0\" ] }");
+
+ AppiumDriver.AndroidDriver = new AndroidDriver(appiumService, driverOptions, TimeSpan.FromMinutes(10));
+ }
+
+ //if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ //{
+ // var driverOptions = new AppiumOptions();
+ // driverOptions.AddAdditionalCapability(MobileCapabilityType.PlatformName, "iOS");
+ // driverOptions.AddAdditionalCapability(MobileCapabilityType.DeviceName, "iPhone 11");
+ // driverOptions.AddAdditionalCapability(MobileCapabilityType.PlatformVersion, "14.4");
+
+ // String assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ // String binariesFolder = Path.Combine(assemblyFolder, "..", "..", "..", "..", @"TransactionMobile.iOS/bin/iPhoneSimulator/Release");
+ // var apkPath = Path.Combine(binariesFolder, "TransactionMobile.iOS.app");
+ // driverOptions.AddAdditionalCapability(MobileCapabilityType.App, apkPath);
+ // driverOptions.AddAdditionalCapability(MobileCapabilityType.FullReset, true);
+ // driverOptions.AddAdditionalCapability(MobileCapabilityType.AutomationName, "XCUITest");
+ // driverOptions.AddAdditionalCapability("useNewWDA", true);
+ // driverOptions.AddAdditionalCapability("wdaLaunchTimeout", 999999999);
+ // driverOptions.AddAdditionalCapability("wdaConnectionTimeout", 999999999);
+ // driverOptions.AddAdditionalCapability("restart", true);
+
+ // AppiumDriver.iOSDriver = new IOSDriver(appiumService, driverOptions, TimeSpan.FromMinutes(5));
+ //}
+
+ // TODO: Implement iOS Tests
+ // TODO: Implement Windows UI Tests
+ // TODO: Implement Mac UI Tests
+ }
+
+ public void StopApp()
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ AppiumDriver.AndroidDriver.Quit();
+ }
+ //else if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ //{
+ // AppiumDriver.iOSDriver.CloseApp();
+ // AppiumDriver.iOSDriver.Quit();
+ //}
+
+ // TODO: Implement iOS Tests
+ // TODO: Implement Windows UI Tests
+ // TODO: Implement Mac UI Tests
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Features/Login.feature b/TransactionMobile.Maui.UITests - Copy/Features/Login.feature
new file mode 100644
index 00000000..b2c6a049
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Features/Login.feature
@@ -0,0 +1,32 @@
+@background @login
+Feature: Login
+
+Background:
+
+# Given I have created the following estates
+# | EstateName |
+# | Test Estate 1 |
+#
+# Given I have created the following operators
+# | EstateName | OperatorName | RequireCustomMerchantNumber | RequireCustomTerminalNumber |
+# | Test Estate 1 | Safaricom | True | True |
+#
+# Given I create the following merchants
+# | MerchantName | EstateName | EmailAddress | Password | GivenName | FamilyName |
+# | Test Merchant 1 | Test Estate 1 | merchantuser@testmerchant1.co.uk | 123456 | TestMerchant | User1 |
+#
+# Given I make the following manual merchant deposits
+# | Amount | DateTime | MerchantName | EstateName |
+# | 1000.00 | Today | Test Merchant 1 | Test Estate 1 |
+# | 1000.00 | Yesterday | Test Merchant 1 | Test Estate 1 |
+#
+# Given the application in in test mode
+
+@PRTest
+Scenario: Login as Merchant
+ Given I am on the Login Screen
+ When I enter 'merchantuser@testmerchant1.co.uk' as the Email Address
+ And I enter '123456' as the Password
+ And I tap on Login
+ Then the Merchant Home Page is displayed
+ #And the available balance is shown as 2000.00
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Features/Login.feature.cs b/TransactionMobile.Maui.UITests - Copy/Features/Login.feature.cs
new file mode 100644
index 00000000..c2358355
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Features/Login.feature.cs
@@ -0,0 +1,141 @@
+// ------------------------------------------------------------------------------
+//
+// This code was generated by SpecFlow (https://www.specflow.org/).
+// SpecFlow Version:3.9.0.0
+// SpecFlow Generator Version:3.9.0.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+#region Designer generated code
+#pragma warning disable
+namespace TransactionMobile.Maui.UITests.Features
+{
+ using TechTalk.SpecFlow;
+ using System;
+ using System.Linq;
+
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.9.0.0")]
+ [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [NUnit.Framework.TestFixtureAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Login")]
+ [NUnit.Framework.CategoryAttribute("background")]
+ [NUnit.Framework.CategoryAttribute("login")]
+ public partial class LoginFeature
+ {
+
+ private TechTalk.SpecFlow.ITestRunner testRunner;
+
+ private string[] _featureTags = new string[] {
+ "background",
+ "login"};
+
+#line 1 "Login.feature"
+#line hidden
+
+ [NUnit.Framework.OneTimeSetUpAttribute()]
+ public virtual void FeatureSetup()
+ {
+ testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
+ TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Login", null, ProgrammingLanguage.CSharp, new string[] {
+ "background",
+ "login"});
+ testRunner.OnFeatureStart(featureInfo);
+ }
+
+ [NUnit.Framework.OneTimeTearDownAttribute()]
+ public virtual void FeatureTearDown()
+ {
+ testRunner.OnFeatureEnd();
+ testRunner = null;
+ }
+
+ [NUnit.Framework.SetUpAttribute()]
+ public virtual void TestInitialize()
+ {
+ }
+
+ [NUnit.Framework.TearDownAttribute()]
+ public virtual void TestTearDown()
+ {
+ testRunner.OnScenarioEnd();
+ }
+
+ public virtual void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
+ {
+ testRunner.OnScenarioInitialize(scenarioInfo);
+ testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext);
+ }
+
+ public virtual void ScenarioStart()
+ {
+ testRunner.OnScenarioStart();
+ }
+
+ public virtual void ScenarioCleanup()
+ {
+ testRunner.CollectScenarioErrors();
+ }
+
+ public virtual void FeatureBackground()
+ {
+#line 4
+#line hidden
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Login as Merchant")]
+ [NUnit.Framework.CategoryAttribute("PRTest")]
+ public virtual void LoginAsMerchant()
+ {
+ string[] tagsOfScenario = new string[] {
+ "PRTest"};
+ System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary();
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Login as Merchant", null, tagsOfScenario, argumentsOfScenario, this._featureTags);
+#line 26
+this.ScenarioInitialize(scenarioInfo);
+#line hidden
+ bool isScenarioIgnored = default(bool);
+ bool isFeatureIgnored = default(bool);
+ if ((tagsOfScenario != null))
+ {
+ isScenarioIgnored = tagsOfScenario.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
+ }
+ if ((this._featureTags != null))
+ {
+ isFeatureIgnored = this._featureTags.Where(__entry => __entry != null).Where(__entry => String.Equals(__entry, "ignore", StringComparison.CurrentCultureIgnoreCase)).Any();
+ }
+ if ((isScenarioIgnored || isFeatureIgnored))
+ {
+ testRunner.SkipScenario();
+ }
+ else
+ {
+ this.ScenarioStart();
+#line 4
+this.FeatureBackground();
+#line hidden
+#line 27
+ testRunner.Given("I am on the Login Screen", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line hidden
+#line 28
+ testRunner.When("I enter \'merchantuser@testmerchant1.co.uk\' as the Email Address", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line hidden
+#line 29
+ testRunner.And("I enter \'123456\' as the Password", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+#line 30
+ testRunner.And("I tap on Login", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+#line 31
+ testRunner.Then("the Merchant Home Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ }
+ this.ScenarioCleanup();
+ }
+ }
+}
+#pragma warning restore
+#endregion
diff --git a/TransactionMobile.Maui.UITests - Copy/Features/LoginFeature.cs b/TransactionMobile.Maui.UITests - Copy/Features/LoginFeature.cs
new file mode 100644
index 00000000..b8f7f3a8
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Features/LoginFeature.cs
@@ -0,0 +1,13 @@
+namespace TransactionMobile.Maui.UITests.Features;
+
+using Common;
+using NUnit.Framework;
+
+[TestFixture(MobileTestPlatform.Android, Category = "Android")]
+public partial class LoginFeature : BaseTestFixture
+{
+ public LoginFeature(MobileTestPlatform mobileTestPlatform)
+ : base(mobileTestPlatform)
+ {
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Hooks/AppiumHooks.cs b/TransactionMobile.Maui.UITests - Copy/Hooks/AppiumHooks.cs
new file mode 100644
index 00000000..c0369f61
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Hooks/AppiumHooks.cs
@@ -0,0 +1,34 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TransactionMobile.Maui.UITests.Hooks
+{
+ using Drivers;
+ using TechTalk.SpecFlow;
+
+ [Binding]
+ public class AppiumHooks
+ {
+ private readonly AppiumDriver _appiumDriver;
+
+ public AppiumHooks(AppiumDriver appiumDriver)
+ {
+ _appiumDriver = appiumDriver;
+ }
+
+ [BeforeScenario()]
+ public void StartApp()
+ {
+ _appiumDriver.StartApp();
+ }
+
+ [AfterScenario()]
+ public void ShutdownApp()
+ {
+ _appiumDriver.StopApp();
+ }
+ }
+}
diff --git a/TransactionMobile.Maui.UITests - Copy/Pages/BasePage.cs b/TransactionMobile.Maui.UITests - Copy/Pages/BasePage.cs
new file mode 100644
index 00000000..09e86090
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Pages/BasePage.cs
@@ -0,0 +1,147 @@
+using System;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TransactionMobile.Maui.UITests
+{
+ using Common;
+ using Drivers;
+ using OpenQA.Selenium;
+ using Shouldly;
+
+ public abstract class BasePage
+ {
+ protected abstract String Trait { get; }
+
+ public async Task AssertOnPage(TimeSpan? timeout = null)
+ {
+ timeout = timeout ?? TimeSpan.FromSeconds(60);
+
+ await Retry.For(async () =>
+ {
+ String message = "Unable to verify on page: " + this.GetType().Name;
+
+ Should.NotThrow(() => this.WaitForElementByAccessibilityId(this.Trait), message);
+ },
+ TimeSpan.FromMinutes(1),
+ timeout).ConfigureAwait(false);
+
+ }
+
+ ///
+ /// Verifies that the trait is no longer present. Defaults to a 5 second wait.
+ ///
+ /// Time to wait before the assertion fails
+ public void WaitForPageToLeave(TimeSpan? timeout = null)
+ {
+ timeout = timeout ?? TimeSpan.FromSeconds(5);
+ var message = "Unable to verify *not* on page: " + this.GetType().Name;
+
+ Should.NotThrow(() => this.WaitForNoElementByAccessibilityId(this.Trait), message);
+ }
+
+ public async Task WaitForElementByAccessibilityId(String x, TimeSpan? timeout = null)
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ return await AppiumDriver.AndroidDriver.WaitForElementByAccessibilityId(x, timeout);
+ }
+ else if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ {
+ return await AppiumDriver.iOSDriver.WaitForElementByAccessibilityId(x, timeout);
+ }
+
+ return null;
+ }
+
+ public async Task GetPageSource()
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ return await AppiumDriver.AndroidDriver.GetPageSource();
+ }
+ else if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ {
+ return await AppiumDriver.iOSDriver.GetPageSource();
+ }
+
+ return null;
+ }
+
+ public async Task WaitForNoElementByAccessibilityId(String x)
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ await AppiumDriver.AndroidDriver.WaitForNoElementByAccessibilityId(x);
+ }
+ else if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ {
+ await AppiumDriver.iOSDriver.WaitForNoElementByAccessibilityId(x);
+ }
+ }
+
+ public async Task WaitForToastMessage(String x)
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ await AppiumDriver.AndroidDriver.WaitForToastMessage(x);
+ }
+ else if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ {
+ await AppiumDriver.iOSDriver.WaitForToastMessage(x);
+ }
+ }
+
+ public void HideKeyboard()
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ AppiumDriver.AndroidDriver.HideKeyboard();
+ }
+ else if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ {
+ //if (AppiumDriver.iOSDriver.IsKeyboardShown())
+ // AppiumDriver.iOSDriver.HideKeyboard();
+ //AppiumDriver.iOSDriver.FindElementByName("Done").Click();
+ //AppiumDriver.iOSDriver.HideKeyboard();
+ }
+ }
+
+ public IWebElement GetAlert()
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ return AppiumDriver.AndroidDriver.FindElementByClassName("androidx.appcompat.widget.AppCompatTextView");
+ }
+ else if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ {
+ return AppiumDriver.iOSDriver.FindElement(By.Name("OK"));
+ }
+
+ return null;
+ }
+
+ public IAlert SwitchToAlert()
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ return AppiumDriver.AndroidDriver.SwitchTo().Alert();
+ }
+
+ return null;
+ }
+
+ public void NavigateBack()
+ {
+ if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ AppiumDriver.AndroidDriver.Navigate().Back();
+ }
+ else if (AppiumDriver.MobileTestPlatform == MobileTestPlatform.iOS)
+ {
+ AppiumDriver.iOSDriver.Navigate().Back();
+ }
+ }
+ }
+}
diff --git a/TransactionMobile.Maui.UITests - Copy/Pages/Extenstions.cs b/TransactionMobile.Maui.UITests - Copy/Pages/Extenstions.cs
new file mode 100644
index 00000000..09819a33
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Pages/Extenstions.cs
@@ -0,0 +1,128 @@
+namespace TransactionMobile.Maui.UITests;
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Common;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Appium.Android;
+using OpenQA.Selenium.Appium.iOS;
+using Shouldly;
+
+public static class Extenstions
+{
+ // TODO: Mac & Windows Extensions
+ // TODO: May need a platform switch
+ public static AndroidElement GetAlert(this AndroidDriver driver)
+ {
+ return driver.FindElementByClassName("androidx.appcompat.widget.AppCompatTextView");
+ }
+
+ public static async Task WaitForElementByAccessibilityId(this AndroidDriver driver,
+ String selector,
+ TimeSpan? timeout = null)
+ {
+ timeout ??= TimeSpan.FromSeconds(60);
+ AndroidElement element = null;
+ await Retry.For(async () =>
+ {
+ element = driver.FindElementByAccessibilityId(selector);
+ element.ShouldNotBeNull();
+ });
+
+ return element;
+ }
+
+ public static async Task WaitForElementByAccessibilityId(this IOSDriver driver,
+ String selector,
+ TimeSpan? timeout = null)
+ {
+ timeout ??= TimeSpan.FromSeconds(60);
+ IOSElement element = null;
+ await Retry.For(async () =>
+ {
+ element = driver.FindElementByAccessibilityId(selector);
+ element.ShouldNotBeNull();
+ });
+
+ return element;
+
+ }
+
+ public static async Task WaitForNoElementByAccessibilityId(this IOSDriver driver,
+ String selector,
+ TimeSpan? timeout = null)
+ {
+ timeout ??= TimeSpan.FromSeconds(60);
+
+ await Retry.For(async () =>
+ {
+ IOSElement? element = driver.FindElementByAccessibilityId(selector);
+ element.ShouldBeNull();
+ });
+
+ }
+
+ public static async Task WaitForNoElementByAccessibilityId(this AndroidDriver driver,
+ String selector,
+ TimeSpan? timeout = null)
+ {
+ timeout ??= TimeSpan.FromSeconds(60);
+
+ await Retry.For(async () =>
+ {
+ AndroidElement? element = driver.FindElementByAccessibilityId(selector);
+ element.ShouldBeNull();
+ });
+
+ }
+
+ public static async Task WaitForToastMessage(this AndroidDriver driver,
+ String expectedToast)
+ {
+ await Retry.For(async () =>
+ {
+ Dictionary args = new Dictionary
+ {
+ {"text", expectedToast},
+ {"isRegexp", false}
+ };
+ driver.ExecuteScript("mobile: isToastVisible", args);
+
+ });
+ }
+
+ public static async Task WaitForToastMessage(this IOSDriver driver,
+ String expectedToast)
+ {
+ Boolean isDisplayed = false;
+ int count = 0;
+ do
+ {
+ if (driver.PageSource.Contains(expectedToast))
+ {
+ Console.WriteLine(driver.PageSource);
+ isDisplayed = true;
+ break;
+ }
+
+ Thread.Sleep(200); //Add your custom wait if exists
+ count++;
+
+ } while (count < 10);
+
+ Console.WriteLine(driver.PageSource);
+ isDisplayed.ShouldBeTrue();
+ }
+
+ public static async Task GetPageSource(this AndroidDriver driver)
+ {
+ return driver.PageSource;
+ }
+
+ public static async Task GetPageSource(this IOSDriver driver)
+ {
+ return driver.PageSource;
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Pages/LoginPage.cs b/TransactionMobile.Maui.UITests - Copy/Pages/LoginPage.cs
new file mode 100644
index 00000000..9b047d5d
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Pages/LoginPage.cs
@@ -0,0 +1,52 @@
+namespace TransactionMobile.Maui.UITests;
+
+using System;
+using System.Threading.Tasks;
+using OpenQA.Selenium;
+
+public class LoginPage : BasePage
+{
+ protected override String Trait => "LoginLabel";
+
+ //private readonly String EmailEntry;
+ //private readonly String PasswordEntry;
+ private readonly String LoginButton;
+ //private readonly String TestModeButton;
+ //private readonly String ErrorLabel;
+
+ public LoginPage()
+ {
+ //this.EmailEntry = "EmailEntry";
+ //this.PasswordEntry = "PasswordEntry";
+ this.LoginButton = "LoginButton";
+ //this.TestModeButton = "TestModeButton";
+ //this.ErrorLabel = "ErrorLabel";
+ }
+
+ public async Task EnterEmailAddress(String emailAddress)
+ {
+ //IWebElement element = await this.WaitForElementByAccessibilityId(this.EmailEntry);
+
+ //element.SendKeys(emailAddress);
+ }
+
+ public async Task EnterPassword(String password)
+ {
+ //IWebElement element = await this.WaitForElementByAccessibilityId(this.PasswordEntry);
+ //element.SendKeys(password);
+ }
+
+ public async Task ClickLoginButton()
+ {
+ this.HideKeyboard();
+ IWebElement element = await this.WaitForElementByAccessibilityId(this.LoginButton);
+ element.Click();
+ }
+
+ public async Task ClickTestModeButton()
+ {
+ //this.HideKeyboard();
+ //IWebElement element = await this.WaitForElementByAccessibilityId(this.TestModeButton);
+ //element.Click();
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Pages/MainPage.cs b/TransactionMobile.Maui.UITests - Copy/Pages/MainPage.cs
new file mode 100644
index 00000000..ce536f4e
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Pages/MainPage.cs
@@ -0,0 +1,70 @@
+namespace TransactionMobile.Maui.UITests;
+
+using System;
+using System.Threading.Tasks;
+
+public class MainPage : BasePage
+{
+ protected override String Trait => "Home";
+
+ private readonly String TransactionsButton;
+
+ private readonly String ReportsButton;
+
+ private readonly String ProfileButton;
+
+ private readonly String SupportButton;
+
+ private readonly String AvailableBalanceLabel;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainPage()
+ {
+ this.TransactionsButton = "TransactionsButton";
+ this.ReportsButton = "ReportsButton";
+ this.ProfileButton = "ProfileButton";
+ this.SupportButton = "SupportButton";
+ this.AvailableBalanceLabel = "AvailableBalanceValueLabel";
+ }
+
+ public async Task ClickTransactionsButton()
+ {
+ var element = await this.WaitForElementByAccessibilityId(this.TransactionsButton);
+ element.Click();
+ }
+
+ public async Task ClickReportsButton()
+ {
+ var element = await this.WaitForElementByAccessibilityId(this.ReportsButton);
+ element.Click();
+ }
+
+ public void ClickProfileButton()
+ {
+ //app.WaitForElement(this.ProfileButton);
+ //app.Tap(this.ProfileButton);
+ }
+
+ public void ClickSupportButton()
+ {
+ //app.WaitForElement(this.SupportButton);
+ //app.Tap(this.SupportButton);
+ }
+
+ public async Task GetAvailableBalanceValue(TimeSpan? timeout = default(TimeSpan?))
+ {
+ //await this.ScrollTo(this.Trait, this.AvailableBalanceLabel);
+ var element = await this.WaitForElementByAccessibilityId(this.AvailableBalanceLabel, timeout: TimeSpan.FromSeconds(30));
+
+ String availableBalanceText = element.Text.Replace(" KES", String.Empty);
+
+ if (Decimal.TryParse(availableBalanceText, out Decimal balanceValue) == false)
+ {
+ throw new Exception($"Failed to parse [{availableBalanceText}] as a Decimal");
+ }
+
+ return balanceValue;
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UITests - Copy/Steps/LoginSteps.cs b/TransactionMobile.Maui.UITests - Copy/Steps/LoginSteps.cs
new file mode 100644
index 00000000..1011aeb1
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/Steps/LoginSteps.cs
@@ -0,0 +1,56 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TransactionMobile.Maui.UITests.Steps
+{
+ using Shouldly;
+ using TechTalk.SpecFlow;
+
+ [Binding]
+ [Scope(Tag = "login")]
+ public class LoginSteps
+ {
+ LoginPage loginPage = new LoginPage();
+ //MainPage mainPage = new MainPage();
+
+ [Given(@"I am on the Login Screen")]
+ public async Task GivenIAmOnTheLoginScreen()
+ {
+ await this.loginPage.AssertOnPage();
+ }
+
+ [When(@"I enter '(.*)' as the Email Address")]
+ public async Task WhenIEnterAsTheEmailAddress(String emailAddress)
+ {
+ await this.loginPage.EnterEmailAddress(emailAddress);
+ }
+
+ [When(@"I enter '(.*)' as the Password")]
+ public async Task WhenIEnterAsThePassword(String password)
+ {
+ await this.loginPage.EnterPassword(password);
+ }
+
+ [When(@"I tap on Login")]
+ public async Task WhenITapOnLogin()
+ {
+ await this.loginPage.ClickLoginButton();
+ }
+
+ [Then(@"the Merchant Home Page is displayed")]
+ public async Task ThenTheMerchantHomePageIsDisplayed()
+ {
+ //await this.mainPage.AssertOnPage();
+ }
+
+ [Then(@"the available balance is shown as (.*)")]
+ public async Task ThenTheAvailableBalanceIsShownAs(Decimal expectedAvailableBalance)
+ {
+ //Decimal availableBalance = await this.mainPage.GetAvailableBalanceValue(TimeSpan.FromSeconds(120)).ConfigureAwait(false);
+ //availableBalance.ShouldBe(expectedAvailableBalance);
+ }
+ }
+}
diff --git a/TransactionMobile.Maui.UITests - Copy/TransactionMobile.Maui.UITests.csproj b/TransactionMobile.Maui.UITests - Copy/TransactionMobile.Maui.UITests.csproj
new file mode 100644
index 00000000..c22100dd
--- /dev/null
+++ b/TransactionMobile.Maui.UITests - Copy/TransactionMobile.Maui.UITests.csproj
@@ -0,0 +1,26 @@
+
+
+
+ net6.0
+ enable
+
+ false
+
+ Debug;Release;TestAuomation
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TransactionMobile.Maui.UiTests/Common/BaseTestFixture.cs b/TransactionMobile.Maui.UiTests/Common/BaseTestFixture.cs
new file mode 100644
index 00000000..09271d7e
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Common/BaseTestFixture.cs
@@ -0,0 +1,17 @@
+using OpenQA.Selenium.Appium;
+using TransactionMobile.Maui.UiTests.Drivers;
+
+namespace TransactionMobile.Maui.UITests.Common
+{
+ public abstract class BaseTestFixture
+ {
+ #region Constructors
+
+ protected BaseTestFixture(MobileTestPlatform mobileTestPlatform)
+ {
+ AppiumDriverWrapper.MobileTestPlatform = mobileTestPlatform;
+ }
+
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UiTests/Common/Retry.cs b/TransactionMobile.Maui.UiTests/Common/Retry.cs
new file mode 100644
index 00000000..c82c362a
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Common/Retry.cs
@@ -0,0 +1,68 @@
+namespace TransactionMobile.Maui.UITests.Common;
+
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+
+public static class Retry
+{
+ #region Fields
+
+ ///
+ /// The default retry for
+ ///
+ private static readonly TimeSpan DefaultRetryFor = TimeSpan.FromSeconds(60);
+
+ ///
+ /// The default retry interval
+ ///
+ private static readonly TimeSpan DefaultRetryInterval = TimeSpan.FromSeconds(5);
+
+ #endregion
+
+ #region Methods
+
+ ///
+ /// Fors the specified action.
+ ///
+ /// The action.
+ /// The retry for.
+ /// The retry interval.
+ ///
+ public static async Task For(Func action,
+ TimeSpan? retryFor = null,
+ TimeSpan? retryInterval = null)
+ {
+ DateTime startTime = DateTime.Now;
+ Exception lastException = null;
+
+ if (retryFor == null)
+ {
+ retryFor = Retry.DefaultRetryFor;
+ }
+
+ while (DateTime.Now.Subtract(startTime).TotalMilliseconds < retryFor.Value.TotalMilliseconds)
+ {
+ try
+ {
+ await action().ConfigureAwait(false);
+ lastException = null;
+ break;
+ }
+ catch (Exception e)
+ {
+ lastException = e;
+
+ // wait before retrying
+ Thread.Sleep(retryInterval ?? Retry.DefaultRetryInterval);
+ }
+ }
+
+ if (lastException != null)
+ {
+ throw lastException;
+ }
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UiTests/Common/SpecflowTableHelper.cs b/TransactionMobile.Maui.UiTests/Common/SpecflowTableHelper.cs
new file mode 100644
index 00000000..2e5e2846
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Common/SpecflowTableHelper.cs
@@ -0,0 +1,135 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TransactionMobile.Maui.UITests.Common
+{
+ using TechTalk.SpecFlow;
+
+ public static class SpecflowTableHelper
+ {
+ #region Methods
+
+ ///
+ /// Gets the enum value.
+ ///
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static T GetEnumValue(TableRow row,
+ String key) where T : struct
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ Enum.TryParse(field, out T myEnum);
+
+ return myEnum;
+ }
+
+ ///
+ /// Gets the boolean value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static Boolean GetBooleanValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return bool.TryParse(field, out Boolean value) && value;
+ }
+
+ ///
+ /// Gets the date for date string.
+ ///
+ /// The date string.
+ /// The today.
+ ///
+ public static DateTime GetDateForDateString(String dateString,
+ DateTime today)
+ {
+ switch (dateString.ToUpper())
+ {
+ case "TODAY":
+ return today.Date;
+ case "YESTERDAY":
+ return today.AddDays(-1).Date;
+ case "LASTWEEK":
+ return today.AddDays(-7).Date;
+ case "LASTMONTH":
+ return today.AddMonths(-1).Date;
+ case "LASTYEAR":
+ return today.AddYears(-1).Date;
+ case "TOMORROW":
+ return today.AddDays(1).Date;
+ default:
+ return DateTime.Parse(dateString);
+ }
+ }
+
+ ///
+ /// Gets the decimal value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static Decimal GetDecimalValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return decimal.TryParse(field, out Decimal value) ? value : 0;
+ }
+
+ ///
+ /// Gets the int value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static Int32 GetIntValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ return int.TryParse(field, out Int32 value) ? value : -1;
+ }
+
+ ///
+ /// Gets the short value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static Int16 GetShortValue(TableRow row,
+ String key)
+ {
+ String field = SpecflowTableHelper.GetStringRowValue(row, key);
+
+ if (short.TryParse(field, out Int16 value))
+ {
+ return value;
+ }
+
+ return -1;
+ }
+
+ ///
+ /// Gets the string row value.
+ ///
+ /// The row.
+ /// The key.
+ ///
+ public static String GetStringRowValue(TableRow row,
+ String key)
+ {
+ return row.TryGetValue(key, out String value) ? value : "";
+ }
+
+ #endregion
+ }
+}
diff --git a/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs b/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs
new file mode 100644
index 00000000..44b7823e
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs
@@ -0,0 +1,108 @@
+using OpenQA.Selenium.Appium;
+using OpenQA.Selenium.Appium.Enums;
+using OpenQA.Selenium.Appium.Service;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TransactionMobile.Maui.UiTests.Drivers
+{
+ public enum MobileTestPlatform
+ {
+ iOS,
+ Android,
+ Windows,
+ MacCatalyst
+ }
+
+ public class AppiumDriverWrapper
+ {
+ public static MobileTestPlatform MobileTestPlatform;
+ public static AppiumDriver Driver;
+
+ public void StartApp()
+ {
+
+ var streamWriter = new StreamWriter("C:\\Temp\\Debugging.log", append:true);
+ try
+ {
+
+ AppiumLocalService appiumService = new AppiumServiceBuilder().UsingPort(4723).Build();
+
+ if (appiumService.IsRunning == false)
+ {
+ appiumService.Start();
+ appiumService.OutputDataReceived += (sender, args) => { Console.WriteLine(args.Data); };
+ }
+
+ if (AppiumDriverWrapper.MobileTestPlatform == MobileTestPlatform.Android) {
+ AppiumDriverWrapper.SetupAndroidDriver(appiumService);
+ }
+ else if (AppiumDriverWrapper.MobileTestPlatform == MobileTestPlatform.iOS) {
+ AppiumDriverWrapper.SetupiOSDriver(appiumService);
+ }
+
+ }
+ catch (Exception e)
+ {
+ streamWriter.Close();
+ throw;
+ }
+ }
+
+ private static void SetupiOSDriver(AppiumLocalService appiumService) {
+ var driverOptions = new AppiumOptions();
+ driverOptions.AutomationName = "XCUITest";
+ driverOptions.PlatformName = "iOS";
+ driverOptions.PlatformVersion = "15.4";
+ driverOptions.DeviceName = "iPhone 11";
+
+ String assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ String binariesFolder = Path.Combine(assemblyFolder, "..", "..", "..", "..", @"TransactionMobile.Maui/bin/Release/net6.0-ios/iossimulator-x64/");
+ var apkPath = Path.Combine(binariesFolder, "TransactionMobile.Maui.app");
+ driverOptions.App = apkPath;
+ driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.FullReset, true);
+ driverOptions.AddAdditionalAppiumOption("useNewWDA", true);
+ driverOptions.AddAdditionalAppiumOption("wdaLaunchTimeout", 999999999);
+ driverOptions.AddAdditionalAppiumOption("wdaConnectionTimeout", 999999999);
+ driverOptions.AddAdditionalAppiumOption("restart", true);
+
+ AppiumDriverWrapper.Driver = new OpenQA.Selenium.Appium.iOS.IOSDriver(appiumService, driverOptions, TimeSpan.FromMinutes(10));
+ }
+
+ private static void SetupAndroidDriver(AppiumLocalService appiumService) {
+ // Do Android stuff to start up
+ var driverOptions = new AppiumOptions();
+ driverOptions.AddAdditionalAppiumOption("adbExecTimeout", TimeSpan.FromMinutes(5).Milliseconds);
+ driverOptions.AutomationName = "UIAutomator2";
+ driverOptions.PlatformName = "Android";
+ driverOptions.PlatformVersion = "9.0";
+ driverOptions.DeviceName = "emulator-5554";
+
+ // TODO: Only do this locally
+ //driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.FullReset, true);
+ driverOptions.AddAdditionalAppiumOption("appPackage", "com.transactionprocessing.pos");
+ //driverOptions.AddAdditionalAppiumOption("forceEspressoRebuild", true);
+ driverOptions.AddAdditionalAppiumOption("enforceAppInstall", true);
+ //driverOptions.AddAdditionalAppiumOption("noSign", true);
+ //driverOptions.AddAdditionalAppiumOption("espressoBuildConfig",
+ // "{ \"additionalAppDependencies\": [ \"com.google.android.material:material:1.0.0\", \"androidx.lifecycle:lifecycle-extensions:2.1.0\" ] }");
+
+ String assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ String binariesFolder = Path.Combine(assemblyFolder, "..", "..", "..", "..", @"TransactionMobile.Maui/bin/Release/net6.0-android/");
+
+ var apkPath = Path.Combine(binariesFolder, "com.transactionprocessing.pos.apk");
+ driverOptions.App = apkPath;
+ AppiumDriverWrapper.Driver = new OpenQA.Selenium.Appium.Android.AndroidDriver(appiumService, driverOptions, TimeSpan.FromMinutes(5));
+ }
+
+ public void StopApp()
+ {
+ AppiumDriverWrapper.Driver?.CloseApp();
+ }
+ }
+}
diff --git a/TransactionMobile.Maui.UiTests/Features/Login.feature b/TransactionMobile.Maui.UiTests/Features/Login.feature
new file mode 100644
index 00000000..a0715d25
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Features/Login.feature
@@ -0,0 +1,13 @@
+@background @login
+Feature: Login
+
+Background:
+
+@PRTest
+Scenario: Login as Merchant
+ Given I am on the Login Screen
+ And the application is in training mode
+ When I enter 'merchantuser@testmerchant1.co.uk' as the Email Address
+ And I enter '123456' as the Password
+ And I tap on Login
+ Then the Merchant Home Page is displayed
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UiTests/Features/Login.feature.cs b/TransactionMobile.Maui.UiTests/Features/Login.feature.cs
new file mode 100644
index 00000000..fb66a47b
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Features/Login.feature.cs
@@ -0,0 +1,132 @@
+// ------------------------------------------------------------------------------
+//
+// This code was generated by SpecFlow (https://www.specflow.org/).
+// SpecFlow Version:3.9.0.0
+// SpecFlow Generator Version:3.9.0.0
+//
+// Changes to this file may cause incorrect behavior and will be lost if
+// the code is regenerated.
+//
+// ------------------------------------------------------------------------------
+#region Designer generated code
+#pragma warning disable
+namespace TransactionMobile.Maui.UiTests.Features
+{
+ using TechTalk.SpecFlow;
+ using System;
+ using System.Linq;
+
+
+ [System.CodeDom.Compiler.GeneratedCodeAttribute("TechTalk.SpecFlow", "3.9.0.0")]
+ [System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
+ [NUnit.Framework.TestFixtureAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Login")]
+ [NUnit.Framework.CategoryAttribute("background")]
+ [NUnit.Framework.CategoryAttribute("login")]
+ public partial class LoginFeature
+ {
+
+ private TechTalk.SpecFlow.ITestRunner testRunner;
+
+ private static string[] featureTags = new string[] {
+ "background",
+ "login"};
+
+#line 1 "Login.feature"
+#line hidden
+
+ [NUnit.Framework.OneTimeSetUpAttribute()]
+ public virtual void FeatureSetup()
+ {
+ testRunner = TechTalk.SpecFlow.TestRunnerManager.GetTestRunner();
+ TechTalk.SpecFlow.FeatureInfo featureInfo = new TechTalk.SpecFlow.FeatureInfo(new System.Globalization.CultureInfo("en-US"), "Features", "Login", null, ProgrammingLanguage.CSharp, featureTags);
+ testRunner.OnFeatureStart(featureInfo);
+ }
+
+ [NUnit.Framework.OneTimeTearDownAttribute()]
+ public virtual void FeatureTearDown()
+ {
+ testRunner.OnFeatureEnd();
+ testRunner = null;
+ }
+
+ [NUnit.Framework.SetUpAttribute()]
+ public void TestInitialize()
+ {
+ }
+
+ [NUnit.Framework.TearDownAttribute()]
+ public void TestTearDown()
+ {
+ testRunner.OnScenarioEnd();
+ }
+
+ public void ScenarioInitialize(TechTalk.SpecFlow.ScenarioInfo scenarioInfo)
+ {
+ testRunner.OnScenarioInitialize(scenarioInfo);
+ testRunner.ScenarioContext.ScenarioContainer.RegisterInstanceAs(NUnit.Framework.TestContext.CurrentContext);
+ }
+
+ public void ScenarioStart()
+ {
+ testRunner.OnScenarioStart();
+ }
+
+ public void ScenarioCleanup()
+ {
+ testRunner.CollectScenarioErrors();
+ }
+
+ public virtual void FeatureBackground()
+ {
+#line 4
+#line hidden
+ }
+
+ [NUnit.Framework.TestAttribute()]
+ [NUnit.Framework.DescriptionAttribute("Login as Merchant")]
+ [NUnit.Framework.CategoryAttribute("PRTest")]
+ public void LoginAsMerchant()
+ {
+ string[] tagsOfScenario = new string[] {
+ "PRTest"};
+ System.Collections.Specialized.OrderedDictionary argumentsOfScenario = new System.Collections.Specialized.OrderedDictionary();
+ TechTalk.SpecFlow.ScenarioInfo scenarioInfo = new TechTalk.SpecFlow.ScenarioInfo("Login as Merchant", null, tagsOfScenario, argumentsOfScenario, featureTags);
+#line 7
+this.ScenarioInitialize(scenarioInfo);
+#line hidden
+ if ((TagHelper.ContainsIgnoreTag(tagsOfScenario) || TagHelper.ContainsIgnoreTag(featureTags)))
+ {
+ testRunner.SkipScenario();
+ }
+ else
+ {
+ this.ScenarioStart();
+#line 4
+this.FeatureBackground();
+#line hidden
+#line 8
+ testRunner.Given("I am on the Login Screen", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Given ");
+#line hidden
+#line 9
+ testRunner.And("the application is in training mode", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+#line 10
+ testRunner.When("I enter \'merchantuser@testmerchant1.co.uk\' as the Email Address", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "When ");
+#line hidden
+#line 11
+ testRunner.And("I enter \'123456\' as the Password", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+#line 12
+ testRunner.And("I tap on Login", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "And ");
+#line hidden
+#line 13
+ testRunner.Then("the Merchant Home Page is displayed", ((string)(null)), ((TechTalk.SpecFlow.Table)(null)), "Then ");
+#line hidden
+ }
+ this.ScenarioCleanup();
+ }
+ }
+}
+#pragma warning restore
+#endregion
diff --git a/TransactionMobile.Maui.UiTests/Features/LoginFeature.cs b/TransactionMobile.Maui.UiTests/Features/LoginFeature.cs
new file mode 100644
index 00000000..498cf8ae
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Features/LoginFeature.cs
@@ -0,0 +1,16 @@
+using TransactionMobile.Maui.UITests.Common;
+using TransactionMobile.Maui.UiTests.Drivers;
+
+namespace TransactionMobile.Maui.UiTests.Features;
+
+using NUnit.Framework;
+
+[TestFixture(MobileTestPlatform.Android, Category = "Android")]
+[TestFixture(MobileTestPlatform.iOS, Category = "iOS")]
+public partial class LoginFeature : BaseTestFixture
+{
+ public LoginFeature(MobileTestPlatform mobileTestPlatform)
+ : base(mobileTestPlatform)
+ {
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UiTests/Hooks/AppiumHooks.cs b/TransactionMobile.Maui.UiTests/Hooks/AppiumHooks.cs
new file mode 100644
index 00000000..7058b7b0
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Hooks/AppiumHooks.cs
@@ -0,0 +1,34 @@
+using OpenQA.Selenium.Appium;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TechTalk.SpecFlow;
+using TransactionMobile.Maui.UiTests.Drivers;
+
+namespace TransactionMobile.Maui.UiTests.Hooks
+{
+ [Binding]
+ public class AppiumHooks
+ {
+ private readonly AppiumDriverWrapper _appiumDriver;
+
+ public AppiumHooks(AppiumDriverWrapper appiumDriver)
+ {
+ _appiumDriver = appiumDriver;
+ }
+
+ [BeforeScenario()]
+ public void StartApp()
+ {
+ _appiumDriver.StartApp();
+ }
+
+ [AfterScenario()]
+ public void ShutdownApp()
+ {
+ _appiumDriver.StopApp();
+ }
+ }
+}
diff --git a/TransactionMobile.Maui.UiTests/Pages/BasePage.cs b/TransactionMobile.Maui.UiTests/Pages/BasePage.cs
new file mode 100644
index 00000000..84137de5
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Pages/BasePage.cs
@@ -0,0 +1,92 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using TransactionMobile.Maui.UiTests.Drivers;
+
+namespace TransactionMobile.Maui.UITests
+{
+ using Common;
+ using OpenQA.Selenium;
+ using Shouldly;
+
+ public abstract class BasePage
+ {
+ protected abstract String Trait { get; }
+
+ public async Task AssertOnPage(TimeSpan? timeout = null)
+ {
+ timeout = timeout ?? TimeSpan.FromSeconds(60);
+
+ await Retry.For(async () =>
+ {
+ String message = $"Unable to verify on page: {this.GetType().Name} {Environment.NewLine} Source: {AppiumDriverWrapper.Driver.PageSource}";
+
+ Should.NotThrow(() => this.WaitForElementByAccessibilityId(this.Trait), message);
+ },
+ TimeSpan.FromMinutes(1),
+ timeout).ConfigureAwait(false);
+ }
+
+ ///
+ /// Verifies that the trait is no longer present. Defaults to a 5 second wait.
+ ///
+ /// Time to wait before the assertion fails
+ public void WaitForPageToLeave(TimeSpan? timeout = null)
+ {
+ timeout = timeout ?? TimeSpan.FromSeconds(5);
+ var message = "Unable to verify *not* on page: " + this.GetType().Name;
+
+ Should.NotThrow(() => this.WaitForNoElementByAccessibilityId(this.Trait), message);
+ }
+
+ public async Task WaitForElementByAccessibilityId(String accessibilityId, TimeSpan? timeout = null)
+ {
+ return await AppiumDriverWrapper.Driver.WaitForElementByAccessibilityId(accessibilityId, timeout);
+ }
+
+ public async Task GetPageSource()
+ {
+ return await AppiumDriverWrapper.Driver.GetPageSource();
+ }
+
+ public async Task WaitForNoElementByAccessibilityId(String accessibilityId)
+ {
+ await AppiumDriverWrapper.Driver.WaitForNoElementByAccessibilityId(accessibilityId);
+ }
+
+ public async Task WaitForToastMessage(String toastMessage)
+ {
+ await AppiumDriverWrapper.Driver.WaitForToastMessage(AppiumDriverWrapper.MobileTestPlatform, toastMessage);
+ }
+
+ public void HideKeyboard()
+ {
+ //AppiumDriverWrapper.Driver.HideKeyboard();
+ if (AppiumDriverWrapper.MobileTestPlatform == MobileTestPlatform.Android)
+ {
+ AppiumDriverWrapper.Driver.HideKeyboard();
+ }
+ else if (AppiumDriverWrapper.MobileTestPlatform == MobileTestPlatform.iOS)
+ {
+ AppiumDriverWrapper.Driver.FindElement(By.Name("Done")).Click();
+ }
+ }
+
+ public IWebElement GetAlert()
+ {
+ return AppiumDriverWrapper.Driver.FindElement(By.Name("OK"));
+ }
+
+ public IAlert SwitchToAlert()
+ {
+ return AppiumDriverWrapper.Driver.SwitchTo().Alert();
+ }
+
+ public void NavigateBack()
+ {
+ AppiumDriverWrapper.Driver.Navigate().Back();
+ }
+ }
+}
diff --git a/TransactionMobile.Maui.UiTests/Pages/Extenstions.cs b/TransactionMobile.Maui.UiTests/Pages/Extenstions.cs
new file mode 100644
index 00000000..72d87cb0
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Pages/Extenstions.cs
@@ -0,0 +1,95 @@
+using OpenQA.Selenium.Appium;
+using TransactionMobile.Maui.UiTests.Drivers;
+
+namespace TransactionMobile.Maui.UITests;
+
+using System;
+using System.Collections.Generic;
+using System.Threading;
+using System.Threading.Tasks;
+using Common;
+using OpenQA.Selenium;
+using OpenQA.Selenium.Appium.Android;
+using OpenQA.Selenium.Appium.iOS;
+using Shouldly;
+
+public static class Extenstions
+{
+ // TODO: Mac & Windows Extensions
+ // TODO: May need a platform switch
+ //public static AndroidElement GetAlert(this AndroidDriver driver)
+ //{
+ // return driver.FindElementByClassName("androidx.appcompat.widget.AppCompatTextView");
+ //}
+
+ public static async Task WaitForElementByAccessibilityId(this AppiumDriver driver,
+ String selector,
+ TimeSpan? timeout = null) {
+ IWebElement? element = null;
+ timeout ??= TimeSpan.FromSeconds(60);
+
+ await Retry.For(async () =>
+ {
+ element = driver.FindElement(MobileBy.AccessibilityId(selector));
+ element.ShouldNotBeNull();
+ });
+ return element;
+ }
+
+ public static async Task WaitForNoElementByAccessibilityId(this AppiumDriver driver,
+ String selector,
+ TimeSpan? timeout = null)
+ {
+ timeout ??= TimeSpan.FromSeconds(60);
+
+ await Retry.For(async () =>
+ {
+ IWebElement? element = driver.FindElement(MobileBy.AccessibilityId(selector));
+ element.ShouldBeNull();
+ });
+
+ }
+
+ public static async Task WaitForToastMessage(this AppiumDriver driver, MobileTestPlatform platform, String expectedToast)
+ {
+ if (platform == MobileTestPlatform.Android)
+ {
+ await Retry.For(async () =>
+ {
+ Dictionary args = new Dictionary
+ {
+ {"text", expectedToast},
+ {"isRegexp", false}
+ };
+ driver.ExecuteScript("mobile: isToastVisible", args);
+
+ });
+ }
+ else if (platform == MobileTestPlatform.iOS)
+ {
+ Boolean isDisplayed = false;
+ int count = 0;
+ do
+ {
+ if (driver.PageSource.Contains(expectedToast))
+ {
+ Console.WriteLine(driver.PageSource);
+ isDisplayed = true;
+ break;
+ }
+
+ Thread.Sleep(200); //Add your custom wait if exists
+ count++;
+
+ } while (count < 10);
+
+ Console.WriteLine(driver.PageSource);
+ isDisplayed.ShouldBeTrue();
+ }
+ }
+
+ public static async Task GetPageSource(this AppiumDriver driver)
+ {
+ return driver.PageSource;
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UiTests/Pages/LoginPage.cs b/TransactionMobile.Maui.UiTests/Pages/LoginPage.cs
new file mode 100644
index 00000000..db5b56c1
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Pages/LoginPage.cs
@@ -0,0 +1,85 @@
+using TransactionMobile.Maui.UiTests.Drivers;
+
+namespace TransactionMobile.Maui.UITests;
+
+using System;
+using System.Threading.Tasks;
+using OpenQA.Selenium;
+
+public class LoginPage : BasePage
+{
+ protected override String Trait => "LoginLabel";
+
+ private readonly String UserNameEntry;
+ private readonly String PasswordEntry;
+ private readonly String LoginButton;
+ private readonly String UseTrainingModeSwitch;
+ //private readonly String TestModeButton;
+ //private readonly String ErrorLabel;
+
+ public LoginPage()
+ {
+ this.UserNameEntry = "UserNameEntry";
+ this.PasswordEntry = "PasswordEntry";
+ this.LoginButton = "LoginButton";
+ this.UseTrainingModeSwitch = "UseTrainingModeSwitch";
+ //this.TestModeButton = "TestModeButton";
+ //this.ErrorLabel = "ErrorLabel";
+ }
+
+ public async Task IsTrainingModeOn()
+ {
+ IWebElement element = await this.WaitForElementByAccessibilityId(this.UseTrainingModeSwitch);
+ String? text = element.Text;
+ if (AppiumDriverWrapper.MobileTestPlatform == MobileTestPlatform.Android) {
+ if (text == "OFF") {
+ return false;
+ }
+
+ return true;
+ }
+ if (AppiumDriverWrapper.MobileTestPlatform == MobileTestPlatform.iOS) {
+ if (text == "0") {
+ return false;
+ }
+
+ return true;
+ }
+
+ return true;
+ }
+
+ public async Task SetTrainingModeOn()
+ {
+ IWebElement element = await this.WaitForElementByAccessibilityId(this.UseTrainingModeSwitch);
+ var text = element.Text;
+ element.Click();
+ }
+
+ public async Task SetTrainingModeOff()
+ {
+ IWebElement element = await this.WaitForElementByAccessibilityId(this.UseTrainingModeSwitch);
+
+ element.Click();
+ }
+
+ public async Task EnterEmailAddress(String emailAddress)
+ {
+ IWebElement element = await this.WaitForElementByAccessibilityId(this.UserNameEntry);
+
+ element.SendKeys(emailAddress);
+ }
+
+ public async Task EnterPassword(String password)
+ {
+ IWebElement element = await this.WaitForElementByAccessibilityId(this.PasswordEntry);
+ element.SendKeys(password);
+ }
+
+ public async Task ClickLoginButton()
+ {
+ //this.HideKeyboard();
+ IWebElement element = await this.WaitForElementByAccessibilityId(this.LoginButton);
+ element.Click();
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UiTests/Pages/MainPage.cs b/TransactionMobile.Maui.UiTests/Pages/MainPage.cs
new file mode 100644
index 00000000..472c283d
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Pages/MainPage.cs
@@ -0,0 +1,71 @@
+namespace TransactionMobile.Maui.UITests;
+
+using System;
+using System.Threading.Tasks;
+
+public class MainPage : BasePage
+{
+ protected override String Trait => "Home";
+
+ private readonly String TransactionsButton;
+
+ private readonly String ReportsButton;
+
+ private readonly String ProfileButton;
+
+ private readonly String SupportButton;
+
+ private readonly String AvailableBalanceLabel;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ public MainPage()
+ {
+ this.TransactionsButton = "TransactionsButton";
+ this.ReportsButton = "ReportsButton";
+ this.ProfileButton = "ProfileButton";
+ this.SupportButton = "SupportButton";
+ this.AvailableBalanceLabel = "AvailableBalanceValueLabel";
+ }
+
+ public async Task ClickTransactionsButton()
+ {
+ //var element = await this.WaitForElementByAccessibilityId(this.TransactionsButton);
+ //element.Click();
+ }
+
+ public async Task ClickReportsButton()
+ {
+ //var element = await this.WaitForElementByAccessibilityId(this.ReportsButton);
+ //element.Click();
+ }
+
+ public void ClickProfileButton()
+ {
+ //app.WaitForElement(this.ProfileButton);
+ //app.Tap(this.ProfileButton);
+ }
+
+ public void ClickSupportButton()
+ {
+ //app.WaitForElement(this.SupportButton);
+ //app.Tap(this.SupportButton);
+ }
+
+ public async Task GetAvailableBalanceValue(TimeSpan? timeout = default(TimeSpan?))
+ {
+ //await this.ScrollTo(this.Trait, this.AvailableBalanceLabel);
+ //var element = await this.WaitForElementByAccessibilityId(this.AvailableBalanceLabel, timeout: TimeSpan.FromSeconds(30));
+
+ //String availableBalanceText = element.Text.Replace(" KES", String.Empty);
+
+ //if (Decimal.TryParse(availableBalanceText, out Decimal balanceValue) == false)
+ //{
+ // throw new Exception($"Failed to parse [{availableBalanceText}] as a Decimal");
+ //}
+
+ //return balanceValue;
+ return 0;
+ }
+}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs b/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs
new file mode 100644
index 00000000..9d26ad55
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs
@@ -0,0 +1,64 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace TransactionMobile.Maui.UITests.Steps
+{
+ using TechTalk.SpecFlow;
+
+ [Binding]
+ [Scope(Tag = "login")]
+ public class LoginSteps
+ {
+ LoginPage loginPage = new LoginPage();
+ MainPage mainPage = new MainPage();
+
+ [Given(@"I am on the Login Screen")]
+ public async Task GivenIAmOnTheLoginScreen()
+ {
+ await this.loginPage.AssertOnPage();
+ }
+
+ [Given(@"the application is in training mode")]
+ public async Task GivenTheApplicationIsInTrainingMode() {
+ var isTrainingModeOn = await this.loginPage.IsTrainingModeOn();
+
+ if (isTrainingModeOn == false)
+ await this.loginPage.SetTrainingModeOn();
+ }
+
+
+ [When(@"I enter '(.*)' as the Email Address")]
+ public async Task WhenIEnterAsTheEmailAddress(String emailAddress)
+ {
+ await this.loginPage.EnterEmailAddress(emailAddress);
+ }
+
+ [When(@"I enter '(.*)' as the Password")]
+ public async Task WhenIEnterAsThePassword(String password)
+ {
+ await this.loginPage.EnterPassword(password);
+ }
+
+ [When(@"I tap on Login")]
+ public async Task WhenITapOnLogin()
+ {
+ await this.loginPage.ClickLoginButton();
+ }
+
+ [Then(@"the Merchant Home Page is displayed")]
+ public async Task ThenTheMerchantHomePageIsDisplayed()
+ {
+ await this.mainPage.AssertOnPage();
+ }
+
+ [Then(@"the available balance is shown as (.*)")]
+ public async Task ThenTheAvailableBalanceIsShownAs(Decimal expectedAvailableBalance)
+ {
+ //Decimal availableBalance = await this.mainPage.GetAvailableBalanceValue(TimeSpan.FromSeconds(120)).ConfigureAwait(false);
+ //availableBalance.ShouldBe(expectedAvailableBalance);
+ }
+ }
+}
diff --git a/TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj b/TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj
new file mode 100644
index 00000000..6f8ea286
--- /dev/null
+++ b/TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj
@@ -0,0 +1,29 @@
+
+
+
+ net6.0
+ enable
+
+ false
+
+
+
+
+
+
+
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+ all
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/TransactionMobile.Maui.sln b/TransactionMobile.Maui.sln
index 5a1818ea..95de4fa9 100644
--- a/TransactionMobile.Maui.sln
+++ b/TransactionMobile.Maui.sln
@@ -18,10 +18,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
Nuget.config = Nuget.config
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TransactionMobile.Maui.UiTests", "TransactionMobile.Maui.UiTests\TransactionMobile.Maui.UiTests.csproj", "{1F7CE7A2-6350-4F8A-B758-5E275C9C88C9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
+ TestAuomation|Any CPU = TestAuomation|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{73668181-7A26-435D-83E3-CF141AC8FD0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
@@ -30,14 +33,27 @@ Global
{73668181-7A26-435D-83E3-CF141AC8FD0B}.Release|Any CPU.ActiveCfg = Release|Any CPU
{73668181-7A26-435D-83E3-CF141AC8FD0B}.Release|Any CPU.Build.0 = Release|Any CPU
{73668181-7A26-435D-83E3-CF141AC8FD0B}.Release|Any CPU.Deploy.0 = Release|Any CPU
+ {73668181-7A26-435D-83E3-CF141AC8FD0B}.TestAuomation|Any CPU.ActiveCfg = Release|Any CPU
+ {73668181-7A26-435D-83E3-CF141AC8FD0B}.TestAuomation|Any CPU.Build.0 = Release|Any CPU
+ {73668181-7A26-435D-83E3-CF141AC8FD0B}.TestAuomation|Any CPU.Deploy.0 = Release|Any CPU
{0894F054-5C4D-4DDD-A8E9-636416189234}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0894F054-5C4D-4DDD-A8E9-636416189234}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0894F054-5C4D-4DDD-A8E9-636416189234}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0894F054-5C4D-4DDD-A8E9-636416189234}.Release|Any CPU.Build.0 = Release|Any CPU
+ {0894F054-5C4D-4DDD-A8E9-636416189234}.TestAuomation|Any CPU.ActiveCfg = Release|Any CPU
+ {0894F054-5C4D-4DDD-A8E9-636416189234}.TestAuomation|Any CPU.Build.0 = Release|Any CPU
{902D54CF-CD5F-4932-B1DC-01A3937AC054}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{902D54CF-CD5F-4932-B1DC-01A3937AC054}.Debug|Any CPU.Build.0 = Debug|Any CPU
{902D54CF-CD5F-4932-B1DC-01A3937AC054}.Release|Any CPU.ActiveCfg = Release|Any CPU
{902D54CF-CD5F-4932-B1DC-01A3937AC054}.Release|Any CPU.Build.0 = Release|Any CPU
+ {902D54CF-CD5F-4932-B1DC-01A3937AC054}.TestAuomation|Any CPU.ActiveCfg = Release|Any CPU
+ {902D54CF-CD5F-4932-B1DC-01A3937AC054}.TestAuomation|Any CPU.Build.0 = Release|Any CPU
+ {1F7CE7A2-6350-4F8A-B758-5E275C9C88C9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F7CE7A2-6350-4F8A-B758-5E275C9C88C9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {1F7CE7A2-6350-4F8A-B758-5E275C9C88C9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {1F7CE7A2-6350-4F8A-B758-5E275C9C88C9}.Release|Any CPU.Build.0 = Release|Any CPU
+ {1F7CE7A2-6350-4F8A-B758-5E275C9C88C9}.TestAuomation|Any CPU.ActiveCfg = Debug|Any CPU
+ {1F7CE7A2-6350-4F8A-B758-5E275C9C88C9}.TestAuomation|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -46,6 +62,7 @@ Global
{73668181-7A26-435D-83E3-CF141AC8FD0B} = {1CBEF4C1-7D90-4A78-AA55-D81F1447A70E}
{0894F054-5C4D-4DDD-A8E9-636416189234} = {AB312EE3-CBA4-469A-8694-67C5466298C5}
{902D54CF-CD5F-4932-B1DC-01A3937AC054} = {1CBEF4C1-7D90-4A78-AA55-D81F1447A70E}
+ {1F7CE7A2-6350-4F8A-B758-5E275C9C88C9} = {AB312EE3-CBA4-469A-8694-67C5466298C5}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {61F7FB11-1E47-470C-91E2-47F8143E1572}
diff --git a/TransactionMobile.Maui/App.xaml.cs b/TransactionMobile.Maui/App.xaml.cs
index d4fd107f..12cb5d6a 100644
--- a/TransactionMobile.Maui/App.xaml.cs
+++ b/TransactionMobile.Maui/App.xaml.cs
@@ -1,9 +1,13 @@
-using TransactionMobile.Maui.Pages.Reports;
+using Microsoft.Maui.Platform;
+using TransactionMobile.Maui.Pages.Reports;
using TransactionMobile.Maui.Pages.Transactions.MobileTopup;
using TransactionMobile.Maui.Pages.Transactions.Voucher;
namespace TransactionMobile.Maui;
+using BusinessLogic.ViewModels;
+using Microsoft.Maui.Handlers;
+using Pages;
using Pages.AppHome;
using Pages.Transactions.Admin;
using TransactionMobile.Maui.BusinessLogic.Services;
@@ -15,7 +19,7 @@ public App()
{
InitializeComponent();
- Microsoft.Maui.Handlers.EntryHandler.ElementMapper.AppendToMapping("TrainingMode", (handler, view) =>
+ Microsoft.Maui.Handlers.LabelHandler.ElementMapper.AppendToMapping("TrainingMode", (handler, view) =>
{
if (view is TitleLabel)
{
@@ -30,7 +34,109 @@ public App()
}
});
- MainPage = new AppShell();
+#if ANDROID
+ ViewHandler.ViewMapper.ModifyMapping("AutomationId", (handler, view, previousAction) =>
+ {
+ if (handler.PlatformView is Android.Views.View androidView)
+ {
+ if (String.IsNullOrWhiteSpace(view.AutomationId))
+ return;
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is not AutomationIdDelegate)
+ AndroidX.Core.View.ViewCompat.SetAccessibilityDelegate(androidView, new AutomationIdDelegate());
+
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is AutomationIdDelegate td)
+ td.AutomationId = view.AutomationId;
+
+ androidView.ContentDescription = view.AutomationId;
+ }
+ });
+
+ EntryHandler.Mapper.ModifyMapping("AutomationId", (handler, view, previousAction) =>
+ {
+ if (handler.PlatformView is Android.Views.View androidView)
+ {
+ if (String.IsNullOrWhiteSpace(view.AutomationId))
+ return;
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is not AutomationIdDelegate)
+ AndroidX.Core.View.ViewCompat.SetAccessibilityDelegate(androidView, new AutomationIdDelegate());
+
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is AutomationIdDelegate td)
+ td.AutomationId = view.AutomationId;
+
+ androidView.ContentDescription = view.AutomationId;
+ }
+ });
+
+ SwitchHandler.Mapper.ModifyMapping("AutomationId", (handler, view, previousAction) =>
+ {
+ if (handler.PlatformView is Android.Views.View androidView)
+ {
+ if (String.IsNullOrWhiteSpace(view.AutomationId))
+ return;
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is not AutomationIdDelegate)
+ AndroidX.Core.View.ViewCompat.SetAccessibilityDelegate(androidView, new AutomationIdDelegate());
+
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is AutomationIdDelegate td)
+ td.AutomationId = view.AutomationId;
+
+ androidView.ContentDescription = view.AutomationId;
+ }
+ });
+
+ LabelHandler.Mapper.ModifyMapping("AutomationId", (handler, view, previousAction) =>
+ {
+ if (handler.PlatformView is Android.Views.View androidView)
+ {
+ if (String.IsNullOrWhiteSpace(view.AutomationId))
+ return;
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is not AutomationIdDelegate)
+ AndroidX.Core.View.ViewCompat.SetAccessibilityDelegate(androidView, new AutomationIdDelegate());
+
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is AutomationIdDelegate td)
+ td.AutomationId = view.AutomationId;
+
+ androidView.ContentDescription = view.AutomationId;
+ }
+ });
+
+ ButtonHandler.Mapper.ModifyMapping("AutomationId", (handler, view, previousAction) =>
+ {
+ if (handler.PlatformView is Android.Views.View androidView)
+ {
+ if (String.IsNullOrWhiteSpace(view.AutomationId))
+ return;
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is not AutomationIdDelegate)
+ AndroidX.Core.View.ViewCompat.SetAccessibilityDelegate(androidView, new AutomationIdDelegate());
+
+
+ if (AndroidX.Core.View.ViewCompat.GetAccessibilityDelegate(androidView) is AutomationIdDelegate td)
+ td.AutomationId = view.AutomationId;
+
+ androidView.ContentDescription = view.AutomationId;
+ }
+ });
+
+#endif
+ var memoryCache = MauiProgram.Container.Services.GetService();
+ memoryCache.TryGetValue("isLoggedIn", out bool isLoggedIn);
+
+ if (isLoggedIn)
+ {
+ MainPage = new AppShell();
+ }
+ else {
+ var loginPageViewModel = MauiProgram.Container.Services.GetService();
+ MainPage = new LoginPage(loginPageViewModel);
+ }
Routing.RegisterRoute(nameof(MobileTopupSelectOperatorPage), typeof(MobileTopupSelectOperatorPage));
Routing.RegisterRoute(nameof(MobileTopupSelectProductPage), typeof(MobileTopupSelectProductPage));
@@ -45,6 +151,24 @@ public App()
Routing.RegisterRoute(nameof(VoucherIssueFailedPage), typeof(VoucherIssueFailedPage));
Routing.RegisterRoute(nameof(AdminPage), typeof(AdminPage));
+ Routing.RegisterRoute(nameof(LoginPage), typeof(LoginPage));
+ }
+}
+
+#if ANDROID
+public class AutomationIdDelegate : MauiAccessibilityDelegateCompat
+{
+ public string AutomationId { get; internal set; }
+
+ public override void OnInitializeAccessibilityNodeInfo(Android.Views.View host, AndroidX.Core.View.Accessibility.AccessibilityNodeInfoCompat info)
+ {
+ base.OnInitializeAccessibilityNodeInfo(host, info);
+
+ if (!String.IsNullOrWhiteSpace(AutomationId))
+ {
+ info.ViewIdResourceName = $"{host.Context.PackageName}:id/{AutomationId}";
+ }
}
}
+#endif
diff --git a/TransactionMobile.Maui/AppShell.xaml b/TransactionMobile.Maui/AppShell.xaml
index b77df859..1ae4af7c 100644
--- a/TransactionMobile.Maui/AppShell.xaml
+++ b/TransactionMobile.Maui/AppShell.xaml
@@ -14,13 +14,10 @@
-->
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/TransactionMobile.Maui/AppShell.xaml.cs b/TransactionMobile.Maui/AppShell.xaml.cs
index d169e3cd..c3345b52 100644
--- a/TransactionMobile.Maui/AppShell.xaml.cs
+++ b/TransactionMobile.Maui/AppShell.xaml.cs
@@ -1,9 +1,19 @@
namespace TransactionMobile.Maui;
+using System.Diagnostics;
+
public partial class AppShell : Shell
{
public AppShell()
{
InitializeComponent();
}
+
+ protected override void OnNavigating(ShellNavigatingEventArgs args) {
+ base.OnNavigating(args);
+ }
+
+ protected override void OnNavigated(ShellNavigatedEventArgs args) {
+ base.OnNavigated(args);
+ }
}
\ No newline at end of file
diff --git a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs
index 6c0b4d78..b9497e8f 100644
--- a/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs
+++ b/TransactionMobile.Maui/Extensions/MauiAppBuilderExtensions.cs
@@ -32,7 +32,7 @@ public static class MauiAppBuilderExtensions
public static MauiAppBuilder ConfigureDatabase(this MauiAppBuilder builder)
{
- String connectionString = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "transactionpos.db");
+ String connectionString = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "transactionpos1.db");
Func logLevelFunc = new Func( () =>
{
return Database.LogLevel.Debug;
diff --git a/TransactionMobile.Maui/MauiProgram.cs b/TransactionMobile.Maui/MauiProgram.cs
index c8d6597b..9a1dc567 100644
--- a/TransactionMobile.Maui/MauiProgram.cs
+++ b/TransactionMobile.Maui/MauiProgram.cs
@@ -21,7 +21,6 @@ public static MauiApp CreateMauiApp()
Platforms.Services.DangerousTrustProvider.Register();
#endif
- //raw.SetProvider(new SQLite3Provider_sqlite3());
Builder = MauiApp.CreateBuilder();
Builder.UseMauiApp()
.ConfigureRequestHandlers()
diff --git a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs
index b90be145..eb959471 100644
--- a/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs
+++ b/TransactionMobile.Maui/Pages/AppHome/HomePage.xaml.cs
@@ -6,4 +6,8 @@ public HomePage()
{
InitializeComponent();
}
+
+ protected override void OnAppearing() {
+ base.OnAppearing();
+ }
}
\ No newline at end of file
diff --git a/TransactionMobile.Maui/Pages/LoginPage.xaml b/TransactionMobile.Maui/Pages/LoginPage.xaml
index 876d172b..57f69a14 100644
--- a/TransactionMobile.Maui/Pages/LoginPage.xaml
+++ b/TransactionMobile.Maui/Pages/LoginPage.xaml
@@ -4,7 +4,7 @@
x:Class="TransactionMobile.Maui.Pages.LoginPage"
Title="LoginPage"
Shell.NavBarIsVisible="False"
- Shell.FlyoutItemIsVisible="False">
+ Shell.FlyoutItemIsVisible="True">
@@ -12,7 +12,8 @@
-
+
@@ -53,7 +54,7 @@
/>
-
+
diff --git a/TransactionMobile.Maui/Pages/LoginPage.xaml.cs b/TransactionMobile.Maui/Pages/LoginPage.xaml.cs
index 0d29304a..ad3510d8 100644
--- a/TransactionMobile.Maui/Pages/LoginPage.xaml.cs
+++ b/TransactionMobile.Maui/Pages/LoginPage.xaml.cs
@@ -9,8 +9,8 @@ public partial class LoginPage : ContentPage
public LoginPage(LoginPageViewModel vm)
{
InitializeComponent();
- BindingContext = vm;
- }
+ BindingContext = vm;
+ }
protected override async void OnAppearing()
{
diff --git a/TransactionMobile.Maui/Platforms/iOS/DeviceService.cs b/TransactionMobile.Maui/Platforms/iOS/DeviceService.cs
index 65240329..54a9890b 100644
--- a/TransactionMobile.Maui/Platforms/iOS/DeviceService.cs
+++ b/TransactionMobile.Maui/Platforms/iOS/DeviceService.cs
@@ -73,9 +73,8 @@ private static string FindVersion()
return "Unknown";
}
- public static partial String Identifier()
- {
- return UIDevice.CurrentDevice.IdentifierForVendor.AsString().Replace("-", "");
+ public static partial String Identifier() {
+ return "Testing";//UIDevice.CurrentDevice.IdentifierForVendor.AsString().Replace("-", "");
}
private static string GetModel(string version)
diff --git a/TransactionMobile.Maui/Platforms/iOS/Info.plist b/TransactionMobile.Maui/Platforms/iOS/Info.plist
index 450d0647..099c8f6c 100644
--- a/TransactionMobile.Maui/Platforms/iOS/Info.plist
+++ b/TransactionMobile.Maui/Platforms/iOS/Info.plist
@@ -30,5 +30,9 @@
Assets.xcassets/appicon.appiconset
CFBundleName
Transaction Processing POS
+ CFBundleIdentifier
+
+ MinimumOSVersion
+ 15.4
diff --git a/TransactionMobile.Maui/Resources/Styles/LightTheme.xaml b/TransactionMobile.Maui/Resources/Styles/LightTheme.xaml
index 04d30ff7..fe04601b 100644
--- a/TransactionMobile.Maui/Resources/Styles/LightTheme.xaml
+++ b/TransactionMobile.Maui/Resources/Styles/LightTheme.xaml
@@ -1,4 +1,5 @@
+
diff --git a/TransactionMobile.Maui/TransactionMobile.Maui.csproj b/TransactionMobile.Maui/TransactionMobile.Maui.csproj
index 7a8a4a51..2922a4d1 100644
--- a/TransactionMobile.Maui/TransactionMobile.Maui.csproj
+++ b/TransactionMobile.Maui/TransactionMobile.Maui.csproj
@@ -23,12 +23,16 @@
True
- 14.2
+ 15.4
14.0
21.0
10.0.17763.0
10.0.17763.0
False
+
+
+
+
@@ -48,14 +52,8 @@
-
-
-
-
-
-
-
+
@@ -191,7 +189,7 @@
- false
+ true
@@ -200,4 +198,29 @@
true
+
+ apk
+ None
+
+
+ None
+
+
+ None
+
+
+ None
+
+
+ None
+
+
+ None
+
+
+ None
+
+
+ None
+
diff --git a/TransactionMobile.Maui/UIServices/ShellNavigationService.cs b/TransactionMobile.Maui/UIServices/ShellNavigationService.cs
index 6cf9c5bf..09ee77bb 100644
--- a/TransactionMobile.Maui/UIServices/ShellNavigationService.cs
+++ b/TransactionMobile.Maui/UIServices/ShellNavigationService.cs
@@ -10,7 +10,8 @@ public class ShellNavigationService : INavigationService
public async Task GoToHome()
{
- await NavigateTo("//home");
+ Application.Current.MainPage = new AppShell();
+ await NavigateTo("///main/home");
}
public async Task GoToMobileTopupFailedPage()
@@ -82,8 +83,15 @@ public async Task GoToVoucherIssueVoucherPage(String operatorIdentifier,
private async Task NavigateTo(String route)
{
- Shared.Logger.Logger.LogInformation($"navigating to {route}");
- await Shell.Current.GoToAsync(route);
+ try {
+ Shared.Logger.Logger.LogInformation($"navigating to {route}");
+ await Shell.Current.GoToAsync(route);
+ }
+ catch(Exception e) {
+
+ }
+
+
}
#endregion