UITest compare value of element in expectation - swift

I am currently trying to figure out when an activity indicator is on or off. I realised that, when on, its value is 1, 0 when off.
I am trying to check if that value is 1 or 0 like this:
let app = XCUIApplication()
let predicate = NSPredicate(format: "value == 0")
let expectation = expectation(
for: predicate,
evaluatedWith: app.activityIndicators["activityId"],
handler: nil)
if XCTWaiter().wait(for:[expectation], timeout: 20) == .completed {
print ("Activity indicator is off")
} else {
print ("Activity indicator is on")
}
The current problem is that the timeout is always reached. Whenever I set a breakpoint at the if and type this in the console:
po app.activityIndicators["activityId"].value
The result is:
Optional<Any>
- some : 0
However, after I let the test run again, the timeout is reached and the else branch is taken.
Can anyone see what I am doing wrong while accessing the value of the element in the expectation?
Thanks for your help in advance.

Assuming you've both launched your app and set the accessibility identifier of your activity indicator correctly (which it seems you've done), the reason your code doesn't work is that value is a string, not an integer.
Replacing your predicate with the following will make your code work:
let predicate = NSPredicate(format: "value == \"0\"")

Related

How to fetch objects from results one by one without do .count

I need to fetch from Realm Results 20 objects or less. A database can be heavy, so Results.count is a long time for calling.
So, what I need is to fetch objects from Results one by one until I get 20 or until last object.
But, when I'm trying to fetch index after the last object it's throwing Realm exception 'Index x is out of bounds (must be less than x)'.
So, this one isn't working:
let searchResult = Ticket().get(filter: "base == nil && deleted == 0 AND orderPaidAt > 0 AND (\(query))").sorted(byKeyPath: "orderPaidAt")
for i in 0..<20 {
if let ticket = searchResult[i] as? Ticket {
...
} else {
break
}
}
If I'm trying to use searchResult.count or searchResult.endIndex it increases a time a lot, especially on old devices. That's why I want to avoid it.
The results are lazily loaded, so you could loop through the results one by one, until the end, or until you hit a self-set count:
let searchResult = Ticket().get(filter: "base == nil && deleted == 0 AND orderPaidAt > 0 AND (\(query))").sorted(byKeyPath: "orderPaidAt")
var count = 0
for thisTicket in searchResult {
// do something
count += 1
if count > 20 { break }
}
This way you are only loading the values that you need, and never calling count or accessing the results out of bounds.
You can use prefix(maxLenght: Int) method to get a subCollection with specified maxLenght.
Example:
realm.objects(ObjectModel.self).prefix(20).count

In XCUITests, how to wait for existence of either of two ui elements

Looking at XCTWaiter().wait(...) I believe we can wait for multiple expectations to become true using this code
let notHittablePredicate = NSPredicate(format: "hittable == false")
let myExpectation = XCTNSPredicateExpectation(predicate: notHittablePredicate, object: element)
let result = XCTWaiter().wait(for: [myExpectation], timeout: timeout)
//for takes array of expectations
But this uses like AND among the supplied expectations. Is there a way to do OR among the supplied expectations.
Like i have a use case at login that after tapping submit, i want to wait for one of two elements. First element is "You are already logged in on another device. If you continue any unsaved data on your other device will be lost?". And second element is the main screen after login. So any one can appear. Currently I'm first waiting for first element until timeout occurs and then for the second element. But I want to optimize time here and move on as soon as any of two elements exist==true. Then i'll check if element1 exists then tap YES and then wait for main screen otherwise just assert existence of element2.
Please comment if something isn't clear in the question. Thanks
Inspired by http://masilotti.com/ui-testing-tdd/, you don't have to rely on XCTWaiter. You can simply run a loop and test whether one of them exists.
/// Waits for either of the two elements to exist (i.e. for scenarios where you might have
/// conditional UI logic and aren't sure which will show)
///
/// - Parameters:
/// - elementA: The first element to check for
/// - elementB: The second, or fallback, element to check for
/// - Returns: the element that existed
#discardableResult
func waitForEitherElementToExist(_ elementA: XCUIElement, _ elementB: XCUIElement) -> XCUIElement? {
let startTime = NSDate.timeIntervalSinceReferenceDate
while (!elementA.exists && !elementB.exists) { // while neither element exists
if (NSDate.timeIntervalSinceReferenceDate - startTime > 5.0) {
XCTFail("Timed out waiting for either element to exist.")
break
}
sleep(1)
}
if elementA.exists { return elementA }
if elementB.exists { return elementB }
return nil
}
then you could just do:
let foundElement = waitForEitherElementToExist(elementA, elementB)
if foundElement == elementA {
// e.g. if it's a button, tap it
} else {
// element B was found
}
lagoman's answer is absolutely correct and great. I needed wait on more than 2 possible elements though, so I tweaked his code to support an Array of XCUIElement instead of just two.
#discardableResult
func waitForAnyElement(_ elements: [XCUIElement], timeout: TimeInterval) -> XCUIElement? {
var returnValue: XCUIElement?
let startTime = Date()
while Date().timeIntervalSince(startTime) < timeout {
if let elementFound = elements.first(where: { $0.exists }) {
returnValue = elementFound
break
}
sleep(1)
}
return returnValue
}
which can be used like
let element1 = app.tabBars.buttons["Home"]
let element2 = app.buttons["Submit"]
let element3 = app.staticTexts["Greetings"]
foundElement = waitForAnyElement([element1, element2, element3], timeout: 5)
// do whatever checks you may want
if foundElement == element1 {
// code
}
NSPredicate supports OR predicates too.
For example I wrote something like this to ensure my application is fully finished launching before I start trying to interact with it in UI tests. This is checking for the existence of various landmarks in the app that I know are uniquely present on each of the possible starting states after launch.
extension XCTestCase {
func waitForLaunchToFinish(app: XCUIApplication) {
let loginScreenPredicate = NSPredicate { _, _ in
app.logInButton.exists
}
let tabBarPredicate = NSPredicate { _, _ in
app.tabBar.exists
}
let helpButtonPredicate = NSPredicate { _, _ in
app.helpButton.exists
}
let predicate = NSCompoundPredicate(
orPredicateWithSubpredicates: [
loginScreenPredicate,
tabBarPredicate,
helpButtonPredicate,
]
)
let finishedLaunchingExpectation = expectation(for: predicate, evaluatedWith: nil, handler: nil)
wait(for: [finishedLaunchingExpectation], timeout: 30)
}
}
In the console while the test is running there's a series of repeated checks for the existence of the various buttons I want to check for, with a variable amount of time between each check.
t = 13.76s Wait for com.myapp.name to idle
t = 18.15s Checking existence of "My Tab Bar" Button
t = 18.88s Checking existence of "Help" Button
t = 20.98s Checking existence of "Log In" Button
t = 22.99s Checking existence of "My Tab Bar" Button
t = 23.39s Checking existence of "Help" Button
t = 26.05s Checking existence of "Log In" Button
t = 32.51s Checking existence of "My Tab Bar" Button
t = 16.49s Checking existence of "Log In" Button
And voila, now instead of waiting for each element individually I can do it concurrently.
This is very flexible of course, since you can add as many elements as you want, with whatever conditions you want. And if you want a combination of OR and AND predicates you can do that too with NSCompoundPredicate. This can easily be adapted into a more generic function that accepts an array of elements like so:
func wait(for elements: XCUIElement...) { … }
Could even pass a parameter that controls whether it uses OR or AND.
Hey other alternative that works for us. I hope help others too.
XCTAssert(
app.staticTexts["Hello Stack"]
.waitForExistence(timeout: 10) || app.staticTexts["Hi Stack"]
.waitForExistence(timeout: 10)
)

Index Out of Range Error in Swift

var numberOfPeople = 1 //get from host
var numberAtCounter = 0
func showNames() {
if peopleInMatch[numberAtCounter] == yourPeerID { //change to peopleinmatcheveryone
if numberOfPeople == 0 {
print("hoho")
personName.isHidden = true
connect.isHidden = false
connect.setTitle("PRESS READY", for: UIControlState.normal)
//change label to ready
} else {
numberAtCounter += 1
numberOfPeople -= 1 // buggy?
print("\(numberAtCounter)")
showNames()
}
} else {
personName.text = "TAKE PHOTO OF \(peopleInMatch[numberAtCounter])'s COLOR"
numberAtCounter += 1
if numberOfPeople <= 0 {
personName.isHidden = true
connect.isHidden = false
connect.setTitle("PRESS READY", for: UIControlState.normal)
//change label to ready
}
numberOfPeople -= 1 //buggy maybe fixed
}
}
I'm getting a Thread 1: EXC_BREAKPOINT error on the if peopleInMatch[numberAtCounter] == yourPeerID line. I'm not entirely sure what out of index means or what is potentially wrong. The code will be run through once then the function calls itself and on the second time through it breaks down on the line I mentioned above. I've checked all the variables and none of them are nill. Any ideas?
Here I made a short example for you to understand where your problem actually is.
ScreenShot 1:
Everything works fine when function is called for first time value at numberAtCounter is printed.
Here in first call either you are decrementing the value as numberAtCounter-=1 which take value from 0 to -1. Thus in second call when the function is called in line at:
if peopleInMatch[numberAtCounter] == yourPeerID // here you need to check value of `numberAtCounter`
make sure it's not getting a negative value or value more than your peopleInMatch array.
Thus if it becomes negative or more than count, result you will get as follows:

expectationForPredicate Fails test case

Recently started working on XCode UI test with SWIFT.
My problem is I need to wait until a element appears on iPhone screen.
I found a solution with '''expectationForPredicate''' and '''waitForExpectationsWithTimeout''' but the problem this is this methods are designed to fail test case if expected predicate not matched within timeout.
I need a code which can wait for element to appear on screen if the element did not appear and timeout exceeded then I don't want test case to fail. rather I would like to return true (element exists) / false (not exists)
I found a solution by avoiding the above mentioned functions as those are failing my tests instead of returning true or false
Below is the method i created
func waitForElementToAppear(element: XCUIElement, file: String = #file, line: UInt = #line) -> Bool {
let TIMEOUT: Double = 120 ;
var isFound = false;
let start = NSDate();
var diff : Double = 0;
repeat{
print("Is element \(element) found : \(element.exists)")
print("Printing debugDescription -> ")
print(XCUIApplication().debugDescription)
if(element.exists){
isFound = true;
break;
}
print("Waiting for element to exists... Time counter :\(diff)")
sleep(1)
let end = NSDate();
diff = end.timeIntervalSinceDate(start);
}while(diff <= TIMEOUT);
return isFound;
}
I hope this will help others, But if you still have any other better solution please answer here.

How to wait for a label to appear with character count greater than zero UI Test

Actually before api call only Label is visible with empty text. After getting response label character count is greater than zero. But here how can wait for label appear with character count greater than zero and perform some action.
This is the code I used but I am getting predicate error
caught “NSUnknownKeyException” , “[<_NSCFString 0xjel990> valueForUndefinedKey:]: this is not key value coding-complaint for the key characters
func testForWishListCountIsReflecting() {
let app = XCUIApplication()
app.decide()
cellTap()
app.navigationBars["MMRecoPageView"].buttons["MenuButton"].tap()
let wishListLable = app.staticTexts["WishListLabel"]
wishListLable.label.characters
let exists = NSPredicate(format: "label.characters.count > 0")
expectationForPredicate(exists, evaluatedWithObject:wishListLable, handler: nil)
waitForExpectationsWithTimeout(30, handler: nil)
let expectedValue:String = app.staticTexts["WishListLabel"].value as! String
app.buttons["Wishlist"].tap()
XCTAssertTrue(app.tables.cells.count == UInt(expectedValue))
}
Your issue is in the error message. The characters() selector cannot be used in an NSPredicate because it does not work with KVO.
this is not key value coding-compliant for the key characters
I suggest a slightly different approach. Do you know what the actual text should be? If so, you can change your predicate to match exactly for that. You can remove manually set the "WishListLabel" accessibility and let the content set it automatically.
func testForWishListCountIsReflecting() {
// ...setup...
let wishListLabel = app.staticTexts["contents of label"]
waitForElementToAppear(wishListLabel)
}
private func waitForElementToAppear(element: XCUIElement, file: String = __FILE__, line: UInt = __LINE__) {
let existsPredicate = NSPredicate(format: "exists == true")
expectationForPredicate(existsPredicate, evaluatedWithObject: element, handler: nil)
waitForExpectationsWithTimeout(5) { (error) -> Void in
if (error != nil) {
let message = "Failed to find \(element) after 5 seconds."
self.recordFailureWithDescription(message, inFile: file, atLine: line, expected: true)
}
}
}