ReviewOS

also looking at this

craft-native/craft

test(ios): tap a real pushed notification that launches the app, and judge what the page gets

#257
Merged glennmichael123 wants to merge test/ios-e2e-notification-tap into main
5 files +308 -10
scripts/mobile-e2e/ios-uitests/NotificationTapColdStartTests.swiftadded+67-0
Changes to scripts/mobile-e2e/ios-uitests/NotificationTapColdStartTests.swift
@@ -0,0 +1,67 @@
1import XCTest
2
3/// Taps a pushed notification that launches the probe app, and reports what
4/// its page was handed (#255).
5///
6/// A person's sequence, end to end: the app asks for permission and is
7/// allowed, the app is killed, a real push arrives through `simctl push`, and
8/// the banner is tapped. `simctl` runs on the host, so the test prints
9/// `CRAFT-E2E-PUSH-READY` once the app is gone and the harness sends the push
10/// when it reads that line.
11///
12/// As with the deep-link test, no assertions about the answer live here. The
13/// page's report is printed and `scripts/mobile-e2e/protocol.ts` judges it.
14final class NotificationTapColdStartTests: XCTestCase {
15 private let environment = ProcessInfo.processInfo.environment
16 private var bundleId: String { environment["PROBE_BUNDLE_ID"] ?? "" }
17 private var permissionLink: String { environment["PROBE_NOTIFY_LINK"] ?? "" }
18 private var pushBody: String { environment["PROBE_PUSH_BODY"] ?? "" }
19
20 func testTapOnAPushLaunchesTheApp() throws {
21 XCTAssertFalse(bundleId.isEmpty, "PROBE_BUNDLE_ID is not set")
22 XCTAssertFalse(permissionLink.isEmpty, "PROBE_NOTIFY_LINK is not set")
23 XCTAssertFalse(pushBody.isEmpty, "PROBE_PUSH_BODY is not set")
24
25 let app = XCUIApplication(bundleIdentifier: bundleId)
26 let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
27
28 // Permission, answered the way a person answers it. Without it iOS
29 // accepts the push and shows nothing, so there is no banner to tap.
30 app.terminate()
31 XCUIDevice.shared.system.open(URL(string: permissionLink)!)
32 let open = springboard.buttons["Open"]
33 if open.waitForExistence(timeout: 5) { open.tap() }
34 XCTAssertTrue(app.wait(for: .runningForeground, timeout: 60), "the permission link did not start the app")
35
36 let allow = springboard.alerts.buttons["Allow"]
37 XCTAssertTrue(allow.waitForExistence(timeout: 60), "the page never asked for notification permission")
38 allow.tap()
39 let permission = app.staticTexts
40 .containing(NSPredicate(format: "label BEGINSWITH 'CRAFT-E2E-NOTIFY-PERMISSION '"))
41 .firstMatch
42 XCTAssertTrue(permission.waitForExistence(timeout: 30), "the page never said how the permission request ended")
43 XCTAssertEqual(permission.label, "CRAFT-E2E-NOTIFY-PERMISSION granted")
44
45 // Killed, so the tap below is what starts it. A running app would be
46 // handed the tap by a Coordinator that already exists, which is the
47 // case that always worked.
48 app.terminate()
49 XCTAssertTrue(app.wait(for: .notRunning, timeout: 15), "the app did not stop, so this would not be a cold start")
50 print("CRAFT-E2E-PUSH-READY")
51
52 // The banner, found by this run's body so a notification left over
53 // from an earlier run cannot be the one tapped.
54 let banner = springboard.descendants(matching: .any)
55 .matching(NSPredicate(format: "label CONTAINS %@", pushBody))
56 .firstMatch
57 XCTAssertTrue(banner.waitForExistence(timeout: 120), "no banner for the push ever appeared")
58 banner.tap()
59 XCTAssertTrue(app.wait(for: .runningForeground, timeout: 60), "tapping the banner did not start the app")
60
61 let report = app.staticTexts
62 .containing(NSPredicate(format: "label BEGINSWITH 'CRAFT-E2E-NOTIFICATION '"))
63 .firstMatch
64 XCTAssertTrue(report.waitForExistence(timeout: 120), "the page never reported what it was handed")
65 print("CRAFT-E2E-NOTIFICATION-RESULT \(report.label)")
66 }
67}