Last active
March 15, 2025 19:12
-
-
Save Jwood97/8c3b9321cb61426ec6a57dcbaed4ec17 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
extension XCUIElement { | |
enum Timeouts: TimeInterval { | |
case veryShort = 2.0 | |
case short = 4.0 | |
case medium = 8.0 | |
case long = 12.0 | |
case veryLong = 15.0 | |
} | |
struct PredicatePollerDefaults { | |
static let minPollInterval: TimeInterval = 0.2 | |
static let pollMultiplier: Double = 1.5 | |
static let maxPollInterval: TimeInterval = 2.0 | |
static let maxIterations: Int = 100 | |
} | |
@discardableResult | |
class func wait(for condition: @escaping () -> Bool, | |
timeout: Timeouts = .medium, | |
failureMessage: String = "Condition not met", | |
hardAssertion: Bool = true, | |
description: String) -> Bool { | |
if condition() { return true } | |
let start = Date() | |
let timeoutDate = start.addingTimeInterval(timeout.rawValue) | |
let expectation = XCTestExpectation(description: description) | |
var pollInterval = PredicatePollerDefaults.minPollInterval | |
var iterationCount = 0 | |
while Date() < timeoutDate { | |
iterationCount += 1 | |
if iterationCount > PredicatePollerDefaults.maxIterations { | |
if hardAssertion { | |
XCTFail("Exceeded maximum allowed iterations (\(PredicatePollerDefaults.maxIterations))") | |
} | |
return false | |
} | |
if condition() { | |
expectation.fulfill() | |
return true | |
} | |
let remainingTime = timeoutDate.timeIntervalSinceNow | |
let effectivePollInterval = min(pollInterval, remainingTime) | |
if effectivePollInterval <= 0 { break } | |
RunLoop.current.run(until: Date().addingTimeInterval(effectivePollInterval)) | |
// Exponential backoff so CI doesn't get overwhelmed | |
pollInterval = min(max(PredicatePollerDefaults.minPollInterval, pollInterval * PredicatePollerDefaults.pollMultiplier), PredicatePollerDefaults.maxPollInterval) | |
} | |
if hardAssertion { XCTFail(failureMessage) } | |
return false | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment