Xcode UITest scrolling to the bottom of an UITableView
Asked Answered
H

3

8

I am writing an UI test case, in which I need to perform an action, and then on the current page, scroll the only UITableView to the bottom to check if specific text shows up inside the last cell in the UITableView.

Right now the only way I can think of is to scroll it using app.tables.cells.element(boundBy: 0).swipeUp(), but if there are too many cells, it doesn't scroll all the way to the bottom. And the number of cells in the UITableView is not always the same, I cannot swipe up more than once because there might be only one cell in the table.

Hokum answered 22/1, 2019 at 20:30 Comment(0)
A
15

One way you could go about this is by getting the last cell from the tableView. Then, run a while loop that scrolls and checks to see if the cell isHittable between each scroll. Once it's determined that isHittable == true, the element can then be asserted against. https://developer.apple.com/documentation/xctest/xcuielement/1500561-ishittable

It would look something like this (Swift answer):

  1. In your XCTestCase file, write a query to identify the table. Then, a subsequent query to identify the last cell.
let tableView = app.descendants(matching: .table).firstMatch
guard let lastCell = tableView.cells.allElementsBoundByIndex.last else { return }
  1. Use a while loop to determine whether or not the cell isHittable/is on screen. Note: isHittable relies on the cell's userInteractionEnabled property being set to true
//Add in a count, so that the loop can escape if it's scrolled too many times
let MAX_SCROLLS = 10
var count = 0
while lastCell.isHittable == false && count < MAX_SCROLLS {
    apps.swipeUp()
    count += 1
}
  1. Check the cell's text using the label property, and compare it against the expected text.
//If there is only one label within the cell
let textInLastCell = lastCell.descendants(matching: .staticText).firstMatch
XCTAssertTrue(textInLastCell.label == "Expected Text" && textInLastCell.isHittable)
Aggi answered 4/2, 2019 at 0:0 Comment(3)
Does lastCell exist ? I f you have 100 cells in a table. lastCell will be the 100th? or will lastCell be the last cell that is currently visible. So 6th ishh etc?Indigo
@Indigo The .allElementsBoundByIndex.last works only on the visible elements. Which means that if you want to find the last element in a list and it is offscreen the .allElementsBoundByIndex.last will not work. Ex: let lastCell = list.cells.allElementsBoundByIndex.last will give you the last visible cell of the list element. I am yet to find a proper way of finding the last element of a list (no matter if it is visible or not)Retrench
Yepp. You have to keep scrolling until you find the last. I use this to do it: github.com/eonist/UITestSugar/blob/master/Sources/UITestSugar/…Indigo
O
13

Blaines answer lead me to dig a little bit more into this topic and I found a different solution that worked for me:

func testTheTest() {
    let app = XCUIApplication()
    app.launch()

    // Opens a menu in my app which contains the table view
    app.buttons["openMenu"].tap()

    // Get a handle for the tableView
    let listpagetableviewTable = app.tables["myTableView"]

    // Get a handle for the not yet existing cell by its content text
    let cell = listpagetableviewTable.staticTexts["This text is from the cell"]

    // Swipe down until it is visible
    while !cell.exists {
        app.swipeUp()
    }

    // Interact with it when visible
    cell.tap()
}

One thing I had to do for this in order to work is set isAccessibilityElement to true and also assign accessibilityLabel as a String to the table view so it can be queried by it within the test code.

This might not be best practice but for what I could see in my test it works very well. I don't know how it would work when the cell has no text, one might be able to reference the cell(which is not really directly referenced here) by an image view or something else. It's obviously missing the counter from Blaines answer but I left it out for simplicity reasons.

Oracle answered 11/2, 2020 at 12:13 Comment(2)
brilliant! so much frustration could have been avoided if I would have found this like 2 hours agoTeece
Thank you for this answer, saved me a lot of timePichardo
K
0

If testing on iPadOS, the scrollBy function does this very well. The following code will scroll to the very bottom of the UITableView. The scroll is quite fast, but does go through every cell. If you don't need to examine each cell in detail this can be useful.

if UIDevice.current.userInterfaceIdiom == .pad {
    let tableView = app.tables.firstMatch
    if let lastCell = tableView.cells.allElementsBoundByIndex.last {
        let tableHeight = lastCell.frame.origin.y + lastCell.frame.size.height
        tableView.scroll(byDeltaX: 0, deltaY: tableHeight)
    }
}

Unfortunately scrollBy is only available on iPadOS >=15 (and macOS). It appears to use the pointing event features under the hood.

Kamchatka answered 18/7 at 18:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.