From 74801915d4e0a7ced970b9eb8b95784263f3d5be Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
<41898282+github-actions[bot]@users.noreply.github.com>
Date: Tue, 7 Jul 2026 13:40:23 +0000
Subject: [PATCH] [ci-fix] De-flake SafeAreaShouldWorkOnAllShellTabs
(StaleElementReferenceException)
The Android UI test SafeAreaShouldWorkOnAllShellTabs intermittently fails with a
StaleElementReferenceException thrown from IUIElement.GetRect(). After App.TapTab
switches tabs, the tab-switch animation recycles the EdgeLabel view between the
WaitForElement query and the geometry read, invalidating the element reference.
Add a stale-resilient WaitForElementAndGetRect helper that re-finds the element and
retries GetRect() when the reference goes stale (mirroring the existing stale-element
handling in AppiumMouseActions), and use it at the two post-navigation rect reads in
the test. This makes the geometry read deterministic without weakening the assertions,
which are left untouched.
Refs: dotnet/maui#36259
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
---
.../Tests/Issues/Issue33034.cs | 4 +--
.../src/UITest.Appium/HelperExtensions.cs | 33 +++++++++++++++++++
2 files changed, 35 insertions(+), 2 deletions(-)
diff --git a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs
index e2b9d4836436..d6b8ba4e9099 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs
@@ -20,12 +20,12 @@ public void SafeAreaShouldWorkOnAllShellTabs()
App.SetOrientationLandscape();
//Adding delay to allow for orientation change to complete
Thread.Sleep(2000);
- var initialRect = App.WaitForElement("EdgeLabel").GetRect();
+ var initialRect = App.WaitForElementAndGetRect("EdgeLabel");
App.TapTab("Second Tab");
App.WaitForElement("EdgeLabel");
App.TapTab("First Tab");
- var afterSwitchRect = App.WaitForElement("EdgeLabel").GetRect();
+ var afterSwitchRect = App.WaitForElementAndGetRect("EdgeLabel");
Assert.That(afterSwitchRect.X, Is.EqualTo(initialRect.X).Within(5));
Assert.That(afterSwitchRect.Width, Is.EqualTo(initialRect.Width).Within(5));
diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
index 9bd0aefa2bc6..1343d574af89 100644
--- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
+++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
@@ -179,6 +179,39 @@ public static Rectangle GetRect(this IUIElement element)
})!;
}
+ ///
+ /// Waits for an element and reads its on-screen rectangle, re-querying and retrying if the
+ /// element reference becomes stale. A navigation or tab-switch animation can recycle the
+ /// underlying view between the element query and the geometry read, which surfaces as a
+ /// from . Re-finding
+ /// the element and reading again once it has settled keeps the geometry read deterministic
+ /// without changing what the caller asserts on. This mirrors the existing stale-element handling
+ /// used elsewhere in the framework (see AppiumMouseActions).
+ ///
+ /// Represents the main gateway to interact with an app.
+ /// The id of the element to locate and measure.
+ /// Optional timeout for each wait operation. Default is null, which uses the default timeout.
+ public static Rectangle WaitForElementAndGetRect(this IApp app, string elementId, TimeSpan? timeout = null)
+ {
+ StaleElementReferenceException? lastStale = null;
+
+ for (int attempt = 0; attempt < 3; attempt++)
+ {
+ try
+ {
+ return app.WaitForElement(elementId, timeout: timeout).GetRect();
+ }
+ catch (StaleElementReferenceException ex)
+ {
+ // The view was recycled between the query and the geometry read (typically
+ // mid-animation); re-find the element and read again now that it should have settled.
+ lastStale = ex;
+ }
+ }
+
+ throw lastStale!;
+ }
+
///
/// Determine if a form or form-like element (checkbox, select, etc...) is selected.
///