Search code examples
swiftuixcode-ui-testing

Finding the accessibility(identifier:) of DatePicker() embedded in a Form in SwiftUI using an XCUIElementQuery


I have a SwiftUI view that has a Form that contains a DatePicker:

struct GettingUpTimeSettingView: View {
    @State private var viewModel = GettingUpTimeSettingViewModel()
    var body: some View {
        VStack {
            
            Form {
                Spacer()
                Text(viewModel.questionString)
                    .accessibility(identifier: "Question")
                Spacer()
                DatePicker("Alarm Time",
                                     selection: $viewModel.gettingUpTime,
                                     displayedComponents: .hourAndMinute)
                    .accessibility(identifier: "Time")
                    .animation(.easeInOut)
                Spacer()
                Text(viewModel.explanationString)
                    .accessibility(identifier: "Explanation")
            }
        }
    }
}

And an XCTestCase class for UI Testing:

class SleepyGPIntroUITests: XCTestCase {
    
    private var app: XCUIApplication!
    
    override func setUp() {
        continueAfterFailure = false
        app = XCUIApplication()
        app.launch()
        greeting = app.staticTexts["Greeting"]
    }
    
    override func tearDown() {
        app = nil
    }
    
    func test_InitialScreen_ChangesTo_GettingUpScreen_Automatically() {
        //Given
        let questionText = "What time do you want to get up?"
        let explanationText = "This should be the same time every day, including at weekends and on days off. Our best chance of great sleep comes when we have a regular routine."
        let timeText = "7:00am"
        let question = app.staticTexts["Question"]
        let explanation = app.staticTexts["Explanation"]
        let time = app.staticTexts["Time"]
        //When
        _ = time.waitForExistence(timeout: 2)
        //Then
        XCTAssertFalse(greeting.exists)
        XCTAssertEqual(question.label, questionText, "Should show question at top of view")
        XCTAssertEqual(explanation.label, explanationText, "Should show explanation in view")
        XCTAssertEqual(time.label, timeText, "Should show the correct default time")
    }

When I run the test, it fails and gives me this message:

Failed to get matching snapshot: No matches found for Elements matching predicate '"Time" IN identifiers' from input {( StaticText, identifier: 'Question', label: 'What time do you want to get up?', StaticText, identifier: 'Explanation', label: 'This should be the same time every day, including at weekends and on days off. Our best chance of great sleep comes when we have a regular routine.' )}

I'm not sure whether this is down to the DatePicker() being contained in a Form, or whether it's because I'm not using the correct XCUIElementQuery to find it?

When I move the DatePicker outside the Form, I can find it, but only by using its label, and not its accessibility identifier.

The accessibility identifiers are working fine for the Text objects.

I can also find it using

app.datePickers.firstMatch

when it's outside the form, but not when it's contained within it.

I've found this answer that describes some odd behaviour when SwiftUI objects are contained in forms, but still haven't managed to solve my problem.

Many thanks.

tl:dr Can't get XCUIElementQuery to return DatePicker element when UITesting if the DatePicker is contained in a SwiftUI Form.


Solution

  • I've found the answer, for anyone that's interested.

    First of all, a piece of basic advice, if you're struggling to find out how to access an element when UI testing in XCode, just use the record function and access the element manually.

    That's what I did, and it showed me that before a DatePicker() is tapped on in SwiftUI, it actually shows as a button, so to access it in the example above I used this code:

    let alarmTimeButton = app.tables.buttons["Time"]