Fix flaky WebKit escape focus tests on slow environments (#57)

Two fixes for escape-focus flakiness on VMs:

1. App-side: recordGotoSplitUITestWebViewFocus now retries with increasing
   delays (0.05, 0.1, 0.25, 0.5s) for the exit-address-bar case, giving
   WebKit more time to accept first responder.

2. Test-side: both testEscapeLeavesOmnibarAndFocusesWebView and
   refocusWebView helper send a second Escape if the first only clears
   suggestions/editing state (Chrome-like two-stage escape behavior).
This commit is contained in:
Lawrence Chen 2026-02-17 21:12:28 -08:00 committed by GitHub
parent f0e4ccdc1d
commit bf3d22fa8a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 23 additions and 2 deletions

View file

@ -157,7 +157,12 @@ final class BrowserPaneNavigationKeybindUITests: XCTestCase {
)
// Escape should leave the omnibar and focus WebKit again.
// Send Escape twice: the first may only clear suggestions/editing state
// (Chrome-like two-stage escape), the second triggers blur to WebView.
app.typeKey(XCUIKeyboardKey.escape.rawValue, modifierFlags: [])
if !waitForDataMatch(timeout: 2.0, predicate: { $0["webViewFocusedAfterAddressBarExit"] == "true" }) {
app.typeKey(XCUIKeyboardKey.escape.rawValue, modifierFlags: [])
}
XCTAssertTrue(
waitForDataMatch(timeout: 5.0) { data in
data["webViewFocusedAfterAddressBarExit"] == "true"

View file

@ -111,7 +111,12 @@ final class MenuKeyEquivalentRoutingUITests: XCTestCase {
)
// Escape should leave the omnibar and focus WebKit again.
// Send Escape twice: the first may only clear suggestions/editing state
// (Chrome-like two-stage escape), the second triggers blur to WebView.
app.typeKey(XCUIKeyboardKey.escape.rawValue, modifierFlags: [])
if !waitForGotoSplitMatch(timeout: 2.0, predicate: { $0["webViewFocusedAfterAddressBarExit"] == "true" }) {
app.typeKey(XCUIKeyboardKey.escape.rawValue, modifierFlags: [])
}
XCTAssertTrue(
waitForGotoSplitMatch(timeout: 5.0) { data in
data["webViewFocusedAfterAddressBarExit"] == "true"

View file

@ -1188,11 +1188,22 @@ final class AppDelegate: NSObject, NSApplicationDelegate, UNUserNotificationCent
}
private func recordGotoSplitUITestWebViewFocus(panelId: UUID, key: String) {
// Give the responder chain time to settle.
DispatchQueue.main.asyncAfter(deadline: .now() + 0.05) { [weak self] in
// Give the responder chain time to settle, retrying for slow environments (e.g. VM).
recordGotoSplitUITestWebViewFocusRetry(panelId: panelId, key: key, attempt: 0)
}
private func recordGotoSplitUITestWebViewFocusRetry(panelId: UUID, key: String, attempt: Int) {
let delays: [Double] = [0.05, 0.1, 0.25, 0.5]
let delay = attempt < delays.count ? delays[attempt] : delays.last!
DispatchQueue.main.asyncAfter(deadline: .now() + delay) { [weak self] in
guard let self, let tabManager, let tab = tabManager.selectedWorkspace,
let panel = tab.browserPanel(for: panelId) else { return }
let focused = self.isWebViewFocused(panel)
// If focus hasn't settled yet and we have retries left, try again.
if !focused && key.contains("Exit") && attempt < delays.count - 1 {
self.recordGotoSplitUITestWebViewFocusRetry(panelId: panelId, key: key, attempt: attempt + 1)
return
}
self.writeGotoSplitTestData([
key: focused ? "true" : "false",
"\(key)PanelId": panelId.uuidString