5

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")
            }
        }
    }
}
G. Marc
  • 4,987
  • 4
  • 32
  • 49

2 Answers2

2

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.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • I tried to access the month and year on the top left button with app.buttons["Show year picker"]. After this tap, another calendar is shown but when I try to adjust the wheel with app.pickerWheels.element(boundBy: 1).adjust(toPickerWheelValue: "2020"), it shows the error Assertions: Assertion Failure at TestCaseUtilsProviding.swift:178: Unsupported picker wheel "2020" PickerWheel of type 6. Do you have any idea to fix this issue? – Jeff Zhang Sep 25 '20 at 03:16
  • 1
    @JeffZhang I am responding to this question. If you have a different question please as it as a Question. – matt Sep 25 '20 at 03:28
  • @matt: Thanks for this, I will test this tomorrow and give you feedback! – G. Marc Sep 25 '20 at 14:53
1

Matt's answer might have worked on iOS 14 devices but it's not working for me on iOS 15.2 devices. The problem I'm facing is that the calendar-style popup is not dismissed. After many hours of trial and error, I've found that the code to dismiss the popup is the same as the code to show it.

In my case, I have declared the DatePicker in my app as follows:

DatePicker("Date Selected", selection: $selectedDate, displayedComponents: [.date])
    .accessibilityIdentifier("DatePicker")
    .datePickerStyle(.compact)

Then, in my UI tests, I am performing the following actions:

// 1. Show the DatePicker popup
application.datePickers["DatePicker"].tap()
        
// 2. Choose a date in the popup
application.datePickers.collectionViews.buttons["Friday, January 14"].tap()
        
// 3. Dismiss the DatePicker popup
application.datePickers["DatePicker"].tap()

I've put together a minimal SwiftUI app which demonstrates this here.

Adil Hussain
  • 30,049
  • 21
  • 112
  • 147