Tomasz Nazarenko
Tomasz Nazarenko

Reputation: 1194

How to find out the (using XCTest UITest) a cell that has a focus in a table view in tvOS AppleTV?

I'm trying to automate UI tests of an app on Apple TV (tvOS).

On the login screen I select the 'enter username' field. Then the screen with previously used logins appears. On the screen I would like to select 'clear all' cell.

To clarify: the "Previously Used" menu seems to be built-in in the tvOS and it might appear automatically whenever someone selects User name textfield in a login menu. I'm unsure about that, so still I need to confirm that information.

enter image description here

So I thought the code like this would be a solution:

    if self.app.staticTexts["Previously Used"].exists {

        let clearAllCell = self.app.cells.staticTexts["Clear All"]

        while clearAllCell.hasFocus == false {
            XCUIRemote.sharedRemote().pressButton(.Down)
            sleep(1)
        }

        XCUIRemote.sharedRemote().pressButton(.Select)
    }

however even in a state like this:

enter image description here

the properties hasFocus and selected of the cell seem to return false:

po self.app.cells.staticTexts["Clear All"].hasFocus
po self.app.cells.staticTexts["Clear All"].selected

enter image description here

How to find the "Clear All" cell when it has "focused state"?

UPDATE:

I've tried the solution proposed by @Oletha, but with no success. clearAllCell.hasFocus unfortunately seems to return always false.

    if self.app.staticTexts["Previously Used"].exists {

        let clearAllCell = self.app.cells.staticTexts["Clear All"]

        let predicateHasFocus = NSPredicate(format: "exists == true", argumentArray: nil)

        expectationForPredicate(predicateHasFocus, evaluatedWithObject: clearAllCell, handler: { () -> Bool in
            if clearAllCell.hasFocus {
                return true
            } else {
                sleep(1)
                XCUIRemote.sharedRemote().pressButton(.Down)
                return false
            }
        })

        waitForExpectationsWithTimeout(10, handler: nil)

        XCUIRemote.sharedRemote().pressButton(.Select)
    }

UPDATE:

A non-elegant solution at the moment is to:

    if self.app.staticTexts["Previously Used"].exists {
        var cellsCount = self.app.cells.count

        // Select Clear All menu button, hack as it was not possible to detect "Clear All" cell in another way
        while cellsCount > 0 {
            sleep(1)
            XCUIRemote.sharedRemote().pressButton(.Down)
            cellsCount -= 1
        }

        sleep(1)
        XCUIRemote.sharedRemote().pressButton(.Up)
        sleep(1)
        XCUIRemote.sharedRemote().pressButton(.Select)
    }

Upvotes: 2

Views: 3475

Answers (3)

Z. Ziv
Z. Ziv

Reputation: 115

first of all it's a native screen in tvOS So all the suggestions related to try and set accessibility ids are not relevant. Tryi to put a breakpoint when you reach that screen and print out which element is focused

I believe the answer will be. A position on screen. Or cell without identifier. I'm struggling with Same issues on Apple TV and would like to know about all the problems you have since I wonder how mature xcuitest is for tvOS

Upvotes: 0

Lindemann
Lindemann

Reputation: 3396

I had similar problems with my app and found 2 things helpful to write running UI tests:

  1. Always make an 1 second delay after every remote button press, because the assert evaluation is often faster than the focus change and therefore returns false

    XCUIRemote.sharedRemote().pressButton(.Down)
    sleep(1)
    
  2. Look up the the exact UI hierarchy position of your focused element. For example print(XCUIApplication().debugDescription) will give you an detailed UI tree and makes it easy to access your element properly.

enter image description here

This assert has returned true:

    XCTAssert(XCUIApplication().tabBars.buttons["Home"].hasFocus)

Upvotes: 2

Oletha
Oletha

Reputation: 7659

Use expectationForPredicate and waitForExpectationsWithTimeout to check if the cell has focus. Using expectations causes the accessibility hierarchy snapshot to be refreshed for each time the check occurs. Your while loop is checking against the same cached hierarchy snapshot every time, so the result won't change.

let app = XCUIApplication()
let clearAllCell = app.cells.staticTexts["Clear All"]
let focusedPredicate = NSPredicate(format: "exists == true")
let remote = XCUIRemote.sharedRemote()

expectationForPredicate(focusedPredicate, evaluatedWithObject: clearAllCell, handler: {() -> Bool in
    // Check if the cell has focus
    if clearAllCell.hasFocus {
        // Fulfill the expectation
        return true
    } else {
        // Move down one cell before the next check
        remote.pressButton(.Down)
        return false
    }
})
waitForExpectationsWithTimeout(5, handler: nil)

remote.pressButton(.Select)

XCUIRemote.sharedRemote() didn't actually work for me, but I'd guess this is working for you - I've not got an Apple TV app to test with.

Upvotes: 0

Related Questions