docs: update notification test guides and remove smoke evidence templates
This commit is contained in:
parent
4373528151
commit
e49fb45dcb
11 changed files with 152 additions and 1435 deletions
|
|
@ -74,35 +74,28 @@ Use the plugin-owned
|
|||
[`Manual Smoke Checklist`](../../packages/messaging_flutter/README.md#manual-smoke-checklist)
|
||||
for FCM delivery, ACK, inline reply, dismiss, and routing evidence.
|
||||
|
||||
For iOS preflight and smoke preparation, keep real Firebase and Apple files out
|
||||
of git. Copy `ios/Runner/GoogleService-Info.example.plist` to
|
||||
`ios/Runner/GoogleService-Info.plist` locally, replace placeholder values, and
|
||||
make sure the real plist is added to the Runner target resources in Xcode.
|
||||
Use `ios/Runner/Runner.example.entitlements` as the minimal Push Notifications
|
||||
entitlement reference, then configure the real Runner target through Xcode.
|
||||
Local routing fixtures live under `ios/Fixtures/*.apns`; replace
|
||||
`REPLACE_WITH_IOS_BUNDLE_ID` before simulator injection.
|
||||
See [`../../docs/ios-notification-test-guide.md`](../../docs/ios-notification-test-guide.md)
|
||||
and [`../../docs/ios-notification-smoke-evidence-template.md`](../../docs/ios-notification-smoke-evidence-template.md).
|
||||
For iOS notification preparation, keep real Firebase and Apple files out of
|
||||
git. The user must provide the Apple Team/signing path, Bundle ID, APNs
|
||||
credential choice, Firebase iOS app, local `GoogleService-Info.plist`, physical
|
||||
device path, and smoke send path before an agent can complete a real APNs/FCM
|
||||
smoke. Use `ios/Runner/GoogleService-Info.example.plist` and
|
||||
`ios/Runner/Runner.example.entitlements` only as placeholder references. See
|
||||
[`../../docs/ios-notification-test-guide.md`](../../docs/ios-notification-test-guide.md).
|
||||
|
||||
For macOS preflight and smoke preparation, keep real signing, provisioning,
|
||||
For macOS notification preparation, keep real signing, provisioning,
|
||||
notarization, Firebase, APNs, device token, and private host values out of git.
|
||||
The macOS host scaffold keeps placeholder entitlements only; configure real
|
||||
signing and notification capability locally when a smoke run requires it. Local
|
||||
routing fixtures live under `macos/Fixtures/*.json`. See
|
||||
[`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md)
|
||||
and
|
||||
[`../../docs/macos-notification-smoke-evidence-template.md`](../../docs/macos-notification-smoke-evidence-template.md).
|
||||
The user must provide the macOS runner, signing mode, Bundle ID if needed,
|
||||
APNs/Firebase scope, UI prompt responsibility, and evidence destination before
|
||||
an agent can complete visible notification smoke. See
|
||||
[`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md).
|
||||
|
||||
For Windows preflight and smoke preparation, keep real Microsoft account,
|
||||
package identity, signing, push credentials, device token, and private host
|
||||
values out of git. This repository does not currently include a Windows host
|
||||
scaffold; add one only through a focused implementation plan that also registers
|
||||
the plugin platform. Local routing fixtures live under
|
||||
`windows/Fixtures/*.json`. See
|
||||
[`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md)
|
||||
and
|
||||
[`../../docs/windows-notification-smoke-evidence-template.md`](../../docs/windows-notification-smoke-evidence-template.md).
|
||||
For Windows notification preparation, keep real Microsoft account, package
|
||||
identity, signing, push credentials, device token, and private host values out
|
||||
of git. The user must provide the Windows runner, packaging mode, notification
|
||||
API path, package identity/signing details if needed, UI prompt responsibility,
|
||||
and evidence destination before an agent can complete visible notification
|
||||
smoke. See
|
||||
[`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md).
|
||||
|
||||
## Verification
|
||||
|
||||
|
|
|
|||
|
|
@ -14,12 +14,11 @@ Module-specific instructions stay with the module that owns them.
|
|||
- Mattermost mobile app upstream snapshot baseline: `../apps/mattermost/UPSTREAM.md`
|
||||
- Mattermost push-proxy upstream snapshot baseline: `../services/push-proxy/UPSTREAM.md`
|
||||
- Runtime Image Validation details: `runtime-image-validation.md`
|
||||
- iOS notification smoke prerequisites and runbook: `ios-notification-test-guide.md`
|
||||
- iOS notification smoke evidence template: `ios-notification-smoke-evidence-template.md`
|
||||
- macOS notification smoke prerequisites and runbook: `macos-notification-test-guide.md`
|
||||
- macOS notification smoke evidence template: `macos-notification-smoke-evidence-template.md`
|
||||
- Windows notification smoke prerequisites and runbook: `windows-notification-test-guide.md`
|
||||
- Windows notification smoke evidence template: `windows-notification-smoke-evidence-template.md`
|
||||
- Notification access preparation:
|
||||
`ios-notification-test-guide.md`,
|
||||
`macos-notification-test-guide.md`,
|
||||
`windows-notification-test-guide.md`,
|
||||
`linux-notification-test-guide.md`
|
||||
|
||||
## Upstream Following Policy
|
||||
|
||||
|
|
|
|||
|
|
@ -1,96 +0,0 @@
|
|||
# iOS Notification Smoke Evidence Template
|
||||
|
||||
Use this template when collecting iOS notification smoke evidence. Replace every
|
||||
private value with a placeholder before storing output in tracked files.
|
||||
|
||||
## Run Metadata
|
||||
|
||||
- Date:
|
||||
- Operator:
|
||||
- Device model:
|
||||
- iOS version:
|
||||
- Xcode version:
|
||||
- Flutter version:
|
||||
- Build mode: Debug | Profile | Release
|
||||
- Bundle ID: `<redacted-or-placeholder>`
|
||||
- Firebase project/app alias: `<redacted-or-placeholder>`
|
||||
- APNs auth path: key | certificate | Firebase-managed | direct APNs console
|
||||
|
||||
## Preflight Inputs
|
||||
|
||||
- Local `GoogleService-Info.plist` present in `apps/flutter-test/ios/Runner/`: yes | no
|
||||
- Example plist matched: yes | no
|
||||
- Entitlements include `aps-environment`: yes | no
|
||||
- Push Notifications capability enabled: yes | no
|
||||
- Background Modes > Remote notifications enabled: yes | no | not tested
|
||||
- iOS token contract used: `apple_rn-v2:<redacted-apns-token>`
|
||||
- FCM smoke token used only for Firebase direct send: yes | no | not tested
|
||||
|
||||
## Permission And Token Evidence
|
||||
|
||||
Paste sanitized logs only.
|
||||
|
||||
```text
|
||||
permission_status=<authorized|denied|provisional|not_determined>
|
||||
apns_registration=<success|failure> reason=<redacted-or-placeholder>
|
||||
apns_token_present=<yes|no>
|
||||
fcm_token_present=<yes|no|not_used>
|
||||
formatted_server_token_prefix=apple_rn-v2
|
||||
```
|
||||
|
||||
## Payload Evidence
|
||||
|
||||
Payload file or sanitized JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "message",
|
||||
"server_url": "https://example.invalid",
|
||||
"server_id": "server-smoke",
|
||||
"channel_id": "channel-smoke",
|
||||
"root_id": "",
|
||||
"is_crt_enabled": "false",
|
||||
"ack_id": "ack-smoke",
|
||||
"post_id": "post-smoke"
|
||||
}
|
||||
```
|
||||
|
||||
Send path:
|
||||
|
||||
- Firebase direct FCM:
|
||||
- Apple Push Notification Console:
|
||||
- nexo push-proxy/server:
|
||||
- Local simulator fixture:
|
||||
|
||||
Response ID or log reference:
|
||||
|
||||
```text
|
||||
<redacted-or-placeholder>
|
||||
```
|
||||
|
||||
## Foreground / Background / Open Routing
|
||||
|
||||
| Scenario | Expected | Actual | Evidence path |
|
||||
|---|---|---|---|
|
||||
| Foreground receipt | Dart/native event logged | | |
|
||||
| Background display | System notification shown | | |
|
||||
| Channel tap | `onNavigateToChannel` with server/channel | | |
|
||||
| Thread tap | `onNavigateToThread` with server/root | | |
|
||||
| Terminated tap | Launch payload forwarded | | |
|
||||
|
||||
## ACK, Dismiss, Inline Reply Boundary
|
||||
|
||||
| Feature | Expected for this run | Actual | Follow-up |
|
||||
|---|---|---|---|
|
||||
| ACK | implemented | not tested | pending gap |
|
||||
| Dismiss | implemented | not tested | pending gap |
|
||||
| Inline reply | implemented | not tested | pending gap |
|
||||
|
||||
## Sanitization Checklist
|
||||
|
||||
- [ ] Full APNs device token removed.
|
||||
- [ ] Full FCM registration token removed.
|
||||
- [ ] Apple Team ID, Key ID, certificates, provisioning profile details removed.
|
||||
- [ ] Firebase API key, app ID, sender ID, service account details removed.
|
||||
- [ ] Private server URL, auth token, signing key, username, channel names removed.
|
||||
- [ ] Screenshots or recordings contain no private message/user data.
|
||||
|
|
@ -1,564 +1,44 @@
|
|||
# iOS Notification Test Guide
|
||||
# iOS Notification Credential Preparation
|
||||
|
||||
Last verified: 2026-06-01
|
||||
Last verified: 2026-06-02
|
||||
|
||||
This guide lists what is needed to run iOS notification smoke tests for
|
||||
`packages/messaging_flutter` through `apps/flutter-test`. It separates external
|
||||
Apple/Firebase setup, local host-app setup, current implementation gaps, and
|
||||
repeatable evidence to collect.
|
||||
Read this when you need to provide Apple/Firebase access so an agent can finish
|
||||
iOS notification implementation and smoke verification. You do not need to run
|
||||
the test from this document.
|
||||
|
||||
Do not commit real Apple, APNs, Firebase, service-account, device-token, auth
|
||||
token, private endpoint, Team ID, or provisioning values into tracked docs.
|
||||
Use placeholders in examples and keep credential files in the approved local or
|
||||
secret-store location.
|
||||
## Provide
|
||||
|
||||
## Current Repository State
|
||||
|
||||
- The iOS plugin now registers the minimum debug/action bridge.
|
||||
`packages/messaging_flutter/ios/Classes/NexoMessagingPlugin.swift` registers
|
||||
the legacy `nexo_messaging` method channel (for `getPlatformVersion`
|
||||
compatibility), the
|
||||
`com.tokilabs.nexo.messaging/notification_actions` method channel, and the
|
||||
`com.tokilabs.nexo.messaging/notifications` event channel.
|
||||
- The iOS bridge implements `debugSendNativeEvent`, `saveDeviceToken`,
|
||||
`getDeviceToken`, `setAuthToken`, `clearAuthToken`, `setSigningKey`, and
|
||||
`getPlatformVersion`. `saveDeviceToken`/`getDeviceToken` persist only the
|
||||
formatted token string via `UserDefaults`; `setAuthToken`, `clearAuthToken`,
|
||||
and `setSigningKey` are validation-only no-ops that do not persist auth tokens
|
||||
or signing secrets on iOS yet.
|
||||
- This is a debug/local-routing bridge only. APNs registration, Firebase token
|
||||
refresh handoff, notification center delegate, ACK, dismiss, and inline reply
|
||||
are not implemented and remain future smoke/implementation work.
|
||||
- The Dart plugin expects native notification and action channels:
|
||||
`com.tokilabs.nexo.messaging/notifications` and
|
||||
`com.tokilabs.nexo.messaging/notification_actions`.
|
||||
- `apps/flutter-test/lib/main.dart` already calls `Firebase.initializeApp()`
|
||||
before starting the app.
|
||||
- `apps/flutter-test/ios/Runner/AppDelegate.swift` does not currently register
|
||||
for remote notifications or install a `UNUserNotificationCenter` delegate.
|
||||
- `apps/flutter-test/ios/Runner/Info.plist` has no notification-specific keys.
|
||||
- `GoogleService-Info.plist` is intentionally ignored by `.gitignore`.
|
||||
|
||||
The first meaningful iOS smoke target is therefore:
|
||||
|
||||
1. Build and install the signed test app on a physical iOS device.
|
||||
2. Initialize Firebase successfully with a local `GoogleService-Info.plist`.
|
||||
3. Request notification permission.
|
||||
4. Obtain an APNs token and an FCM registration token.
|
||||
5. Deliver one visible FCM notification through APNs.
|
||||
6. Map a notification open into the Dart `NotificationOpenedEvent` contract.
|
||||
|
||||
The minimum iOS debug/action bridge (channel registration plus the method
|
||||
contract above) now exists, so Level 0 contract tests and local injection can
|
||||
exercise the Dart routing path. APNs registration, FCM token handoff, ACK,
|
||||
dismiss, inline reply, and native notification persistence are still follow-up
|
||||
implementation work.
|
||||
|
||||
## Decisions Needed Before Testing
|
||||
|
||||
Record the answers in a private run note or task evidence, not in tracked docs.
|
||||
|
||||
| Decision | Why it matters |
|
||||
| Needed | What to prepare |
|
||||
|---|---|
|
||||
| Apple Developer Team | Determines signing identity, App ID ownership, and provisioning profile. |
|
||||
| Bundle ID | Must match Xcode, Apple App ID, Firebase iOS app, and APNs topic. |
|
||||
| Provisioning flow | Automatic signing is easiest for smoke; manual profiles are better for controlled evidence. |
|
||||
| APNs auth style | Prefer token key `.p8` for Firebase/FCM; certificate `.p12` is a fallback if the project requires it. |
|
||||
| Firebase project/app | Existing project vs new smoke-only project affects data isolation and credential access. |
|
||||
| Firebase config storage | This repo ignores `GoogleService-Info.plist`; decide who provides it and where local copies live. |
|
||||
| Physical device | FCM via APNs requires a real iOS device for meaningful delivery testing. |
|
||||
| Evidence path | Decide where screenshots, logs, payloads, and run notes are stored. |
|
||||
| CI boundary | Keep real APNs/FCM delivery manual unless a signed device farm is explicitly available. |
|
||||
|
||||
## Apple Setup Checklist
|
||||
|
||||
### Account And App ID
|
||||
|
||||
- Active Apple Developer Program membership.
|
||||
- Access to the target Apple Developer Team.
|
||||
- Bundle ID reserved for the iOS smoke app.
|
||||
- App ID with Push Notifications enabled.
|
||||
- Registered physical device if manual provisioning is used.
|
||||
|
||||
The Bundle ID must be consistent everywhere:
|
||||
|
||||
- Xcode `PRODUCT_BUNDLE_IDENTIFIER`.
|
||||
- Apple Developer App ID.
|
||||
- Firebase iOS app bundle ID.
|
||||
- APNs `apns-topic` when sending directly to APNs.
|
||||
|
||||
### Xcode Capabilities
|
||||
|
||||
Open `apps/flutter-test/ios/Runner.xcworkspace` in Xcode and configure the
|
||||
`Runner` target.
|
||||
|
||||
- `Signing & Capabilities > Team`: selected smoke Team.
|
||||
- `Signing & Capabilities > Bundle Identifier`: selected smoke Bundle ID.
|
||||
- `Signing & Capabilities > Push Notifications`: enabled.
|
||||
- `Signing & Capabilities > Background Modes`: enable `Remote notifications`
|
||||
if the smoke includes background/silent delivery or background FCM callbacks.
|
||||
|
||||
Expected local file outcome:
|
||||
|
||||
- A Runner entitlements file exists or Xcode build settings point to one.
|
||||
- The signed app includes `aps-environment` as `development` for debug smoke.
|
||||
- Provisioning profile includes the App ID and push entitlement.
|
||||
|
||||
Useful verification commands after building:
|
||||
|
||||
```sh
|
||||
codesign -d --entitlements :- build/ios/iphoneos/Runner.app
|
||||
```
|
||||
|
||||
Look for:
|
||||
|
||||
```xml
|
||||
<key>aps-environment</key>
|
||||
<string>development</string>
|
||||
```
|
||||
|
||||
### APNs Auth Key Or Certificate
|
||||
|
||||
Preferred path for Firebase/FCM:
|
||||
|
||||
- Create or reuse an APNs Authentication Key in Apple Developer.
|
||||
- Download the `.p8` file once.
|
||||
- Record the Key ID privately.
|
||||
- Record the Team ID privately.
|
||||
- Upload the `.p8`, Key ID, and Team ID to Firebase Cloud Messaging settings
|
||||
for the matching iOS app.
|
||||
|
||||
Fallback path:
|
||||
|
||||
- Create an APNs provider certificate for the App ID.
|
||||
- Export it as `.p12` if Firebase or a provider server requires certificate
|
||||
auth.
|
||||
- Track expiry and renewal ownership.
|
||||
|
||||
Do not confuse these with iOS code-signing certificates. Code signing lets the
|
||||
app install and carry the push entitlement. APNs provider credentials let a
|
||||
server, Firebase, or Apple Push Notification Console send pushes.
|
||||
|
||||
## Firebase Setup Checklist
|
||||
|
||||
### Project And iOS App
|
||||
|
||||
- Firebase project exists.
|
||||
- iOS app is registered with the exact smoke Bundle ID.
|
||||
- `GoogleService-Info.plist` is downloaded for that iOS app.
|
||||
- The plist is copied locally to `apps/flutter-test/ios/Runner/GoogleService-Info.plist`.
|
||||
- The plist is added to the Xcode `Runner` target resources.
|
||||
- The plist remains untracked because `.gitignore` ignores
|
||||
`**/GoogleService-Info.plist`.
|
||||
|
||||
The test app already calls:
|
||||
|
||||
```dart
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
await Firebase.initializeApp();
|
||||
```
|
||||
|
||||
If Firebase initialization fails on iOS, check that the plist is present in the
|
||||
Runner target and that the bundle ID in the plist matches the signed app.
|
||||
|
||||
### Cloud Messaging Settings
|
||||
|
||||
In Firebase Console:
|
||||
|
||||
- Open Project Settings.
|
||||
- Open Cloud Messaging.
|
||||
- Find the matching iOS app configuration.
|
||||
- Upload APNs auth key `.p8`, or configure APNs certificates if that is the
|
||||
selected path.
|
||||
- Confirm Firebase Cloud Messaging API is enabled for HTTP v1 sends.
|
||||
|
||||
For server-side sends:
|
||||
|
||||
- Use Firebase Admin SDK, or
|
||||
- Use FCM HTTP v1 with a service account or Application Default Credentials.
|
||||
|
||||
Do not place service-account JSON in the repo.
|
||||
|
||||
## Native Bridge Work Needed For A Full Smoke
|
||||
|
||||
The minimum debug/action bridge is in place, but the iOS native side still
|
||||
cannot complete a full APNs/FCM delivery smoke. Already done:
|
||||
|
||||
- Register the expected EventChannel:
|
||||
`com.tokilabs.nexo.messaging/notifications`.
|
||||
- Register the expected MethodChannel:
|
||||
`com.tokilabs.nexo.messaging/notification_actions`.
|
||||
- Implement `saveDeviceToken`, `getDeviceToken`, `setAuthToken`,
|
||||
`clearAuthToken`, `setSigningKey`, and `debugSendNativeEvent` for iOS, with
|
||||
auth token/signing key kept as validation-only no-ops for now.
|
||||
|
||||
Remaining native bridge work:
|
||||
|
||||
- Request user notification authorization or rely consistently on
|
||||
FlutterFire's `FirebaseMessaging.requestPermission`.
|
||||
- Call `UIApplication.shared.registerForRemoteNotifications()`.
|
||||
- Handle `application(_:didRegisterForRemoteNotificationsWithDeviceToken:)`.
|
||||
- Handle `application(_:didFailToRegisterForRemoteNotificationsWithError:)`.
|
||||
- Decide whether Firebase Messaging method swizzling stays enabled.
|
||||
- If swizzling is disabled, set `Messaging.messaging().apnsToken` manually.
|
||||
- Subscribe to FCM token refresh and forward token changes to Dart.
|
||||
- Set `UNUserNotificationCenter.current().delegate`.
|
||||
- Forward foreground receipt, notification tap, and launch-from-notification
|
||||
payloads into Dart.
|
||||
- Map APNs/FCM userInfo fields to `NotificationOpenedEvent`.
|
||||
|
||||
## iOS Token Contract
|
||||
|
||||
The current Dart implementation stores Android tokens as
|
||||
`android_rn-v2:<raw-fcm-token>`. For iOS preflight, reserve the
|
||||
Mattermost-compatible APNs server-registration format:
|
||||
|
||||
```text
|
||||
apple_rn-v2:<raw-apns-token>
|
||||
```
|
||||
|
||||
Use this distinction during testing:
|
||||
|
||||
- APNs device token: server registration candidate for the existing
|
||||
Mattermost-compatible Apple push-proxy path.
|
||||
- FCM registration token: Firebase direct-send smoke token. Do not register it
|
||||
with the nexo server unless a future server/push-proxy path explicitly
|
||||
supports FCM-backed iOS delivery.
|
||||
- Direct APNs sends through Apple Push Notification Console also require the raw
|
||||
APNs token, not the FCM registration token.
|
||||
|
||||
Follow-up implementation should make token formatting platform-aware instead of
|
||||
using the current Android-oriented Dart constant.
|
||||
|
||||
## Payload Contract
|
||||
|
||||
For nexo routing, keep custom fields outside the APNs `aps` dictionary. FCM
|
||||
`data` values must be strings.
|
||||
|
||||
Required or high-value fields:
|
||||
|
||||
| Field | Purpose |
|
||||
|---|---|
|
||||
| `type` | `message`, `opened`, `clear`, `session`, or `token_refresh` equivalent in the Dart contract. |
|
||||
| `server_url` | Preferred direct routing target when available. |
|
||||
| `server_id` | Fallback key if the app resolves server IDs locally. |
|
||||
| `channel_id` | Channel routing. |
|
||||
| `root_id` | Thread routing when CRT is enabled. |
|
||||
| `is_crt_enabled` | Determines channel vs thread navigation behavior. |
|
||||
| `ack_id` | ACK contract and signed push validation. |
|
||||
| `post_id` | Notification identity and ACK enrichment. |
|
||||
|
||||
Visible FCM HTTP v1 smoke payload shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": {
|
||||
"token": "<FCM_REGISTRATION_TOKEN>",
|
||||
"notification": {
|
||||
"title": "nexo smoke",
|
||||
"body": "iOS notification smoke"
|
||||
},
|
||||
"data": {
|
||||
"type": "message",
|
||||
"server_url": "https://<SMOKE_SERVER>",
|
||||
"channel_id": "<CHANNEL_ID>",
|
||||
"root_id": "",
|
||||
"is_crt_enabled": "false",
|
||||
"ack_id": "<ACK_ID>",
|
||||
"post_id": "<POST_ID>"
|
||||
},
|
||||
"apns": {
|
||||
"headers": {
|
||||
"apns-push-type": "alert",
|
||||
"apns-priority": "10",
|
||||
"apns-topic": "<BUNDLE_ID>"
|
||||
},
|
||||
"payload": {
|
||||
"aps": {
|
||||
"sound": "default"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Silent/background smoke payload shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"message": {
|
||||
"token": "<FCM_REGISTRATION_TOKEN>",
|
||||
"data": {
|
||||
"type": "message",
|
||||
"server_url": "https://<SMOKE_SERVER>",
|
||||
"channel_id": "<CHANNEL_ID>"
|
||||
},
|
||||
"apns": {
|
||||
"headers": {
|
||||
"apns-push-type": "background",
|
||||
"apns-priority": "5",
|
||||
"apns-topic": "<BUNDLE_ID>"
|
||||
},
|
||||
"payload": {
|
||||
"aps": {
|
||||
"content-available": 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Use visible alert smoke first. Silent pushes are best-effort and easier to
|
||||
misread during early setup.
|
||||
|
||||
## Test Levels
|
||||
|
||||
### Level 0: Repo Contract Tests
|
||||
|
||||
No Apple/Firebase credentials required.
|
||||
|
||||
- Dart widget tests.
|
||||
- Dart event mapping tests using `handleNativeEvent`.
|
||||
- Integration tests using `debugSendNativeEvent` when native debug bridge exists.
|
||||
|
||||
This level validates the Dart routing contract only. It does not prove APNs or
|
||||
FCM delivery.
|
||||
|
||||
### Level 1: Simulator / Local Injection
|
||||
|
||||
No real FCM via APNs.
|
||||
|
||||
- Use simulator/local notification payload injection only to exercise
|
||||
userInfo parsing and open routing.
|
||||
- Do not mark FCM delivery, APNs token registration, or background delivery as
|
||||
passed from simulator evidence.
|
||||
|
||||
Example local `.apns` fixture shape:
|
||||
|
||||
```json
|
||||
{
|
||||
"Simulator Target Bundle": "<BUNDLE_ID>",
|
||||
"aps": {
|
||||
"alert": {
|
||||
"title": "nexo smoke",
|
||||
"body": "local routing fixture"
|
||||
},
|
||||
"sound": "default"
|
||||
},
|
||||
"type": "message",
|
||||
"server_url": "https://<SMOKE_SERVER>",
|
||||
"channel_id": "<CHANNEL_ID>",
|
||||
"root_id": "",
|
||||
"is_crt_enabled": "false"
|
||||
}
|
||||
```
|
||||
|
||||
Tracked fixture files live in `apps/flutter-test/ios/Fixtures/`:
|
||||
|
||||
- `open_channel.apns`: channel routing payload.
|
||||
- `open_thread.apns`: CRT thread routing payload.
|
||||
|
||||
Before simulator injection, replace `REPLACE_WITH_IOS_BUNDLE_ID` with the
|
||||
signed Runner bundle identifier. These fixtures prove local payload shape and
|
||||
open-routing parsing only; they do not prove APNs registration, FCM delivery, or
|
||||
server ACK behavior.
|
||||
|
||||
### Level 2: Physical Device FCM Smoke
|
||||
|
||||
Required for meaningful iOS delivery.
|
||||
|
||||
- Signed debug or release app installed on a real iPhone/iPad.
|
||||
- Notification permission requested and accepted.
|
||||
- APNs registration succeeds.
|
||||
- FCM registration token is displayed or logged.
|
||||
- A visible FCM message arrives.
|
||||
- Tapping the notification opens or resumes the app.
|
||||
- Dart receives a notification-open event with routing fields.
|
||||
|
||||
### Level 3: Nexo Server End-To-End Smoke
|
||||
|
||||
Required before claiming server contract compatibility.
|
||||
|
||||
- FCM token is registered with the nexo server using the agreed iOS token
|
||||
prefix.
|
||||
- Server can send a nexo-compatible push payload.
|
||||
- Signed payload validation behavior is known.
|
||||
- ACK request behavior is observed.
|
||||
- Open routing reaches the host app callback.
|
||||
|
||||
## Manual Smoke Runbook
|
||||
|
||||
### 1. Preflight
|
||||
|
||||
- Confirm Xcode version and iOS device OS version.
|
||||
- Confirm Bundle ID in Xcode, Apple Developer, and Firebase match.
|
||||
- Confirm `GoogleService-Info.plist` exists locally and is part of Runner
|
||||
target resources.
|
||||
- Confirm APNs auth key or certificate is configured in Firebase.
|
||||
- Confirm the app target has Push Notifications capability.
|
||||
- Confirm `aps-environment` is present in the built app entitlements.
|
||||
- Confirm no real credential values are staged in git.
|
||||
|
||||
### 2. Build And Install
|
||||
|
||||
From the Flutter test app:
|
||||
|
||||
```sh
|
||||
cd apps/flutter-test
|
||||
flutter pub get
|
||||
flutter run -d <IOS_DEVICE_ID>
|
||||
```
|
||||
|
||||
If Xcode signing configuration is not already set, open:
|
||||
|
||||
```sh
|
||||
open ios/Runner.xcworkspace
|
||||
```
|
||||
|
||||
Build from Xcode once to resolve signing and provisioning issues.
|
||||
|
||||
### 3. Permission And Token
|
||||
|
||||
Expected evidence:
|
||||
|
||||
- App launches without Firebase initialization error.
|
||||
- iOS notification permission prompt appears.
|
||||
- Permission state is logged.
|
||||
- APNs registration success is logged.
|
||||
- FCM token is logged or displayed in the app UI.
|
||||
- `Device token:` in the Flutter test app changes from `pending` to a formatted
|
||||
token once the Dart/native bridge supports iOS token handoff.
|
||||
|
||||
Important distinction:
|
||||
|
||||
- FCM sends to the FCM registration token returned by Firebase Messaging.
|
||||
- Apple Push Notification Console or direct APNs sends require the APNs device
|
||||
token from `didRegisterForRemoteNotificationsWithDeviceToken`.
|
||||
|
||||
### 4. Send A Visible FCM Message
|
||||
|
||||
Use Firebase Admin SDK or FCM HTTP v1. Keep payload, response ID, timestamp,
|
||||
device state, and app logs as evidence.
|
||||
|
||||
Expected result:
|
||||
|
||||
- Foreground: app receives callback or foreground presentation according to the
|
||||
native delegate policy.
|
||||
- Background: system displays a notification.
|
||||
- Terminated: tapping the notification starts the app and exposes the launch
|
||||
payload.
|
||||
|
||||
### 5. Verify Routing
|
||||
|
||||
For channel routing:
|
||||
|
||||
- Payload has `is_crt_enabled=false`.
|
||||
- Payload has `server_url` and `channel_id`.
|
||||
- App UI updates `Last opened`.
|
||||
- App UI updates `Last navigation` with `channel:<server>/<channel>`.
|
||||
|
||||
For thread routing:
|
||||
|
||||
- Payload has `is_crt_enabled=true`.
|
||||
- Payload has `server_url` and `root_id`.
|
||||
- App UI updates `Last opened`.
|
||||
- App UI updates `Last navigation` with `thread:<server>/<root>`.
|
||||
|
||||
### 6. Verify ACK, Dismiss, Inline Reply Boundaries
|
||||
|
||||
Current expected state:
|
||||
|
||||
- ACK is not passed unless an iOS ACK client/native bridge exists.
|
||||
- Dismiss is not passed unless notification category/delete handling exists.
|
||||
- Inline reply is not passed unless `UNTextInputNotificationAction` and reply
|
||||
forwarding exist.
|
||||
|
||||
If these are not implemented, record them as known iOS gaps instead of smoke
|
||||
failures.
|
||||
|
||||
## Evidence To Keep
|
||||
|
||||
Do keep:
|
||||
|
||||
- Date, operator, device model, iOS version, Xcode version.
|
||||
- Bundle ID shape, but not private Team ID if policy treats it as sensitive.
|
||||
- Build configuration: Debug/Profile/Release.
|
||||
- Entitlement proof with secrets redacted.
|
||||
- Firebase project/app alias, not raw private IDs if policy forbids them.
|
||||
- Permission status.
|
||||
- APNs registration success/failure log.
|
||||
- FCM token presence, redacted.
|
||||
- FCM message ID or API response ID.
|
||||
- Payload shape with private values replaced by placeholders.
|
||||
- Screenshot or screen recording of notification display and app routing UI.
|
||||
- App logs around receipt, tap, routing, ACK/dismiss/reply if implemented.
|
||||
|
||||
Use `docs/ios-notification-smoke-evidence-template.md` as the repeatable
|
||||
evidence record when real device smoke begins.
|
||||
|
||||
Do not keep:
|
||||
|
||||
- `.p8`, `.p12`, provisioning profile, private key, or service-account JSON.
|
||||
- Full APNs device token or FCM registration token.
|
||||
- Full auth token, signing key, private server URL, or user data.
|
||||
|
||||
## Troubleshooting Matrix
|
||||
|
||||
| Symptom | Likely cause | Check |
|
||||
|---|---|---|
|
||||
| Firebase initialization fails | Missing or wrong `GoogleService-Info.plist` | Confirm file is in Runner target and Bundle ID matches. |
|
||||
| Permission prompt never appears | Permission already decided, request not called, or initialization failed | Check iOS Settings and app logs. |
|
||||
| APNs registration fails | Missing entitlement or bad signing profile | Inspect built entitlements and provisioning profile. |
|
||||
| FCM token is null | APNs token not linked, Firebase app mismatch, network issue, or swizzling/delegate conflict | Check Firebase logs, APNs token callback, and Messaging setup. |
|
||||
| FCM send succeeds but no notification | Wrong token, wrong Firebase project, APNs key not uploaded, device offline, notification disabled | Confirm token source, Firebase app, APNs config, device notification settings. |
|
||||
| Direct APNs send fails | Using FCM token instead of APNs token, wrong topic, wrong environment | Use APNs device token and matching Bundle ID/environment. |
|
||||
| Foreground receives nothing | `UNUserNotificationCenter` delegate missing or not forwarding | Confirm delegate setup and callback implementation. |
|
||||
| Tap opens app but no routing | Launch/tap payload not forwarded to Dart | Check native open handler and EventChannel emission. |
|
||||
| Background/silent unreliable | iOS best-effort delivery, wrong headers, app force-quit, missing background mode | Start with visible alert smoke and test silent separately. |
|
||||
|
||||
## Completion Criteria For This Milestone
|
||||
|
||||
Minimum pass for `ios-notification-test` planning smoke:
|
||||
|
||||
- Apple/Firebase ownership and credential flow are decided.
|
||||
- Bundle ID and provisioning approach are documented privately.
|
||||
- Firebase iOS app and local plist path are confirmed.
|
||||
- Physical-device smoke path is confirmed.
|
||||
- Current native bridge gaps are listed.
|
||||
- A visible FCM/APNs smoke checklist exists with evidence fields.
|
||||
- CI/headless boundary is explicit.
|
||||
|
||||
Minimum pass for a future implementation smoke:
|
||||
|
||||
- iOS native bridge emits Dart notification events.
|
||||
- FCM token is obtained and handed to Dart with an iOS-compatible prefix.
|
||||
- Visible FCM notification arrives on a physical device.
|
||||
- Tapping the notification triggers `NotificationOpenedEvent`.
|
||||
- Channel and thread routing callbacks are exercised.
|
||||
- ACK/dismiss/inline reply are either implemented and verified or explicitly
|
||||
tracked as remaining gaps.
|
||||
|
||||
## Official References
|
||||
|
||||
- Apple: Registering your app with APNs
|
||||
https://developer.apple.com/documentation/usernotifications/registering-your-app-with-apns
|
||||
- Apple: Generating a remote notification
|
||||
https://developer.apple.com/documentation/usernotifications/generating-a-remote-notification
|
||||
- Apple: Sending notification requests to APNs
|
||||
https://developer.apple.com/documentation/usernotifications/sending-notification-requests-to-apns
|
||||
- Apple: Testing notifications using the Push Notification Console
|
||||
https://developer.apple.com/documentation/usernotifications/testing-notifications-using-the-push-notification-console
|
||||
- Firebase: Add Firebase to your Apple project
|
||||
https://firebase.google.com/docs/ios/setup
|
||||
- Firebase: Set up a Firebase Cloud Messaging client app on Apple platforms
|
||||
https://firebase.google.com/docs/cloud-messaging/get-started?platform=ios
|
||||
- Firebase: Receive messages in Apple platform apps
|
||||
https://firebase.google.com/docs/cloud-messaging/ios/receive-messages
|
||||
- Firebase: Build app server send requests
|
||||
https://firebase.google.com/docs/cloud-messaging/send-message
|
||||
- FlutterFire: FCM via APNs Integration
|
||||
https://firebase.flutter.dev/docs/messaging/apple-integration/
|
||||
- FlutterFire: Cloud Messaging usage
|
||||
https://firebase.flutter.dev/docs/messaging/usage
|
||||
| Apple Team | The Apple Developer Team that owns the smoke app. |
|
||||
| Bundle ID | A Bundle ID reserved for the iOS smoke app. |
|
||||
| Signing path | Either allow automatic signing or provide the provisioning profile path privately. |
|
||||
| Push capability | Enable Push Notifications for the App ID. |
|
||||
| APNs credential | Prefer APNs auth key `.p8` plus Key ID and Team ID; certificate is a fallback. |
|
||||
| Firebase app | Create/select the Firebase iOS app with the exact same Bundle ID. |
|
||||
| Firebase plist | Provide `GoogleService-Info.plist` privately for `apps/flutter-test/ios/Runner/`. |
|
||||
| Physical device | Provide an installable iPhone/iPad or define who operates it remotely. |
|
||||
| Send path | Choose Firebase Console, FCM HTTP v1, or an existing private sender. |
|
||||
| Evidence path | Choose where sanitized logs/screenshots/run notes should be stored. |
|
||||
|
||||
## Do Not Put In Tracked Files
|
||||
|
||||
- Apple Team ID, private Bundle ID, provisioning profile, `.p8`, `.p12`, Key ID,
|
||||
certificate, or Apple account values.
|
||||
- `GoogleService-Info.plist` or service-account JSON.
|
||||
- APNs device token, FCM token, auth token, private endpoint, channel name, user
|
||||
name, or server URL.
|
||||
|
||||
## Agent Can Then
|
||||
|
||||
- configure the local iOS host app with the selected signing and Bundle ID;
|
||||
- place the private Firebase plist in the ignored local path;
|
||||
- implement or verify APNs registration, token handoff, and open routing;
|
||||
- run or coordinate the smoke through the approved send path;
|
||||
- record sanitized evidence in the task or private run note.
|
||||
|
||||
## Current Repo Note
|
||||
|
||||
The iOS plugin has a minimum debug/action bridge, but real APNs/FCM smoke still
|
||||
needs the private Apple/Firebase/device setup above plus native APNs/token/open
|
||||
routing work.
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
# Linux Notification Smoke Evidence Template
|
||||
|
||||
Use this template when collecting Linux notification smoke evidence. Replace
|
||||
every private value with a placeholder before storing output in tracked files.
|
||||
|
||||
## Run Metadata
|
||||
|
||||
- Date:
|
||||
- Operator:
|
||||
- Hardware or VM:
|
||||
- Distribution and version:
|
||||
- Desktop environment:
|
||||
- Session type: X11 | Wayland | headless | other
|
||||
- Notification daemon:
|
||||
- Desktop portal available: yes | no | not tested
|
||||
- Flutter version:
|
||||
- Build mode: Debug | Profile | Release
|
||||
- Package/runtime dependency path: D-Bus | desktop portal | libnotify | debug injection | other
|
||||
|
||||
## Preflight Inputs
|
||||
|
||||
- Linux platform registered in `packages/messaging_flutter/pubspec.yaml`: yes | no
|
||||
- Linux plugin scaffold present: yes | no
|
||||
- Linux test host scaffold present: yes | no
|
||||
- Linux bridge registers `com.tokilabs.nexo.messaging/notifications`: yes | no
|
||||
- Linux bridge registers `com.tokilabs.nexo.messaging/notification_actions`: yes | no
|
||||
- Local fixture used: `open_channel.json` | `open_thread.json` | other
|
||||
- Real account, private host, token, and machine-identifying values kept out of tracked files: yes | no
|
||||
|
||||
## Environment Evidence
|
||||
|
||||
Paste sanitized logs only.
|
||||
|
||||
```text
|
||||
distribution=<redacted-or-placeholder>
|
||||
desktop_environment=<GNOME|KDE|XFCE|other|not_tested>
|
||||
session_type=<X11|Wayland|headless|not_tested>
|
||||
notification_daemon=<redacted-or-placeholder|not_detected|not_tested>
|
||||
portal_status=<available|missing|not_tested>
|
||||
```
|
||||
|
||||
## Display Evidence
|
||||
|
||||
Payload file or sanitized JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "message",
|
||||
"server_url": "https://example.invalid",
|
||||
"server_id": "server-smoke",
|
||||
"channel_id": "channel-smoke",
|
||||
"root_id": "",
|
||||
"is_crt_enabled": "false",
|
||||
"ack_id": "ack-smoke",
|
||||
"post_id": "post-smoke"
|
||||
}
|
||||
```
|
||||
|
||||
Display path:
|
||||
|
||||
- Local debug injection:
|
||||
- D-Bus notification daemon:
|
||||
- Desktop portal notification:
|
||||
- libnotify wrapper:
|
||||
- Not tested:
|
||||
|
||||
Observed result:
|
||||
|
||||
```text
|
||||
displayed=<yes|no|not_implemented>
|
||||
notification_title=<redacted-or-placeholder>
|
||||
notification_body=<redacted-or-placeholder>
|
||||
action_data_present=<yes|no|not_implemented>
|
||||
```
|
||||
|
||||
## Click / Open Routing
|
||||
|
||||
| Scenario | Expected | Actual | Evidence path |
|
||||
|---|---|---|---|
|
||||
| Channel click | `onNavigateToChannel` with server/channel | | |
|
||||
| Thread click | `onNavigateToThread` with server/root | | |
|
||||
| Foreground receipt | Raw `onNotification` event logged | | |
|
||||
| Background click | App resumes and emits opened event | | |
|
||||
| Cold start click | Launch payload forwarded | | |
|
||||
|
||||
## Lifecycle Evidence
|
||||
|
||||
```text
|
||||
app_state_before=<foreground|background|not_running|not_tested>
|
||||
app_state_after=<foreground|background|not_running|not_tested>
|
||||
event_type=<message|opened|not_implemented>
|
||||
userInteraction=<true|false|not_set>
|
||||
```
|
||||
|
||||
## ACK, Dismiss, Inline Reply Boundary
|
||||
|
||||
| Feature | Expected for this run | Actual | Follow-up |
|
||||
|---|---|---|---|
|
||||
| ACK | gap documented unless implemented by a later plan | | |
|
||||
| Dismiss | gap documented unless implemented by a later plan | | |
|
||||
| Inline reply | gap documented unless implemented by a later plan | | |
|
||||
|
||||
## Sanitization Checklist
|
||||
|
||||
- [ ] Private host, username, channel name, and machine-identifying values removed.
|
||||
- [ ] Full device token or remote push token removed.
|
||||
- [ ] Private server URL, auth token, signing key, username, channel names removed.
|
||||
- [ ] Screenshots or recordings contain no private message/user data.
|
||||
- [ ] Distribution, daemon, and desktop details are sanitized when they identify a private environment.
|
||||
|
|
@ -1,169 +1,38 @@
|
|||
# Linux Notification Test Guide
|
||||
# Linux Notification Environment Preparation
|
||||
|
||||
Last verified: 2026-06-02
|
||||
|
||||
This guide fixes the preflight contract for Linux notification work before a
|
||||
Linux test machine, distribution, desktop environment, notification daemon, or
|
||||
real OS notification UI evidence is available.
|
||||
Read this when you need to provide Linux runner and desktop notification
|
||||
environment choices so an agent can finish Linux notification implementation and
|
||||
smoke verification. You do not need to run the test from this document.
|
||||
|
||||
Do not commit private host values, device tokens, auth tokens, signing keys,
|
||||
runtime endpoints, user names, channel names, desktop session details that
|
||||
identify a private machine, or private package repository values into tracked
|
||||
docs. Use placeholders in examples and keep credential files in approved local
|
||||
or secret-store locations.
|
||||
## Provide
|
||||
|
||||
## Current Repository State
|
||||
| Needed | What to prepare |
|
||||
|---|---|
|
||||
| Linux runner | Local machine, VM, remote desktop, headless-only runner, or deferral. |
|
||||
| Distribution | Distribution and version, such as Ubuntu, Debian, or Fedora. |
|
||||
| Desktop session | GNOME/KDE/XFCE/other and X11/Wayland/headless. |
|
||||
| Notification path | D-Bus notifications, XDG Desktop Portal, libnotify, or debug injection only. |
|
||||
| Dependencies | Whether the agent may install required system packages. |
|
||||
| Operator path | Who handles UI prompts/clicks if the runner is remote. |
|
||||
| Evidence path | Choose where sanitized logs/screenshots/run notes should be stored. |
|
||||
|
||||
- `packages/messaging_flutter/pubspec.yaml` does not currently register a
|
||||
Linux plugin platform.
|
||||
- There is no `packages/messaging_flutter/linux/` plugin scaffold and no
|
||||
`apps/flutter-test/linux/` host scaffold in the current repository.
|
||||
- Dart already expects native notification and action channels:
|
||||
`com.tokilabs.nexo.messaging/notifications` and
|
||||
`com.tokilabs.nexo.messaging/notification_actions`.
|
||||
- The Linux bridge is therefore not implemented yet. Until it exists, local
|
||||
routing can only be tested through Dart unit tests or a future debug injection
|
||||
bridge.
|
||||
- Local routing fixtures live under `apps/flutter-test/linux/Fixtures/*.json`.
|
||||
## Do Not Put In Tracked Files
|
||||
|
||||
The first useful Linux smoke target is:
|
||||
- Private host names, user names, private endpoints, auth tokens, channel names,
|
||||
server URLs, machine-identifying session details, private package repository
|
||||
credentials, device tokens, or signing keys.
|
||||
|
||||
1. Add a Linux plugin platform registration and generated host scaffold in a
|
||||
controlled implementation plan.
|
||||
2. Register the expected EventChannel and MethodChannel in the Linux plugin.
|
||||
3. Implement a debug-only local injection path compatible with
|
||||
`debugSendNativeEvent`.
|
||||
4. Choose a notification API path and runtime dependency strategy for the smoke.
|
||||
5. Map notification daemon action data or portal activation data into the Dart
|
||||
`NotificationOpenedEvent` contract.
|
||||
6. Collect display, click/open routing, lifecycle, and known ACK, dismiss, and
|
||||
inline reply gap evidence.
|
||||
## Agent Can Then
|
||||
|
||||
## Platform Registration Contract
|
||||
- add Linux platform registration and host scaffold if selected;
|
||||
- implement the native notification/action bridge;
|
||||
- configure the selected desktop notification path;
|
||||
- run routing checks and, if allowed, visible notification smoke;
|
||||
- record sanitized evidence in the task or private run note.
|
||||
|
||||
Do not add a partial Linux platform entry without the matching source and host
|
||||
scaffold. A future implementation plan should update these together:
|
||||
## Current Repo Note
|
||||
|
||||
```yaml
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
linux:
|
||||
pluginClass: NexoMessagingPlugin
|
||||
```
|
||||
|
||||
Expected source/host areas for that plan:
|
||||
|
||||
- `packages/messaging_flutter/linux/` for the Linux plugin implementation.
|
||||
- `apps/flutter-test/linux/` for the test host scaffold generated by Flutter.
|
||||
- `packages/messaging_flutter/pubspec.yaml` for platform registration.
|
||||
- `apps/flutter-test/README.md` and package docs for smoke instructions.
|
||||
|
||||
The current preflight milestone documents this contract only. It does not claim
|
||||
Linux runtime support.
|
||||
|
||||
## Minimum Bridge Contract
|
||||
|
||||
The Dart SDK expects the Linux native side to preserve these channel names:
|
||||
|
||||
| Channel | Name | Direction | Minimum Linux behavior |
|
||||
|---|---|---|---|
|
||||
| EventChannel | `com.tokilabs.nexo.messaging/notifications` | native to Dart | Emit codec-safe maps for notification, open, token, clear, session, and future action-failure events. |
|
||||
| MethodChannel | `com.tokilabs.nexo.messaging/notification_actions` | Dart to native | Accept SDK action methods used by Dart without crashing the host app. |
|
||||
|
||||
Minimum methods for the action channel:
|
||||
|
||||
| Method | Minimum preflight result | Production expectation |
|
||||
|---|---|---|
|
||||
| `debugSendNativeEvent` | Validate the argument is a map and emit it on the EventChannel. | Debug/test-only injection path. |
|
||||
| `saveDeviceToken` | Accept and store or no-op a formatted token string without logging secrets. | Store the Linux server-registration token once the platform token source is chosen. |
|
||||
| `getDeviceToken` | Return the last stored formatted token or null. | Return the current formatted Linux server-registration token. |
|
||||
| `setAuthToken` | Validate required string arguments and no-op. | Persist token for ACK/reply work only after storage rules are designed. |
|
||||
| `clearAuthToken` | Validate `serverUrl` and no-op. | Clear persisted token for the server. |
|
||||
| `setSigningKey` | Validate required string arguments and no-op without persistence. | Persist the signing key only after secure storage rules are designed. |
|
||||
|
||||
Fallback boundaries:
|
||||
|
||||
- Missing bridge methods should return the Flutter Linux equivalent of "not
|
||||
implemented", not a successful placeholder.
|
||||
- Token, auth, signing-key, ACK, dismiss, and inline reply persistence are not
|
||||
implied by this preflight milestone.
|
||||
- Real notification display and activation handling belong to `Linux 알림 테스트`
|
||||
unless a smaller bridge implementation plan explicitly handles them first.
|
||||
|
||||
## Notification API Matrix
|
||||
|
||||
| Candidate | Environment fit | Strength | Preflight constraint |
|
||||
|---|---|---|---|
|
||||
| D-Bus `org.freedesktop.Notifications` | Common desktop notification daemon path | Broad support across GNOME, KDE, XFCE, and other desktops | Action callbacks and capabilities vary by daemon and session. |
|
||||
| XDG Desktop Portal notifications | Flatpak/sandbox-friendly desktops | Better fit for portal-mediated desktop environments | Portal availability and notification support vary by distribution and session. |
|
||||
| libnotify wrapper | Thin client around the freedesktop notification spec | Simple display smoke for common desktops | Callback/action support still depends on the underlying daemon. |
|
||||
| Local debug injection only | No daemon, package, or desktop requirement | Deterministic EventChannel/opened-routing verification | Does not prove OS display or activation. |
|
||||
|
||||
Preflight default: use local debug injection for contract tests, and defer the
|
||||
D-Bus vs desktop portal vs libnotify runtime decision to the `Linux 알림 테스트`
|
||||
milestone or a dedicated implementation plan.
|
||||
|
||||
## Runtime Dependency Boundaries
|
||||
|
||||
Future Linux notification implementation should record the runtime assumptions
|
||||
before claiming smoke support:
|
||||
|
||||
- Distribution and version, such as Ubuntu, Fedora, Debian, or another target.
|
||||
- Desktop environment and session type, such as GNOME/KDE/XFCE and X11/Wayland.
|
||||
- Notification daemon or portal availability.
|
||||
- Required build packages and runtime libraries.
|
||||
- Whether action callbacks are supported by the chosen daemon or portal path.
|
||||
|
||||
Tracked docs should keep these as sanitized evidence fields, not private machine
|
||||
configuration dumps.
|
||||
|
||||
## Payload Mapping
|
||||
|
||||
Linux notification action data maps into Dart using the same stable keys as the
|
||||
Android, iOS, macOS, and Windows contracts:
|
||||
|
||||
| Payload key | Dart event field | Notes |
|
||||
|---|---|---|
|
||||
| `type` | `type` | Use `message` for visible message notifications and `opened` for direct open events. |
|
||||
| `server_url` | `server_url` | Required before host navigation callbacks can run. |
|
||||
| `server_id` | `server_id` | Diagnostic and future server URL fallback input. |
|
||||
| `channel_id` | `channel_id` | Drives `onNavigateToChannel` when thread routing is not selected. |
|
||||
| `root_id` | `root_id` | Drives `onNavigateToThread` when CRT is enabled. |
|
||||
| `is_crt_enabled` | `is_crt_enabled` | Accept boolean `true`, string `"true"`, or string `"1"`. |
|
||||
| `ack_id` | `ack_id` | Preserve for future ACK evidence; do not invent ACK behavior. |
|
||||
| `post_id` | `post_id` | Preserve for notification identity and future ACK/display evidence. |
|
||||
| `userInteraction` | `userInteraction` | Set to `true` when a click/open path caused the event. |
|
||||
|
||||
Routing expectation:
|
||||
|
||||
- Channel fixture: `server_url` and `channel_id` are present, `root_id` is empty
|
||||
and `is_crt_enabled` is false, so Dart calls `onNavigateToChannel`.
|
||||
- Thread fixture: `server_url` and `root_id` are present and
|
||||
`is_crt_enabled` is true, so Dart calls `onNavigateToThread`.
|
||||
|
||||
## Local Fixtures
|
||||
|
||||
Use the fixture files as sanitized payloads for a future debug injection method
|
||||
or for manual D-Bus/portal action data construction during smoke testing:
|
||||
|
||||
- `apps/flutter-test/linux/Fixtures/open_channel.json`
|
||||
- `apps/flutter-test/linux/Fixtures/open_thread.json`
|
||||
|
||||
The fixtures intentionally use `https://example.invalid` and smoke IDs. Replace
|
||||
private runtime values only in local run notes or sanitized evidence.
|
||||
|
||||
## Evidence
|
||||
|
||||
Use [`linux-notification-smoke-evidence-template.md`](linux-notification-smoke-evidence-template.md)
|
||||
for Linux runs. Preflight evidence should explicitly separate:
|
||||
|
||||
- distribution, desktop environment, session, and daemon or portal state
|
||||
- visible display state
|
||||
- click/open routing result
|
||||
- app lifecycle state
|
||||
- ACK, dismiss, and inline reply implementation gaps
|
||||
|
||||
Until the scaffold, bridge, and real OS smoke exist, a valid preflight result
|
||||
may be "not implemented yet" with a concrete follow-up plan, rather than a
|
||||
failed runtime claim.
|
||||
Linux is not registered yet and has no plugin or test-host scaffold. Smoke work
|
||||
first needs platform scaffold plus the environment choices above.
|
||||
|
|
|
|||
|
|
@ -1,104 +0,0 @@
|
|||
# macOS Notification Smoke Evidence Template
|
||||
|
||||
Use this template when collecting macOS notification smoke evidence. Replace
|
||||
every private value with a placeholder before storing output in tracked files.
|
||||
|
||||
## Run Metadata
|
||||
|
||||
- Date:
|
||||
- Operator:
|
||||
- Hardware:
|
||||
- macOS version:
|
||||
- Xcode version:
|
||||
- Flutter version:
|
||||
- Build mode: Debug | Profile | Release
|
||||
- Bundle ID: `<redacted-or-placeholder>`
|
||||
- Signing mode: local ad hoc | Apple Developer | not signed for notification smoke
|
||||
- Notarization state: not tested | local placeholder | submitted | accepted | failed
|
||||
|
||||
## Preflight Inputs
|
||||
|
||||
- macOS bridge registers `com.tokilabs.nexo.messaging/notifications`: yes | no
|
||||
- macOS bridge registers `com.tokilabs.nexo.messaging/notification_actions`: yes | no
|
||||
- Legacy `nexo_messaging` channel retained for `getPlatformVersion`: yes | no
|
||||
- Local fixture used: `open_channel.json` | `open_thread.json` | other
|
||||
- Entitlements inspected: yes | no
|
||||
- Notification capability present in built app: yes | no | not tested
|
||||
- Real signing values kept out of tracked files: yes | no
|
||||
|
||||
## Permission Evidence
|
||||
|
||||
Paste sanitized logs only.
|
||||
|
||||
```text
|
||||
permission_status=<authorized|denied|provisional|not_determined|not_implemented>
|
||||
request_path=<FlutterFire|UserNotifications|not_implemented>
|
||||
notes=<redacted-or-placeholder>
|
||||
```
|
||||
|
||||
## Display Evidence
|
||||
|
||||
Payload file or sanitized JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "message",
|
||||
"server_url": "https://example.invalid",
|
||||
"server_id": "server-smoke",
|
||||
"channel_id": "channel-smoke",
|
||||
"root_id": "",
|
||||
"is_crt_enabled": "false",
|
||||
"ack_id": "ack-smoke",
|
||||
"post_id": "post-smoke"
|
||||
}
|
||||
```
|
||||
|
||||
Display path:
|
||||
|
||||
- Local debug injection:
|
||||
- Local notification API:
|
||||
- Remote push provider:
|
||||
- Not tested:
|
||||
|
||||
Observed result:
|
||||
|
||||
```text
|
||||
displayed=<yes|no|not_implemented>
|
||||
notification_title=<redacted-or-placeholder>
|
||||
notification_body=<redacted-or-placeholder>
|
||||
```
|
||||
|
||||
## Click / Open Routing
|
||||
|
||||
| Scenario | Expected | Actual | Evidence path |
|
||||
|---|---|---|---|
|
||||
| Channel click | `onNavigateToChannel` with server/channel | | |
|
||||
| Thread click | `onNavigateToThread` with server/root | | |
|
||||
| Foreground receipt | Raw `onNotification` event logged | | |
|
||||
| Background click | App resumes and emits opened event | | |
|
||||
| Terminated click | Launch payload forwarded | | |
|
||||
|
||||
## Lifecycle Evidence
|
||||
|
||||
```text
|
||||
app_state_before=<foreground|background|terminated|not_tested>
|
||||
app_state_after=<foreground|background|terminated|not_tested>
|
||||
event_type=<message|opened|not_implemented>
|
||||
userInteraction=<true|false|not_set>
|
||||
```
|
||||
|
||||
## ACK, Dismiss, Inline Reply Boundary
|
||||
|
||||
| Feature | Expected for this run | Actual | Follow-up |
|
||||
|---|---|---|---|
|
||||
| ACK | gap documented unless implemented by a later plan | | |
|
||||
| Dismiss | gap documented unless implemented by a later plan | | |
|
||||
| Inline reply | gap documented unless implemented by a later plan | | |
|
||||
|
||||
## Sanitization Checklist
|
||||
|
||||
- [ ] Apple Team ID, signing identity, provisioning profile, certificate, and notarization values removed.
|
||||
- [ ] Full device token or remote push token removed.
|
||||
- [ ] Firebase API key, app ID, sender ID, service account details removed if Firebase was used.
|
||||
- [ ] Private server URL, auth token, signing key, username, channel names removed.
|
||||
- [ ] Screenshots or recordings contain no private message/user data.
|
||||
|
|
@ -1,143 +1,38 @@
|
|||
# macOS Notification Test Guide
|
||||
# macOS Notification Access Preparation
|
||||
|
||||
Last verified: 2026-06-02
|
||||
|
||||
This guide fixes the preflight contract for macOS notification work before
|
||||
Apple signing, a macOS test device, or real OS notification UI evidence is
|
||||
available.
|
||||
Read this when you need to provide macOS runner/signing access so an agent can
|
||||
finish macOS notification implementation and smoke verification. You do not need
|
||||
to run the test from this document.
|
||||
|
||||
Do not commit real Apple, APNs, Firebase, service-account, device-token, auth
|
||||
token, private endpoint, Team ID, signing identity, provisioning, notarization,
|
||||
or private host values into tracked docs. Use placeholders in examples and keep
|
||||
credential files in approved local or secret-store locations.
|
||||
## Provide
|
||||
|
||||
## Current Repository State
|
||||
| Needed | What to prepare |
|
||||
|---|---|
|
||||
| macOS runner | Local Mac, remote Mac, or a decision that visible smoke is deferred. |
|
||||
| Operator path | Who handles UI prompts if the runner is remote. |
|
||||
| Signing mode | Local ad hoc, Apple Developer signing, or distribution/notarization path. |
|
||||
| Bundle ID | Required if signing beyond ad hoc is needed. |
|
||||
| Apple access | Team/provisioning access if notification entitlement or APNs is in scope. |
|
||||
| Push scope | Decide local notification only, APNs/Firebase-backed smoke, or deferred push. |
|
||||
| Evidence path | Choose where sanitized logs/screenshots/run notes should be stored. |
|
||||
|
||||
- The macOS plugin is still a scaffold. `packages/messaging_flutter/macos/Classes/NexoMessagingPlugin.swift`
|
||||
currently registers only the legacy `nexo_messaging` method channel and
|
||||
implements `getPlatformVersion`.
|
||||
- Dart expects native notification and action channels:
|
||||
`com.tokilabs.nexo.messaging/notifications` and
|
||||
`com.tokilabs.nexo.messaging/notification_actions`.
|
||||
- The minimum macOS bridge is therefore not implemented yet. Until it exists,
|
||||
local routing can only be tested through Dart unit tests or a future debug
|
||||
injection bridge.
|
||||
- `apps/flutter-test/macos` is a host scaffold. It carries sandbox
|
||||
entitlements, but no notification entitlement, signing team, provisioning
|
||||
profile, hardened runtime, or notarization value is tracked in this repo.
|
||||
- Local routing fixtures live under `apps/flutter-test/macos/Fixtures/*.json`.
|
||||
## Do Not Put In Tracked Files
|
||||
|
||||
The first useful macOS smoke target is:
|
||||
- Team ID, signing identity, provisioning profile, notarization profile, Apple
|
||||
ID, APNs/Firebase secrets, device token, private endpoint, auth token, channel
|
||||
name, user name, or server URL.
|
||||
|
||||
1. Register the expected EventChannel and MethodChannel in the macOS plugin.
|
||||
2. Implement a debug-only local injection path compatible with
|
||||
`debugSendNativeEvent`.
|
||||
3. Map notification `userInfo` fields into the Dart
|
||||
`NotificationOpenedEvent` contract.
|
||||
4. Build and run the test host with local placeholder signing.
|
||||
5. Collect permission, display, click/open routing, lifecycle, and known ACK,
|
||||
dismiss, and inline reply gap evidence.
|
||||
## Agent Can Then
|
||||
|
||||
## Minimum Bridge Contract
|
||||
- configure the macOS host app for the selected signing mode;
|
||||
- implement or verify the native notification/action bridge;
|
||||
- run local routing checks and, if allowed, visible notification smoke;
|
||||
- record sanitized evidence in the task or private run note.
|
||||
|
||||
The Dart SDK expects the macOS native side to preserve these channel names:
|
||||
## Current Repo Note
|
||||
|
||||
| Channel | Name | Direction | Minimum macOS behavior |
|
||||
|---|---|---|---|
|
||||
| EventChannel | `com.tokilabs.nexo.messaging/notifications` | native to Dart | Emit codec-safe maps for notification, open, token, clear, session, and future action-failure events. |
|
||||
| MethodChannel | `com.tokilabs.nexo.messaging/notification_actions` | Dart to native | Accept SDK action methods used by Dart without crashing the host app. |
|
||||
| Legacy MethodChannel | `nexo_messaging` | Dart/test compatibility | Keep `getPlatformVersion` until the generated example compatibility path is intentionally removed. |
|
||||
|
||||
Minimum methods for the action channel:
|
||||
|
||||
| Method | Minimum preflight result | Production expectation |
|
||||
|---|---|---|
|
||||
| `debugSendNativeEvent` | Validate the argument is a map and emit it on the EventChannel. | Debug/test-only injection path. |
|
||||
| `saveDeviceToken` | Accept and store or no-op a formatted token string without logging secrets. | Store the macOS server-registration token once the platform token source is chosen. |
|
||||
| `getDeviceToken` | Return the last stored formatted token or null. | Return the current formatted macOS server-registration token. |
|
||||
| `setAuthToken` | Validate required string arguments and no-op. | Persist token for ACK/reply work only after storage rules are designed. |
|
||||
| `clearAuthToken` | Validate `serverUrl` and no-op. | Clear persisted token for the server. |
|
||||
| `setSigningKey` | Validate required string arguments and no-op without persistence. | Persist the signing key only after secure storage rules are designed. |
|
||||
|
||||
Fallback boundaries:
|
||||
|
||||
- Missing bridge methods should return `FlutterMethodNotImplemented`, not a
|
||||
successful placeholder.
|
||||
- Token, auth, signing-key, ACK, dismiss, and inline reply persistence are not
|
||||
implied by this preflight milestone.
|
||||
- Real notification permission prompts and visible notification display belong
|
||||
to `macOS 알림 테스트` unless a small debug bridge task explicitly implements
|
||||
them first.
|
||||
|
||||
## Host Placeholder Criteria
|
||||
|
||||
Tracked macOS host files must stay generic:
|
||||
|
||||
- Keep real signing Team ID, identity, provisioning profile, notarization
|
||||
profile, Apple ID, and private host values out of tracked files.
|
||||
- Keep `apps/flutter-test/macos/Runner/DebugProfile.entitlements` and
|
||||
`Release.entitlements` as placeholder entitlements until a smoke run requires
|
||||
a specific capability.
|
||||
- Add notification capability through Xcode or local build settings for a smoke
|
||||
run, then record sanitized evidence instead of committing private signing
|
||||
state.
|
||||
- Do not add real credential files under `apps/flutter-test/macos`.
|
||||
|
||||
Expected local placeholder checks:
|
||||
|
||||
```sh
|
||||
codesign -d --entitlements :- build/macos/Build/Products/Debug/flutter_test.app
|
||||
```
|
||||
|
||||
Look for sandbox values and any locally added notification-related capability.
|
||||
Record private identifiers as placeholders in evidence.
|
||||
|
||||
## Payload Mapping
|
||||
|
||||
macOS notification `userInfo` maps into Dart using the same stable keys as the
|
||||
Android and iOS contracts:
|
||||
|
||||
| `userInfo` key | Dart event field | Notes |
|
||||
|---|---|---|
|
||||
| `type` | `type` | Use `message` for visible message notifications and `opened` for direct open events. |
|
||||
| `server_url` | `server_url` | Required before host navigation callbacks can run. |
|
||||
| `server_id` | `server_id` | Diagnostic and future server URL fallback input. |
|
||||
| `channel_id` | `channel_id` | Drives `onNavigateToChannel` when thread routing is not selected. |
|
||||
| `root_id` | `root_id` | Drives `onNavigateToThread` when CRT is enabled. |
|
||||
| `is_crt_enabled` | `is_crt_enabled` | Accept boolean `true`, string `"true"`, or string `"1"`. |
|
||||
| `ack_id` | `ack_id` | Preserve for future ACK evidence; do not invent ACK behavior. |
|
||||
| `post_id` | `post_id` | Preserve for notification identity and future ACK/display evidence. |
|
||||
| `userInteraction` | `userInteraction` | Set to `true` when a click/open path caused the event. |
|
||||
|
||||
Routing expectation:
|
||||
|
||||
- Channel fixture: `server_url` and `channel_id` are present, `root_id` is empty
|
||||
and `is_crt_enabled` is false, so Dart calls `onNavigateToChannel`.
|
||||
- Thread fixture: `server_url` and `root_id` are present and
|
||||
`is_crt_enabled` is true, so Dart calls `onNavigateToThread`.
|
||||
|
||||
## Local Fixtures
|
||||
|
||||
Use the fixture files as sanitized payloads for a future debug injection method
|
||||
or for manual `userInfo` construction during smoke testing:
|
||||
|
||||
- `apps/flutter-test/macos/Fixtures/open_channel.json`
|
||||
- `apps/flutter-test/macos/Fixtures/open_thread.json`
|
||||
|
||||
The fixtures intentionally use `https://example.invalid` and smoke IDs. Replace
|
||||
private runtime values only in local run notes or sanitized evidence.
|
||||
|
||||
## Evidence
|
||||
|
||||
Use [`macos-notification-smoke-evidence-template.md`](macos-notification-smoke-evidence-template.md)
|
||||
for macOS runs. Preflight evidence should explicitly separate:
|
||||
|
||||
- permission state
|
||||
- visible display state
|
||||
- click/open routing result
|
||||
- app lifecycle state
|
||||
- ACK, dismiss, and inline reply implementation gaps
|
||||
|
||||
Until the bridge and real OS smoke exist, a valid preflight result may be
|
||||
"not implemented yet" with a concrete follow-up plan, rather than a failed
|
||||
runtime claim.
|
||||
The macOS plugin is still a scaffold. It currently has only the legacy
|
||||
`nexo_messaging` channel, so visible notification smoke also needs bridge
|
||||
implementation work.
|
||||
|
|
|
|||
|
|
@ -1,95 +0,0 @@
|
|||
# Windows Notification Smoke Evidence Template
|
||||
|
||||
Use this template when collecting Windows notification smoke evidence. Replace
|
||||
every private value with a placeholder before storing output in tracked files.
|
||||
|
||||
## Run Metadata
|
||||
|
||||
- Date:
|
||||
- Operator:
|
||||
- Hardware or VM:
|
||||
- Windows version:
|
||||
- Visual Studio version:
|
||||
- Windows SDK version:
|
||||
- Flutter version:
|
||||
- Build mode: Debug | Profile | Release
|
||||
- Packaging mode: unpackaged | MSIX | sparse package | not tested
|
||||
- Package identity/AppUserModelID: `<redacted-or-placeholder>`
|
||||
- Notification API path: Windows App SDK | WinRT toast | debug injection | other
|
||||
|
||||
## Preflight Inputs
|
||||
|
||||
- Windows platform registered in `packages/messaging_flutter/pubspec.yaml`: yes | no
|
||||
- Windows plugin scaffold present: yes | no
|
||||
- Windows test host scaffold present: yes | no
|
||||
- Windows bridge registers `com.tokilabs.nexo.messaging/notifications`: yes | no
|
||||
- Windows bridge registers `com.tokilabs.nexo.messaging/notification_actions`: yes | no
|
||||
- Local fixture used: `open_channel.json` | `open_thread.json` | other
|
||||
- Real account, package, signing, and private host values kept out of tracked files: yes | no
|
||||
|
||||
## Display Evidence
|
||||
|
||||
Payload file or sanitized JSON:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "message",
|
||||
"server_url": "https://example.invalid",
|
||||
"server_id": "server-smoke",
|
||||
"channel_id": "channel-smoke",
|
||||
"root_id": "",
|
||||
"is_crt_enabled": "false",
|
||||
"ack_id": "ack-smoke",
|
||||
"post_id": "post-smoke"
|
||||
}
|
||||
```
|
||||
|
||||
Display path:
|
||||
|
||||
- Local debug injection:
|
||||
- Windows App SDK notification:
|
||||
- WinRT toast notification:
|
||||
- Not tested:
|
||||
|
||||
Observed result:
|
||||
|
||||
```text
|
||||
displayed=<yes|no|not_implemented>
|
||||
notification_title=<redacted-or-placeholder>
|
||||
notification_body=<redacted-or-placeholder>
|
||||
activation_arguments_present=<yes|no|not_implemented>
|
||||
```
|
||||
|
||||
## Click / Open Routing
|
||||
|
||||
| Scenario | Expected | Actual | Evidence path |
|
||||
|---|---|---|---|
|
||||
| Channel click | `onNavigateToChannel` with server/channel | | |
|
||||
| Thread click | `onNavigateToThread` with server/root | | |
|
||||
| Foreground receipt | Raw `onNotification` event logged | | |
|
||||
| Background click | App resumes and emits opened event | | |
|
||||
| Cold start click | Launch payload forwarded | | |
|
||||
|
||||
## Lifecycle Evidence
|
||||
|
||||
```text
|
||||
app_state_before=<foreground|background|not_running|not_tested>
|
||||
app_state_after=<foreground|background|not_running|not_tested>
|
||||
event_type=<message|opened|not_implemented>
|
||||
userInteraction=<true|false|not_set>
|
||||
```
|
||||
|
||||
## ACK, Dismiss, Inline Reply Boundary
|
||||
|
||||
| Feature | Expected for this run | Actual | Follow-up |
|
||||
|---|---|---|---|
|
||||
| ACK | gap documented unless implemented by a later plan | | |
|
||||
| Dismiss | gap documented unless implemented by a later plan | | |
|
||||
| Inline reply | gap documented unless implemented by a later plan | | |
|
||||
|
||||
## Sanitization Checklist
|
||||
|
||||
- [ ] Microsoft account, package identity, certificate, Store association, and AppUserModelID values removed.
|
||||
- [ ] Full device token or remote push token removed.
|
||||
- [ ] Private server URL, auth token, signing key, username, channel names removed.
|
||||
- [ ] Screenshots or recordings contain no private message/user data.
|
||||
|
|
@ -1,155 +1,38 @@
|
|||
# Windows Notification Test Guide
|
||||
# Windows Notification Environment Preparation
|
||||
|
||||
Last verified: 2026-06-02
|
||||
|
||||
This guide fixes the preflight contract for Windows notification work before a
|
||||
Windows test machine, Microsoft account, packaging decision, or real OS
|
||||
notification UI evidence is available.
|
||||
Read this when you need to provide Windows runner, packaging, and signing
|
||||
choices so an agent can finish Windows notification implementation and smoke
|
||||
verification. You do not need to run the test from this document.
|
||||
|
||||
Do not commit Microsoft account values, package identity values, signing
|
||||
certificates, store association files, push credentials, device tokens, auth
|
||||
tokens, private endpoints, or private host values into tracked docs. Use
|
||||
placeholders in examples and keep credential files in approved local or
|
||||
secret-store locations.
|
||||
## Provide
|
||||
|
||||
## Current Repository State
|
||||
| Needed | What to prepare |
|
||||
|---|---|
|
||||
| Windows runner | Local PC, VM, remote machine, or a decision that visible smoke is deferred. |
|
||||
| Toolchain | Windows version plus Visual Studio/C++ desktop workload/Windows SDK availability. |
|
||||
| Packaging mode | Unpackaged, MSIX, sparse package, or debug injection only. |
|
||||
| Notification API | Windows App SDK, WinRT toast, desktop AppUserModelID fallback, or debug injection only. |
|
||||
| Identity | Package identity or AppUserModelID if visible toast activation is in scope. |
|
||||
| Signing | Test certificate, organization cert, Store association, or no signing. |
|
||||
| Evidence path | Choose where sanitized logs/screenshots/run notes should be stored. |
|
||||
|
||||
- `packages/messaging_flutter/pubspec.yaml` does not currently register a
|
||||
Windows plugin platform.
|
||||
- There is no `packages/messaging_flutter/windows/` plugin scaffold and no
|
||||
`apps/flutter-test/windows/` host scaffold in the current repository.
|
||||
- Dart already expects native notification and action channels:
|
||||
`com.tokilabs.nexo.messaging/notifications` and
|
||||
`com.tokilabs.nexo.messaging/notification_actions`.
|
||||
- The Windows bridge is therefore not implemented yet. Until it exists, local
|
||||
routing can only be tested through Dart unit tests or a future debug injection
|
||||
bridge.
|
||||
- Local routing fixtures live under `apps/flutter-test/windows/Fixtures/*.json`.
|
||||
## Do Not Put In Tracked Files
|
||||
|
||||
The first useful Windows smoke target is:
|
||||
- Microsoft account values, package identity if private, signing certificate
|
||||
contents/passwords, Store association files, push credentials, device token,
|
||||
private endpoint, auth token, channel name, user name, or server URL.
|
||||
|
||||
1. Add a Windows plugin platform registration and generated host scaffold in a
|
||||
controlled implementation plan.
|
||||
2. Register the expected EventChannel and MethodChannel in the Windows plugin.
|
||||
3. Implement a debug-only local injection path compatible with
|
||||
`debugSendNativeEvent`.
|
||||
4. Choose a notification API path and package identity strategy for the smoke.
|
||||
5. Map notification activation arguments or user data into the Dart
|
||||
`NotificationOpenedEvent` contract.
|
||||
6. Collect display, click/open routing, lifecycle, and known ACK, dismiss, and
|
||||
inline reply gap evidence.
|
||||
## Agent Can Then
|
||||
|
||||
## Platform Registration Contract
|
||||
- add Windows platform registration and host scaffold if selected;
|
||||
- implement the native notification/action bridge;
|
||||
- configure the selected notification API and packaging path;
|
||||
- run routing checks and, if allowed, visible toast smoke;
|
||||
- record sanitized evidence in the task or private run note.
|
||||
|
||||
Do not add a partial Windows platform entry without the matching source and host
|
||||
scaffold. A future implementation plan should update these together:
|
||||
## Current Repo Note
|
||||
|
||||
```yaml
|
||||
flutter:
|
||||
plugin:
|
||||
platforms:
|
||||
windows:
|
||||
pluginClass: NexoMessagingPlugin
|
||||
```
|
||||
|
||||
Expected source/host areas for that plan:
|
||||
|
||||
- `packages/messaging_flutter/windows/` for the Windows plugin implementation.
|
||||
- `apps/flutter-test/windows/` for the test host scaffold generated by Flutter.
|
||||
- `packages/messaging_flutter/pubspec.yaml` for platform registration.
|
||||
- `apps/flutter-test/README.md` and package docs for smoke instructions.
|
||||
|
||||
The current preflight milestone documents this contract only. It does not claim
|
||||
Windows runtime support.
|
||||
|
||||
## Minimum Bridge Contract
|
||||
|
||||
The Dart SDK expects the Windows native side to preserve these channel names:
|
||||
|
||||
| Channel | Name | Direction | Minimum Windows behavior |
|
||||
|---|---|---|---|
|
||||
| EventChannel | `com.tokilabs.nexo.messaging/notifications` | native to Dart | Emit codec-safe maps for notification, open, token, clear, session, and future action-failure events. |
|
||||
| MethodChannel | `com.tokilabs.nexo.messaging/notification_actions` | Dart to native | Accept SDK action methods used by Dart without crashing the host app. |
|
||||
|
||||
Minimum methods for the action channel:
|
||||
|
||||
| Method | Minimum preflight result | Production expectation |
|
||||
|---|---|---|
|
||||
| `debugSendNativeEvent` | Validate the argument is a map and emit it on the EventChannel. | Debug/test-only injection path. |
|
||||
| `saveDeviceToken` | Accept and store or no-op a formatted token string without logging secrets. | Store the Windows server-registration token once the platform token source is chosen. |
|
||||
| `getDeviceToken` | Return the last stored formatted token or null. | Return the current formatted Windows server-registration token. |
|
||||
| `setAuthToken` | Validate required string arguments and no-op. | Persist token for ACK/reply work only after storage rules are designed. |
|
||||
| `clearAuthToken` | Validate `serverUrl` and no-op. | Clear persisted token for the server. |
|
||||
| `setSigningKey` | Validate required string arguments and no-op without persistence. | Persist the signing key only after secure storage rules are designed. |
|
||||
|
||||
Fallback boundaries:
|
||||
|
||||
- Missing bridge methods should return the Flutter Windows equivalent of
|
||||
"not implemented", not a successful placeholder.
|
||||
- Token, auth, signing-key, ACK, dismiss, and inline reply persistence are not
|
||||
implied by this preflight milestone.
|
||||
- Real notification display and activation handling belong to `Windows 알림 테스트`
|
||||
unless a smaller bridge implementation plan explicitly handles them first.
|
||||
|
||||
## Notification API Matrix
|
||||
|
||||
| Candidate | Packaging fit | Strength | Preflight constraint |
|
||||
|---|---|---|---|
|
||||
| Windows App SDK notifications | Best with package identity or sparse package | Modern toast activation path and current Microsoft guidance | Requires Windows App SDK dependency and packaging decision. |
|
||||
| WinRT toast notifications | Best with MSIX/package identity | Direct OS toast APIs and activation arguments | Packaging/AppUserModelID details must be decided before reliable smoke. |
|
||||
| Desktop bridge / AppUserModelID shortcuts | Unpackaged desktop fallback | Can support classic desktop notification experiments | More local setup variance and weaker repeatability. |
|
||||
| Local debug injection only | No packaging requirement | Deterministic EventChannel/opened-routing verification | Does not prove OS display or activation. |
|
||||
|
||||
Preflight default: use local debug injection for contract tests, and defer the
|
||||
Windows App SDK vs WinRT packaging decision to the `Windows 알림 테스트` milestone
|
||||
or a dedicated implementation plan.
|
||||
|
||||
## Payload Mapping
|
||||
|
||||
Windows activation payloads map into Dart using the same stable keys as the
|
||||
Android, iOS, and macOS contracts:
|
||||
|
||||
| Payload key | Dart event field | Notes |
|
||||
|---|---|---|
|
||||
| `type` | `type` | Use `message` for visible message notifications and `opened` for direct open events. |
|
||||
| `server_url` | `server_url` | Required before host navigation callbacks can run. |
|
||||
| `server_id` | `server_id` | Diagnostic and future server URL fallback input. |
|
||||
| `channel_id` | `channel_id` | Drives `onNavigateToChannel` when thread routing is not selected. |
|
||||
| `root_id` | `root_id` | Drives `onNavigateToThread` when CRT is enabled. |
|
||||
| `is_crt_enabled` | `is_crt_enabled` | Accept boolean `true`, string `"true"`, or string `"1"`. |
|
||||
| `ack_id` | `ack_id` | Preserve for future ACK evidence; do not invent ACK behavior. |
|
||||
| `post_id` | `post_id` | Preserve for notification identity and future ACK/display evidence. |
|
||||
| `userInteraction` | `userInteraction` | Set to `true` when a click/open path caused the event. |
|
||||
|
||||
Routing expectation:
|
||||
|
||||
- Channel fixture: `server_url` and `channel_id` are present, `root_id` is empty
|
||||
and `is_crt_enabled` is false, so Dart calls `onNavigateToChannel`.
|
||||
- Thread fixture: `server_url` and `root_id` are present and
|
||||
`is_crt_enabled` is true, so Dart calls `onNavigateToThread`.
|
||||
|
||||
## Local Fixtures
|
||||
|
||||
Use the fixture files as sanitized payloads for a future debug injection method
|
||||
or for manual activation/user-data construction during smoke testing:
|
||||
|
||||
- `apps/flutter-test/windows/Fixtures/open_channel.json`
|
||||
- `apps/flutter-test/windows/Fixtures/open_thread.json`
|
||||
|
||||
The fixtures intentionally use `https://example.invalid` and smoke IDs. Replace
|
||||
private runtime values only in local run notes or sanitized evidence.
|
||||
|
||||
## Evidence
|
||||
|
||||
Use [`windows-notification-smoke-evidence-template.md`](windows-notification-smoke-evidence-template.md)
|
||||
for Windows runs. Preflight evidence should explicitly separate:
|
||||
|
||||
- packaging and identity state
|
||||
- visible display state
|
||||
- click/open routing result
|
||||
- app lifecycle state
|
||||
- ACK, dismiss, and inline reply implementation gaps
|
||||
|
||||
Until the scaffold, bridge, and real OS smoke exist, a valid preflight result
|
||||
may be "not implemented yet" with a concrete follow-up plan, rather than a
|
||||
failed runtime claim.
|
||||
Windows is not registered yet and has no plugin or test-host scaffold. Smoke
|
||||
work first needs platform scaffold plus the environment choices above.
|
||||
|
|
|
|||
|
|
@ -51,8 +51,8 @@ copying upstream mobile code.
|
|||
| --- | --- | --- |
|
||||
| Android | Production target | Native FCM service, Room storage, JJWT verification, OkHttp ACK client, notification builder, inline reply receiver, dismiss receiver |
|
||||
| iOS | Stub | Plugin scaffold exists, behavior is not implemented yet |
|
||||
| macOS | Stub | Plugin scaffold exists; preflight bridge, fixture, host placeholder, and evidence criteria are documented in [`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md) |
|
||||
| Windows | Not registered | No Flutter plugin scaffold yet; platform registration, bridge scope, API matrix, fixture, and evidence criteria are documented in [`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md) |
|
||||
| macOS | Stub | Plugin scaffold exists; user-provided signing, machine, APNs/Firebase scope, and evidence handoff requirements are documented in [`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md) |
|
||||
| Windows | Not registered | No Flutter plugin scaffold yet; user-provided Windows runner, packaging, identity, signing, and evidence handoff requirements are documented in [`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md) |
|
||||
|
||||
## Repository Layout
|
||||
|
||||
|
|
@ -516,8 +516,9 @@ implementation work starts. Dart expects the macOS plugin to register
|
|||
notification `userInfo` fields to `NotificationOpenedEvent`.
|
||||
|
||||
Use [`../../docs/macos-notification-test-guide.md`](../../docs/macos-notification-test-guide.md)
|
||||
for the bridge scope, no-op/fallback boundaries, host placeholder criteria,
|
||||
payload mapping, fixture paths, and evidence expectations.
|
||||
to identify the Apple signing, macOS runner, APNs/Firebase scope, and private
|
||||
handoff values the user must provide before an agent can complete macOS
|
||||
notification smoke work.
|
||||
|
||||
## Windows Preflight Notes
|
||||
|
||||
|
|
@ -532,8 +533,9 @@ Dart still expects any future Windows bridge to use
|
|||
or debug injection payloads to `NotificationOpenedEvent`.
|
||||
|
||||
Use [`../../docs/windows-notification-test-guide.md`](../../docs/windows-notification-test-guide.md)
|
||||
for platform registration scope, bridge boundaries, notification API candidates,
|
||||
fixture paths, and evidence expectations.
|
||||
to identify the Windows runner, packaging mode, notification API path, package
|
||||
identity, signing, and private handoff values the user must provide before an
|
||||
agent can complete Windows notification smoke work.
|
||||
|
||||
## Test Strategy
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue