How do I dismiss the DatePicker popup in UI tests that are running on iOS 16 devices?

I have defined a DatePicker to have the compact style, as follows:

struct ContentView: View {
    
    @State private var selectedDate = Date()
    
    var body: some View {
        DatePicker("Date Selected", selection: $selectedDate, displayedComponents: [.date])
            .accessibilityIdentifier("DatePicker")
            .datePickerStyle(.compact)
    }
}

I have defined a UI test that taps on the DatePicker to show the DatePicker popup, then taps on one of the dates in the DatePicker popup to select that date, and then taps on the DatePicker again to dismiss the DatePicker popup, as follows:

func test_date_picker() {
    let application = XCUIApplication()

    // 0. Launch the app
    application.launch()
        
    // 1. Show the DatePicker popup
    application.datePickers["DatePicker"].tap()
        
    // 2. Change the selected date
    application.datePickers.collectionViews.buttons["Friday, November 25"].tap()
        
    // 3. Dismiss the DatePicker popup
    application.datePickers["DatePicker"].tap()
}

This test works on iOS 14 and iOS 15 devices. Sadly, the final application.datePickers["DatePicker"].tap() call is failing on iOS 16 devices because application.datePickers["DatePicker"] is not hittable. How do I dismiss the DatePicker popup on iOS 16 devices?

For what it's worth, I'm running the test via Xcode 14.1 on iOS 16.1 simulator devices. I do not have a real iOS 16 device to hand so I cannot verify the behaviour on a real iOS 16 device.

Lastly, you can find a minimal application project that demonstrates the problem here.

Answered by darkpaw in 737512022

It's because the object you're trying to tap() isn't Hittable - you can check that by adding an assertTrue.

Here's a way to do it using an extension:

extension XCUIElement {
	func forceTap() {
		if(self.isHittable) {
			self.tap()

		} else {
			let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset: CGVector(dx:0.0, dy:0.0))
			coordinate.tap()
		}
	}
}

And call it with application.datePickers["DatePicker"].forceTap().

Accepted Answer

It's because the object you're trying to tap() isn't Hittable - you can check that by adding an assertTrue.

Here's a way to do it using an extension:

extension XCUIElement {
	func forceTap() {
		if(self.isHittable) {
			self.tap()

		} else {
			let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset: CGVector(dx:0.0, dy:0.0))
			coordinate.tap()
		}
	}
}

And call it with application.datePickers["DatePicker"].forceTap().

How do I dismiss the DatePicker popup in UI tests that are running on iOS 16 devices?
 
 
Q