diff --git a/.github/workflows/NewsReader.CI.yml b/.github/workflows/NewsReader.CI.yml
index 98c8e3d3..6af7295c 100644
--- a/.github/workflows/NewsReader.CI.yml
+++ b/.github/workflows/NewsReader.CI.yml
@@ -180,7 +180,15 @@ jobs:
- name: 🛠️ Build
run: |
cd src/NewsReader/NewsReader.MauiSystem
- dotnet build -f net10.0-ios -c:Release /p:packageApp=false /p:buildForSimulator=true /p:ArchiveOnBuild=false -p:ApplicationVersion=${{ needs.GetVersion.outputs.version }} -p:ApplicationDisplayVersion=${{ needs.GetVersion.outputs.version }}
+ dotnet build -f net10.0-ios -c:Release -p:RuntimeIdentifier=iossimulator-arm64 -p:ApplicationVersion=${{ needs.GetVersion.outputs.version }} -p:ApplicationDisplayVersion=${{ needs.GetVersion.outputs.version }}
+
+ - name: 📦 Upload iOS App for simulator
+ uses: actions/upload-artifact@v6
+ with:
+ name: NewsReaderiOSSimulator
+ if-no-files-found: error
+ path: |
+ ./src/NewsReader/NewsReader.MauiSystem/bin/Release/*/iossimulator-arm64/NewsReader.MauiSystem.app/**
Test:
runs-on: windows-2025
diff --git a/.github/workflows/NewsReader.UITestAndroid.yml b/.github/workflows/NewsReader.UITestAndroid.yml
index 791745be..0a938c42 100644
--- a/.github/workflows/NewsReader.UITestAndroid.yml
+++ b/.github/workflows/NewsReader.UITestAndroid.yml
@@ -1,6 +1,11 @@
name: NewsReader.UITestAndroid
on:
+ push:
+ paths:
+ - '**/NewsReader.UITestAndroid.yml'
+ - 'src/NewsReader.UITest/**'
+
workflow_run:
workflows: ["NewsReader.CI"]
types:
diff --git a/.github/workflows/NewsReader.UITestIOS.yml b/.github/workflows/NewsReader.UITestIOS.yml
new file mode 100644
index 00000000..05a576be
--- /dev/null
+++ b/.github/workflows/NewsReader.UITestIOS.yml
@@ -0,0 +1,83 @@
+name: NewsReader.UITestIOS
+
+on:
+ push:
+ paths:
+ - '**/NewsReader.UITestIOS.yml'
+ - 'src/NewsReader.UITest/**'
+
+ workflow_run:
+ workflows: ["NewsReader.CI"]
+ types:
+ - completed
+
+ workflow_dispatch:
+
+jobs:
+ UITest:
+ if: ${{ github.event_name != 'workflow_run' || github.event.workflow_run.conclusion == 'success' }}
+ runs-on: macos-26
+
+ steps:
+ - name: 🔖 Check-out
+ uses: actions/checkout@v6
+
+ - name: 📜 Authenticate GitHub CLI
+ run: echo "${{ secrets.GITHUB_TOKEN }}" | gh auth login --with-token
+
+ - name: ⚙️ Get latest successful run ID
+ id: get_run
+ run: |
+ RUN_ID=$(gh run list \
+ --workflow="NewsReader.CI.yml" \
+ --branch=master \
+ --json databaseId,status,conclusion \
+ --jq '.[] | select(.conclusion=="success") | .databaseId' \
+ | head -n 1)
+ echo "run_id=$RUN_ID" >> $GITHUB_OUTPUT
+ echo "run_id=$RUN_ID"
+
+ - name: 📦 Download Simulator App
+ run: |
+ gh run download ${{ steps.get_run.outputs.run_id }} --name="NewsReaderiOSSimulator" --dir=./NewsReaderiOSSimulator
+
+ - name: ⚙️ Show available simulators
+ run: xcrun simctl list devices available
+
+ - name: ⚙️ Create and boot simulator and install app
+ run: |
+ DEVICE_TYPE="com.apple.CoreSimulator.SimDeviceType.iPhone-17"
+ RUNTIME=$(xcrun simctl list runtimes | grep 'iOS 26.4' | grep -o 'com.apple.CoreSimulator.SimRuntime.iOS-[0-9-]*' | head -1)
+ UDID=$(xcrun simctl create "CI iPhone" "$DEVICE_TYPE" "$RUNTIME")
+ echo "UITestDeviceId=$UDID" >> "$GITHUB_ENV"
+ xcrun simctl boot "$UDID"
+ xcrun simctl bootstatus "$UDID" -b
+ xcrun simctl install "$UDID" NewsReaderiOSSimulator/net10.0-ios/iossimulator-arm64/NewsReader.MauiSystem.app
+
+ - name: ⚙️ Install Appium Server
+ run: |
+ npm install appium -g
+ appium driver install xcuitest
+ appium --version
+
+ - name: ⚙️ Start Appium Server
+ run: |
+ mkdir -p $GITHUB_WORKSPACE/out/NewsReader.UITest
+ appium > $GITHUB_WORKSPACE/out/NewsReader.UITest/appium.log 2>&1 &
+
+ - name: 🖥️ UI Test
+ env:
+ UITestApp: NewsReaderiOSSimulator/net10.0-ios/iossimulator-arm64/NewsReader.MauiSystem.app
+ run: |
+ echo "## 🕵️ Test Results - NewsReader.UITest.sln" >> $Env:GITHUB_STEP_SUMMARY
+ cd ./src/NewsReader.UITest
+ dotnet test --solution ./NewsReader.UITest.slnx --filter-trait DevicePlatform=IOS
+
+ - name: 📦 Upload UI Test results
+ uses: actions/upload-artifact@v6
+ if: always()
+ with:
+ name: NewsReaderUITestResults
+ if-no-files-found: ignore
+ path: |
+ out/NewsReader.UITest/
diff --git a/src/NewsReader.UITest/NewsReader.UITest/UITest.cs b/src/NewsReader.UITest/NewsReader.UITest/UITest.cs
index 42b4e4b8..51631941 100644
--- a/src/NewsReader.UITest/NewsReader.UITest/UITest.cs
+++ b/src/NewsReader.UITest/NewsReader.UITest/UITest.cs
@@ -2,11 +2,14 @@
namespace UITest.NewsReader;
-public class UITest() : UITestBase("Waf.NewsReader",
+public class UITest() : UITestBase(
+ deviceId: Environment.GetEnvironmentVariable("UITestDeviceId"),
+ appId: "Waf.NewsReader",
androidApkFile: Environment.GetEnvironmentVariable("UITestApkFile") ?? "src/NewsReader/NewsReader.MauiSystem/bin/Release/net10.0-android/Waf.NewsReader-Signed.apk",
androidAppActivity: "Waf.NewsReader.MainActivity",
+ iosApp: Environment.GetEnvironmentVariable("UITestApp"),
windowsAppId: "Waf.NewsReader_a8txtqew917ny!App",
- Environment.GetEnvironmentVariable("UITestOutputPath") ?? "out/NewsReader.UITest/")
+ testOutputPath: Environment.GetEnvironmentVariable("UITestOutputPath") ?? "out/NewsReader.UITest/")
{
public ShellWindow GetShellWindow() => new(Driver);
}
diff --git a/src/NewsReader.UITest/UITest.Core/UITestBase.cs b/src/NewsReader.UITest/UITest.Core/UITestBase.cs
index 0662a9fa..ca70a267 100644
--- a/src/NewsReader.UITest/UITest.Core/UITestBase.cs
+++ b/src/NewsReader.UITest/UITest.Core/UITestBase.cs
@@ -10,16 +10,17 @@ namespace UITest;
public abstract class UITestBase : IDisposable
{
+ private readonly string? deviceId;
private readonly string appId;
private readonly string app;
private readonly string androidAppActivity;
private readonly string rootPath;
private readonly string testOutPath;
- protected UITestBase(string appId, string androidApkFile, string androidAppActivity, string windowsAppId, string testOutputPath)
+ protected UITestBase(string? deviceId, string appId, string androidApkFile, string androidAppActivity, string? iosApp, string windowsAppId, string testOutputPath)
{
var devicePlatform = DeviceManager.GetDevicePlatform(GetType());
-
+ this.deviceId = deviceId;
this.appId = appId;
this.androidAppActivity = androidAppActivity;
var assemblyPath = Assembly.GetAssembly(typeof(UITestBase))!.Location;
@@ -29,11 +30,12 @@ protected UITestBase(string appId, string androidApkFile, string androidAppActiv
app = devicePlatform switch
{
DevicePlatform.Android => androidApkFile,
- DevicePlatform.IOS => "",
+ DevicePlatform.IOS => iosApp ?? "",
DevicePlatform.Windows => windowsAppId,
_ => throw new NotSupportedException()
};
Log.WriteLine(("DevicePlatform:", $"{devicePlatform}"));
+ Log.WriteLine(("DeviceId:", $"{deviceId}"));
Log.WriteLine(("AppId:", $"{appId}"));
Log.WriteLine(("App:", $"{app}"));
Log.WriteLine(("AndroidAppActivity:", $"{androidAppActivity}"));
@@ -89,7 +91,15 @@ private IOSDriver SetupIOS(Uri serverUri)
AutomationName = AutomationName.iOSXcuiTest,
PlatformName = MobilePlatform.IOS,
};
+ if (!string.IsNullOrEmpty(app))
+ {
+ var appPath = Path.GetFullPath(Path.IsPathFullyQualified(app) ? app : Path.Combine(rootPath, app));
+ driverOptions.App = appPath;
+ }
+ if (!string.IsNullOrEmpty(deviceId)) driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.Udid, deviceId);
driverOptions.AddAdditionalAppiumOption("bundleId", appId);
+ driverOptions.AddAdditionalAppiumOption("simulatorStartupTimeout", 300_000); // Increased timeouts for CI, as simulator startup can be slow
+ driverOptions.AddAdditionalAppiumOption("wdaLaunchTimeout", 300_000); // Increased timeouts for CI, as WebDriverAgent startup can be slow
// TODO: Use this if you have a physical device
//driverOptions.AddAdditionalAppiumOption(MobileCapabilityType.Udid, "");
@@ -98,7 +108,7 @@ private IOSDriver SetupIOS(Uri serverUri)
//driverOptions.AddAdditionalAppiumOption("xcodeOrgId", "");
//driverOptions.AddAdditionalAppiumOption("xcodeSigningId", "");
- return new(serverUri, driverOptions, TimeSpan.FromMinutes(3));
+ return new(serverUri, driverOptions, TimeSpan.FromMinutes(10)); // Increased timeout for CI, as simulator and WebDriverAgent startup can be slow
}
private WindowsDriver SetupWindows(Uri serverUri)
diff --git a/src/NewsReader/NewsReader.MauiSystem/NewsReader.MauiSystem.csproj b/src/NewsReader/NewsReader.MauiSystem/NewsReader.MauiSystem.csproj
index 50d4faa8..4bb68bc0 100644
--- a/src/NewsReader/NewsReader.MauiSystem/NewsReader.MauiSystem.csproj
+++ b/src/NewsReader/NewsReader.MauiSystem/NewsReader.MauiSystem.csproj
@@ -21,9 +21,6 @@
true
True
-
-
- SdkOnly