ReviewOS

also looking at this

craft-native/craft

fix(ios): schedule a notification with the data the page gave it

#262
Merged glennmichael123 wants to merge fix/ios-notification-data into main
8 files +242 -22
scripts/mobile-e2e/protocol.test.tsmodified+43-1
Changes to scripts/mobile-e2e/protocol.test.ts
@@ -1,7 +1,7 @@
11import { describe, expect, it } from 'bun:test'
22import { readFileSync } from 'node:fs'
33import { join } from 'node:path'
4import { ANDROID_DECLINE_PHRASES, androidDeclines, awaitedNeeds, deepLinkProblems, deepLinkReports, deepLinkResults, DISMISS_SHARE_MENU, elfSectionNames, evaluateRun, hasTerminated, NOTIFICATION_ROUTE, notificationBody, notificationPayload, notificationReceiptProblems, notificationReceiptReport, notificationTapProblems, notificationTapReport, parseDriverOutput, REQUIRED_CASES, requiredCaseProblems, runtimePermissionGranted, shareMenuInFront, strippedLibraryProblems, ZIG_REFUSED_ACTIONS, ZIG_SERVED_ACTIONS, ZIG_TESTED_ACTIONS, zigDispatchedActions, zigHandBacks, zigRefusals } from './protocol'
4import { ANDROID_DECLINE_PHRASES, androidDeclines, awaitedNeeds, deepLinkProblems, deepLinkReports, deepLinkResults, DISMISS_SHARE_MENU, elfSectionNames, evaluateRun, hasTerminated, localNotificationProblems, NOTIFICATION_ROUTE, notificationBody, notificationPayload, notificationReceiptProblems, notificationReceiptReport, notificationTapProblems, notificationTapReport, parseDriverOutput, REQUIRED_CASES, requiredCaseProblems, runtimePermissionGranted, shareMenuInFront, strippedLibraryProblems, ZIG_REFUSED_ACTIONS, ZIG_SERVED_ACTIONS, ZIG_TESTED_ACTIONS, zigDispatchedActions, zigHandBacks, zigRefusals } from './protocol'
55
66const ESC = String.fromCharCode(27)
77
@@ -435,6 +435,48 @@ describe('notification tap cold start', () => {
435435 })
436436})
437437
438describe('tap on a notification the page scheduled with data', () => {
439 const RUN = 'craft-e2e-ios-runtime-1'
440 const tap = { craftE2E: RUN, stage: 'local', nested: { kept: true } }
441 const pushed = { craftE2E: RUN, stage: 'cold', route: NOTIFICATION_ROUTE }
442 const output = (report: object) => [
443 `CRAFT-E2E-NOTIFICATION-RESULT CRAFT-E2E-NOTIFICATION ${JSON.stringify({ dispatched: 1, taps: [pushed], onTap: 'function', unsubscribe: 'function' })}`,
444 `CRAFT-E2E-LOCAL-RESULT CRAFT-E2E-NOTIFICATION ${JSON.stringify(report)}`,
445 ].join('\n')
446 const judge = (report: object) => localNotificationProblems(notificationTapReport(output(report), 'CRAFT-E2E-LOCAL-RESULT'), RUN)
447
448 it('passes a tap that hands back the data it was scheduled with, nested values included', () => {
449 expect(judge({ dispatched: 1, taps: [tap], onTap: 'function', unsubscribe: 'function' })).toEqual([])
450 })
451
452 // The bug: neither scheduler set userInfo.
453 it('fails a tap that hands the page {}', () => {
454 expect(judge({ dispatched: 1, taps: [{}], onTap: 'function', unsubscribe: 'function' }))
455 .toEqual(['a tap on a scheduled notification handed the page {}: the data it was scheduled with never reached userInfo'])
456 })
457
458 it('fails lost nesting, another run\'s data, a double delivery, and a tap never dispatched', () => {
459 expect(judge({ dispatched: 1, taps: [{ ...tap, nested: '[object Object]' }], onTap: 'function', unsubscribe: 'function' })[0]).toContain('nested data did not survive')
460 expect(judge({ dispatched: 1, taps: [{ ...tap, craftE2E: 'an-earlier-run' }], onTap: 'function', unsubscribe: 'function' })[0]).toContain('expected the data it was scheduled with once')
461 expect(judge({ dispatched: 1, taps: [tap, tap], onTap: 'function', unsubscribe: 'function' })[0]).toContain('expected the data it was scheduled with once')
462 expect(judge({ dispatched: 0, taps: [], onTap: 'function', unsubscribe: 'function' })).toEqual(['the app never dispatched the tap on the scheduled notification'])
463 })
464
465 it('reads each tap report by its own label', () => {
466 // The push's report is not the scheduled notification's, and a run that
467 // never got to the local stage has no local report at all.
468 expect(notificationTapReport(output({ dispatched: 1, taps: [tap] }))?.taps).toEqual([pushed])
469 expect(notificationTapReport(output({ dispatched: 1, taps: [tap] }), 'CRAFT-E2E-LOCAL-RESULT')?.taps).toEqual([tap])
470 expect(localNotificationProblems(notificationTapReport(output({}).split('\n')[0]!, 'CRAFT-E2E-LOCAL-RESULT'), RUN))
471 .toEqual(['the tap on the scheduled notification never reported; see xcodebuild-notification.log'])
472 })
473
474 it('finds the local banner by text the push banners do not share', () => {
475 expect(notificationBody(RUN, 'local')).toBe(`${RUN} local`)
476 expect(notificationBody(RUN, 'cold')).not.toContain(notificationBody(RUN, 'local'))
477 })
478})
479
438480describe('notification that arrives while the app is open', () => {
439481 const RUN = 'craft-e2e-ios-shim-1'
440482 const arrival = { aps: { alert: { title: 'Craft E2E', body: `${RUN} foreground` } }, craftE2E: RUN, stage: 'foreground', route: NOTIFICATION_ROUTE }