Reputation: 301
Currently, I have to create automation ui test with XCUI and I have some action to open external browser by default is Safari.
I need to create some uitest's behavior like this.
Is it impossible to do that ?.
Upvotes: 3
Views: 6121
Reputation: 2217
✅ Following code snippet has worked for me.
static func validateBrowserURL(url: String){
let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari")
defer {safari.terminate()}
let addressBarIpad = safari.buttons["Address"]
let addressBarIphone = safari.textFields["Address"]
let addressBarTextField = safari.textFields["Address"]
if app.isIPad {
addressBarIpad.tapWhenDidAppear(waitSecondsForExistence: 10)
} else {
addressBarIphone.tapWhenDidAppear(waitSecondsForExistence: 10)
}
let urlValue = addressBarTextField.value as! String
XCTAssertTrue(urlValue.contains(url), "Couldn't locate \(url)")
}
Upvotes: 0
Reputation: 849
You could achieve this by capturing the Safari app and validating your URL in its address bar in two steps. Assuming the tested URL is https://myapp.com/something
, here is how to test if it's opened inside Safari:
let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari")
guard safari.wait(for: .runningForeground, timeout: 10) else {
XCTFail("could not open Safari")
return
}
// we use CONTAINS because at first, only the base URL is displayed inside the text field. For example, if the URL is https://myapp.com/something, the value of the address bar would be 'myapp.com'
let pred = NSPredicate(format: "value CONTAINS[cd] %@", "myapp.com")
let addressBar = safari.textFields.element(matching: pred)
// tap is needed to reveal the full URL string inside the address bar
addressBar.tap()
let textField = safari.textFields.element(matching: pred)
XCTAssertEqual(textField.value as? String, "https://myapp.com/something")
What happens here is that, as soon as the app opens the link inside Safari, it tries to read the target URL from the address bar, tap on it in order to be able to read its text field content, and finally asserts the expected URL string against the value of the address bar.
This approach is more stable as it directly checks the value of all the text fields inside the browser instead of referring to their identifier
s, for example, checking the "Address" identifier won't work today as it no longer exists in the Safari view hierarchy.
Please note that this is a working solution for the actual state of the iOS which might be prone to breaking changes in the future.
Upvotes: 0
Reputation: 4549
The previous answers work fine for an external browser. If you app happens to launch inline browsers, they won't work as there is no URL bar. You could detect the inline browser and tap the Safari button to launch it externally before checking the URL, but then you'd have to reactivate your app and close the inline browser. Many more steps.
THERE IS AN EASIER WAY THAT WILL WORK WITH BOTH BROWSER TYPES!
What do these browsers share? A share button! How can we get the URL from there? Copy
will place the URL in the pasteboard, which we have access to.
app.buttons["ShareButton"].tap()
_ = copyButton.waitForExistence(timeout: Waits.short.rawValue) // a tiny wait is necessary here for the share page to open fully
app.buttons["Copy"].tap()
let URL = UIPasteboard.general.string!
There are two catches here:
app
is your app. When external, it's com.apple.mobilesafari
. You'll have to have logic to handle that.ReloadButton
. When that goes away, you should expect to see a StopButton
. If the reload button doesn't disappear, the page isn't loading completely so you may need to hit the stop button, but do so in a safe way as I have seen maybe 1% of the time the page load completes in the milliseconds between giving up on it and performing that tap - I do a quick isHittable
check before tapping.Upvotes: 0
Reputation: 321
Providing the exact code, that worked for me
enum SafariError: Error {
case appLoadTimeout
}
let safari = XCUIApplication(bundleIdentifier: "com.apple.mobilesafari")
app.buttons["MyButtom"].tap() // MyButton launches Safari
guard safari.wait(for: .runningForeground, timeout: 5) else {
throw SafariError.appLoadTimeout
}
safari.otherElements["Address"].tap()
XCTAssertEqual(safari.textFields["Address"].value as! String, "https://check-url.com")
app.activate() //Back to my app
Upvotes: 4
Reputation: 4549
Absolutely. Safari is an XCUIApplication just like any other with a bundle identifier of com.apple.mobilesafari
.
To check the URL you'll tap()
the URL
field (it's a button when a page loads) and read the value of the URL
field (at this point it's a textField). Once you're done asserting that, activate()
your app and you'll be back in it (note: my tests finish after asserting so I don't have to do this, but it's the published method - you could always enter debug and find how to tap the button to return to your application in the top-left of the screen if this doesn't work).
I'm happy to provide exact code if you show me you've tried this and can't get it working, but it's pretty straightforward XCUI automation.
Upvotes: 2