mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
a8c79fdd46
* Render live location share start push notifications in the NSE Live location sharing starts with a cleartext org.matrix.msc3672.beacon_info STATE event, which the NSE previously rendered as the generic fallback notification because NotificationContentBuilder only handled message-like event content. - Handle .state(.beaconInfo) in NotificationContentBuilder: sender display name as the title (same as other messages) with a new localized body, "Started sharing their live location". - Let beaconInfo state events through NotificationHandler's preprocessing (all other state events are still discarded). - Add notification_live_location_started_body, imported from Localazy. - Unit tests for the new state-event notification paths. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> * rename processMessageLike in processAsMessage --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Mauro Romito <mauro.romito@element.io>
301 lines
19 KiB
Swift
301 lines
19 KiB
Swift
//
|
|
// Copyright 2025 Element Creations Ltd.
|
|
//
|
|
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
|
// Please see LICENSE files in the repository root for full details.
|
|
//
|
|
|
|
import Dynamic
|
|
@testable import ElementX
|
|
import MatrixRustSDK
|
|
import MatrixRustSDKMocks
|
|
import Testing
|
|
import UserNotifications
|
|
|
|
nonisolated struct NotificationContentBuilderTests {
|
|
var notificationContentBuilder: NotificationContentBuilder
|
|
var mediaProvider: MediaProviderMock
|
|
var notificationContent: UNMutableNotificationContent
|
|
|
|
init() {
|
|
notificationContent = .init()
|
|
let stringBuilder = RoomMessageEventStringBuilder(attributedStringBuilder: AttributedStringBuilder(mentionBuilder: PlainMentionBuilder()),
|
|
style: .plain)
|
|
mediaProvider = MediaProviderMock(.init())
|
|
notificationContentBuilder = NotificationContentBuilder(messageEventStringBuilder: stringBuilder,
|
|
notificationSoundName: UNNotificationSoundName("message.caf"),
|
|
userSession: NSEUserSessionMock(.init()))
|
|
}
|
|
|
|
@Test
|
|
mutating func dmMessageNotification() async {
|
|
let notificationItem = NotificationItemProxyMock(.init(roomID: "!test:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "Alice",
|
|
roomJoinedMembers: 2,
|
|
isRoomDirect: true,
|
|
isRoomPrivate: true,
|
|
isNoisy: true))
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
// Checking if nil without using asObject always fails
|
|
#expect(communicationContext.displayName.asObject == nil)
|
|
#expect(communicationContext.sender.displayName == "Alice")
|
|
#expect(notificationContent.body == "Hello world!")
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.threadRootEventID == nil)
|
|
#expect(notificationContent.sound != nil)
|
|
// Remember we remove the @ due to an iOS bug
|
|
#expect(notificationContent.threadIdentifier == "bob:matrix.org!test:matrix.org")
|
|
#expect(notificationContent.attachments == [])
|
|
}
|
|
|
|
@Test
|
|
mutating func dmMessageNotificationWithMention() async {
|
|
let notificationItem = NotificationItemProxyMock(.init(roomID: "!test:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "Alice",
|
|
roomJoinedMembers: 2,
|
|
isRoomDirect: true,
|
|
isRoomPrivate: true,
|
|
isNoisy: true,
|
|
hasMention: true))
|
|
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
// Checking if nil without using asObject always fails
|
|
#expect(communicationContext.displayName.asObject == nil)
|
|
#expect(communicationContext.sender.displayName == L10n.notificationSenderMentionReply("Alice"))
|
|
#expect(notificationContent.body == "Hello world!")
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.threadRootEventID == nil)
|
|
#expect(notificationContent.sound != nil)
|
|
// Remember we remove the @ due to an iOS bug
|
|
#expect(notificationContent.threadIdentifier == "bob:matrix.org!test:matrix.org")
|
|
#expect(notificationContent.attachments == [])
|
|
}
|
|
|
|
@Test
|
|
mutating func dmMessageNotificationWithThread() async {
|
|
let notificationItem = NotificationItemProxyMock(.init(roomID: "!test:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "Alice",
|
|
roomJoinedMembers: 2,
|
|
isRoomDirect: true,
|
|
isRoomPrivate: true,
|
|
isNoisy: true,
|
|
hasMention: false,
|
|
threadRootEventID: "thread"))
|
|
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
#expect(communicationContext.displayName == L10n.commonThread)
|
|
#expect(communicationContext.sender.displayName == "Alice")
|
|
#expect(notificationContent.body == "Hello world!")
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.threadRootEventID != nil)
|
|
#expect(notificationContent.sound != nil)
|
|
// Remember we remove the @ due to an iOS bug
|
|
#expect(notificationContent.threadIdentifier == "bob:matrix.org!test:matrix.orgthread")
|
|
#expect(notificationContent.attachments == [])
|
|
}
|
|
|
|
@Test
|
|
mutating func dmMessageNotificationWithThreadAndMention() async {
|
|
let notificationItem = NotificationItemProxyMock(.init(roomID: "!test:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "Alice",
|
|
roomJoinedMembers: 2,
|
|
isRoomDirect: true,
|
|
isRoomPrivate: true,
|
|
isNoisy: true,
|
|
hasMention: true,
|
|
threadRootEventID: "thread"))
|
|
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
#expect(communicationContext.displayName == L10n.commonThread)
|
|
#expect(communicationContext.sender.displayName == L10n.notificationSenderMentionReply("Alice"))
|
|
#expect(notificationContent.body == "Hello world!")
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.threadRootEventID != nil)
|
|
#expect(notificationContent.sound != nil)
|
|
// Remember we remove the @ due to an iOS bug
|
|
#expect(notificationContent.threadIdentifier == "bob:matrix.org!test:matrix.orgthread")
|
|
#expect(notificationContent.attachments == [])
|
|
}
|
|
|
|
@Test
|
|
mutating func roomMessageNotification() async {
|
|
let notificationItem = NotificationItemProxyMock(.init(roomID: "!testroom:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "General",
|
|
roomJoinedMembers: 5,
|
|
isRoomDirect: false,
|
|
isRoomPrivate: false,
|
|
isNoisy: false))
|
|
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
|
|
#expect(communicationContext.displayName == "General")
|
|
#expect(communicationContext.sender.displayName == "Alice")
|
|
#expect(notificationContent.body == "Hello world!")
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.threadRootEventID == nil)
|
|
#expect(notificationContent.sound == nil)
|
|
// Remember we remove the @ due to an iOS bug
|
|
#expect(notificationContent.threadIdentifier == "bob:matrix.org!testroom:matrix.org")
|
|
#expect(notificationContent.attachments == [])
|
|
}
|
|
|
|
@Test
|
|
mutating func roomMessageNotificationWithMention() async {
|
|
let notificationItem = NotificationItemProxyMock(.init(roomID: "!testroom:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "General",
|
|
roomJoinedMembers: 5,
|
|
isRoomDirect: false,
|
|
isRoomPrivate: false,
|
|
isNoisy: true,
|
|
hasMention: true))
|
|
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
#expect(communicationContext.displayName == "General")
|
|
#expect(communicationContext.sender.displayName == L10n.notificationSenderMentionReply("Alice"))
|
|
#expect(notificationContent.body == "Hello world!")
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.threadRootEventID == nil)
|
|
#expect(notificationContent.sound != nil)
|
|
#expect(notificationContent.threadIdentifier == "bob:matrix.org!testroom:matrix.org")
|
|
#expect(notificationContent.attachments == [])
|
|
}
|
|
|
|
@Test
|
|
mutating func roomMessageNotificationWithThread() async {
|
|
let notificationItem = NotificationItemProxyMock(.init(roomID: "!testroom:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "General",
|
|
roomJoinedMembers: 5,
|
|
isRoomDirect: false,
|
|
isRoomPrivate: false,
|
|
isNoisy: false,
|
|
threadRootEventID: "thread123"))
|
|
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
#expect(communicationContext.displayName == L10n.notificationThreadInRoom("General"))
|
|
#expect(communicationContext.sender.displayName == "Alice")
|
|
#expect(notificationContent.body == "Hello world!")
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.threadRootEventID != nil)
|
|
#expect(notificationContent.sound == nil)
|
|
#expect(notificationContent.threadIdentifier == "bob:matrix.org!testroom:matrix.orgthread123")
|
|
#expect(notificationContent.attachments == [])
|
|
}
|
|
|
|
@Test
|
|
mutating func liveLocationStartNotification() async {
|
|
let event = TimelineEventSDKMock()
|
|
event.eventIdReturnValue = UUID().uuidString
|
|
event.contentReturnValue = .state(content: .beaconInfo)
|
|
|
|
let notificationItem = NotificationItemProxyMock(.init(event: .timeline(event: event),
|
|
roomID: "!test:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "Alice",
|
|
roomJoinedMembers: 2,
|
|
isRoomDirect: true,
|
|
isRoomPrivate: true,
|
|
isNoisy: true))
|
|
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
#expect(communicationContext.sender.displayName == "Alice")
|
|
#expect(notificationContent.body == L10n.notificationLiveLocationStartedBody)
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.sound != nil)
|
|
}
|
|
|
|
@Test
|
|
mutating func otherStateEventNotification() async {
|
|
let event = TimelineEventSDKMock()
|
|
event.eventIdReturnValue = UUID().uuidString
|
|
event.contentReturnValue = .state(content: .roomName)
|
|
|
|
let notificationItem = NotificationItemProxyMock(.init(event: .timeline(event: event),
|
|
roomID: "!test:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "Alice",
|
|
roomJoinedMembers: 2,
|
|
isRoomDirect: true,
|
|
isRoomPrivate: true,
|
|
isNoisy: true))
|
|
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
|
|
#expect(notificationContent.body == L10n.notification)
|
|
}
|
|
|
|
@Test
|
|
mutating func roomMessageNotificationWithThreadAndMention() async {
|
|
let notificationItem = NotificationItemProxyMock(.init(roomID: "!testroom:matrix.org",
|
|
receiverID: "@bob:matrix.org",
|
|
senderDisplayName: "Alice",
|
|
roomDisplayName: "General",
|
|
roomJoinedMembers: 5,
|
|
isRoomDirect: false,
|
|
isRoomPrivate: false,
|
|
isNoisy: true,
|
|
hasMention: true,
|
|
threadRootEventID: "thread123"))
|
|
await notificationContentBuilder.process(notificationContent: ¬ificationContent,
|
|
notificationItem: notificationItem,
|
|
mediaProvider: mediaProvider)
|
|
let communicationContext = Dynamic(notificationContent, memberName: "communicationContext")
|
|
#expect(communicationContext.displayName == L10n.notificationThreadInRoom("General"))
|
|
#expect(communicationContext.sender.displayName == L10n.notificationSenderMentionReply("Alice"))
|
|
#expect(notificationContent.body == "Hello world!")
|
|
#expect(notificationContent.categoryIdentifier == NotificationConstants.Category.message)
|
|
#expect(notificationContent.threadRootEventID != nil)
|
|
#expect(notificationContent.sound != nil)
|
|
#expect(notificationContent.threadIdentifier == "bob:matrix.org!testroom:matrix.orgthread123")
|
|
#expect(notificationContent.attachments == [])
|
|
}
|
|
}
|