Any way to automate SFSafariViewController in UI tests?
Asked Answered
Z

2

12

Is there any way to automate SFSafariViewController? I like the Xcode 7 UI test feature, but it seems it does not support SFSafariViewController automation. Some of the UI flows I am testing require a web browser so the app uses SFSafariViewController to make it safer vs a web view.

Zetland answered 24/9, 2015 at 13:44 Comment(4)
I'm also very interested in this. Have you found some kind of way around this?Whitcher
@Whitcher Unfortunately nothing at this time.Zetland
It's not clear to me what you want to automate exactly.Nealah
In my case I want to automate a login flow into a third-party service with a test account.Zetland
A
2

If it's similar to launching extensions (currently broken with direct interactions), try tapping the screen at the point where the element you're looking for is:

Example of tapping an action sheet which launches an extension:

func tapElementInActionSheetByPosition(element: XCUIElement!) {
    let tableSize = app.tables.elementBoundByIndex(0).frame.size
    let elementFrame = element.frame

    // get the frame of the cancel button, because it has a real origin point
    let CancelY = app.buttons["Cancel"].frame.origin.y

    // 8 is the standard apple margin between views
    let yCoordinate = CancelY - 8.0 - tableSize.height + elementFrame.midY

    // tap the button at its screen position since tapping a button in the extension picker directly is currently broken
    app.coordinateWithNormalizedOffset(CGVectorMake(elementFrame.midX / tableSize.width, yCoordinate / app.frame.size.height)).tap()
}

Note: You must tap at the XCUIApplication query layer. Tapping the element by position doesn't work.

Attribute answered 12/4, 2016 at 20:48 Comment(0)
C
0

For now Xcode 9.3 has support for that, but it doesn't work properly because of annoying Xcode bug.

In test you can print app.webViews.buttons.debugDescription or app.webViews.textFields.debugDescription, and it prints correct information, but after tap or typeText you have crash.

To workaround you can parse debugDescription, extract coordinates and tap by coordinate. For text field you can insert text via "Paste" menu.

private func coordinate(forWebViewElement element: XCUIElement) -> XCUICoordinate? {
    // parse description to find its frame
    let descr = element.firstMatch.debugDescription
    guard let rangeOpen = descr.range(of: "{{", options: [.backwards]),
        let rangeClose = descr.range(of: "}}", options: [.backwards]) else {
            return nil
    }

    let frameStr = String(descr[rangeOpen.lowerBound..<rangeClose.upperBound])
    let rect = CGRectFromString(frameStr)

    // get the center of rect
    let center = CGVector(dx: rect.midX, dy: rect.midY)
    let coordinate = XCUIApplication().coordinate(withNormalizedOffset: .zero).withOffset(center)
    return coordinate
}

func tap(onWebViewElement element: XCUIElement) {
    // xcode has bug, so we cannot directly access webViews XCUIElements
    // as workaround we can check debugDesciption, find frame and tap by coordinate
    let coord = coordinate(forWebViewElement: element)
    coord?.tap()
}

Article about that: https://medium.com/@pilot34/work-with-sfsafariviewcontroller-or-wkwebview-in-xcode-ui-tests-8b14fd281a1f

Full code is here: https://gist.github.com/pilot34/09d692f74d4052670f3bae77dd745889

Circumbendibus answered 26/4, 2018 at 9:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.