Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
67 commits
Select commit Hold shift + click to select a range
140aef8
Add iOS Navigation Tests workflow to GitHub Actions
StuartFerguson Apr 8, 2025
31bf67f
Add logging and MVVM helpers, exclude from iOS
StuartFerguson Apr 8, 2025
fe53311
Add conditional compilation for non-iOS platforms
StuartFerguson Apr 8, 2025
3ccd705
:|
StuartFerguson Apr 8, 2025
d34d71d
Update iOS driver to target PlatformVersion 17.4
StuartFerguson Apr 8, 2025
cf625fe
Update binaries path for iOS simulator architecture
StuartFerguson Apr 8, 2025
7015b48
Refactor AppiumDriver.cs with new namespaces and options
StuartFerguson Apr 8, 2025
6f060cd
Refactor Appium option addition in AppiumDriver.cs
StuartFerguson Apr 8, 2025
437a06c
Add shouldWaitForQuiescence option to AppiumDriver
StuartFerguson Apr 8, 2025
3b30e17
Update Appium XCUI driver installation version
StuartFerguson Apr 8, 2025
18bb4ef
..
StuartFerguson Apr 8, 2025
7c3a865
..
StuartFerguson Apr 8, 2025
445bcfa
.
StuartFerguson Apr 8, 2025
31be91e
..
StuartFerguson Apr 8, 2025
22dcd25
..
StuartFerguson Apr 8, 2025
7b1e294
:|
StuartFerguson Apr 8, 2025
1dba33e
..
StuartFerguson Apr 8, 2025
aa2e794
..
StuartFerguson Apr 8, 2025
6155098
.
StuartFerguson Apr 8, 2025
517e34b
..
StuartFerguson Apr 8, 2025
59dc252
.
StuartFerguson Apr 8, 2025
1f9aa08
.
StuartFerguson Apr 8, 2025
2b891ff
..
StuartFerguson Apr 8, 2025
9109ae9
..
StuartFerguson Apr 8, 2025
344f9b2
.
StuartFerguson Apr 9, 2025
d77ca17
.
StuartFerguson Apr 9, 2025
ab2d417
:|
StuartFerguson Apr 9, 2025
4c82e34
..
StuartFerguson Apr 9, 2025
850fc99
.
StuartFerguson Apr 9, 2025
528f77a
..
StuartFerguson Apr 9, 2025
fc9701e
..
StuartFerguson Apr 9, 2025
9781fec
.
StuartFerguson Apr 9, 2025
baca503
.
StuartFerguson Apr 9, 2025
1416897
.
StuartFerguson Apr 9, 2025
74cbc0e
:|
StuartFerguson Apr 9, 2025
cc9440d
..
StuartFerguson Apr 9, 2025
c923150
:|
StuartFerguson Apr 9, 2025
cfaed46
..
StuartFerguson Apr 9, 2025
c7981aa
:|
StuartFerguson Apr 9, 2025
2a7fb1d
..
StuartFerguson Apr 10, 2025
ac194a5
..
StuartFerguson Apr 10, 2025
4472bce
.
StuartFerguson Apr 10, 2025
fe4913b
more debug
StuartFerguson Apr 10, 2025
bc456c1
..
StuartFerguson Apr 10, 2025
3e934c0
..
StuartFerguson Apr 10, 2025
33d867a
..
StuartFerguson Apr 10, 2025
b857753
:|
StuartFerguson Apr 10, 2025
4f68ae8
..
StuartFerguson Apr 10, 2025
9c2169e
:|
StuartFerguson Apr 10, 2025
9debb19
..
StuartFerguson Apr 10, 2025
3614b3d
.
StuartFerguson Apr 10, 2025
d2cfa0d
:|
StuartFerguson Apr 10, 2025
72a5385
.
StuartFerguson Apr 10, 2025
c223d8d
.
StuartFerguson Apr 10, 2025
54be43a
:|
StuartFerguson Apr 10, 2025
31d8dd9
:|
StuartFerguson Apr 10, 2025
65f72cd
more tweaks
StuartFerguson Apr 10, 2025
949cb45
..
StuartFerguson Apr 11, 2025
b02eb38
:|
StuartFerguson Apr 11, 2025
2677f15
:|
StuartFerguson Apr 11, 2025
bbdd7b3
:|
StuartFerguson Apr 11, 2025
1a91409
add some debug :|
StuartFerguson Apr 11, 2025
b16d73c
:|
StuartFerguson Apr 11, 2025
e706f0c
all nav tests
StuartFerguson Apr 11, 2025
773a213
...
StuartFerguson Apr 11, 2025
58ad6d3
:|
StuartFerguson Apr 11, 2025
1b4ad41
:|
StuartFerguson Apr 11, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
76 changes: 76 additions & 0 deletions .github/workflows/ios_navigation_tests.yml
Original file line number Diff line number Diff line change
@@ -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


Original file line number Diff line number Diff line change
Expand Up @@ -52,9 +52,12 @@
<PackageReference Include="SQLitePCLRaw.provider.sqlite3" Version="2.1.11" />
<PackageReference Include="SQLitePCLRaw.bundle_green" Version="2.1.11" />
<PackageReference Include="SQLitePCLRaw.provider.dynamic_cdecl" Version="2.1.11" />
<PackageReference Include="MetroLog.Maui" Version="2.1.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'net8.0-ios'">
<PackageReference Include="Microsoft.AppCenter" Version="5.0.7" />
<PackageReference Include="Microsoft.AppCenter.Distribute" Version="5.0.7" />
<PackageReference Include="MetroLog.Maui" Version="2.1.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'net8.0-android'">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -36,7 +38,8 @@

#region Methods

#if !IOS
public async Task Initialise(CancellationToken cancellationToken) {

Check warning on line 42 in TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs

View workflow job for this annotation

GitHub Actions / software_navigation_tests

'HomePageViewModel.Initialise(CancellationToken)' hides inherited member 'ExtendedBaseViewModel.Initialise(CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Check warning on line 42 in TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs

View workflow job for this annotation

GitHub Actions / hardware_navigation_tests

'HomePageViewModel.Initialise(CancellationToken)' hides inherited member 'ExtendedBaseViewModel.Initialise(CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Check warning on line 42 in TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs

View workflow job for this annotation

GitHub Actions / software_navigation_tests

'HomePageViewModel.Initialise(CancellationToken)' hides inherited member 'ExtendedBaseViewModel.Initialise(CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Check warning on line 42 in TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs

View workflow job for this annotation

GitHub Actions / unit_tests

'HomePageViewModel.Initialise(CancellationToken)' hides inherited member 'ExtendedBaseViewModel.Initialise(CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Check warning on line 42 in TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs

View workflow job for this annotation

GitHub Actions / end_to_end_tests

'HomePageViewModel.Initialise(CancellationToken)' hides inherited member 'ExtendedBaseViewModel.Initialise(CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.

Check warning on line 42 in TransactionMobile.Maui.BusinessLogic/ViewModels/HomePageViewModel.cs

View workflow job for this annotation

GitHub Actions / end_to_end_tests

'HomePageViewModel.Initialise(CancellationToken)' hides inherited member 'ExtendedBaseViewModel.Initialise(CancellationToken)'. To make the current member override that implementation, add the override keyword. Otherwise add the new keyword.
Configuration configuration = this.ApplicationCache.GetConfiguration();

if (configuration.EnableAutoUpdates) {
Expand All @@ -59,13 +62,14 @@
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
Expand Down Expand Up @@ -111,6 +115,7 @@
// Return true if you're using your own dialog, false otherwise
return true;
}
#endif

#endregion
#endregion
}
99 changes: 79 additions & 20 deletions TransactionMobile.Maui.UiTests/Drivers/AppiumDriver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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){
Expand Down Expand Up @@ -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) {
Expand Down
7 changes: 5 additions & 2 deletions TransactionMobile.Maui.UiTests/Pages/LoginPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -107,14 +107,17 @@ public async Task<string> 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();
});
Expand Down
10 changes: 8 additions & 2 deletions TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,6 @@
[Given(@"the application is in training mode")]
public async Task GivenTheApplicationIsInTrainingMode() {
var isTrainingModeOn = await this.loginPage.IsTrainingModeOn();

if (isTrainingModeOn == false)
await this.loginPage.SetTrainingModeOn();
}
Expand Down Expand Up @@ -90,11 +89,18 @@

[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 (.*)")]
public async Task ThenTheAvailableBalanceIsShownAs(Decimal expectedAvailableBalance) {

Check warning on line 103 in TransactionMobile.Maui.UiTests/Steps/LoginSteps.cs

View workflow job for this annotation

GitHub Actions / end_to_end_tests

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.
//Decimal availableBalance = await this.mainPage.GetAvailableBalanceValue(TimeSpan.FromSeconds(120)).ConfigureAwait(false);
//availableBalance.ShouldBe(expectedAvailableBalance);
}
Expand Down
5 changes: 4 additions & 1 deletion TransactionMobile.Maui/TransactionMobile.Maui.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,12 @@
<PackageReference Include="LiveChartsCore.SkiaSharpView.Maui" Version="2.0.0-rc2" />
<PackageReference Include="MediatR" Version="12.4.1" />
<PackageReference Include="MetroLog.Maui" Version="2.1.0" />
<PackageReference Include="Refractored.MvvmHelpers" Version="1.6.2" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' != 'net8.0-ios'">
<PackageReference Include="Microsoft.AppCenter" Version="5.0.7" />
<PackageReference Include="Microsoft.AppCenter.Distribute" Version="5.0.7" />
<PackageReference Include="Refractored.MvvmHelpers" Version="1.6.2" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading