[ci-fix] De-flake SafeAreaShouldWorkOnAllShellTabs (StaleElementReferenceException on tab switch) - #36429
Conversation
…enceException) 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: #36259 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
MauiBot
left a comment
There was a problem hiding this comment.
Expert Review — 1 findings
See inline comments for details.
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
|
🎯 Target test validated green on head
The remaining red legs are unrelated flakes — this fix is implicated in none of them:
The change is purely additive — it adds a new Transitioning this PR from draft → ready for review (attempt 1/10). A maintainer still reviews and merges; the unrelated red legs can be re-run with
|
|
Target test
|
…obust) The Step 3.6 preconditions stop with "already-ready" for any non-draft PR BEFORE reaching the T3 mark-ready path, so the p/0 label-reconcile added in the prior commit could never fire for a PR that was already flipped to ready. Move the reconcile into the already-ready precondition itself: an already-ready [ci-fix] PR (correct title + agentic-workflows label) that is missing p/0 now gets it added there. This makes every already-marked-ready loop PR self-heal to carry p/0, and makes the label path testable on PRs that are already ready (e.g. #36429). T3's idempotency is simplified to defer label reconciliation to that precondition. Mirrored to both twins; locks recompiled (0/0, body_hash only). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Code LGTM — but CI needs a maintainer flake-confirmation before mergeCode (safe): the CI (needs confirmation): the run shows 19 red (Bot-authored |
This comment has been minimized.
This comment has been minimized.
MauiBot
left a comment
There was a problem hiding this comment.
AI Review Summary
@github-actions[bot] — new AI review results are available based on this last commit:
7480191. To request a fresh review after new comments or commits, comment/review rerun.
🗂️ Review Sessions — click to expand
🚦 Gate — Test Before & After Fix
Gate: Test Verification (Failure-Only Mode)
Result: ❌ FAILED
This is a test-only change (no fix files detected in the diff), so the gate only verifies that the new/changed tests fail against the merge base — proving they reproduce the bug they target.
Platform: ANDROID
Merge base: 0395a53b
| Test | Type | Outcome |
|---|---|---|
Issue33034 |
UITest | PASS ❌ (should fail) |
📱 UI Tests — SafeAreaEdges
Detected UI test categories: SafeAreaEdges
🧪 UI Test Execution Results (deep, platform pool)
| Category | Tests | Snapshot diffs |
|---|---|---|
SafeAreaEdges |
0/61 (setup failed; 61 marked failed) | — |
⚠️ SafeAreaEdges — fixture setup failed for 61 tests
NUnit reported a OneTimeSetUp/fixture setup failure before test bodies ran; the TRX marked each affected test failed.
Multiple setup failure signatures were present; showing the first one. See the TRX artifact for all details.
OneTimeSetUp: System.TimeoutException : Verify AdjustPan mode does not apply keyboard insets (the app did not recover after crash-recovery attempts)
at Microsoft.Maui.TestCases.Tests.UtilExtensions.WaitForGoToTestButtonWithRecovery(IApp app, String timeoutMessage) in /_/src/Controls/tests/TestCases.Shared.Tests/UtilExtensions.cs:line 91
at Microsoft.Maui.TestCases.Tests._IssuesUITest.NavigateToIssue(String issue) in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 54
at Microsoft.Maui.TestCases.Tests._IssuesUITest.TryToResetTestState() in /_/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/_IssuesUITest.cs:line 25
at Microsoft.Maui.TestCases.Tests.UITest.FixtureSetup() in /_/src/Controls/tests/TestCases.Shared.Tests/UITest.cs:line 576
at UITest.Appium.NUnit.UITestBase.OneTimeSetup() in /_/src/TestUtils/src/UITest.NUnit/UITestBase.cs:line 221
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.RuntimeMethodHandle.InvokeMethod(ObjectHandleOnStack target, Void** arguments, ObjectHandleOnStack sig, BOOL isConstructor, ObjectHandleOnStack result)
at System.Reflection.MethodBaseInvoker.InterpretedInvoke_Method(Object obj, IntPtr* args)
at System.Reflection.MethodBaseInvoker.InvokeWithNoArgs(Object obj, BindingFlags invokeAttr)
📎 Download drop-deep-uitests artifact (TRX + snapshot diffs)
📋 Pre-Flight — Context & Validation
Issue: #36259 - [ci-scan] Recurring: SafeAreaShouldWorkOnAllShellTabs fails with StaleElementReferenceException on Android API 30 in maui-pr-uitests
PR: #36429 - [ci-fix] De-flake SafeAreaShouldWorkOnAllShellTabs (StaleElementReferenceException on tab switch)
Platforms Affected: Android (test is compiled for Android and iOS; requested test platform: android)
Files Changed: 1 implementation/test-infrastructure, 1 test
Key Findings
- PR #36429 changes
Issue33034.SafeAreaShouldWorkOnAllShellTabsto use a newWaitForElementAndGetRecthelper for the initial and post-tab-switch geometry reads. - The linked issue reports intermittent Android API 30
StaleElementReferenceExceptionfromGetRect()after Shell tab switching; the PR's current fix treats this as an Appium stale-element race. - A prior MauiBot inline review raised a critical regression-prevention concern: the test waits for the second tab, but measures only the first tab before and after navigation.
ghis unauthenticated in this environment, so pre-flight used localpr-review-36429checkout plus public GitHub API/curl; required-check status could not be collected viagh pr checks --required.
Code Review Summary
Verdict: NEEDS_CHANGES
Confidence: low
Errors: 1 | Warnings: 1 | Suggestions: 1
Key code review findings:
- ✗
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs:28— test still does not validate the second tab's safe-area geometry; it compares first-tab geometry before/after navigation. - ⚠
src/TestUtils/src/UITest.Appium/HelperExtensions.cs:198— stale-element retries happen back-to-back, so the helper can repeatedly race the same transition window. - ℹ
src/TestUtils/src/UITest.Appium/HelperExtensions.cs:189— the AppiumMouseActions citation is imprecise for this exact stale-element behavior.
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| PR | PR #36429 | Add WaitForElementAndGetRect helper that re-finds EdgeLabel and retries GetRect() on StaleElementReferenceException; use it for both rect reads in Issue33034. |
❌ Gate failed (prior step: tests did not behave as expected) | src/TestUtils/src/UITest.Appium/HelperExtensions.cs, src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs |
Improves stale read handling but does not address second-tab coverage concern. |
🔬 Code Review — Deep Analysis
Code Review — PR #36429
Independent Assessment
What this changes: Adds WaitForElementAndGetRect() to retry GetRect() when Appium reports a stale element, and uses it in Issue33034.
Inferred motivation: De-flake a SafeAreaEdges UI test where tab switching recycles EdgeLabel between element lookup and geometry read.
Reconciliation with PR Narrative
Author claims: The PR fixes intermittent StaleElementReferenceException without weakening assertions or adding sleeps.
Agreement/disagreement: The stale retry direction is reasonable, but the test still does not validate the second tab’s geometry, and the retry loop may re-hit the same animation race because retries are immediate.
Prior Review Reconciliation
| Prior ❌ Error Finding | Source | Status | Evidence |
|---|---|---|---|
| Test compares first-tab geometry before/after navigation and never asserts second-tab safe-area geometry | MauiBot inline review, Issue33034.cs:28, [critical] Regression Prevention |
❌ Unresolved | Current code still captures initialRect on first tab, taps second tab only to wait, then taps first tab and measures afterSwitchRect at Issue33034.cs:23-28. |
Blast Radius Assessment
- Runs for all instances: No — only this UI test currently calls the helper.
- Startup impact: No.
- Static/shared state: No.
- Shared helper impact: Yes — helper is added to common Appium test infrastructure and may be reused by future tests.
CI Status
- Required-check result:
gh pr checks --requiredunavailable;ghis unauthenticated (gh auth loginrequired). - Public check-run fallback: PR head has failing/pending checks, including
maui-pr,Build Analysis, severalmaui-pr-uitestsfailures/cancellations, and queuedmaui-pr-uitests. - Classification: Undetermined from available public data; not safe to treat as green.
- Action taken: Invoked
azdo-build-investigatorskill per workflow; confidence capped low.
Findings
❌ Error — Test still does not validate the second tab
src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs:28
The test waits for EdgeLabel on the second tab, but never records or asserts that tab’s rectangle. It immediately taps back to the first tab and compares first-tab geometry before/after navigation. A regression where only the second tab collides with the cutout can still pass.
⚠️ Warning — Stale-element retries happen back-to-back
src/TestUtils/src/UITest.Appium/HelperExtensions.cs:198
WaitForElement() performs its first query immediately, and if the element is present, the loop can retry all three GetRect() calls without any pause. For an animation/view-recycle race, this may repeatedly hit the same stale window rather than waiting for the view to settle.
💡 Suggestion — Inaccurate AppiumMouseActions citation
src/TestUtils/src/UITest.Appium/HelperExtensions.cs:189
The doc says this mirrors stale-element handling in AppiumMouseActions, but that code catches broader click failures and does not handle StaleElementReferenceException specifically. Consider removing or correcting the reference.
Failure-Mode Probing
- What if only the second tab has bad safe-area padding? The current test can still pass because it never compares the second tab’s rect.
- What if the tab-switch animation is still recycling views? Immediate retries may keep racing the same transition.
- What if
WaitForElement()times out orGetRect()fails with a non-stale error? Those exceptions still propagate, which is appropriate.
Verdict: NEEDS_CHANGES
Confidence: low
Summary: The PR improves one stale-element symptom, but an unresolved prior critical finding remains: the test still does not assert the tab it claims to protect. CI is also not verifiably green from this environment.
🛠️ Fix — Analysis & Comparison
Fix Candidates
| # | Source | Approach | Test Result | Files Changed | Notes |
|---|---|---|---|---|---|
| 1 | maui-expert-reviewer + try-fix | Use unique tab-specific EdgeLabel automation ids and a test-local stable-geometry polling helper; assert the second tab directly before returning to the first tab. |
✅ PASS | 3 files | Demonstrably better than PR fix: resolves prior critical coverage finding and avoids shared immediate stale-retry helper. |
| PR | PR #36429 | Add shared WaitForElementAndGetRect helper with three immediate retries on StaleElementReferenceException; use it for both existing first-tab rect reads. |
❌ Gate failed (prior step) | 2 files | Does not validate second-tab geometry; retry loop can hit same animation window. |
Cross-Pollination
| Model | Round | New Ideas? | Details |
|---|---|---|---|
| gpt-5.5 / maui-expert-reviewer | 1 | Yes | Stable active-tab geometry with distinct automation ids and direct second-tab assertion. |
Exhausted: No — stopped because candidate #1 passed the Android target test and is demonstrably better than the PR fix.
Selected Fix: Candidate #1 — It validates the actual active second tab and passed SafeAreaShouldWorkOnAllShellTabs on Android while keeping assertions strict.
📝 Recommended PR Title & Description
Assessment: ✏️ Recommend updating — the current metadata accurately describes the raw PR helper, but the winning fix changes the test fixture/assertion strategy instead.
Recommended title
[Android] SafeArea: De-flake Shell tab safe-area UI test
Recommended description
## Summary
De-flakes the Android UI test `SafeAreaShouldWorkOnAllShellTabs` (`Issue33034`), which intermittently fails with a `StaleElementReferenceException` from `IUIElement.GetRect()` during Shell tab switching.
Target branch: main
Refs: dotnet/maui#36259
Flake class: test-quality (genuine test defect) -> de-flake
## Reproduction evidence
Pipeline `maui-pr-uitests` (def 313), branch `main`. The signature is intermittent across recent completed builds, which indicates a real flake rather than a permanently fixed issue:
| Build | Leg outcome | Signature |
|---|---|---|
| 1484478 | failed on first attempt, passed on retry | `StaleElementReferenceException` at `get_Rect` |
| 1486368 | failed on first attempt, passed on retry | present |
| 3 newest (incl. latest 1495318) | passed | absent |
Failed-then-passed-on-retry and present-in-some-but-not-all recent builds -> genuinely flaky. `Issue33034.cs` was last modified well before the failures began, so the recent greens are luck, not a landed fix.
## Root cause
The test reads Appium geometry while Shell tab switching can still be recycling native views:
```csharp
App.TapTab("Second Tab");
App.WaitForElement("EdgeLabel");
App.TapTab("First Tab");
var afterSwitchRect = App.WaitForElement("EdgeLabel").GetRect();
WaitForElement returns as soon as the label exists, but the element reference can become stale before the getRect round-trip completes. The original test also used the same EdgeLabel automation id on both tabs and only compared first-tab geometry before/after navigation, so it did not directly prove that the second tab's safe-area layout was correct.
Fix
Update Issue33034 so each Shell tab has a unique label automation id (FirstTabEdgeLabel, SecondTabEdgeLabel), then assert the second tab's geometry while the second tab is active before returning to the first tab.
The test now uses a test-local WaitForStableRect helper that:
- re-queries the active tab's label before each rect read;
- catches
StaleElementReferenceExceptioncaused by tab-switch view recycling; - waits briefly between attempts instead of retrying back-to-back in the same animation window;
- requires two consecutive stable bounds before returning.
The existing .Within(5) X/Width assertions remain strict. No [ActiveIssue], [SkipOnPlatform], category exclusion, timeout-only workaround, muted assertion, or weakened tolerance is added.
Key files
src/Controls/tests/TestCases.HostApp/Issues/Issue33034.cssrc/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs
Validation
Android HostApp UI test run:
pwsh -NoProfile -File .github/scripts/BuildAndRunHostApp.ps1 -Platform android -TestFilter 'FullyQualifiedName~Issue33034'Result: SafeAreaShouldWorkOnAllShellTabs passed on Android.
</details>
---
<details>
<summary><strong>🏁 Report — Final Recommendation</strong></summary>
<br/>
## Comparative Report — PR #36429
### Candidates
| Rank | Candidate | Regression result | Assessment |
|---:|---|---|---|
| 1 | `try-fix-1` | ✅ PASS | Best candidate. It uses unique tab-specific automation ids, directly asserts the second tab's safe-area geometry, waits for stable rects across navigation animation/recycle windows, and passed `SafeAreaShouldWorkOnAllShellTabs` on Android. |
| 2 | `pr-plus-reviewer` | Not re-run | Strong code candidate and functionally equivalent to `try-fix-1` after applying expert feedback in a sandbox. Ranked below `try-fix-1` only because the equivalent try-fix candidate has the recorded Android pass. |
| 3 | `pr` | ❌ Gate failed | Improves the stale `GetRect()` symptom, but still compares only first-tab geometry and uses a shared immediate retry helper that can race the same transition window. Failed candidates must rank below passing candidates. |
### Why `try-fix-1` wins
`try-fix-1` is the only candidate with both the corrected assertion model and a recorded passing Android regression run. It fixes the core coverage defect by measuring `SecondTabEdgeLabel` while the second tab is active, then confirms the first tab remains stable after returning. Its test-local stable-geometry polling also addresses the Appium stale-element race without adding a shared helper with broad future blast radius.
### Candidate details
#### `pr`
The raw PR adds `WaitForElementAndGetRect()` and uses it for two first-tab rect reads. This is not sufficient because a regression limited to the second tab can still pass: the test taps the second tab only long enough to wait for an `EdgeLabel`, then returns to the first tab before taking the second measured rectangle. The expert reviewer also found that the helper retries immediately after stale exceptions, which may not allow the tab-switch animation/recycle window to settle.
#### `pr-plus-reviewer`
The sandboxed reviewer-improved PR applies the expert feedback by adopting the same approach as `try-fix-1`: unique first/second tab automation ids, direct second-tab assertion, and stable rect polling. This is a good implementation direction, but it does not have a separate recorded regression-test result in this phase.
#### `try-fix-1`
`try-fix-1` changes the HostApp test fixture so each tab has a distinct label id, then updates the test to assert first-tab, second-tab, and return-to-first-tab geometry with strict existing `.Within(5)` tolerances. Its `WaitForStableRect()` loop re-queries the active element, catches stale references, waits 250 ms between attempts, and requires consecutive stable bounds before returning. The recorded Android run selected `Issue33034` and passed.
### Winner
**Winner:** `try-fix-1`
**Rationale:** It is the only candidate that both passed the Android regression run and fully addresses the expert review's coverage/flakiness findings.
</details>
</details>
<!-- SESSION:7480191 END -->
---
<details>
<summary><strong>🧭 Next Steps</strong> — alternative fix proposed (<code>try-fix-1</code>)</summary>
<br/>
**Automated review — alternative fix proposed**
The expert-reviewer evaluation compared the PR fix against automatically generated candidates and selected <code>try-fix-1</code> as the strongest fix.
**Why:** try-fix-1 wins because it directly validates the active second tab, handles stale geometry reads with stable polling, and is the only candidate with a recorded passing Android regression run. The raw PR failed the gate and leaves the second-tab coverage gap unresolved.
Please consider applying the candidate diff below (or use it as guidance). Once you push an update, this workflow will re-trigger and re-evaluate.
<details><summary>Candidate diff (<code>try-fix-1</code>)</summary>
````diff
diff --git a/src/Controls/tests/TestCases.HostApp/Issues/Issue33034.cs b/src/Controls/tests/TestCases.HostApp/Issues/Issue33034.cs
index 509e349f82..de10b1614e 100644
--- a/src/Controls/tests/TestCases.HostApp/Issues/Issue33034.cs
+++ b/src/Controls/tests/TestCases.HostApp/Issues/Issue33034.cs
@@ -12,7 +12,7 @@ public class Issue33034 : TestShell
{
Title = "First Tab",
AutomationId = "FirstTab",
- ContentTemplate = new DataTemplate(typeof(Issue33034TabContent)),
+ ContentTemplate = new DataTemplate(() => new Issue33034TabContent("FirstTabEdgeLabel")),
Route = "tab1"
});
@@ -20,7 +20,7 @@ public class Issue33034 : TestShell
{
Title = "Second Tab",
AutomationId = "SecondTab",
- ContentTemplate = new DataTemplate(typeof(Issue33034TabContent)),
+ ContentTemplate = new DataTemplate(() => new Issue33034TabContent("SecondTabEdgeLabel")),
Route = "tab2"
});
@@ -31,13 +31,13 @@ public class Issue33034 : TestShell
public class Issue33034TabContent : ContentPage
{
- public Issue33034TabContent()
+ public Issue33034TabContent(string edgeLabelAutomationId)
{
// Full-width label to detect safe area padding on either side
var edgeLabel = new Label
{
Text = "EDGE LABEL",
- AutomationId = "EdgeLabel",
+ AutomationId = edgeLabelAutomationId,
FontSize = 18,
FontAttributes = FontAttributes.Bold,
BackgroundColor = Colors.Red,
@@ -52,4 +52,3 @@ public class Issue33034TabContent : ContentPage
};
}
}
-
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 d6b8ba4e90..23eb1aa815 100644
--- a/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs
+++ b/src/Controls/tests/TestCases.Shared.Tests/Tests/Issues/Issue33034.cs
@@ -1,5 +1,7 @@
#if ANDROID || IOS // SafeAreaEdges not supported on Catalyst and Windows
+using System.Drawing;
+using OpenQA.Selenium;
using NUnit.Framework;
using UITest.Appium;
using UITest.Core;
@@ -16,19 +18,56 @@ public class Issue33034 : _IssuesUITest
[Category(UITestCategories.SafeAreaEdges)]
public void SafeAreaShouldWorkOnAllShellTabs()
{
- App.WaitForElement("EdgeLabel");
+ App.WaitForElement("FirstTabEdgeLabel");
App.SetOrientationLandscape();
//Adding delay to allow for orientation change to complete
Thread.Sleep(2000);
- var initialRect = App.WaitForElementAndGetRect("EdgeLabel");
+ var initialRect = WaitForStableRect("FirstTabEdgeLabel");
App.TapTab("Second Tab");
- App.WaitForElement("EdgeLabel");
+ var secondTabRect = WaitForStableRect("SecondTabEdgeLabel");
+
+ Assert.That(secondTabRect.X, Is.EqualTo(initialRect.X).Within(5));
+ Assert.That(secondTabRect.Width, Is.EqualTo(initialRect.Width).Within(5));
+
App.TapTab("First Tab");
- var afterSwitchRect = App.WaitForElementAndGetRect("EdgeLabel");
+ var afterSwitchRect = WaitForStableRect("FirstTabEdgeLabel");
Assert.That(afterSwitchRect.X, Is.EqualTo(initialRect.X).Within(5));
Assert.That(afterSwitchRect.Width, Is.EqualTo(initialRect.Width).Within(5));
}
+
+ Rectangle WaitForStableRect(string elementId)
+ {
+ Rectangle? previous = null;
+ var started = DateTime.UtcNow;
+
+ while (DateTime.UtcNow - started < TimeSpan.FromSeconds(15))
+ {
+ try
+ {
+ var rect = App.WaitForElement(elementId).GetRect();
+
+ if (previous is Rectangle last &&
+ Math.Abs(last.X - rect.X) <= 1 &&
+ Math.Abs(last.Y - rect.Y) <= 1 &&
+ Math.Abs(last.Width - rect.Width) <= 1 &&
+ Math.Abs(last.Height - rect.Height) <= 1)
+ {
+ return rect;
+ }
+
+ previous = rect;
+ }
+ catch (StaleElementReferenceException)
+ {
+ previous = null;
+ }
+
+ Thread.Sleep(250);
+ }
+
+ throw new TimeoutException($"Timed out waiting for '{elementId}' to report stable bounds.");
+ }
}
#endif
diff --git a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
index 1343d574af..9bd0aefa2b 100644
--- a/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
+++ b/src/TestUtils/src/UITest.Appium/HelperExtensions.cs
@@ -179,39 +179,6 @@ namespace UITest.Appium
})!;
}
- /// <summary>
- /// 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
- /// <see cref="StaleElementReferenceException"/> from <see cref="GetRect(IUIElement)"/>. 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).
- /// </summary>
- /// <param name="app">Represents the main gateway to interact with an app.</param>
- /// <param name="elementId">The id of the element to locate and measure.</param>
- /// <param name="timeout">Optional timeout for each wait operation. Default is null, which uses the default timeout.</param>
- 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!;
- }
-
/// <summary>
/// Determine if a form or form-like element (checkbox, select, etc...) is selected.
/// </summary>
|
/azp run |
|
Azure Pipelines successfully started running 3 pipeline(s). |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Tests Failure Analysis
Test Failure Review: Not ready - click to expandOverall verdict: Not ready: against several recent
Coverage: 160 checks · 149 passing · 10 failing · 1 pending · 1 unmapped · 16 unexplained build legs · 1 aborted · 0 device-test unverified · 21 unattributed · 2 regressed-vs-base. Deterministic ceiling: Not ready — 2 legs regressed vs base. Builds (this PR): maui-pr 1504721 · maui-pr-uitests 1504722 · maui-pr-devicetests 1504723 (succeeded). Base sampling ( Scope: ci-fix / UI-test retry. Base branch: main. Recommended actionConfirm the 2 regressed-vs-base failure(s) flagged above are resolved before merging; the remaining failures reproduce on the base branch and are not caused by this PR. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
…#36461) > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## What this changes Follow-up to #36317 (the self-watching `ci-status-fix` loop). Today, when a `[ci-fix]` draft PR's CI comes back green — or red only on **unrelated** flakes — the loop posts a comment but **leaves the PR as a draft forever**. A maintainer has to notice it, confirm the specific fixed test actually passed, and flip it to ready. In practice these validated-green draft PRs sit unreviewed for weeks. This PR adds a **target-test verification + mark-ready gate (Step 3.6)** to both twins (`main` + `net11.0`). When a draft `[ci-fix]` / `[ci-fix-net11]` PR reaches the green-surface or unrelated-flake branch, the loop now drills into the PR's **own** AzDO test-results for the **specific test(s)** the fix targeted. If every target test is `Passed` on ≥1 leg and `Failed` on none (VALIDATED-GREEN), it: - posts a `🎯 Target test validated green on <headSha>` comment (naming the test + legs + buildId), and - transitions the draft PR to **ready for review**. This is a **state transition only** — it never approves and never merges; a human still reviews and merges. Overall red on **unrelated** legs no longer keeps a validated fix parked as a draft. ## How it works - **Preconditions** (all required): PR `isDraft == true`; it is unmistakably this workflow's own PR (`[ci-fix]`/`[ci-fix-net11]` title prefix **and** `agentic-workflows` label); and it was reached from the green or **unrelated-flake** path (never from caused-by-fix — that path advances an attempt instead). - **T1 — identify target test(s)** from the `[ci-scan]` issue signature + the PR diff. If no specific test can be identified (e.g. a product build-break), it records a skip — build-only fixes are validated by overall-green, which the existing green branch already handles. - **T2 — drill AzDO test-results** for the build(s) on the PR's current head SHA, filtered to the target test's `testCaseTitle`. A test that never ran (e.g. an `/azp`-gated `maui-pr-uitests`/`maui-pr-devicetests` leg that wasn't kicked) is **not** validated — the loop records an honest "not yet executed" skip and does **not** mark ready. No overclaiming: a green *sibling* leg is not the target test. - **T3 — mark ready + report**, guarded by a per-head-SHA idempotency marker and the existing `dry_run` gate (dry-run emits nothing and tallies `would-mark-ready`). ## Safe-output Adds the `mark-pull-request-as-ready-for-review` safe-output to both twins (`max: 3`, `target: "*"`, `required-title-prefix` + `required-labels`). Unlike `update-pull-request`, this output's `required-*` guards **do** survive the gh-aw v0.80.9 compile (verified against the generated locks), so which-PR scoping is enforced at the handler level in addition to the Step 3.6 preconditions and `min-integrity: approved`. No gh-aw version bump is required — the capability already exists at our pinned v0.80.9. ## Enabling fix — the loop's own create-PR commit no longer counts as "human engaged" While validating this feature against #36429 I found the mark-ready path was **unreachable for every loop-owned draft PR**, and traced it to a regression from #36317's own review-hardening. gh-aw's `create_pull_request` builds a PR's initial commit through the GitHub API, which stamps `author=github-actions[bot]` but **`committer=web-flow`**. Commit `2f6b77b330` (in #36317) removed `web-flow` from the prefetch's bot-login denylist so a maintainer "Update branch" would correctly hand the PR off — but that also made `Test-AnyHumanCommitActor`'s committer check read the loop's **own** first commit as human engagement. Result: every freshly opened `[ci-fix]` draft PR computed `humanEngaged=true` from commit #1, so the watch loop skipped it forever — never surfacing green, never marking ready. This regression is live on `main` today (all four open loop-owned draft PRs have `committer=web-flow`). Fix (`Query-CiFixPRs.ps1`): a human **committer** only trips the hand-off when the commit **author** is not one of this workflow's own bot identities (`$LoopBotCommitAuthors`). A human **author** still counts unconditionally, so maintainer direct commits and web-flow-authored "Update branch" merges continue to hand off correctly. Unit-tested across all six author/committer shapes. ## Validation - Both twins recompiled with `gh aw compile` (0 errors / 0 warnings); locks show no action-SHA or `compiler_version` drift (only frontmatter/body hash + the new handler config). - `mark_pull_request_as_ready_for_review` config confirmed present in **both** locks with the correct `[ci-fix] ` / `[ci-fix-net11] ` prefixes and `agentic-workflows` label; the `safe_outputs` job carries `pull-requests: write`. - Twin symmetry preserved (only `[ci-fix]` ↔ `[ci-fix-net11]` / `ci-scan` ↔ `ci-scan-net11` token differences). - **Live dry-run against #36429** (scoped `workflow_dispatch`, `dry_run=true`, this branch): the workflow prefetch now computes `humanEngaged=false` for #36429 (was `true` pre-fix), and the agent's gate walk correctly advances **past** the human-engaged gate to the CI-pending WAIT gate — `checksSettled=false` because #36429's macOS `SafeAreaEdges` leg is still queued. Once that leg settles green, the same run path reaches Step 3.6 and marks the PR ready. Emitted zero writes (dry-run). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
kubaflo
left a comment
There was a problem hiding this comment.
🔍 AI-generated review (multi-model pipeline: Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro), on behalf of @kubaflo.
✅ LGTM — 3-model consensus (unanimous, high confidence) at 74801915
Three independent reviews (Opus 4.8 · GPT-5.5 · Gemini 3.1 Pro) all returned LGTM / high, with zero blocking findings. The de-flake is correct, targeted, and strictly non-regressive.
What it does: wraps the flaky WaitForElement("EdgeLabel").GetRect() (which races a tab-switch animation recycling the native view between the element query and the geometry read) in a new additive helper WaitForElementAndGetRect that re-queries the element fresh and retries up to 3× on StaleElementReferenceException.
Verified across all three models:
- Fresh reference on retry —
WaitForElement → FindElement → Driver.FindElements → new AppiumDriverElementre-queries on every iteration, so each attempt discards the stale handle. The retry does exactly what's intended. - The stale exception actually surfaces as the caught type —
GetRectruns insideRunWithTimeout/Task.Run; a faulted task'sAggregateExceptionis unwrapped and rethrown viaExceptionDispatchInfoas the exactStaleElementReferenceException, so the newcatchgenuinely intercepts it (not masked as a timeout). - Failure semantics preserved — only
StaleElementReferenceExceptionis caught;TimeoutException(element never found) andGetRect'sInvalidOperationExceptionstill propagate unchanged. Happy path is byte-for-byte equivalent to the original.throw lastStale!is provably null-safe (only reachable after 3 caught staleness). Worst case is bounded (3 finds + 3 timeout-protected reads) — no hot loop. - Zero blast radius — purely additive to the shared
HelperExtensions.cs; no existing member modified, no overload collision.StaleElementReferenceExceptionresolves viausing OpenQA.Selenium;(line 6).
Non-blocking 💡 (2/3 model agreement, optional): throw lastStale! overwrites the exception's original stack trace with the helper location. Since RunWithTimeout in this same file already uses ExceptionDispatchInfo.Capture(x).Throw() to preserve stacks, matching that here (ExceptionDispatchInfo.Capture(lastStale!).Throw()) would keep triage stacks intact. Also optional: a small Thread.Sleep in the catch if the flake ever recurs under a long recycle window. Neither is required to merge.
CI note: maui-pr shows red, but the failures are not PR-caused — the two Build legs fail with MSB4024 (.buildtasks/*.targets "could not be loaded / being used by another process"), a known in-tree buildtasks load race, and the red uitests legs are unrelated CollectionView/Image categories (this PR only touches a SafeArea/Shell test). Pack + Helix unit + integration legs are green. A re-run should clear the infra flakes.
ℹ Automated multi-model review. This is advisory — a human maintainer makes the final merge decision.
This comment has been minimized.
This comment has been minimized.
Tests Failure Analysis
Test Failure Review: Not ready - click to expandOverall verdict: Not ready. Against
Coverage: 160 checks · 149 passing · 10 failing · 1 pending · 0 inaccessible · 1 unmapped · 15 unexplained build legs · 0 unaccounted failing checks · 1 aborted failing check · 0 canceled-build checks · 0 device-test unverified · 21 unattributed · 2 regressed-vs-base. Deterministic ceiling: Not ready — pending/in-progress check, unexplained build legs, an aborted check, and 2 legs regressed vs base. Builds (this PR): maui-pr 1504721, maui-pr-uitests 1504722, maui-pr-devicetests 1504723 (device tests confirmed clean). Base sampling (main, 5 recent builds per definition): 1503430, 1503618, 1503431. Recommended actionHuman investigation needed: confirm whether the 2 regressed-vs-base build legs and the MSB4024 |
…enceException on tab switch) (#36429) > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Summary De-flakes the Android UI test `SafeAreaShouldWorkOnAllShellTabs` (`Issue33034`), which intermittently fails with a `StaleElementReferenceException` thrown from `IUIElement.GetRect()`. Target branch: main Refs: #36259 Attempt: 1/5 Flake class: test-quality (genuine test defect) → de-flake ## Reproduction evidence Pipeline `maui-pr-uitests` (def 313), branch `main`. The signature is **intermittent** across the most recent completed builds — the hallmark of a real flake, not a fixed issue: | Build | Leg outcome | Signature | |---|---|---| | 1484478 | failed on first attempt, passed on retry | `StaleElementReferenceException` at `get_Rect` | | 1486368 | failed on first attempt, passed on retry | present | | 3 newest (incl. latest 1495318) | passed | absent | Failed-then-passed-on-retry **and** present-in-some-but-not-all recent builds → genuinely flaky. `Issue33034.cs` was last modified well before the failures began, so the recent greens are luck, not a landed fix. ## Root cause ``` App.TapTab("First Tab"); // starts tab-switch animation var afterSwitchRect = App.WaitForElement("EdgeLabel").GetRect(); // find succeeds, then get_Rect races the animation ``` `WaitForElement` returns as soon as `EdgeLabel` exists, but the tab-switch animation recycles the underlying view between the query and the `getRect` round-trip, invalidating the element reference → `StaleElementReferenceException` on `get_Rect()`. This is a *pre-assertion crash*, not an assertion failure. ## Fix Add a stale-resilient `WaitForElementAndGetRect` helper to `HelperExtensions` that re-finds the element and retries `GetRect()` when the reference goes stale, then use it at the two post-UI-change rect reads in the test. - **Condition-based, not a fixed wait** — it retries on the actual stale condition (re-querying a fresh element each attempt via `WaitForElement`), with no new `Thread.Sleep`/`Task.Delay`. - **Mirrors existing framework behavior** — `AppiumMouseActions.ProcessException` already re-finds stale elements (*"Appium elements will sometimes become stale ... we retrieve it here"*). - **Assertions untouched** — the `.Within(5)` tolerance checks are unchanged; nothing is weakened, muted, or skipped. ## Not muting No `[ActiveIssue]`, `[SkipOnPlatform]`, `[Retry]`/`[Repeat]`, category exclusion, csproj incompatibility, weakened assertion, or timeout bump. The test still exercises and asserts the same SafeArea behavior on every tab; it is simply made robust against a known Appium view-recycling artifact during navigation animations. ## Validation⚠️ This is an on-device Android UI test that cannot be executed in the fixer's sandbox, so the change is **not runner-validated** (help-style). It is a targeted, type-checked synchronization change following an established framework pattern; CI on this PR should confirm the leg is green. > Generated by [CI Failure Fixer (main)](https://github.com/dotnet/maui/actions/runs/28868353740) · 919.8 AIC · ⌖ 46 AIC · ⊞ 24.2K · [◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+ci-status-fix%22&type=pullrequests) <!-- gh-aw-agentic-workflow: CI Failure Fixer (main), engine: copilot, version: 1.0.63, model: claude-opus-4.8, id: 28868353740, workflow_id: ci-status-fix, run: https://github.com/dotnet/maui/actions/runs/28868353740 --> <!-- gh-aw-workflow-id: ci-status-fix --> <!-- gh-aw-workflow-call-id: dotnet/maui/ci-status-fix --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…dotnet#36461) > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## What this changes Follow-up to dotnet#36317 (the self-watching `ci-status-fix` loop). Today, when a `[ci-fix]` draft PR's CI comes back green — or red only on **unrelated** flakes — the loop posts a comment but **leaves the PR as a draft forever**. A maintainer has to notice it, confirm the specific fixed test actually passed, and flip it to ready. In practice these validated-green draft PRs sit unreviewed for weeks. This PR adds a **target-test verification + mark-ready gate (Step 3.6)** to both twins (`main` + `net11.0`). When a draft `[ci-fix]` / `[ci-fix-net11]` PR reaches the green-surface or unrelated-flake branch, the loop now drills into the PR's **own** AzDO test-results for the **specific test(s)** the fix targeted. If every target test is `Passed` on ≥1 leg and `Failed` on none (VALIDATED-GREEN), it: - posts a `🎯 Target test validated green on <headSha>` comment (naming the test + legs + buildId), and - transitions the draft PR to **ready for review**. This is a **state transition only** — it never approves and never merges; a human still reviews and merges. Overall red on **unrelated** legs no longer keeps a validated fix parked as a draft. ## How it works - **Preconditions** (all required): PR `isDraft == true`; it is unmistakably this workflow's own PR (`[ci-fix]`/`[ci-fix-net11]` title prefix **and** `agentic-workflows` label); and it was reached from the green or **unrelated-flake** path (never from caused-by-fix — that path advances an attempt instead). - **T1 — identify target test(s)** from the `[ci-scan]` issue signature + the PR diff. If no specific test can be identified (e.g. a product build-break), it records a skip — build-only fixes are validated by overall-green, which the existing green branch already handles. - **T2 — drill AzDO test-results** for the build(s) on the PR's current head SHA, filtered to the target test's `testCaseTitle`. A test that never ran (e.g. an `/azp`-gated `maui-pr-uitests`/`maui-pr-devicetests` leg that wasn't kicked) is **not** validated — the loop records an honest "not yet executed" skip and does **not** mark ready. No overclaiming: a green *sibling* leg is not the target test. - **T3 — mark ready + report**, guarded by a per-head-SHA idempotency marker and the existing `dry_run` gate (dry-run emits nothing and tallies `would-mark-ready`). ## Safe-output Adds the `mark-pull-request-as-ready-for-review` safe-output to both twins (`max: 3`, `target: "*"`, `required-title-prefix` + `required-labels`). Unlike `update-pull-request`, this output's `required-*` guards **do** survive the gh-aw v0.80.9 compile (verified against the generated locks), so which-PR scoping is enforced at the handler level in addition to the Step 3.6 preconditions and `min-integrity: approved`. No gh-aw version bump is required — the capability already exists at our pinned v0.80.9. ## Enabling fix — the loop's own create-PR commit no longer counts as "human engaged" While validating this feature against dotnet#36429 I found the mark-ready path was **unreachable for every loop-owned draft PR**, and traced it to a regression from dotnet#36317's own review-hardening. gh-aw's `create_pull_request` builds a PR's initial commit through the GitHub API, which stamps `author=github-actions[bot]` but **`committer=web-flow`**. Commit `2f6b77b330` (in dotnet#36317) removed `web-flow` from the prefetch's bot-login denylist so a maintainer "Update branch" would correctly hand the PR off — but that also made `Test-AnyHumanCommitActor`'s committer check read the loop's **own** first commit as human engagement. Result: every freshly opened `[ci-fix]` draft PR computed `humanEngaged=true` from commit #1, so the watch loop skipped it forever — never surfacing green, never marking ready. This regression is live on `main` today (all four open loop-owned draft PRs have `committer=web-flow`). Fix (`Query-CiFixPRs.ps1`): a human **committer** only trips the hand-off when the commit **author** is not one of this workflow's own bot identities (`$LoopBotCommitAuthors`). A human **author** still counts unconditionally, so maintainer direct commits and web-flow-authored "Update branch" merges continue to hand off correctly. Unit-tested across all six author/committer shapes. ## Validation - Both twins recompiled with `gh aw compile` (0 errors / 0 warnings); locks show no action-SHA or `compiler_version` drift (only frontmatter/body hash + the new handler config). - `mark_pull_request_as_ready_for_review` config confirmed present in **both** locks with the correct `[ci-fix] ` / `[ci-fix-net11] ` prefixes and `agentic-workflows` label; the `safe_outputs` job carries `pull-requests: write`. - Twin symmetry preserved (only `[ci-fix]` ↔ `[ci-fix-net11]` / `ci-scan` ↔ `ci-scan-net11` token differences). - **Live dry-run against dotnet#36429** (scoped `workflow_dispatch`, `dry_run=true`, this branch): the workflow prefetch now computes `humanEngaged=false` for dotnet#36429 (was `true` pre-fix), and the agent's gate walk correctly advances **past** the human-engaged gate to the CI-pending WAIT gate — `checksSettled=false` because dotnet#36429's macOS `SafeAreaEdges` leg is still queued. Once that leg settles green, the same run path reaches Step 3.6 and marks the PR ready. Emitted zero writes (dry-run). Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: PureWeen <223556219+Copilot@users.noreply.github.com>
…enceException on tab switch) (#36429) > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Summary De-flakes the Android UI test `SafeAreaShouldWorkOnAllShellTabs` (`Issue33034`), which intermittently fails with a `StaleElementReferenceException` thrown from `IUIElement.GetRect()`. Target branch: main Refs: #36259 Attempt: 1/5 Flake class: test-quality (genuine test defect) → de-flake ## Reproduction evidence Pipeline `maui-pr-uitests` (def 313), branch `main`. The signature is **intermittent** across the most recent completed builds — the hallmark of a real flake, not a fixed issue: | Build | Leg outcome | Signature | |---|---|---| | 1484478 | failed on first attempt, passed on retry | `StaleElementReferenceException` at `get_Rect` | | 1486368 | failed on first attempt, passed on retry | present | | 3 newest (incl. latest 1495318) | passed | absent | Failed-then-passed-on-retry **and** present-in-some-but-not-all recent builds → genuinely flaky. `Issue33034.cs` was last modified well before the failures began, so the recent greens are luck, not a landed fix. ## Root cause ``` App.TapTab("First Tab"); // starts tab-switch animation var afterSwitchRect = App.WaitForElement("EdgeLabel").GetRect(); // find succeeds, then get_Rect races the animation ``` `WaitForElement` returns as soon as `EdgeLabel` exists, but the tab-switch animation recycles the underlying view between the query and the `getRect` round-trip, invalidating the element reference → `StaleElementReferenceException` on `get_Rect()`. This is a *pre-assertion crash*, not an assertion failure. ## Fix Add a stale-resilient `WaitForElementAndGetRect` helper to `HelperExtensions` that re-finds the element and retries `GetRect()` when the reference goes stale, then use it at the two post-UI-change rect reads in the test. - **Condition-based, not a fixed wait** — it retries on the actual stale condition (re-querying a fresh element each attempt via `WaitForElement`), with no new `Thread.Sleep`/`Task.Delay`. - **Mirrors existing framework behavior** — `AppiumMouseActions.ProcessException` already re-finds stale elements (*"Appium elements will sometimes become stale ... we retrieve it here"*). - **Assertions untouched** — the `.Within(5)` tolerance checks are unchanged; nothing is weakened, muted, or skipped. ## Not muting No `[ActiveIssue]`, `[SkipOnPlatform]`, `[Retry]`/`[Repeat]`, category exclusion, csproj incompatibility, weakened assertion, or timeout bump. The test still exercises and asserts the same SafeArea behavior on every tab; it is simply made robust against a known Appium view-recycling artifact during navigation animations. ## Validation⚠️ This is an on-device Android UI test that cannot be executed in the fixer's sandbox, so the change is **not runner-validated** (help-style). It is a targeted, type-checked synchronization change following an established framework pattern; CI on this PR should confirm the leg is green. > Generated by [CI Failure Fixer (main)](https://github.com/dotnet/maui/actions/runs/28868353740) · 919.8 AIC · ⌖ 46 AIC · ⊞ 24.2K · [◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+ci-status-fix%22&type=pullrequests) <!-- gh-aw-agentic-workflow: CI Failure Fixer (main), engine: copilot, version: 1.0.63, model: claude-opus-4.8, id: 28868353740, workflow_id: ci-status-fix, run: https://github.com/dotnet/maui/actions/runs/28868353740 --> <!-- gh-aw-workflow-id: ci-status-fix --> <!-- gh-aw-workflow-call-id: dotnet/maui/ci-status-fix --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…enceException on tab switch) (#36429) > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Summary De-flakes the Android UI test `SafeAreaShouldWorkOnAllShellTabs` (`Issue33034`), which intermittently fails with a `StaleElementReferenceException` thrown from `IUIElement.GetRect()`. Target branch: main Refs: #36259 Attempt: 1/5 Flake class: test-quality (genuine test defect) → de-flake ## Reproduction evidence Pipeline `maui-pr-uitests` (def 313), branch `main`. The signature is **intermittent** across the most recent completed builds — the hallmark of a real flake, not a fixed issue: | Build | Leg outcome | Signature | |---|---|---| | 1484478 | failed on first attempt, passed on retry | `StaleElementReferenceException` at `get_Rect` | | 1486368 | failed on first attempt, passed on retry | present | | 3 newest (incl. latest 1495318) | passed | absent | Failed-then-passed-on-retry **and** present-in-some-but-not-all recent builds → genuinely flaky. `Issue33034.cs` was last modified well before the failures began, so the recent greens are luck, not a landed fix. ## Root cause ``` App.TapTab("First Tab"); // starts tab-switch animation var afterSwitchRect = App.WaitForElement("EdgeLabel").GetRect(); // find succeeds, then get_Rect races the animation ``` `WaitForElement` returns as soon as `EdgeLabel` exists, but the tab-switch animation recycles the underlying view between the query and the `getRect` round-trip, invalidating the element reference → `StaleElementReferenceException` on `get_Rect()`. This is a *pre-assertion crash*, not an assertion failure. ## Fix Add a stale-resilient `WaitForElementAndGetRect` helper to `HelperExtensions` that re-finds the element and retries `GetRect()` when the reference goes stale, then use it at the two post-UI-change rect reads in the test. - **Condition-based, not a fixed wait** — it retries on the actual stale condition (re-querying a fresh element each attempt via `WaitForElement`), with no new `Thread.Sleep`/`Task.Delay`. - **Mirrors existing framework behavior** — `AppiumMouseActions.ProcessException` already re-finds stale elements (*"Appium elements will sometimes become stale ... we retrieve it here"*). - **Assertions untouched** — the `.Within(5)` tolerance checks are unchanged; nothing is weakened, muted, or skipped. ## Not muting No `[ActiveIssue]`, `[SkipOnPlatform]`, `[Retry]`/`[Repeat]`, category exclusion, csproj incompatibility, weakened assertion, or timeout bump. The test still exercises and asserts the same SafeArea behavior on every tab; it is simply made robust against a known Appium view-recycling artifact during navigation animations. ## Validation⚠️ This is an on-device Android UI test that cannot be executed in the fixer's sandbox, so the change is **not runner-validated** (help-style). It is a targeted, type-checked synchronization change following an established framework pattern; CI on this PR should confirm the leg is green. > Generated by [CI Failure Fixer (main)](https://github.com/dotnet/maui/actions/runs/28868353740) · 919.8 AIC · ⌖ 46 AIC · ⊞ 24.2K · [◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+ci-status-fix%22&type=pullrequests) <!-- gh-aw-agentic-workflow: CI Failure Fixer (main), engine: copilot, version: 1.0.63, model: claude-opus-4.8, id: 28868353740, workflow_id: ci-status-fix, run: https://github.com/dotnet/maui/actions/runs/28868353740 --> <!-- gh-aw-workflow-id: ci-status-fix --> <!-- gh-aw-workflow-call-id: dotnet/maui/ci-status-fix --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…enceException on tab switch) (#36429) > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Summary De-flakes the Android UI test `SafeAreaShouldWorkOnAllShellTabs` (`Issue33034`), which intermittently fails with a `StaleElementReferenceException` thrown from `IUIElement.GetRect()`. Target branch: main Refs: #36259 Attempt: 1/5 Flake class: test-quality (genuine test defect) → de-flake ## Reproduction evidence Pipeline `maui-pr-uitests` (def 313), branch `main`. The signature is **intermittent** across the most recent completed builds — the hallmark of a real flake, not a fixed issue: | Build | Leg outcome | Signature | |---|---|---| | 1484478 | failed on first attempt, passed on retry | `StaleElementReferenceException` at `get_Rect` | | 1486368 | failed on first attempt, passed on retry | present | | 3 newest (incl. latest 1495318) | passed | absent | Failed-then-passed-on-retry **and** present-in-some-but-not-all recent builds → genuinely flaky. `Issue33034.cs` was last modified well before the failures began, so the recent greens are luck, not a landed fix. ## Root cause ``` App.TapTab("First Tab"); // starts tab-switch animation var afterSwitchRect = App.WaitForElement("EdgeLabel").GetRect(); // find succeeds, then get_Rect races the animation ``` `WaitForElement` returns as soon as `EdgeLabel` exists, but the tab-switch animation recycles the underlying view between the query and the `getRect` round-trip, invalidating the element reference → `StaleElementReferenceException` on `get_Rect()`. This is a *pre-assertion crash*, not an assertion failure. ## Fix Add a stale-resilient `WaitForElementAndGetRect` helper to `HelperExtensions` that re-finds the element and retries `GetRect()` when the reference goes stale, then use it at the two post-UI-change rect reads in the test. - **Condition-based, not a fixed wait** — it retries on the actual stale condition (re-querying a fresh element each attempt via `WaitForElement`), with no new `Thread.Sleep`/`Task.Delay`. - **Mirrors existing framework behavior** — `AppiumMouseActions.ProcessException` already re-finds stale elements (*"Appium elements will sometimes become stale ... we retrieve it here"*). - **Assertions untouched** — the `.Within(5)` tolerance checks are unchanged; nothing is weakened, muted, or skipped. ## Not muting No `[ActiveIssue]`, `[SkipOnPlatform]`, `[Retry]`/`[Repeat]`, category exclusion, csproj incompatibility, weakened assertion, or timeout bump. The test still exercises and asserts the same SafeArea behavior on every tab; it is simply made robust against a known Appium view-recycling artifact during navigation animations. ## Validation⚠️ This is an on-device Android UI test that cannot be executed in the fixer's sandbox, so the change is **not runner-validated** (help-style). It is a targeted, type-checked synchronization change following an established framework pattern; CI on this PR should confirm the leg is green. > Generated by [CI Failure Fixer (main)](https://github.com/dotnet/maui/actions/runs/28868353740) · 919.8 AIC · ⌖ 46 AIC · ⊞ 24.2K · [◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+ci-status-fix%22&type=pullrequests) <!-- gh-aw-agentic-workflow: CI Failure Fixer (main), engine: copilot, version: 1.0.63, model: claude-opus-4.8, id: 28868353740, workflow_id: ci-status-fix, run: https://github.com/dotnet/maui/actions/runs/28868353740 --> <!-- gh-aw-workflow-id: ci-status-fix --> <!-- gh-aw-workflow-call-id: dotnet/maui/ci-status-fix --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…enceException on tab switch) (#36429) > [!NOTE] > Are you waiting for the changes in this PR to be merged? > It would be very helpful if you could [test the resulting artifacts](https://github.com/dotnet/maui/wiki/Testing-PR-Builds) from this PR and let us know in a comment if this change resolves your issue. Thank you! ## Summary De-flakes the Android UI test `SafeAreaShouldWorkOnAllShellTabs` (`Issue33034`), which intermittently fails with a `StaleElementReferenceException` thrown from `IUIElement.GetRect()`. Target branch: main Refs: #36259 Attempt: 1/5 Flake class: test-quality (genuine test defect) → de-flake ## Reproduction evidence Pipeline `maui-pr-uitests` (def 313), branch `main`. The signature is **intermittent** across the most recent completed builds — the hallmark of a real flake, not a fixed issue: | Build | Leg outcome | Signature | |---|---|---| | 1484478 | failed on first attempt, passed on retry | `StaleElementReferenceException` at `get_Rect` | | 1486368 | failed on first attempt, passed on retry | present | | 3 newest (incl. latest 1495318) | passed | absent | Failed-then-passed-on-retry **and** present-in-some-but-not-all recent builds → genuinely flaky. `Issue33034.cs` was last modified well before the failures began, so the recent greens are luck, not a landed fix. ## Root cause ``` App.TapTab("First Tab"); // starts tab-switch animation var afterSwitchRect = App.WaitForElement("EdgeLabel").GetRect(); // find succeeds, then get_Rect races the animation ``` `WaitForElement` returns as soon as `EdgeLabel` exists, but the tab-switch animation recycles the underlying view between the query and the `getRect` round-trip, invalidating the element reference → `StaleElementReferenceException` on `get_Rect()`. This is a *pre-assertion crash*, not an assertion failure. ## Fix Add a stale-resilient `WaitForElementAndGetRect` helper to `HelperExtensions` that re-finds the element and retries `GetRect()` when the reference goes stale, then use it at the two post-UI-change rect reads in the test. - **Condition-based, not a fixed wait** — it retries on the actual stale condition (re-querying a fresh element each attempt via `WaitForElement`), with no new `Thread.Sleep`/`Task.Delay`. - **Mirrors existing framework behavior** — `AppiumMouseActions.ProcessException` already re-finds stale elements (*"Appium elements will sometimes become stale ... we retrieve it here"*). - **Assertions untouched** — the `.Within(5)` tolerance checks are unchanged; nothing is weakened, muted, or skipped. ## Not muting No `[ActiveIssue]`, `[SkipOnPlatform]`, `[Retry]`/`[Repeat]`, category exclusion, csproj incompatibility, weakened assertion, or timeout bump. The test still exercises and asserts the same SafeArea behavior on every tab; it is simply made robust against a known Appium view-recycling artifact during navigation animations. ## Validation⚠️ This is an on-device Android UI test that cannot be executed in the fixer's sandbox, so the change is **not runner-validated** (help-style). It is a targeted, type-checked synchronization change following an established framework pattern; CI on this PR should confirm the leg is green. > Generated by [CI Failure Fixer (main)](https://github.com/dotnet/maui/actions/runs/28868353740) · 919.8 AIC · ⌖ 46 AIC · ⊞ 24.2K · [◷](https://github.com/search?q=repo%3Adotnet%2Fmaui+%22gh-aw-workflow-id%3A+ci-status-fix%22&type=pullrequests) <!-- gh-aw-agentic-workflow: CI Failure Fixer (main), engine: copilot, version: 1.0.63, model: claude-opus-4.8, id: 28868353740, workflow_id: ci-status-fix, run: https://github.com/dotnet/maui/actions/runs/28868353740 --> <!-- gh-aw-workflow-id: ci-status-fix --> <!-- gh-aw-workflow-call-id: dotnet/maui/ci-status-fix --> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Note
Are you waiting for the changes in this PR to be merged?
It would be very helpful if you could test the resulting artifacts from this PR and let us know in a comment if this change resolves your issue. Thank you!
Summary
De-flakes the Android UI test
SafeAreaShouldWorkOnAllShellTabs(Issue33034), which intermittently fails with aStaleElementReferenceExceptionthrown fromIUIElement.GetRect().Target branch: main
Refs: #36259
Attempt: 1/5
Flake class: test-quality (genuine test defect) → de-flake
Reproduction evidence
Pipeline
maui-pr-uitests(def 313), branchmain. The signature is intermittent across the most recent completed builds — the hallmark of a real flake, not a fixed issue:StaleElementReferenceExceptionatget_RectFailed-then-passed-on-retry and present-in-some-but-not-all recent builds → genuinely flaky.
Issue33034.cswas last modified well before the failures began, so the recent greens are luck, not a landed fix.Root cause
WaitForElementreturns as soon asEdgeLabelexists, but the tab-switch animation recycles the underlying view between the query and thegetRectround-trip, invalidating the element reference →StaleElementReferenceExceptiononget_Rect(). This is a pre-assertion crash, not an assertion failure.Fix
Add a stale-resilient
WaitForElementAndGetRecthelper toHelperExtensionsthat re-finds the element and retriesGetRect()when the reference goes stale, then use it at the two post-UI-change rect reads in the test.WaitForElement), with no newThread.Sleep/Task.Delay.AppiumMouseActions.ProcessExceptionalready re-finds stale elements ("Appium elements will sometimes become stale ... we retrieve it here")..Within(5)tolerance checks are unchanged; nothing is weakened, muted, or skipped.Not muting
No
[ActiveIssue],[SkipOnPlatform],[Retry]/[Repeat], category exclusion, csproj incompatibility, weakened assertion, or timeout bump. The test still exercises and asserts the same SafeArea behavior on every tab; it is simply made robust against a known Appium view-recycling artifact during navigation animations.Validation