Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

XCUITest and Today Widget

I have an App with Today Widget. So I would like to perform some UI testing on it.

I found a way to open Today/Notifications panel. It seems easy:

let statusBar = XCUIApplication().statusBars.elementBoundByIndex(0)
statusBar.swipeDown()

But then I can't find a way to do something useful. It is possible to record UI interactions in Today/Notifications panel, but such code can't reproduce my actions.

like image 326
Maxim Pervushin Avatar asked Mar 30 '16 11:03

Maxim Pervushin


2 Answers

First you need to open Today View, you can use this way:

    let app = XCUIApplication()
    // Open Notification Center
    let bottomPoint = app.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 2))
    app.coordinate(withNormalizedOffset: CGVector(dx: 0, dy: 0)).press(forDuration: 0.1, thenDragTo: bottomPoint)
    // Open Today View
    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    springboard.scrollViews.firstMatch.swipeRight()

Then, to access everything you need, just use springboard, for example:

let editButton = springboard.buttons["Edit"]
like image 87
Nikaaner Avatar answered Nov 15 '22 14:11

Nikaaner


There's a similar problem testing extensions. I've found that what you must do is tap the element at where it is on the screen rather than the element itself in order to drive the interaction. I haven't tested this with your scenario, but I haven't found anything un-tappable via this method yet.

Here is a Swift example of tapping the "X" button on the Springboard for an app icon, which similarly cannot be tapped via typical interaction:

let iconFrame = icon.frame // App icon on the springboard
let springboardFrame = springboard.frame // The springboard (homescreen)
icon.pressForDuration(1.3) // tap and hold

// Tap the little "X" button at approximately where it is. The X is not exposed directly
springboard.coordinateWithNormalizedOffset(CGVectorMake((iconFrame.minX + 3) / springboardFrame.maxX, (iconFrame.minY + 3) / springboardFrame.maxY)).tap()

By getting the frame of the superview and the subview, you can calculate where on the screen the element should be. Note that coordinateWithNormalizedOffset takes a vector in the range [0,1], not a frame or pixel offset. Tapping the element itself at a coordinate doesn't work, either, so you must tap at the superview / XCUIApplication() layer.

More generalized example:

let myElementFrame = myElement.frame
let appFrame = XCUIApplication().frame
let middleOfElementVector = CGVectorMake(iconFrame.midX / appFrame.maxX, iconFrame.midY / appFrame.maxY)

// Tap element from the app-level at the given coordinate
XCUIApplication().coordinateWithNormalizedOffset(middleOfElementVector).tap()

If you need to access the Springboard layer and go outside your application, you can do so with:

let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")
springboard.resolve()

But you'll need to expose some private XCUITest methods with Objective-C:

@interface XCUIApplication (Private) {
    - (id)initPrivateWithPath:(id)arg1 bundleID:(id)arg2;
}

@interface XCUIElement (Private) {
    - (void) resolve;
}
like image 42
Chase Holland Avatar answered Nov 15 '22 16:11

Chase Holland