Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
76b0191
Add resilience to UI tests for frozen/unresponsive apps
PureWeen Feb 12, 2026
2d2b1a6
Improve process checks & timeout handling
kubaflo Feb 13, 2026
9b92e65
Improve UITest resilience for frozen/unresponsive apps
PureWeen Feb 18, 2026
0ed9107
Remove UI test cases (belong in PR 34024, not infra PR)
PureWeen Feb 18, 2026
7b71415
Match local simulator selection to CI defaults
PureWeen Feb 18, 2026
7171dad
Retry AppState check once before declaring app unresponsive
PureWeen Feb 19, 2026
a20b79d
Fix CapTimeout: don't starve short-timeout Appium commands
PureWeen Feb 19, 2026
daaeb04
Revert CapTimeout to 2s floor, fix Issue7678_1 timeout to 45s
PureWeen Feb 19, 2026
220dd09
Cherry-pick safe area layout fix + tests from PR #34024 with animatio…
PureWeen Feb 19, 2026
76cf017
Replace safe area rate limiter with window-level insets guard
PureWeen Feb 19, 2026
56c8087
Remove safe area fix and tests (moved to PR #34024)
PureWeen Feb 19, 2026
53af5ca
Improve process checks & timeout handling
kubaflo Feb 13, 2026
363b7f0
Improve UITest resilience for frozen/unresponsive apps
PureWeen Feb 18, 2026
7afa742
Remove UI test cases (belong in PR 34024, not infra PR)
PureWeen Feb 18, 2026
43507bc
Fix CapTimeout: don't starve short-timeout Appium commands
PureWeen Feb 19, 2026
76930a3
Revert CapTimeout to 2s floor, fix Issue7678_1 timeout to 45s
PureWeen Feb 19, 2026
7f811bc
Cherry-pick safe area layout fix + tests from PR #34024 with animatio…
PureWeen Feb 19, 2026
c4ddb3e
Replace safe area rate limiter with window-level insets guard
PureWeen Feb 19, 2026
a6d621f
Remove safe area fix and tests (moved to PR #34024)
PureWeen Feb 19, 2026
54cf97f
Prefer latest iOS minor version when selecting simulator
PureWeen Feb 19, 2026
6485c79
Fix: Remove duplicate TimeoutException catch block in UITestBase.cs
PureWeen Feb 20, 2026
e6d71fd
Increase WaitForElement timeouts for tests affected by RunWithTimeout…
PureWeen Feb 20, 2026
14c1462
Fix MacCatalyst UITests: set AppPath so Appium mac2 driver can launch…
PureWeen Feb 20, 2026
70984df
Rewrite Issue16910 test as self-verifying to avoid catalyst timeout
PureWeen Feb 21, 2026
1e5d4e1
Restore BindingUpdatesFromInteractiveRefresh test with updated elemen…
PureWeen Feb 21, 2026
01fd679
Fix CI: add ScrollView content for pull-to-refresh, increase catalyst…
PureWeen Feb 22, 2026
53fd62f
Move dangerous commands warning to uitests instructions
PureWeen Feb 22, 2026
4112ebd
Fix Issue16910 layout for Android pull-to-refresh, bump catalyst time…
PureWeen Feb 22, 2026
fa0828b
Fix catalyst test failures: reduce accessibility tree sizes and remov…
PureWeen Feb 23, 2026
7644c14
Rewrite slow catalyst tests to be self-verifying with smaller accessi…
PureWeen Feb 23, 2026
1722c66
Restore original bug triggers while keeping self-verifying pattern
PureWeen Feb 23, 2026
04da4d6
Fix test coverage issues identified in review
PureWeen Feb 23, 2026
83126e3
Restore original test patterns per multi-model review
PureWeen Feb 23, 2026
2176eb2
Exclude Issue16910 BindingUpdatesFromProgrammaticRefresh from catalyst
PureWeen Feb 24, 2026
ae6a3ea
Fix TEST_FAILS_ON_CATALYST directive polarity for Issue16910
PureWeen Feb 24, 2026
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
11 changes: 11 additions & 0 deletions .github/instructions/uitests.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -523,6 +523,17 @@ cat /tmp/ios_crash.log | grep -A 20 -B 5 "Exception"
5. **Check for platform-specific issues** - iOS version compatibility, permissions, etc.
6. If you can't determine the fix, **ask for guidance** with the full exception details

### Dangerous System Commands (Never Run)

**🚨 NEVER run these commands — they cause destructive system-wide side effects:**

- **`tccutil reset`** — Wipes ALL macOS permissions (Accessibility, Camera, etc.) system-wide. This breaks Appium/WebDriverAgent, Xcode, and other tools. Once reset, permissions must be manually re-granted through System Settings.
- **`csrutil disable`** — Disables System Integrity Protection
- **`networksetup`** — Modifies network configuration
- **`defaults delete`** on system domains — Resets system preferences

**General rule:** Do not run commands that modify macOS system-level privacy, security, or permission settings. If you need to check permissions, read them — never reset or modify them.

## Before Committing

Verify the following checklist before committing UI tests:
Expand Down
4 changes: 3 additions & 1 deletion .github/scripts/BuildAndRunHostApp.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,9 @@ if ($Platform -eq "catalyst") {
& chmod +x $executablePath
}

Write-Success "MacCatalyst app prepared (Appium will launch with test name)"
# Set MAC_APP_PATH so Appium mac2 driver can launch the app directly
$env:MAC_APP_PATH = $appPath
Write-Success "MacCatalyst app prepared (MAC_APP_PATH=$appPath)"
} else {
Write-Warn "MacCatalyst app not found at: $appPath"
Write-Warn "Test may use wrong app bundle if another version is registered"
Expand Down
24 changes: 17 additions & 7 deletions .github/scripts/shared/Start-Emulator.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
.DESCRIPTION
Handles device detection and startup for both Android and iOS platforms.
- Android: Automatically selects and starts emulator with priority: API 30 Nexus > API 30 > Nexus > First available
- iOS: Automatically selects iPhone Xs with iOS 18.5 by default
- iOS: Automatically selects iPhone Xs with iOS 18.x (or iPhone 11 Pro with iOS 26.x) to match CI

.PARAMETER Platform
Target platform: "android" or "ios"
Expand Down Expand Up @@ -344,10 +344,17 @@ if ($Platform -eq "android") {
Write-Info "Auto-detecting iOS simulator..."
$simList = xcrun simctl list devices available --json | ConvertFrom-Json

# Preferred devices in order of priority
$preferredDevices = @("iPhone 16 Pro", "iPhone 15 Pro", "iPhone 14 Pro", "iPhone Xs")
# Preferred iOS versions in order (stable preferred, beta fallback)
$preferredVersions = @("iOS-18", "iOS-17", "iOS-26")
# Preferred devices per iOS version to match CI configuration:
# iOS 18.x → iPhone Xs (matches CI default in UITest.cs)
# iOS 26.x → iPhone 11 Pro (matches CI visual test requirement)
# iOS 17.x → iPhone Xs (fallback)
$preferredDevicesPerVersion = @{
"iOS-18" = @("iPhone Xs", "iPhone 16 Pro", "iPhone 15 Pro", "iPhone 14 Pro")
"iOS-17" = @("iPhone Xs", "iPhone 15 Pro", "iPhone 14 Pro")
"iOS-26" = @("iPhone 11 Pro", "iPhone 16 Pro", "iPhone 15 Pro")
}

$selectedDevice = $null
$selectedVersion = $null
Expand All @@ -356,13 +363,16 @@ if ($Platform -eq "android") {
foreach ($version in $preferredVersions) {
if ($selectedDevice) { break }

# Get all runtimes matching this version prefix
# Get all runtimes matching this version prefix, sorted by version descending
# so the latest minor version is preferred (e.g., iOS-18-5 before iOS-18-3)
$matchingRuntimes = $simList.devices.PSObject.Properties |
Where-Object { $_.Name -match $version }
Where-Object { $_.Name -match $version } |
Sort-Object { $_.Name } -Descending

if ($matchingRuntimes) {
# Try each preferred device
foreach ($deviceName in $preferredDevices) {
# Try each preferred device for this version
$devicesForVersion = if ($preferredDevicesPerVersion.ContainsKey($version)) { $preferredDevicesPerVersion[$version] } else { @("iPhone Xs", "iPhone 16 Pro") }
foreach ($deviceName in $devicesForVersion) {
$device = $null
$deviceRuntime = $null
foreach ($rt in $matchingRuntimes) {
Expand Down
47 changes: 31 additions & 16 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue16910.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,35 @@
<ContentPage xmlns="http://schemas.microsoft.com/dotnet/2021/maui"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="Maui.Controls.Sample.Issues.Issue16910">
<ContentPage.Content>
<Grid RowDefinitions="Auto,100,*,50,50" x:Name="grid">
<Label Grid.Row="1" Text="Interact with the RefreshView and make sure the IsRefreshing Label correctly Represents current state." />
<RefreshView x:Name="refreshView" Grid.Row="2" IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}">
<CollectionView ItemsSource="{Binding ItemSource}" AutomationId="CollectionView">
<CollectionView.ItemTemplate>
<DataTemplate>
<Label Text="{Binding Text}" AutomationId="{Binding AutomationId}" />
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</RefreshView>
<Button x:Name="StopRefreshing" AutomationId="StopRefreshing" Grid.Row="3" Text="Stop Refresh" Clicked="OnStopRefreshClicked" />
<Button x:Name="StartRefreshing" AutomationId="StartRefreshing" Grid.Row="4" Text="Refresh" Clicked="OnRefreshClicked" />
</Grid>
</ContentPage.Content>
<Grid RowDefinitions="Auto,*,50,50" x:Name="grid">
<Label Grid.Row="0" Text="Interact with the RefreshView and make sure the IsRefreshing Label correctly Represents current state." />
<RefreshView x:Name="refreshView" Grid.Row="1" IsRefreshing="{Binding IsRefreshing, Mode=TwoWay}">
<ScrollView AutomationId="RefreshScrollView">
<VerticalStackLayout Padding="10" Spacing="5">
<Label Text="Item 1" />
<Label Text="Item 2" />
<Label Text="Item 3" />
<Label Text="Item 4" />
<Label Text="Item 5" />
<Label Text="Item 6" />
<Label Text="Item 7" />
<Label Text="Item 8" />
<Label Text="Item 9" />
<Label Text="Item 10" />
<Label Text="Item 11" />
<Label Text="Item 12" />
<Label Text="Item 13" />
<Label Text="Item 14" />
<Label Text="Item 15" />
<Label Text="Item 16" />
<Label Text="Item 17" />
<Label Text="Item 18" />
<Label Text="Item 19" />
<Label Text="Item 20" />
</VerticalStackLayout>
</ScrollView>
</RefreshView>
<Button x:Name="StopRefreshing" AutomationId="StopRefreshing" Grid.Row="2" Text="Stop Refresh" Clicked="OnStopRefreshClicked" />
<Button x:Name="StartRefreshing" AutomationId="StartRefreshing" Grid.Row="3" Text="Refresh" Clicked="OnRefreshClicked" />
</Grid>
</ContentPage>
14 changes: 2 additions & 12 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue16910.xaml.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System.Collections;

namespace Maui.Controls.Sample.Issues
namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.Github, 16910, "IsRefreshing binding works", PlatformAffected.All)]
public partial class Issue16910 : ContentPage
Expand All @@ -10,8 +8,6 @@ public partial class Issue16910 : ContentPage

bool _isRefreshing;

public IEnumerable ItemSource { get; set; }

public bool IsRefreshing
{
get => _isRefreshing;
Expand Down Expand Up @@ -45,15 +41,9 @@ public Issue16910()
{
InitializeComponent();
UpdateRefreshingLabels();
ItemSource =
Enumerable.Range(0, 100)
.Select(x => new { Text = $"Item {x}", AutomationId = $"Item{x}" })
.ToList();

this.BindingContext = this;
BindingContext = this;
}


void OnStopRefreshClicked(object sender, EventArgs e)
{
refreshView.IsRefreshing = false;
Expand Down
119 changes: 75 additions & 44 deletions src/Controls/tests/TestCases.HostApp/Issues/Issue1905.cs
Original file line number Diff line number Diff line change
@@ -1,50 +1,81 @@
using Microsoft.Maui.Controls.PlatformConfiguration;
using Microsoft.Maui.Controls.PlatformConfiguration;
using Microsoft.Maui.Controls.PlatformConfiguration.iOSSpecific;
using ListView = Microsoft.Maui.Controls.ListView;

namespace Maui.Controls.Sample.Issues
{
[Issue(IssueTracker.Github, 1905, "Pull to refresh doesn't work if iOS 11 large titles is enabled", PlatformAffected.iOS)]
public class Issue1905LargeTitles : TestNavigationPage
{
protected override void Init()
{
// Large titles is the specific trigger for this bug
On<iOS>().SetPrefersLargeTitles(true);

var statusLabel = new Label
{
Text = "Ready",
AutomationId = "TestResult",
FontSize = 20,
};

bool refreshCompleted = false;

var items = new List<string>();
for (int i = 0; i < 20; i++)
{
items.Add($"pull to {i}");
}

var lst = new ListView
{
IsPullToRefreshEnabled = true,
ItemsSource = items,
};

lst.RefreshCommand = new Command(async () =>
{
await Task.Delay(500);
lst.ItemsSource = new List<string> { "data refreshed" };
lst.EndRefresh();
refreshCompleted = true;
});

var runButton = new Button
{
Text = "Run Test",
AutomationId = "RunTest",
};

runButton.Clicked += async (s, e) =>
{
statusLabel.Text = "Running...";
refreshCompleted = false;

// BeginRefresh programmatically — this is what fails with large titles
lst.BeginRefresh();

// Wait for refresh to complete (max 10s)
for (int i = 0; i < 20; i++)
{
await Task.Delay(500);
if (refreshCompleted)
break;
}

statusLabel.Text = refreshCompleted ? "SUCCESS" : "FAIL: refresh did not complete";
};

var page = new ContentPage
{
Title = "Pull Large Titles",
Content = new VerticalStackLayout
{
Children = { runButton, statusLabel, lst }
}
};

[Issue(IssueTracker.Github, 1905, "Pull to refresh doesn't work if iOS 11 large titles is enabled", PlatformAffected.iOS)]
public class Issue1905LargeTitles : TestNavigationPage
{
protected override void Init()
{
On<iOS>().SetPrefersLargeTitles(true);
var items = new List<string>();
for (int i = 0; i < 1000; i++)
{
items.Add($"pull to {DateTime.Now.Ticks}");
}
var page = new ContentPage
{
Title = "Pull Large Titles"
};

var lst = new ListView();
lst.IsPullToRefreshEnabled = true;
lst.ItemsSource = items;
lst.RefreshCommand = new Command(async () =>
{
var newitems = new List<string>();
newitems.Add("data refreshed");
await Task.Delay(5000);
for (int i = 0; i < 1000; i++)
{
newitems.Add($"data {DateTime.Now.Ticks} refreshed");
}
lst.ItemsSource = newitems;
lst.EndRefresh();
});
page.Content = new StackLayout { Children = { lst } };
page.Appearing += async (sender, e) =>
{
await Task.Delay(500);
lst.BeginRefresh();
};
page.ToolbarItems.Add(new ToolbarItem { Text = "Refresh", Command = new Command((obj) => lst.BeginRefresh()), AutomationId = "btnRefresh" });
Navigation.PushAsync(page);

}
}
}
Navigation.PushAsync(page);
}
}
}
Loading
Loading