Any way to automate SFSafariViewController in UI t

2019-04-19 10:58发布

问题:

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.

回答1:

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.



回答2:

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