-
Notifications
You must be signed in to change notification settings - Fork 2k
[ci-fix-net11] De-flake iOS keyboard-scrolling UI test helper (NoSuchElementException race on EditorsScrollingPageTest) #36404
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -108,20 +108,45 @@ internal static void HideKeyboard(IApp app, AppiumDriver? driver, bool isEditor) | |
| app.DismissKeyboard(); | ||
| } | ||
|
|
||
| // Polls for a keyboard-related element using the non-throwing FindElements API instead of a | ||
| // bare FindElement. On iOS the soft keyboard (and its buttons) can still be animating in when | ||
| // the lookup runs, so a bare FindElement throws NoSuchElementException intermittently. Returns | ||
| // null if the element never appears within the timeout so callers can react deterministically. | ||
| // See dotnet/maui#36396 and dotnet/maui#36393. | ||
| static AppiumElement? WaitForKeyboardElement(AppiumDriver? driver, OpenQA.Selenium.By locator, TimeSpan? timeout = null) | ||
| { | ||
| if (driver is null) | ||
| return null; | ||
|
|
||
| timeout ??= TimeSpan.FromSeconds(5); | ||
| var start = DateTime.Now; | ||
| while (true) | ||
| { | ||
| var elements = driver.FindElements(locator); | ||
| if (elements.Count > 0) | ||
| return elements[0]; | ||
|
|
||
| if (DateTime.Now - start >= timeout.Value) | ||
| return null; | ||
|
|
||
| System.Threading.Thread.Sleep(200); | ||
| } | ||
| } | ||
|
|
||
| internal static System.Drawing.Point? FindiOSKeyboardLocation(AppiumDriver? driver) | ||
| { | ||
| if (driver?.IsKeyboardShown() == true) | ||
| { | ||
| var keyboard = driver.FindElement(MobileBy.ClassName("UIAKeyboard")); | ||
| return keyboard.Location; | ||
| var keyboard = WaitForKeyboardElement(driver, MobileBy.ClassName("UIAKeyboard")); | ||
| return keyboard?.Location; | ||
| } | ||
| return null; | ||
| } | ||
|
|
||
| internal static void CloseiOSEditorKeyboard(IApp app, AppiumDriver? driver) | ||
| { | ||
| var doneButtonName = app is AppiumIOSApp iosApp && HelperExtensions.IsIOS26OrHigher(iosApp) ? "selected" : "Done"; | ||
| var keyboardDoneButton = driver?.FindElement(MobileBy.Name(doneButtonName)); | ||
| var keyboardDoneButton = WaitForKeyboardElement(driver, MobileBy.Name(doneButtonName)); | ||
| keyboardDoneButton?.Click(); | ||
| } | ||
|
|
||
|
|
@@ -148,7 +173,7 @@ internal static void EntryNextEditorScrollingTest(IApp app, string galleryName) | |
| // Unintentionally types a 'V' but also presses the next keyboard key | ||
| internal static void NextiOSKeyboardPress(AppiumDriver? driver) | ||
| { | ||
| var keyboard = driver?.FindElement(MobileBy.ClassName("UIAKeyboard")); | ||
| var keyboard = WaitForKeyboardElement(driver, MobileBy.ClassName("UIAKeyboard")); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
[major] UI Test Reliability — This lookup now returns null on timeout, but |
||
| keyboard?.SendKeys("\n"); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[major] UI Test Reliability —
WaitForKeyboardElementcan now time out and return null, butCloseiOSEditorKeyboardstill treats the Done/selected key as optional via the following null-conditional click. If the accessory button never appears, the helper silently leaves the keyboard open and the test either fails later in an unrelated place or can continue with the wrong state. Please assert/fail when this required keyboard button is missing (for exampleClassicAssert.NotNull(keyboardDoneButton, ...)) before clicking it.