0

I've spent several hours trying every possible solution I've seen without success, I hope I have more luck asking directly.

I'm using XCode 13.2.1 and I'm testing with an iPhone X (iOS 15.3.1).

I want to make a XCTest that send a push notification to my (killed) app, then the test opens the notification, make click on it, and the app opens showing a specific view.

So far, I managed to send the push notification, it's shown in the device just a second after the test send it, and then it dissapears. If I manually open the notification center, then the notification is there, ok.

But I haven't been able to click on the notification. This is what I tried:

Test 1: Notification dissapears and it's not clicked.

func testWhenPushNotificationOpenThenCorrectPageIsShown() {

    sendPushNotification()
    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    springboard.otherElements["MyApp, now, My notification text"].tap()
}

Test 2: Notification dissapears and it's not clicked.

func testWhenPushNotificationOpenThenCorrectPageIsShown() {

    sendPushNotification()
    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    springboard.otherElements["Notification"].descendants(matching: .any)["NotificationShortLookView"].tap()
}

Test 3: Notification dissapears and it's not clicked.

func testWhenPushNotificationOpenThenCorrectPageIsShown() {

    sendPushNotification()
    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    springboard.otherElements["Notification"].firstMatch.tap()
}

I tested these 3 cases adding springboard.activate() too.

I thought on opening the notification center making sweep from the status bar doing this:

Test 4: Notification dissapears, the notification center is shown, with the push notification in it, but I don't know how to make click on it (taking into account that when I manually click on this notification it shows an "Open" button that I must click to open the app).

func testWhenPushNotificationOpenThenCorrectPageIsShown() {

    sendPushNotification()
    let app = XCUIApplication()
    app.launch()
    let coord1 = app.coordinate(withNormalizedOffset: CGVector(dx: 0.1, dy: 0.01))
    let coord2 = app.coordinate(withNormalizedOffset: CGVector(dx: 0.1, dy: 0.8))
    coord1.press(forDuration: 0.1, thenDragTo: coord2)
}

Finally I tried to change my app notification settings to make them persistent. Now when the device received it, it doesn't dissappears but with every test detailed above I have no success, the notification is not clicked.

With the notification persistent I logged the springboard content and this is what I get regarding to the push notification:

springboard.debugDescription

Attributes: Application, 0x127f137a0, pid: 62, label: ' '
Element subtree:
 →Application, 0x127f137a0, pid: 62, label: ' '
    ...
    Window (Main), 0x129a20120, {{0.0, 0.0}, {375.0, 812.0}}
      Other, 0x129a20230, {{0.0, 0.0}, {375.0, 812.0}}
        Other, 0x129a0ff60, {{0.0, 0.0}, {375.0, 812.0}}
          BannerNotification, 0x129a10070, {{8.0, 40.0}, {359.0, 75.3}}
            Other, 0x129a12620, {{8.0, 40.0}, {359.0, 75.3}}, label: 'Notification'
              Other, 0x129a12730, {{8.0, 40.0}, {359.0, 75.3}}
                BannerNotification, 0x129a071f0, {{8.0, 40.0}, {359.0, 75.3}}, identifier: 'NotificationShortLookView', label: 'MyApp, now, My notification text'

I don't like this last option because I have to manually set the persistent mode for my notifications (I guess it's not possible to do it programmatically), but if it's the best option I would choose it.

What could I try?

EDIT: I've tried this:

let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let notif = springboard.otherElements["Notification"].descendants(matching: .any)["NotificationShortLookView"]
print(notif.debugDescription)
notif.tap()

The log of notif.debugDescription shows this:

Attributes: BannerNotification, 0x1037413e0, {{8.0, 40.0}, {359.0, 75.3}}, identifier: 'NotificationShortLookView', label: 'MyApp, now, My notification text'
Element subtree:
 →BannerNotification, 0x1037413e0, {{8.0, 40.0}, {359.0, 75.3}}, identifier: 'NotificationShortLookView', label: 'MyApp, now, My notification text'
Path to element:
 →Application, 0x10372c7e0, pid: 62, label: ' '
  ↳Window (Main), 0x103740d80, {{0.0, 0.0}, {375.0, 812.0}}
   ↳Other, 0x103740e90, {{0.0, 0.0}, {375.0, 812.0}}
    ↳Other, 0x103740fa0, {{0.0, 0.0}, {375.0, 812.0}}
     ↳BannerNotification, 0x1037410b0, {{8.0, 40.0}, {359.0, 75.3}}
      ↳Other, 0x1037411c0, {{8.0, 40.0}, {359.0, 75.3}}, label: 'Notification'
       ↳Other, 0x1037412d0, {{8.0, 40.0}, {359.0, 75.3}}
        ↳BannerNotification, 0x1037413e0, {{8.0, 40.0}, {359.0, 75.3}}, identifier: 'NotificationShortLookView', label: 'MyApp, now, My notification text'

There, it appears the notifation, but again the notif.tap() gives me a crash with notif cannot be nil error.

I think I'm close to the solution but I'm missing something.

Wonton
  • 1,033
  • 16
  • 33

3 Answers3

0

You were almost there with your 3rd attempt. This is what consistently works for me in my project on iOS 14.5 and 15.5:

let notification = springboard.otherElements["Notification"]
notification.tap()

without using firstMatch.

drunkencheetah
  • 354
  • 1
  • 8
0

Try this (it works in my project on Xcode 14.2, iPhone 16.3):

let notification: XCUIElement
if #available(iOS 14.0, *) {
    notification = springBoard.otherElements.descendants(matching: .any)["NotificationShortLookView"]
} else {
    notification = springBoard.otherElements["NotificationShortLookView"]
}
Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Chaeun Gong
  • 13
  • 1
  • 4
0

Works for me.

Init showNotification object:

var shownNotification: XCUIElement!

override func setUpWithError() throws {
    ...
    shownNotification = springBoard.otherElements["Notification"].descendants(matching: .any)["NotificationShortLookView"]
    ...
}

Wait notification function :

func waitForElementToAppear(object: Any) {
    let exists = NSPredicate(format: "exists == true")
    expectation(for: exists, evaluatedWith: object, handler: nil)
    waitForExpectations(timeout: 30, handler: nil)
}

Test function :

func waitNotificationAndTap() {
    XCUIDevice.shared.press(XCUIDevice.Button.home)
    sleep(2)
   
    if let notification = shownNotification {
        waitForElementToAppear(object: notification)
        notification.tap()
    }
}
Oliv
  • 1
  • 2