skip to Main Content

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.

  1. Go to external browser when click my button.
  2. Check is open correct url in browser (or just open browser) ?
  3. Go back to our app with resume state.

Is it impossible to do that ?.

3

Answers


  1. 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.

    Login or Signup to reply.
  2. 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
    
    Login or Signup to reply.
  3. 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:

    1. When inline, app is your app. When external, it’s com.apple.mobilesafari. You’ll have to have logic to handle that.
    2. You want to wait for the page to finish loading. By not doing so, you will run into flakiness. When the page is loading, there is a 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.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search