I'm struggling fixing my UI tests for iOS 14 that set date on DatePicker views. They were previously running without any problems on iOS 13 when they were shown as wheel pickers. My code used to set the different wheels but since there aren't any wheels anymore on iOS 14, this is not working anymore.

I've tried to use a small demo project and record the change of the date using XCode 12 record button, but it's not working (on Mac OS 10.15.6) because of an error: "Timestamped Event Matching Error: Failed to find matching element" after pressing the date button.

What I'm looking for is a UI test case to set a date for the new date picker of iOS 14:

enter image description here

Here's my demo project:

import SwiftUI

struct ContentView: View {
    @State private var date = Date()

    var body: some View {
        Form {
            DatePicker(selection: $date, displayedComponents: .date) {
                Text("Date")
            }
        }
    }
}

The problem is only how to tap outside the expanded picker to dismiss it. You can do that with a simple click, which you can emulate using an extension:

extension XCUIApplication {
    func tapCoordinate(at point: CGPoint) {
        let normalized = coordinate(withNormalizedOffset: .zero)
        let offset = CGVector(dx: point.x, dy: point.y)
        let coordinate = normalized.withOffset(offset)
        coordinate.tap()
    }
}

Here's a somewhat naive approach, based on the recording; you can clean it up as desired:

func testExample() throws {
    let app = XCUIApplication()
    app.launch()
    XCTAssertTrue(app.cells["Date, Date Picker, Sep 24, 2020"].exists)
    app.tables.datePickers.containing(.other, identifier:"Date Picker").element.tap()
    app.datePickers.collectionViews.buttons["Friday, September 4"].otherElements.containing(.staticText, identifier:"4").element.tap()
    app.tapCoordinate(at: CGPoint(x:30,y:30))
    XCTAssertTrue(app.cells["Date, Date Picker, Sep 4, 2020"].exists)
}

Of course, that works only today. But it will get you going.

If you want to see that the dismiss tap work, add a delay:

    app.tapCoordinate(at: CGPoint(x:30,y:30))
    let delayExpectation = XCTestExpectation()
    delayExpectation.isInverted = true
    wait(for: [delayExpectation], timeout: 1)

You will see that the expanded picker does indeed dismiss before the test ends.