Xcode 7 UI Testing: Dismiss Push and Location alerts
Asked Answered
K

4

31

I encountered a problem with Xcode 7 UI Testing.

The app displays two alerts after my user logs in, the Request Location Alert and the Push Notifications Alert. Those notifications are shown one right after the other. The Location one appears first.

I try to dismiss them automatically to start my tests.

In order to do that, I add two UIInterruptionMonitor, the first one for the Location Alert and the second one for the Notification Push Alert.

    addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
        /* Dismiss Location Dialog */
        if alert.collectionViews.buttons["Allow"].exists {
            alert.collectionViews.buttons["Allow"].tap()
            return true
        }
        return false
    }
    addUIInterruptionMonitorWithDescription("Push Dialog") { (alert) -> Bool in
        /* Dismiss Push Dialog */
        if alert.collectionViews.buttons["OK"].exists {
            alert.collectionViews.buttons["OK"].tap()
            return true
        }
        return false
    }

But only the Location one is triggered, the handler of Push Notifications UIInterruptionMonitor is never called.

If I return true in the Request Location UIInterruptionMonitor as this other post accepted answer specifies. Both handler are called but the alert parameter in both UIInterruptionMonitor links to the Request Location Alert View so the "OK" button is never found.

How can I dismiss those two successive alerts views?

Kisung answered 23/11, 2015 at 16:0 Comment(3)
I'm dealing with the exact same issue, but haven't found a solution yet. I tried using only one and checking for "Allow" and "OK" in both, but that didn't work either....Supporter
I have been wrestling this exact issue for days now. Do you have any progress? SO frustratingAdim
I'm stuck with this problem also. A single alert? No Problem. Two successive alerts => Only 1 is tapped.Aurum
C
11

While not ideal, I found that if you simply wait until one authorization dialog has finished before presenting another one in the app, UI tests can pick up multiple requests in a row.

    if CLLocationManager.authorizationStatus() == .AuthorizedWhenInUse || CLLocationManager.authorizationStatus() == .AuthorizedAlways {
        self.locationManager.requestLocation()
    } else {
        self.contactStore.requestAccessForEntityType(.Contacts) { _ in
            self.locationManager.requestWhenInUseAuthorization()
        }
    }

I'm actually requesting access to contacts in a different place in my code, but it can handle multiple simultaneous requests just fine.

Then in my test:

    addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
        let button = alert.buttons["Allow"]
        if button.exists {
            button.tap()
            return true
        }
        return false
    }
    addUIInterruptionMonitorWithDescription("Contacts Dialog") { (alert) -> Bool in
        let button = alert.buttons["OK"]
        if button.exists {
            button.tap()
            return true
        }
        return false
    }

    app.buttons["Location"].tap()

    app.tap() // need to interact with the app for the handler to fire
    app.tap() // need to interact with the app for the handler to fire
Claqueur answered 18/2, 2016 at 18:8 Comment(0)
A
3

As I noted in the answer you mentioned, you must interact with the application after the alert appears.

Second, after presenting the alert you must interact with the interface. Simply tapping the app works just fine, but is required.

// add UI interruption handlers

app.buttons["Request Location"].tap()
app.tap() // need to interact with the app for the handler to fire
Academician answered 23/11, 2015 at 16:35 Comment(3)
I tried doing what you said, without success. To solve my problem, I had to delay one of the two alerts.Kisung
Great! If this is working for you please accept the answer so others know that it should work for them.Academician
How did you add a delay?Aurum
R
0
class BaseTest: XCTestCase {
    let pushSent = NSNotification.Name.init("alert.pushSent")
    var notificationMonitor: NSObjectProtocol?

    override func setUp() {
        listenNotifications()
        let app = XCUIApplication()

        notificationMonitor = addUIInterruptionMonitor(withDescription: "Push Notifications") { [unowned self] (alert) -> Bool in
            let btnAllow = app.buttons["Allow"]
            //1:
            if btnAllow.exists {
                btnAllow.tap()
                NotificationCenter.default.post(name: self.pushSent, object: nil)
                return true
            }
            //2:
            //takeScreenshot
            XCTFail("Unexpected System Alert")
            return false
        }
        //3:
        //add code for "Request Location" monitor

        app.launchEnvironment = ["UITEST_DISABLE_ANIMATIONS" : "YES"]
        //4:
        app.launch()

    }

    func listenNotifications() {
        NotificationCenter.default.addObserver(forName: pushSent, object: nil, queue: nil) { (notification) in
            if let locationDialogHandeler = self.notificationMonitor {
                //5:
                self.removeUIInterruptionMonitor(locationDialogHandeler)
            }
        }
    }
}

1: Check if you're in the correct alert, tap the button and find a way to remove the monitor (I'm using NotificationCenter)

2: If you enter a monitor and can not find the right button, it means it's an unexpected flow. Fail the test (but take a screenshot first).

3: Add other monitors

4: I am adding monitor even before launching the app. If you add a monitor after the alert appears, it will not be triggered.

5: Remove the monitor, that way when a new alert appears, the next monitor in the stack will be called.

P.S: You should add monitors in reverse order, therefore, add "Request Location" after "Push Notifications"

Rocca answered 16/11, 2017 at 16:53 Comment(0)
S
-1

To dismiss system alert views (ie: Push Notification) you can define custom flags for the testing environment.

Then, you just have to change the application's code to avoid specific initialisations (ie: Push Notification) :

#if !TESTING
let settings: UIUserNotificationSettings = UIUserNotificationSettings(forTypes: [.Alert, .Badge, .Sound], categories: nil)
application.registerUserNotificationSettings(settings)
#endif

I use this trick in order to be able to take screenshots with snapshot.

Skivvy answered 26/11, 2015 at 13:29 Comment(2)
This doesn't address the question, only disables one of the capabilities in the app.Simone
The question was : "How can I dismiss those two successive alerts views?"Skivvy

© 2022 - 2024 — McMap. All rights reserved.