Fix UI test helper closure captures

This commit is contained in:
Lawrence Chen 2026-03-17 00:14:28 -07:00
parent 60aab29e39
commit b0d994c99f
No known key found for this signature in database
4 changed files with 41 additions and 39 deletions

View file

@ -98,7 +98,7 @@ final class BrowserOmnibarSuggestionsUITests: XCTestCase {
// Note: example.com may redirect to example.org in some environments.
XCTAssertTrue(
waitForCondition(timeout: 8.0) {
containsExampleDomain((omnibar.value as? String) ?? "")
self.containsExampleDomain((omnibar.value as? String) ?? "")
},
"Expected omnibar to navigate to example.com after keyboard nav + Enter. value=\(String(describing: omnibar.value))"
)
@ -134,7 +134,7 @@ final class BrowserOmnibarSuggestionsUITests: XCTestCase {
// Note: example.com may redirect to example.org in some environments.
XCTAssertTrue(
waitForCondition(timeout: 8.0) {
containsExampleDomain((omnibar.value as? String) ?? "")
self.containsExampleDomain((omnibar.value as? String) ?? "")
},
"Expected committed omnibar value to contain example.com or example.org. value=\(String(describing: omnibar.value))"
)
@ -328,7 +328,7 @@ final class BrowserOmnibarSuggestionsUITests: XCTestCase {
let loaded = waitForCondition(timeout: 8.0) {
let value = ((omnibar.value as? String) ?? "").lowercased()
return containsExampleDomain(value)
return self.containsExampleDomain(value)
}
XCTAssertTrue(loaded, "Expected baseline navigation to load before Cmd+L fast-typing check.")
@ -642,7 +642,7 @@ final class BrowserOmnibarSuggestionsUITests: XCTestCase {
private func waitForSuggestionRowToBeSelected(_ row: XCUIElement, timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
isSuggestionRowSelected(row)
self.isSuggestionRowSelected(row)
}
}

View file

@ -973,14 +973,14 @@ final class BrowserPaneNavigationKeybindUITests: XCTestCase {
private func waitForData(keys: [String], timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
guard let data = loadData() else { return false }
guard let data = self.loadData() else { return false }
return keys.allSatisfy { data[$0] != nil }
}
}
private func waitForDataMatch(timeout: TimeInterval, predicate: ([String: String]) -> Bool) -> Bool {
private func waitForDataMatch(timeout: TimeInterval, predicate: @escaping ([String: String]) -> Bool) -> Bool {
waitForCondition(timeout: timeout) {
guard let data = loadData() else { return false }
guard let data = self.loadData() else { return false }
return predicate(data)
}
}

View file

@ -4,6 +4,16 @@ import CoreGraphics
import ImageIO
import Darwin
private extension XCTestCase {
func waitForCondition(timeout: TimeInterval, predicate: @escaping () -> Bool) -> Bool {
let expectation = XCTNSPredicateExpectation(
predicate: NSPredicate { _, _ in predicate() },
object: nil
)
return XCTWaiter().wait(for: [expectation], timeout: timeout) == .completed
}
}
final class MenuKeyEquivalentRoutingUITests: XCTestCase {
private var gotoSplitPath = ""
private var keyequivPath = ""
@ -127,21 +137,21 @@ final class MenuKeyEquivalentRoutingUITests: XCTestCase {
private func waitForGotoSplit(keys: [String], timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
guard let data = loadGotoSplit() else { return false }
guard let data = self.loadGotoSplit() else { return false }
return keys.allSatisfy { data[$0] != nil }
}
}
private func waitForGotoSplitMatch(timeout: TimeInterval, predicate: ([String: String]) -> Bool) -> Bool {
private func waitForGotoSplitMatch(timeout: TimeInterval, predicate: @escaping ([String: String]) -> Bool) -> Bool {
waitForCondition(timeout: timeout) {
guard let data = loadGotoSplit() else { return false }
guard let data = self.loadGotoSplit() else { return false }
return predicate(data)
}
}
private func waitForKeyequivInt(key: String, toBeAtLeast expected: Int, timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
let value = loadKeyequiv()[key].flatMap(Int.init) ?? 0
let value = self.loadKeyequiv()[key].flatMap(Int.init) ?? 0
return value >= expected
}
}
@ -798,14 +808,14 @@ final class SplitCloseRightBlankRegressionUITests: XCTestCase {
private func waitForData(keys: [String], timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
guard let data = loadData() else { return false }
guard let data = self.loadData() else { return false }
return keys.allSatisfy { data[$0] != nil }
}
}
private func waitForAnyData(timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
loadData() != nil
self.loadData() != nil
}
}
@ -813,7 +823,7 @@ final class SplitCloseRightBlankRegressionUITests: XCTestCase {
var last: [String: String]?
_ = waitForCondition(timeout: timeout) {
guard let data = loadData() else { return false }
guard let data = self.loadData() else { return false }
last = data
if let setupError = data["setupError"], !setupError.isEmpty {
@ -866,24 +876,16 @@ final class SplitCloseRightBlankRegressionUITests: XCTestCase {
private func waitForSocketPong(timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
socketCommand("ping") == "PONG"
self.socketCommand("ping") == "PONG"
}
}
private func waitForVisualDone(timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
loadData()?["visualDone"] == "1"
self.loadData()?["visualDone"] == "1"
}
}
private func waitForCondition(timeout: TimeInterval, predicate: @escaping () -> Bool) -> Bool {
let expectation = XCTNSPredicateExpectation(
predicate: NSPredicate { _, _ in predicate() },
object: nil
)
return XCTWaiter().wait(for: [expectation], timeout: timeout) == .completed
}
private func socketCommand(_ cmd: String) -> String? {
if socketClient == nil {
socketClient = ControlSocketClient(path: socketPath)

View file

@ -423,7 +423,7 @@ final class MultiWindowNotificationsUITests: XCTestCase {
private func waitForFocusChange(from token: String?, timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
guard let data = loadData(),
guard let data = self.loadData(),
let current = data["focusToken"],
!current.isEmpty else {
return false
@ -434,14 +434,14 @@ final class MultiWindowNotificationsUITests: XCTestCase {
private func waitForData(keys: [String], timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
guard let data = loadData() else { return false }
guard let data = self.loadData() else { return false }
return keys.allSatisfy { (data[$0] ?? "").isEmpty == false }
}
}
private func waitForDataMatch(timeout: TimeInterval, predicate: ([String: String]) -> Bool) -> Bool {
private func waitForDataMatch(timeout: TimeInterval, predicate: @escaping ([String: String]) -> Bool) -> Bool {
waitForCondition(timeout: timeout) {
guard let data = loadData() else { return false }
guard let data = self.loadData() else { return false }
return predicate(data)
}
}
@ -449,7 +449,7 @@ final class MultiWindowNotificationsUITests: XCTestCase {
private func waitForSocketPong(timeout: TimeInterval) -> String? {
var lastResponse: String?
_ = waitForCondition(timeout: timeout) {
lastResponse = socketCommand("ping")
lastResponse = self.socketCommand("ping")
return lastResponse == "PONG"
}
return lastResponse == "PONG" ? "PONG" : (socketCommand("ping") ?? lastResponse)
@ -457,7 +457,7 @@ final class MultiWindowNotificationsUITests: XCTestCase {
private func waitForTerminalFocus(surfaceId: String, timeout: TimeInterval) -> Bool {
waitForCondition(timeout: timeout) {
socketCommand("is_terminal_focused \(surfaceId)") == "true"
self.socketCommand("is_terminal_focused \(surfaceId)") == "true"
}
}
@ -465,8 +465,8 @@ final class MultiWindowNotificationsUITests: XCTestCase {
var lastStdout: String?
var lastStderr: String?
let didSucceed = waitForCondition(timeout: timeout) {
let result = runCmuxCommand(
socketPath: socketPath,
let result = self.runCmuxCommand(
socketPath: self.socketPath,
arguments: ["ping"],
responseTimeoutSeconds: 2.0
)
@ -481,8 +481,8 @@ final class MultiWindowNotificationsUITests: XCTestCase {
if result.terminationStatus == 0, stdout == "PONG" {
return true
}
if isSocketPermissionFailure(stderr),
waitForSocketPong(timeout: 0.5) == "PONG" {
if self.isSocketPermissionFailure(stderr),
self.waitForSocketPong(timeout: 0.5) == "PONG" {
return true
}
return false
@ -553,7 +553,7 @@ final class MultiWindowNotificationsUITests: XCTestCase {
private func waitForSurfaceId(forWorkspaceId workspaceId: String, timeout: TimeInterval) -> String? {
var surfaceId: String?
_ = waitForCondition(timeout: timeout) {
surfaceId = firstSurfaceId(forWorkspaceId: workspaceId)
surfaceId = self.firstSurfaceId(forWorkspaceId: workspaceId)
return surfaceId != nil
}
return surfaceId ?? firstSurfaceId(forWorkspaceId: workspaceId)
@ -562,7 +562,7 @@ final class MultiWindowNotificationsUITests: XCTestCase {
private func waitForSurfaceIdViaCLI(forWorkspaceId workspaceId: String, timeout: TimeInterval) -> String? {
var surfaceId: String?
_ = waitForCondition(timeout: timeout) {
surfaceId = firstSurfaceIdViaCLI(forWorkspaceId: workspaceId)
surfaceId = self.firstSurfaceIdViaCLI(forWorkspaceId: workspaceId)
return surfaceId != nil
}
return surfaceId ?? firstSurfaceIdViaCLI(forWorkspaceId: workspaceId)
@ -899,15 +899,15 @@ final class MultiWindowNotificationsUITests: XCTestCase {
guard FileManager.default.fileExists(atPath: candidate) else { continue }
// Primary candidate is the explicitly requested CMUX_SOCKET_PATH. If it responds,
// prefer it even before workspace contents are fully initialized.
if socketRespondsToPing(at: candidate) {
if self.socketRespondsToPing(at: candidate) {
resolvedPath = candidate
return true
}
}
for candidate in fallbackCandidates {
guard FileManager.default.fileExists(atPath: candidate) else { continue }
if socketRespondsToPing(at: candidate),
socketMatchesRequiredWorkspace(candidate, workspaceId: requiredWorkspaceId) {
if self.socketRespondsToPing(at: candidate),
self.socketMatchesRequiredWorkspace(candidate, workspaceId: requiredWorkspaceId) {
resolvedPath = candidate
return true
}