I'm trying to make a simple UI Test in my iOS Application. I'd like to fill a textfield with some text but an error always appears.
First try:
let searchField = app.textFields.elementBoundByIndex(0);
searchField.tap()
searchField.typeText("Holy Grail")
The field is highlighted, the keyboard appears and, after some seconds, randomly one of these:
- Timed out waiting for IDE barrier message to complete
- failed to get attributes within 15.0s ui test
- failed to get snaphots within 15.0s ui test
Second try:
func setText(text: String, application: XCUIApplication) {
//Instead of typing the text, it pastes the given text.
UIPasteboard.generalPasteboard().string = text
doubleTap()
application.menuItems["Paste"].tap()
}
...
let searchField = app.textFields.elementBoundByIndex(0);
searchField.tap()
searchField.setText("Holy Grail")
Same result.
Tried with Connect Hardware Keyboard on and off.
Tried with iPhone 6s simulator, iPad Retina simulator, iPad 2 simulator.
Tried only with Xcode 7 (8 will break the project)
Ideas? Thanks in advance!
I don't have an answer on why this is happening, but for any future guy that has my same problem here is my workaround, on which I'm working on.
Basic idea is to make the keyboard appear, and then hit every single key I need to build my string.
func testPlayground() {
let app = XCUIApplication()
waitAndTap(app.textFields["MyTextField"])
type(app, text: "hej")
}
func waitAndTap(elm: XCUIElement){
let exists = NSPredicate(format: "exists == true")
expectationForPredicate(exists, evaluatedWithObject: elm, handler: nil)
waitForExpectationsWithTimeout(10.0, handler: nil)
elm.tap()
}
func type(app: XCUIApplication, text : String){
//Wait for the keyboard to appear.
let k = app.keyboards;
waitForContent(k, time: 10.0)
//Capitalize the string.
var s = text.lowercaseString;
s.replaceRange(s.startIndex...s.startIndex, with: String(s[s.startIndex]).capitalizedString)
//For each char I type the corrispondant key.
var key: XCUIElement;
for i in s.characters {
if "0"..."9" ~= i || "a"..."z" ~= i || "A"..."Z" ~= i {
// Then it's alphanumeric!
key = app.keys[String(i)];
}
else {
// Then is special character and is necessary re-map them.
switch i {
case "\n":
key = app.keys["Next:"]; // This is to generalize A LOT.
default:
key = app.keys["space"];
}
}
waitAndTap(key);
}
So: waitAndTap() is a function that I use A LOT in my tests. It waits for the existence of an element and it taps it. If in ten seconds it doesn't show up, fail the test.
testPlayground() taps the textfield in which I wanna write and then calls type()
type() is the core. Basically, look at every single character in the string and tap it. Problems:
If I look for character H in a keyboard where shift is not activated it will never find an uppercase letter. My solution is to capitalize the string, but it works only in specific cases. This is enhanceable
It doesn't work with special characters, where you cannot look for it exactly, but for the key´s name ("space" instead of " "). Well, no solution here, aside that horrible switch-case, but well, for what I need to do it works.
Hope this can help someone!
First try to disable I/O -> Hardware Keyboard and see what will happen.
If still not working
instead of
searchField.tap()
searchField.typeText("Holy Grail")
try this
app.keys["H"].tap()
app.keys["o"].tap()
app.keys["l"].tap()
app.keys["y"].tap()
This method allows you to enter Uppercase and Lowercase strings
func enterUsername(username: String?) {
guard
let input = username,
var previousCharaterIsLowerCase = input.first?.isUppercase else {
XCTFail("Username is not set")
return
}
for char in input {
if char.isUppercase && previousCharaterIsLowerCase {
// Switch on capslock
app.buttons["shift"].tap()
app.buttons["shift"].tap()
}
if char.isLowercase && previousCharaterIsLowerCase == false {
// Switch off capslock
app.buttons["shift"].tap()
app.buttons["shift"].tap()
app.buttons["shift"].tap()
}
previousCharaterIsLowerCase = char.isLowercase
app.keys[String(char)].tap()
}
}
Related
I have weather app. It fetches the data from API. I enter needed city, then next screen opens and shows me the name of the city and temperature. I am writing UI test, which should open the app, handle an alert which asks to use location, then test should write the city name and check if this city exists in the screen. All works except checking the city name at the end. I thought maybe the problem is because it needs some time to get the answer from API, and tests doesn’t wait for it. Maybe I need to set timer to wait for answer. Or the problem is in smth else?
Here is my code and it fails at the last line.
func testExample() throws {
let app = XCUIApplication()
app.launchArguments = ["enable-testing"]
app.launch()
app/*#START_MENU_TOKEN#*/.staticTexts["My location"]/*[[".buttons[\"My location\"].staticTexts[\"My location\"]",".staticTexts[\"My location\"]"],[[[-1,1],[-1,0]]],[0]]#END_MENU_TOKEN#*/.tap()
addUIInterruptionMonitor(withDescription: "Allow “APP” to access your location?") { (alert) -> Bool in
let button = alert.buttons["Only While Using the App"]
if button.exists {
button.tap()
return true // The alert was handled
}
return false // The alert was not handled
}
app.textFields["Enter your city"].tap()
app.textFields["Enter your city"].typeText("Barcelona")
app.buttons["Check weather"].tap()
XCTAssertTrue(app.staticTexts["Barcelona"].exists)
}
XCTest comes with a built-in function you need
Documentation: https://developer.apple.com/documentation/xctest/xcuielement/2879412-waitforexistence/
Example:
XCTAssertTrue(myButton.waitForExistence(timeout: 3), "Button did not appear")
I found the function and used it to wait before the result.
Here is the function and its usage in my code.
func waitForElementToAppear(_ element: XCUIElement) -> Bool {
let predicate = NSPredicate(format: "exists == true")
let expectation = expectation(for: predicate, evaluatedWith: element,
handler: nil)
let result = XCTWaiter().wait(for: [expectation], timeout: 5)
return result == .completed
}
app.textFields["Enter your city"].tap()
app.textFields["Enter your city"].typeText("Barcelona")
app.buttons["Check weather"].tap()
let result = app.staticTexts["Barcelona"]
waitForElementToAppear(result)
XCTAssertTrue(result.exists)
So I have this XCTestCase, it runs a simple function and I'm satisfied with the results, but every time the function ends, it displays "TEST FAILED" and shut down the app.
One more thing, I tried to change the continueAfterFailure boolean to true and yet it still shut down the app after failure...
I couldn't find a solution yet, hope someone can help me :)
Thanks
Update:
Here's the code:
func loginSuccess (element: XCUIElement) {
//Entering invalid input to the text field
//App has validator a that doesn't let the input in
element.typeText("!##$%^")
XCTAssertTrue((element.value as? String == ""), "Test Passed!")
if element.value as? String == "" {
print ("Test Passed!") //This line works every run
} else {
XCTFail("Invalid text can be inserted") //Managed to force fail and succeded, but not pass.
}
}
You have things reversed. XCTAssertTrue evaluates that an expression is true and logs the provided message and fails the test if the expression is false.
I am using UI Test with Xcode 7 but have a few problems.
When I do record UI Test, Xcode translates Chinese to Unicode with uppercase 'U' and it shows errors.
And ui test code
XCUIApplication *app = [[XCUIApplication alloc] init];
[app.navigationBars[#"\u5934\u6761"].images[#"new_not_login30"] tap];
XCUIElementQuery *tablesQuery = app.tables;
[tablesQuery.cells.staticTexts[#"\u6211\u7684\u864e\u94b1"] tap];
the problem is: after tapping image, there is an animation showing sidebar with UITableView or showing UIAlertController but I cannot handle the duration time. Actually, within animation the testing continues to find next elements to match but these elements do not exist or generating. So the test always be failed.
Any solution to answer this question? Please help me. Thanks.
try it with expectationForPredicate. I don't know the syntax in objective-c. But here is a part of code in swift:
let app = XCUIApplication()
app.navigationBars["\u5934\u6761"].images["new_not_login30"].tap()
let label = app.cells.staticTexts["\u6211\u7684\u864e\u94b1"]
let exists = NSPredicate(format: "exists == 1")
expectationForPredicate(exists, evaluatedWithObject: label) {
// If the label exists, also check that it is enabled
if label.enabled {
label.tap()
return true
} else {
return false
}
}
waitForExpectationsWithTimeout(5) { error in
if (error != nil) { assertion ....}
}
Just translate this code in objective-c.
Cheers
I am trying to write a do-try-catch in swift for my iOS UI test which uses XCUI testing. I am reading the error-handling section: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/Swift_Programming_Language/ErrorHandling.html#//apple_ref/doc/uid/TP40014097-CH42-ID508
but am unsure of which error should be thrown when an element is not found.
func tapElement(string:String) throws {
do{
waitFor(app.staticTexts[string], 5)
try app.staticTexts[string].tap()
}
catch {
NSLog("Element was not found: \(string)")
//how can i check specifically that the element was not found?
}
}
....
func waitFor(element:XCUIElement, seconds waitSeconds:Double) {
NSLog("Waiting for element: \(element)")
let exists = NSPredicate(format: "exists == 1")
expectationForPredicate(exists, evaluatedWithObject: element, handler: nil)
waitForExpectationsWithTimeout(waitSeconds, handler: nil)
}
any help greatly appreciated!!
You should use element.exists AND element.isHittable
element.exists checks whether the element is an element of the UIApplication/ScrollView/..
element.isHittable determines if a hit point can be computed for the element.
If you don't check for both, than element.tap() throws the following error, for example, if the element is under the keyboard:
Failed: Failed to scroll to visible (by AX action) TextField,...
Example code:
let textField = elementsQuery.textFields.allElementsBoundByIndex[i]
if textField.exists && textField.isHittable {
textField.tap()
} else {
// text field is not hittable or doesn't exist!
XCTFail()
}
You shouldn't need to try-catch finding elements in UI Testing. Ask the framework if the element exists() before trying to tap() it.
let app = XCUIApplication()
let element = app.staticTexts["item"]
if element.exists {
element.tap()
} else {
NSLog("Element does not exist")
}
Check out my blog post on getting started with UI Testing for more specific examples, like tapping an button.
I am using the UI test APIs introduced in Xcode 7 XCTest. On my screen I have a text that is loaded from the network.
The test fails if I simply check it with exists property.
XCTAssert(app.staticTexts["Text from the network"].exists) // fails
It does work though if I first send the tap or any other event to the text like this:
app.staticTexts["Text from the network"].tap()
XCTAssert(app.staticTexts["Text from the network"].exists) // works
It looks like if I just call exists it evaluates it immediately and fails because the text has not been downloaded from the network yet. But I think when I call the tap() method it waits for the text to appear.
Is there a better way to check for the presence of a text that is delivered from the network?
Something like (this code will not work):
XCTAssert(app.staticTexts["Text from the network"].eventuallyExists)
Xcode 7 Beta 4 added native support for asynchronous events. Here's a quick example of how to wait for a UILabel to appear.
XCUIElement *label = self.app.staticTexts[#"Hello, world!"];
NSPredicate *exists = [NSPredicate predicateWithFormat:#"exists == 1"];
[self expectationForPredicate:exists evaluatedWithObject:label handler:nil];
[self waitForExpectationsWithTimeout:5 handler:nil];
First create a query to wait for a label with text "Hello, world!" to appear. The predicate matches when the element exists (element.exists == YES). Then pass the predicate in and evaluate it against the label.
If five seconds pass before the expectation is met then the test will fail. You can also attach a handler block in that gets called when the expectation fails or times out.
If you're looking for more information regarding UI Testing in general, check out UI Testing in Xcode 7.
Swift 3:
let predicate = NSPredicate(format: "exists == 1")
let query = app!.staticTexts["identifier"]
expectation(for: predicate, evaluatedWith: query, handler: nil)
waitForExpectations(timeout: 5, handler: nil)
It will continuously check for 5 seconds whether that text is displayed or not.
As soon as it will find the text may be in less than 5 seconds, it will execute further code.
XCode9 has a method waitForExistence(timeout: TimeInterval) of XCUIElement
extension XCUIElement {
// A method for tap element
#discardableResult
func waitAndTap() -> Bool {
let _ = self.waitForExistence(timeout: 10)
let b = self.exists && self.isHittable
if (b) {
self.tap()
}
return b
}
}
// Ex:
if (btnConfig.waitAndTap() == true) {
// Continue UI automation
} else {
// `btnConfig` is not exist or not hittable.
}
But I encounter another problem, the element is exist, but not hittable. So I extend a method to wait an element to be hittable.
extension XCTestCase {
/// Wait for XCUIElement is hittable.
func waitHittable(element: XCUIElement, timeout: TimeInterval = 30) {
let predicate = NSPredicate(format: "isHittable == 1")
expectation(for: predicate, evaluatedWith: element, handler: nil)
waitForExpectations(timeout: timeout, handler: nil)
}
}
// Ex:
// waitHittable(element: btnConfig)
If I'm right in understanding you that the target text has already displayed when you're checking it's existing, you can try to use hittable property.