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

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()

Related

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

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.

Adding a cell to another TableView. (selecting an item from a tableview, showing it in another tableview)

I'm building an app which which has built in with 2 different tabs. First tab is is "Home" which basically has a tableview with cells that configured from an api.(The api gets me country names for now)
Those cells also have a "Star" button which prints the data of the specific cell for now.
Second tab is "Saved" tab(SavedViewController), where I want to show the "starred" countries, using a tableview.
You can see the image below in order to get an idea for the app.
App simulation Image
The star button has a function in my CountriesTableViewCell. I'm using a saveButtonDelegate in order to let the SavedViewController know about an item is going to be saved. The code in CountriesTableViewCell for star button is as below.
#objc func buttonTapped() {
//If Button is selected fill the image. Else unfill it.
if !isSaveButtonSelected {
saveButton.setImage(UIImage(systemName: "star.fill"), for: .normal)
isSaveButtonSelected = true
saveButtonDelegate?.saveButtonClicked(with: countryData) //Checking if save button is clicked
}
}
countryData is the data that I get from the api, and this is the data I want to pass to SavedViewController.
struct CountryData: Codable {
let name : String
}
So on the SavedViewController, I'm handling the data using the SaveButtonProtocol conformance as below:
extension SavedViewController: SaveButtonProtocol {
func saveButtonClicked(with data: CountryData) {
countryDataArray.append(data)
print("saveButtonClicked")
print("countryData in savevc is \(countryDataArray)")
DispatchQueue.main.async {
self.countriesTableView.reloadData()
}
}
}
Whenever I click the star button on the first tab, this function is getting called on SavedViewController. So whenever I click to button, those print statements above work fine.
The problem is, whenever the star button is clicked, it should append the data of the current clicked cell to countryDataArray in SavedViewController. But the array is not populating as it should.
Let's say I pressed the first cell's star button, my print("countryData in savevc is (countryDataArray)") statement prints : ["Vatican City"], then I press the second cell's star button it only prints ["Ethiopia"] while it should print ["Vatican City", "Ethiopia"]
Why this issue is happening? My best guess is I'm delegating SavedViewController from the cell class so it behaves differently for every other cell. If that is the problem what should I do to solve it?
Many Thanks.
You should store your data in a shared (static array) object so you only have one source and add the saved indicator in your country struct so you do not rely on what is displayed in one view controller.

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.

I can't figure out how to set the correct button to be unhidden using swift

I'm trying to create a quiz app for a project, I'm having a problem when I try to set another button to be revealed when you select the correct answer. ex.
1+2=?
2
3
5
9
When they select 3, I want the continue button to appear to go to the next page.
#IBAction func Seaturtle(sender: UIButton) {
if sender.tag == 2 {
7.hidden = false
}
}
Go into your Storyboard and find the Continue button you wish to hide/unhide. Use Xcode's split screen view to have both your Storyboard and the associated code file open. Select the button, then Ctrl+Drag from the button into the code, creating an IBOutlet instead of an IBAction. Say you give it the name continueButton. Then, when you want to hide/unhide just call
continueButton.hidden = true
or
continueButton.hidden = false

Disable user interaction on tabBar

I am displaying an image right after the app didFinishLaunchingWithOptions, the app consists in a tab bar, and in the first view i have some buttons.
The user can only continue to use the app after he press the button in that first image, the problem is, some users can interact with the tab bar, and the buttons in the first view even with the image above all.
How i can completely disable the user interaction on those buttons and in the tabBar, and enable then only when the button is pressed and the image disapear?
To disable:
UITabBarController.tabBar.userInteractionEnabled = NO;
To enable:
UITabBarController.tabBar.userInteractionEnabled = YES;
if let items = self.tabBarController?.tabBar.items {
for i in 0 ..< items.count {
let itemToDisable = items[i]
itemToDisable.isEnabled = false
}
}
somehow i didn't fully understand what you described,but have you tried disabling your buttons using following code?
button1.Enabled=false;
also you can disable images click function using
image1.Enabled=false;

Resources