diff --git a/.github/workflows/ios_navigation_tests.yml b/.github/workflows/ios_navigation_tests.yml
new file mode 100644
index 000000000..5eebf5f6e
--- /dev/null
+++ b/.github/workflows/ios_navigation_tests.yml
@@ -0,0 +1,76 @@
+name: Build and Run iOS Navigation Tests
+
+on:
+ pull_request:
+ branches:
+ - main
+
+jobs:
+ software_navigation_tests:
+ runs-on: macos-14
+ env:
+ PLATFORM_VERSION: "17.2"
+ DEVICE_NAME: "iPhone 15"
+ APP_PATH: "./MyTestApp.app"
+
+ steps:
+ - name: ๐งพ Checkout repo
+ uses: actions/checkout@v4
+
+ - name: ๐ง Set up Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: '20'
+
+ - name: ๐ง Set up .NET
+ uses: actions/setup-dotnet@v4
+ with:
+ dotnet-version: '8.0.x'
+
+ - name: ๐ฆ Install Appium + XCUITest driver
+ run: |
+ npm install -g appium
+ appium driver install xcuitest
+
+ - name: ๐งน Clean DerivedData and WDA cache
+ run: |
+ rm -rf ~/Library/Developer/Xcode/DerivedData
+ rm -rf ~/.appium/node_modules/appium-webdriveragent/Build
+
+ - name: ๐งช Start Appium Server
+ run: |
+ nohup appium --log appium.log &
+
+ - name: ๐ฑ Create iOS Simulator (if needed)
+ run: |
+ SIMULATOR_NAME="ci-sim-$RANDOM"
+ xcrun simctl create "$SIMULATOR_NAME" "$DEVICE_NAME" "com.apple.CoreSimulator.SimRuntime.iOS-${PLATFORM_VERSION//./-}"
+ echo "SIMULATOR_NAME=$SIMULATOR_NAME" >> $GITHUB_ENV
+
+ - name: ๐ Boot simulator
+ run: |
+ xcrun simctl boot "$SIMULATOR_NAME"
+ xcrun simctl list | grep Booted
+
+ - name: Install MAUI Workloads
+ run: |
+ dotnet workload install ios --ignore-failed-sources
+ dotnet workload install maui --ignore-failed-sources
+
+ - name: Restore MAUI App for iOS
+ run: dotnet restore TransactionMobile.Maui.sln --source ${{ secrets.PUBLICFEEDURL }} --source ${{ secrets.PRIVATEFEED_URL }} --source https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json
+
+ - name: Build Code
+ run: dotnet build TransactionMobile.Maui/TransactionMobile.Maui.csproj -f net8.0-ios -c Release --no-restore
+
+ - name: Run iOS Navigation Tests
+ run: dotnet test TransactionMobile.Maui.UiTests/TransactionMobile.Maui.UiTests.csproj --filter "(Category=PRNavTest)&(Category=iOS)" --no-restore
+
+ - name: Upload Appium Logs on Failure
+ if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ name: ios-software_navigation_tests_appium
+ path: appium.log
+
+
\ No newline at end of file
diff --git a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj
index d196f715f..15818a5e2 100644
--- a/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj
+++ b/TransactionMobile.Maui.BusinessLogic/TransactionMobile.Maui.BusinessLogic.csproj
@@ -52,9 +52,12 @@
+
+
+
+
-
diff --git a/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs b/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs
index 4d464f2d6..de2bc28a8 100644
--- a/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs
+++ b/TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs
@@ -3,8 +3,10 @@
using System.Diagnostics.CodeAnalysis;
using Logging;
using Maui.UIServices;
+#if !IOS
using Microsoft.AppCenter;
using Microsoft.AppCenter.Distribute;
+#endif
using Models;
using Services;
using UIServices;
@@ -36,6 +38,7 @@ public async Task ShowDebugMessage(String message) {
#region Methods
+#if !IOS
public async Task Initialise(CancellationToken cancellationToken) {
Configuration configuration = this.ApplicationCache.GetConfiguration();
@@ -59,13 +62,14 @@ public async Task Initialise(CancellationToken cancellationToken) {
Logger.LogError("Error during initialise", ex);
}
}
+#endif
private async void NoReleaseAvailable() {
await this.DialogService.ShowDialog("Software Update", "No Release Available","OK");
}
- //private Boolean IsIOS() => this.DeviceService.IsIOS//DeviceInfo.Current.Platform == DevicePlatform.iOS;
+#if !IOS
private Boolean OnReleaseAvailable(ReleaseDetails releaseDetails) {
Logger.LogInformation("In OnReleaseAvailable");
// Look at releaseDetails public properties to get version information, release notes text or release notes URL
@@ -111,6 +115,7 @@ private Boolean OnReleaseAvailable(ReleaseDetails releaseDetails) {
// Return true if you're using your own dialog, false otherwise
return true;
}
+#endif
- #endregion
+#endregion
}
\ No newline at end of file
diff --git a/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs b/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs
index 4a4329c8e..05ed07423 100644
--- a/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs
+++ b/TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs
@@ -8,14 +8,17 @@
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
+using Google.Protobuf.WellKnownTypes;
namespace TransactionMobile.Maui.UiTests.Drivers
{
+ using Microsoft.Testing.Platform.Capabilities;
+ using OpenQA.Selenium;
+ using OpenQA.Selenium.Appium.Service.Options;
+ using OpenQA.Selenium.Appium.Windows;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Threading;
- using OpenQA.Selenium;
- using OpenQA.Selenium.Appium.Windows;
public enum MobileTestPlatform
{
@@ -30,8 +33,11 @@ public class AppiumDriverWrapper
public static MobileTestPlatform MobileTestPlatform;
public static AppiumDriver Driver;
- public void StartApp(){
+ public void StartApp() {
+ //OptionCollector o = new OptionCollector();
+ //o.AddArguments(GeneralOptionList.BasePath("/wd/hub"));
AppiumLocalService appiumService = new AppiumServiceBuilder().UsingPort(4723)
+ //.WithArguments(o)
.Build();
if (appiumService.IsRunning == false){
@@ -70,25 +76,78 @@ private static void SetupWindowsDriver(AppiumLocalService appiumService){
}
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/net8.0-ios/iossimulator-x64/");
+ String binariesFolder = Path.Combine(assemblyFolder, "..", "..", "..", "..", @"TransactionMobile.Maui/bin/Release/net8.0-ios/iossimulator-arm64/");
var apkPath = Path.Combine(binariesFolder, "TransactionMobile.Maui.app");
- driverOptions.App = apkPath;
- driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.NewCommandTimeout, 6000);
- //driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.FullReset, true);
- //driverOptions.AddAdditionalAppiumOption("useNewWDA", true);
- //driverOptions.AddAdditionalAppiumOption("wdaLaunchTimeout", 999999999);
- //driverOptions.AddAdditionalAppiumOption("wdaConnectionTimeout", 999999999);
- //driverOptions.AddAdditionalAppiumOption("restart", true);
- //driverOptions.AddAdditionalAppiumOption("simulatorStartupTimeout", 5 * 60 * 1000);
-
- AppiumDriverWrapper.Driver = new OpenQA.Selenium.Appium.iOS.IOSDriver(appiumService, driverOptions, TimeSpan.FromMinutes(10));
+
+ var caps = new AppiumOptions();
+ caps.PlatformName = "iOS";
+ caps.PlatformVersion = "17.4";
+ caps.DeviceName = "iPhone 15";
+ caps.AutomationName = "XCUITest";
+ caps.App = apkPath;
+ caps.AddAdditionalAppiumOption("fullReset", true);
+ caps.AddAdditionalAppiumOption("noReset", false);
+ caps.AddAdditionalAppiumOption("useNewWDA", true);
+
+ //var driverOptions = new AppiumOptions();
+ //driverOptions.AutomationName = "XCUITest";
+ ////driverOptions.PlatformName = "iOS";
+ ////driverOptions.PlatformVersion = "17.4";
+ ////driverOptions.DeviceName = "iPhone 11";
+ //driverOptions.AutomationName = "XCUITest";
+ //driverOptions.PlatformName = "iOS";
+ //driverOptions.PlatformVersion = "17.2";
+ //driverOptions.AddAdditionalAppiumOption("udid", Environment.GetEnvironmentVariable("UDID")); // Corrected capability.
+
+ //String assemblyFolder = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
+ //String binariesFolder = Path.Combine(assemblyFolder, "..", "..", "..", "..", @"TransactionMobile.Maui/bin/Release/net8.0-ios/iossimulator-arm64/");
+ //var apkPath = Path.Combine(binariesFolder, "TransactionMobile.Maui.app");
+ //driverOptions.App = apkPath;
+ //driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.NewCommandTimeout, 6000);
+ //driverOptions.AddAdditionalAppiumOption("waitForQuiescence", false);
+ //driverOptions.AddAdditionalAppiumOption("shouldWaitForQuiescence", false);
+ //driverOptions.AddAdditionalAppiumOption("showXcodeLog", true);
+ ////driverOptions.AddAdditionalAppiumOption("useNewWDA", true);
+ ////driverOptions.AddAdditionalAppiumOption("wdaLaunchTimeout", 60000);
+ ////driverOptions.AddAdditionalAppiumOption("wdaConnectionTimeout", 60000);
+ ////driverOptions.AddAdditionalAppiumOption("wdaLaunchTimeout", 120000);
+ ////driverOptions.AddAdditionalAppiumOption("wdaConnectionTimeout", 120000);
+ ////driverOptions.AddAdditionalAppiumOption("useNewWDA", false);
+ ////driverOptions.AddAdditionalAppiumOption("wdaStartupRetryInterval", 10000);
+ ////driverOptions.AddAdditionalAppiumOption("wdaStartupRetries", 4);
+ //driverOptions.AddAdditionalAppiumOption("usePrebuiltWDA", true);
+ //driverOptions.AddAdditionalAppiumOption("skipServerInstallation", true);
+ //driverOptions.AddAdditionalAppiumOption("skipProvisioningDeviceDetection", true);
+ //driverOptions.AddAdditionalAppiumOption("wdaLocalPort", Environment.GetEnvironmentVariable("WDA_PATH"));
+ //driverOptions.AddAdditionalAppiumOption("updatedWDABundleId", "WebDriverAgent/build");
+ // Tell Appium to use the prebuilt WDA
+ //driverOptions.AddAdditionalAppiumOption("usePrebuiltWDA", true);
+ //driverOptions.AddAdditionalAppiumOption("useNewWDA", false);
+ //driverOptions.AddAdditionalAppiumOption("shouldUseSingletonTestManager", true);
+
+ //driverOptions.AddAdditionalAppiumOption("derivedDataPath", "/Users/runner/work/WebDriverAgent/build/Build/Products/Debug-iphonesimulator");
+ //driverOptions.AddAdditionalAppiumOption("wdaLocalPort", 8100);
+
+
+ //// Avoid unnecessary WDA rebuild attempts and add resilience
+ //driverOptions.AddAdditionalAppiumOption("wdaLaunchTimeout", 60000);
+ //driverOptions.AddAdditionalAppiumOption("wdaStartupRetries", 3);
+ //driverOptions.AddAdditionalAppiumOption("wdaStartupRetryInterval", 10000);
+
+ //// (Optional but often helpful)
+ //driverOptions.AddAdditionalAppiumOption("waitForQuiescence", false);
+ //driverOptions.AddAdditionalAppiumOption("startIWDP", true); // if you want to inspect webviews
+ //driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.FullReset, false);
+ //driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.NoReset, true);
+ //driverOptions.AddAdditionalAppiumOption("usePrebuiltWDA", true);
+ //driverOptions.AddAdditionalAppiumOption("wdaLocalPort", 8100); // optional, but helpful
+ //driverOptions.AddAdditionalAppiumOption("shouldUseSingletonTestManager", true); // avoids extra processes
+ //driverOptions.AddAdditionalAppiumOption("showXcodeLog", true); // shows build errors in logs
+ //driverOptions.AddAdditionalAppiumOption("bundleId", "com.appium.WebDriverAgentRunner"); // match WDA bundle ID
+
+
+ AppiumDriverWrapper.Driver = new OpenQA.Selenium.Appium.iOS.IOSDriver(appiumService, caps, TimeSpan.FromMinutes(10));
}
private static void SetupAndroidDriver(AppiumLocalService appiumService) {
diff --git a/TransactionMobile.Maui.UiTests/Pages/LoginPage.cs b/TransactionMobile.Maui.UiTests/Pages/LoginPage.cs
index 7ae04288a..cd9b2eb18 100644
--- a/TransactionMobile.Maui.UiTests/Pages/LoginPage.cs
+++ b/TransactionMobile.Maui.UiTests/Pages/LoginPage.cs
@@ -107,14 +107,17 @@ public async Task GetEmailAddress()
public async Task EnterPassword(String password)
{
IWebElement element = await this.WaitForElementByAccessibilityId(this.PasswordEntry);
-
-
element.SendKeys(password);
}
public async Task ClickLoginButton(){
await Retry.For(async () => {
IWebElement element = await this.WaitForElementByAccessibilityId(this.LoginButton);
+ if (element.Displayed == false)
+ {
+ this.HideKeyboard();
+ }
+
//element.Displayed.ShouldBeTrue();
element.Click();
});
diff --git a/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs b/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs
index 7f394e0ca..a276983e9 100644
--- a/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs
+++ b/TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs
@@ -56,7 +56,6 @@ public async Task GivenMyDeviceIsRegistered() {
[Given(@"the application is in training mode")]
public async Task GivenTheApplicationIsInTrainingMode() {
var isTrainingModeOn = await this.loginPage.IsTrainingModeOn();
-
if (isTrainingModeOn == false)
await this.loginPage.SetTrainingModeOn();
}
@@ -90,7 +89,14 @@ public async Task WhenITapOnLogin() {
[Then(@"the Merchant Home Page is displayed")]
public async Task ThenTheMerchantHomePageIsDisplayed() {
- await this.mainPage.AssertOnPage();
+ try {
+ await this.mainPage.AssertOnPage();
+ }
+ catch(Exception ex)
+ {
+ String pageSource = await this.mainPage.GetPageSource();
+ throw new Exception($"Unable to verify on page: {this.mainPage.GetType().Name} {Environment.NewLine} Page Source: {pageSource}", ex);
+ }
}
[Then(@"the available balance is shown as (.*)")]
diff --git a/TransactionMobile.Maui/TransactionMobile.Maui.csproj b/TransactionMobile.Maui/TransactionMobile.Maui.csproj
index 8700726a0..88342da7f 100644
--- a/TransactionMobile.Maui/TransactionMobile.Maui.csproj
+++ b/TransactionMobile.Maui/TransactionMobile.Maui.csproj
@@ -58,9 +58,12 @@
+
+
+
+
-