How do you access a textfield which does not have a label or static text in XCTest? - ios

so I've recently started testing an iOS app with xctest. I'm on a time model view where I would like to change the number in a cell. The number in this cell is the number of days after which the selected time model repeats itself. But I'm unable to access this textfield and change the number as it does not have a label / name / static text. When I record a tap on this field, Xcode gives me a strange element hierarchy which I've defined as the parameter 'onDay' below
func testRepetitionTypeMonthsOnDay() throws {
let app = XCUIApplication()
let tablesQuery = app.tables
let cellsQuery = tablesQuery.cells
XCTAssertTrue(app.navigationBars["Zeitmodelle"].waitForExistence(timeout: standardTimeout)) //wait for the time model view to open
app.staticTexts["Wiederholend"].firstMatch.tap() //tapping on a cell
XCTAssertTrue(app.navigationBars["Wiederholend"].waitForExistence(timeout: standardTimeout)) // wait for the cell to open
let repetitionType = app.tables.cells["Am, Am Tag, Expand"] //cell 1 from screenshot
let onDay = tablesQuery.children(matching: .cell).element(boundBy: 7).children(matching: .other).element(boundBy: 1).children(matching: .other).element.children(matching: .textField).element //cell 2
let endDate = app.tables.cells["Endet, Endet nicht, Expand"] // cell 3 from screenshot
onDay.tap()
onDay.clearAndEnterText("5")
}
However, Xcode cannot find the parameter onDay that itself has generated in the previous step, tap on it and enter a new text. I've attached a screenshot of the app with this cell here. The cells above and below the marked cell can be identified easily and the assertions for their existence work. The marked cell, however, is a different matter as it does not have a label / static text. Do you have an idea how I can get around this? Thanks a lot!

So I've finally found a way to access this element. Instead of going into the cell view I've accessed the textfields array from tables view.
func testRepetitionTypeMonthsOnDay() throws {
let app = XCUIApplication()
let tablesQuery = app.tables
XCTAssertTrue(app.navigationBars["Zeitmodelle"].waitForExistence(timeout: standardTimeout)) //wait for the time model view to open
app.staticTexts["Wiederholend"].firstMatch.tap() //tapping on a cell
XCTAssertTrue(app.navigationBars["Wiederholend"].waitForExistence(timeout: standardTimeout)) // wait for the cell to open
let onDay = tablesQuery.textFields.element(boundBy: 3)
onDay.tap()
onDay.clearAndEnterText("5")
}
Because the app had a mixture of picker wheels and textfields in the same view, it was a bit problematic for Xcode to find the element I wanted from a general element index. But the use of the textfields array filtered the index list down to only textfields. Xcode was then able to identify the element without any conflict and perform the test. I hope this helps anyone having the same problem.

Related

How to create an XCUIElement query for the last UITableViewCell in a UITableView?

I am creating an XCTestCase. For one of the test methods, call it func testConfirmLastCellContent() { ... }, I want to confirm that there exists a staticText child element having a value of "98765" within a UITableViewCell (I put the text value in in the textLabel.text property of the table view cell). There are 4 table view cells listed above this cell. Each of them has a String value of "12345" for its textLabel.text, except for the second cell, which has the same text value as the last: "98765".
The String values for the textLabel.text property of each cell, then, looks like this:
"12345"
"98765"
"12345"
"12345"
"98765"
These textLabel.text values are the only visual sub-elements within the table view cells respectively.
If I create a simple XCUIElementQuery like so...
let staticTextForLastCell = XCUIApplication().staticTexts["98765"]
...and assert its existence as true like so...
XCTAssert(staticTextForLastCell.exists == true)
...then the test will pass, but how do I know I got the last cell? Obviously, I cannot know from that query, because it could be pulling from the 2nd cell.
Thus far, I cannot find an XCTest class/method to get ahold of the last UITableViewCell specifically and query its details.
My goal is to get the last cell and confirm its textLabel.text value is something I expect. How can I do this?
Note: I cannot add/subtract/change textual, visual, etc. elements on any of the table view cells.
Edit:
I should also note that the UITableViewCells are generated dynamically by the app being tested. So, I cannot tag them or populate the accessibilityIdentifier property for the textLabel or the cell.
My setup is the following:
Operating System: macOS Mojave 10.14.6
IDE: Xcode 10.3
SDK: iOS 12.4 SDK
Deploy Target: iOS 12.0
Language: Swift 5
You should probably add an extension to XCUIElementQuery
extension XCUIElementQuery {
var lastMatch : XCUIElement {return self.element(boundBy: self.count - 1)}
}
Then you can assert your cell like this:
XCTAssertEqual(app.cells.lastMatch.staticTexts.element.label,
"98765",
"Unexpected text of the last cell")
Here is something that works...
let app = XCUIApplication()
let cells = app.cells
let lastCell = cells.element(boundBy: cells.count - 1)
let textOfLastCell = lastCell.staticTexts["98765"]
XCTAssert(textOfLastCell.exists)
Note: Smart Monkey's answer used an extension, which may be advisable. The above is a more inline way.
Edit: I previously stated that I could not get it to work without querying for tables before querying for cells. It does work without the tables query.
Edit: Changed XCTAssert(textOfLastCell.exists == true) to XCTAssert(textOfLastCell.exists) based on comment from Chris. A bit cleaner.

Printing random string from an array on textview

I can't seem to get my array printed onto my textview. I have an array and have set my textview (texthold) to the randomIndex of the array.. Anything you guys see I'm doing wrong?
This is my button that when clicked is supposed to get a random string from the devices array and print it into the textview field. I am getting no errors just nothing displays onto the textview when the button is clicked
#IBAction func Generate(_ sender: Any) {
let devices = ["Apple Watch", "iPhone", "iPad", "Mac", "Apple TV"]
// Generate a random index
let randomIndex = Int(arc4random_uniform(UInt32(devices.count)))
// Get a random item
let randomItem = devices[randomIndex]
texthold.text = devices[randomIndex]
}
This code works in my program. Check your layout using "Debug view hierarchy tool", maybe it will help.
And:
texthold.text = randomItem
Name methods with lowercase (Generate -> generate)
Check if your button touch calls this method
Check if your UI items have connected outlets.
There may be problem with changing text from background thread, but if you really have this method connected from storyboard/xib as method for outlet's action - this situation impossible.
And check that your code is being called on .touchUpInside action of your button (second screenshot in storyboard)!

UI Testing UITableView cell count

I'm trying to verify that a table view has been filled with a certain number of cells in my UI test. Here is my code:
XCUIApplication().tables.element.swipeUp()
let count = XCUIApplication().tables.element.children(matching: .cell).count
XCTAssert(count > 0)
The assert fails because the count is always 0, even though the swipe up scrolls the obviously successfully filled table view. I have also tried:
XCTAssert(app.tables.cells.count > 0)
with the same results. I even created an empty project with a table view with 3 static cells, in just one screen, to remove any other possible distractions for the UI test, and it still always returns 0. I'm testing on iOS 11 with Xcode 9.
Cells don't always register as cells, depending on how you have your accessibility configured. Double check that accessibility is actually seeing cells in Xcode menu -> Open Developer Tool -> Accessibility Inspector.
It's likely what accessibility is seeing is staticTexts instead of cells, depending on your layout code -- in that case, you should assert
XCTAssert(app.tables.staticTexts.count > 0)
If you want cells, configure your cells like so in tableView:cellForItemAtIndexPath:
cell.isAccessibilityElement = true
cell.accessibilityLabel = cell.titleLabel.text // (for example)
cell.accessibilityIdentifier = "MyCellId" // (or whatever you want XCUITest to identify it as)
This will mark the cell as the root accessible item, rather than each of the cell's subviews being discrete elements.
XCUIApplication().tables.children(matching: .cell).count
Deletes all rows from the table view. Written using Xcode 11.1 and tested using SwiftUI.
func deleteAllCellsFromTable() {
let app = XCUIApplication()
let tablesQuery = app.tables
app.navigationBars["Navigation Bar Title"].buttons["Edit"].tap()
while app.tables.children(matching: .cell).count > 0 {
let deleteButton = tablesQuery.children(matching: .cell).element(boundBy: 0).buttons["Delete "]
deleteButton.tap()
let deleteButton2 = tablesQuery.children(matching: .cell).element(boundBy: 0).buttons["trailing0"]
deleteButton2.tap()
}
}

UI Testing swipe-to-delete table view cell

I'm trying to build a UI test in a basic shopping list app that makes sure swiping to delete a table view cell does in fact delete the cell from the table view.
I am running the test code below, but when it comes time to swipe the table view cell left (to display the delete button), it doesn't swipe. It appears that it may be tapping it, but it doesn't swipe. Because of this, the test fails when trying to tap the Delete button because "No matches found for button."
How does one test swipe to delete in a table view?
func testDeletingCell() {
let app = XCUIApplication()
app.navigationBars["ShoppingList.ShoppingListView"].buttons["Add"].tap()
let element = app.otherElements.containing(.navigationBar, identifier:"ShoppingList.AddShoppingListItemView").children(matching: .other).element.children(matching: .other).element.children(matching: .other).element
let textField = element.children(matching: .textField).element(boundBy: 0)
textField.tap()
textField.typeText("abc")
let textField2 = element.children(matching: .textField).element(boundBy: 1)
textField2.tap()
textField2.typeText("123")
app.navigationBars["ShoppingList.AddShoppingListItemView"].buttons["Save"].tap()
let tablesQuery = app.tables
tablesQuery.children(matching: .cell).element(boundBy: 0).staticTexts["123"].swipeLeft()
tablesQuery.buttons["Delete"].tap()
XCTAssert(app.tables.cells.count == 0)
}
Try this new Swift 3 syntax instead:
let tablesQuery = app.tables.cells
tablesQuery.element(boundBy: 0).swipeLeft()
tablesQuery.element(boundBy: 0).buttons["Delete"].tap()
Xcode 12.4 | Swift 5
Delete all cells inside a UITableView in XCTestCase:
let app = XCUIApplication()
app.launch()
//Ensure your UIViewController loaded with data
sleep(5)
let tablesQuery = app.tables["<YourTableViewIdentifier>"].cells
for i in 0..<tablesQuery.allElementsBoundByAccessibilityElement.count{
tablesQuery.element(boundBy: 0).swipeLeft()
//Sometimes the swipeLeft() itself is enough to delete the cell,
//but if it is not use what #Rashwan L suggests
//tablesQuery.element(boundBy: 0).buttons["Delete"].tap()
}
XCTAssertEqual(tablesQuery.allElementsBoundByAccessibilityElement.count, 0)
The reason I don't swipe left and then tap delete is because swipeLeft() was already enough to deleted my cell from the UITableView.
If you want to swipe left on a cell and ensure it won't be deleted use:
swipeLeft(velocity: .slow)
This velocity is a XCUIGestureVelocity and you can set it to .fast, .slow, or its .default.

ui testing xcode, how to tap on a table view cell button with cellquery

I am currently doing ui tests for my app and stuck on clicking on the log in button of my app. I am unable to find the element which i have gave the identifier signup button(the element is the 3rd by index, this is not the issue).
let cellQuery = self.app.tables.cells.element(boundBy: 3)
let signInButton = cellQuery.buttons["signup button"]
if signInButton.exists {
signInButton.tap()
}
If the button is present in the 3rd cell then it should be:
let cellQuery = self.app.tables.cells.element(boundBy: 2)
cellQuery.buttons["signup button"].tap()
If button is 3rd in the cell then add accessibility for the cells and then:
app.cells["AccessibilityIdentifier"].buttons.element(boundBy: 2).tap()
Can be done like this:
XCUIApplication().tables.cells.element(boundBy: 2).buttons["signup button"].tap()

Resources