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.
Related
Okay, so I'm currently trying to write the code for a very inaccurate stoplight--one that is only meant to help me understand what I've learned, but I can't seem to figure it out! In my code, at the very end, I say print("Green Light!") after setting up a few variables, but I would like to indicate what the computer should do instead of flat out saying "print this phrase," if that makes sense... not sure it does.
How would I write this if I wanted to simply print the Boolean value of greenLight without saying print("Green Light!")?
I'm very much a beginner and I might be asking the wrong question--I know that, but I'm hoping someone can help!
Something tells me I haven't learned enough to do this, yet, but I really wanna know how this works.
This is what I've written so far. It runs, but I would like to change it so all I have to say is print(greenLight) or print(Bool).
When I try putting in print(greenLight), it returns an error:
Output:
Review.swift:14:7: error: variable 'greenLight' used before being initialized
print(greenLight)
^
Review.swift:4:5: note: variable defined here
var greenLight: Bool
^
var carAtRightIntersection = false
var carAtLeftIntersection = false
var carStraightAhead = true
var greenLight: Bool
if !(carAtRightIntersection && carAtLeftIntersection) && carStraightAhead {
greenLight = true
}
if carAtRightIntersection && carAtLeftIntersection && !(carStraightAhead) {
greenLight = false
}
print("Green light!")
Edit: I consulted a few coding friends, and they provided a very good solution! Provided here:
var carAtRightIntersection = false
var carAtLeftIntersection = false
var carStraightAhead = true
var colorOfLight: String = "Red"
if !(carAtRightIntersection && carAtLeftIntersection) && carStraightAhead {
colorOfLight = "Green"
}
/*
if carAtRightIntersection && carAtLeftIntersection && !(carStraightAhead) {
greenLight = false
}
*/
print(colorOfLight + " light :)")
If you don't want to print the actual phrases, You could use a switch case statement
switch greenLight{
case true:
print("Green Light!")
case false:
print("Red Light!")
default:
print("Yellow Light!")
}
After the first block executes
[MacOS 10.14.1, Xcode 10.1, Swift 4.2]
I'm working on creating a getopt style CLI argument processor whilst practising Swift. In my design, I decided to create a computed variable, represented as a [String:Bool] dictionary, that can be checked to see if an option (key) is just a switch (value = true) or whether it may include parameters (value = false). So I've written the code below, all of which is, at the moment, in my small (300 lines) main.swift file.
The code works correctly in a playground, but in my Swift Xcode project, whilst the dictionary's keys are correct, values are always false and inconsistent with the printed messages.
let options = "cwt:i:o:"
//lazy var optionIsSwitch : [String:Bool] = { (This will be moved to a class)
var optionIsSwitch : [String:Bool] = {
var tmpOptionIsSwitch : [String:Bool] = [:]
let optionsStrAsArray = Array(options)
let flags = Array(options.filter { !":".contains($0) } )
tmpOptionIsSwitch.reserveCapacity(flags.count)
for thisOption in 0...flags.count-1 {
var posInOptionsStr = 0
while posInOptionsStr < optionsStrAsArray.count-1 && flags[thisOption] != optionsStrAsArray[posInOptionsStr] {
posInOptionsStr += 1
}
if posInOptionsStr < optionsStrAsArray.count-1 && optionsStrAsArray[posInOptionsStr+1] == ":" {
tmpOptionIsSwitch[String(flags[thisOption])] = false
print("\(flags[thisOption]) is FALSE")
} else {
tmpOptionIsSwitch[String(flags[thisOption])] = true
print("\(flags[thisOption]) is TRUE")
}
}
return tmpOptionIsSwitch
}()
I've stepped through the code in my project to observe the execution sequence, and found it to be correct. As per the first image, tmpOptionIsSwitch returns a dictionary containing the right keys but all the values are set to false, which is inconsistent with the print statements.
As part of my debugging activities, I copied the above code into a Swift Playground where I found it gave the correct results, as per the image below.
Has anyone has such an issue? Is there something I've done wrong?
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)
)
I am puzzled. I need to compare product date codes. they look like 12-34-56. I wrote some code to break the parts up and compare them. this code works fin in the play ground. But when i make it a function in a view controller values come up NIL and i get a lot of "Optional("12-34-56")" values when printed to the log or viewed in a break. I tried unwrapping in many locations but nothing takes.? don't be confused by the variables date and month because they are not product codes can have 90 days and 90 months depending on the production machine used.
func compaireSerial(oldNumIn: NSString, newNumIn: String) -> Bool {
// take the parts of the number and compare the pics on at a time.
// Set up the old Num in chunks
let oldNum = NSString(string: oldNumIn)
let oldMonth = Int(oldNum.substringToIndex(2))
let oldDay = Int(oldNum.substringWithRange(NSRange(location: 3, length: 2)))
let oldYear = Int(oldNum.substringFromIndex(6))
print(oldMonth,oldDay, oldYear)
// Set up the new Num in chunks
let newNum = NSString(string: newNumIn)
let newMonth = Int(newNum.substringToIndex(2))
let newDay = Int(newNum.substringWithRange(NSRange(location: 3, length: 2)))
let newYear = Int(newNum.substringFromIndex(6))
print(newMonth, newDay, newYear)
// LETS Do the IF comparison steps.
if oldYear < newYear {
return true
} else if oldMonth < newMonth {
return true
} else if oldDay < newDay {
return true
} else {
return false
}
}
May thanks to any one. Im totally stumped
All Int() initializers with String parameters return always an optional Int.
The realtime result column in a Playground doesn't indicate the optional but printing it does.
let twentyTwo = Int("22") | 22
print(twentyTwo) | "Optional(22)\n"
I don't see how i can delete my question so ill post this to let others know it is fixed. Turns out the auction works okay but the NSUserDefaults value coming in was optional. So i was feeding the optional in. After unwrapping the NSUser value all works.
First off, I'm fairly new to programming and trying to learn Swift, though I've worked with python and perl in the past.
I'm creating a simple prime number command line application. The program runs correctly when I provide the arguments for the functions, but causes consistently incorrect outputs when I prompt user input. I researched the best way to implement this behavior in Swift, as it doesn't have scanf() or raw_input() type commands, but I have something screwed up.
The program has several different functions, but the one I've been fighting with checks whether an integer is prime. The code for the function follows:
func testForPrime(num:Int)->Bool{
var num = num
var counter = 0
var primeTest : Bool = true
if num <= 1 || num % 2 == 0{
println("\(num) is not a prime number")
primeTest = false
}else{
##'checkerNumbers' is another function to determine the denominator
for i in checkerNumbers(Double(num)){
if num % i == 0 {
++counter
println("\(num) is not a prime number.")
println("\(num / i)*\(i)=\(num)")
primeTest = false
break
}
}
if counter == 0{
println("\(num) is a prime number!")
primeTest = true
}
}
return primeTest
}
And here is the input for the function:
var input = NSFileHandle.fileHandleWithStandardInput()
println("Enter a number to check if it is prime")
if let data : NSData = input.availableData as NSData? {
if let var x : Int = NSInteger(NSUTF8StringEncoding) as NSInteger?{
var intInput = x
testForPrime(intInput)
}
}
When a any number is entered in the terminal, the output is always '4'. However, if I run the function with testForPrime(13), instead of with user input, it responds as I would expect. I was thinking it may have to do with the conversion from NSString and NSUTF8StringEncoding to Int...
Can someone help me sort this out?
Thanks in advance!
Ok... So I figured out a way to make it work, though it may not be the best. I replaced the whole input section with the following:
println("Enter a number to check if it is prime")
var input = NSString(data: NSFileHandle.fileHandleWithStandardInput().availableData, encoding:NSUTF8StringEncoding)
var intInput = input?.intValue
testForPrime(Int(intInput!))