also looking at this
test(ios): tap a real pushed notification that launches the app, and judge what the page gets
#257
5 files
+308
-10
| @@ -0,0 +1,67 @@ | ||
| 1 | import 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. | |
| 14 | final 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 | } | |