The following is from a Swift 5.4, SwiftUI app scenario which would fail UI Testing.
app.cells.firstMatch.tap()
// transition from list view to detail view for item
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.typeText("7620") // :FAIL:
Test: Failed to synthesize event: Neither element nor any descendant has keyboard focus.
Solution: Tap Again
A second tap()
was found to pass.
app.cells.firstMatch.tap()
// transition from list view to detail view for item
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.typeText("7620") // :PASS:
However, .doubleTap()
and tap(withNumberOfTaps: 2, numberOfTouches: 1)
would still fail.
Solution: Await TextField Arrival, Then Tap
Sometimes a view transition needs to expressly await the TextEdit arrival. The approach below awaits the arrival of the targeted TextEdit field.
XCTestCase.swift extension:
extension XCTestCase {
func awaitArrival(element: XCUIElement, timeout: TimeInterval) {
let predicate = NSPredicate(format: "exists == 1")
expectation(for: predicate, evaluatedWith: element)
waitForExpectations(timeout: timeout)
}
func awaitDeparture(element: XCUIElement, timeout: TimeInterval) {
let predicate = NSPredicate(format: "exists == 0")
expectation(for: predicate, evaluatedWith: element)
waitForExpectations(timeout: timeout)
}
}
Example use:
app.cells.firstMatch.tap()
// transition from list view to detail view for item
awaitArrival(
element: app.textFields.matching(identifier: "balance_textedit").element,
timeout: 5)
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.typeText("7620") // :PASS:
Solution: waitForExistence
, Then Tap
Use the waitForExistence(timeout:)
method provided by XCUIElement
.
app.cells.firstMatch.tap()
// transition from list view to detail view for item
app.textFields.matching(identifier: "balance_textedit")
.element.waitForExistence(timeout: 5)
app.textFields
.matching(identifier: "balance_textedit")
.element.tap()
app.textFields
.matching(identifier: "balance_textedit")
.element.typeText("7620") // :PASS: