mirror of
https://github.com/samsonjs/NotificationSmuggler.git
synced 2026-03-25 08:25:48 +00:00
Add tests
This commit is contained in:
parent
ba2c16d298
commit
59effef18f
4 changed files with 169 additions and 9 deletions
|
|
@ -14,8 +14,11 @@ tktk
|
|||
|
||||
## Installation
|
||||
|
||||
Honestly you should probably just copy [BetterNotification.swift]() into your project.
|
||||
|
||||
The only way to install this package is with Swift Package Manager (SPM). Please [file a new issue][] or submit a pull-request if you want to use something else.
|
||||
|
||||
[BetterNotification.swift]: https://github.com/samsonjs/BetterNotification/blob/main/Sources/BetterNotification/BetterNotification.swift
|
||||
[file a new issue]: https://github.com/samsonjs/BetterNotification/issues/new
|
||||
|
||||
### Supported Platforms
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ public extension BetterNotification {
|
|||
static var userInfoKey: String { notificationName.rawValue }
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public extension Notification {
|
||||
/// Creates a `Notification` instance for the given `BetterNotification` value.
|
||||
///
|
||||
|
|
@ -41,7 +43,25 @@ public extension Notification {
|
|||
userInfo: [BN.userInfoKey: betterNotification]
|
||||
)
|
||||
}
|
||||
|
||||
/// Extracts a `BetterNotification` value of the specified type from this `Notification`'s `userInfo`.
|
||||
///
|
||||
/// - Returns: The extracted `BetterNotification` value when found, otherwise `nil`.
|
||||
func better<BN: BetterNotification>() -> BN? {
|
||||
guard let object = userInfo?[BN.userInfoKey] else {
|
||||
NSLog("[\(BN.self)] Object missing from userInfo[\"\(BN.userInfoKey)\"]")
|
||||
return nil
|
||||
}
|
||||
guard let betterNotification = object as? BN else {
|
||||
NSLog("[\(BN.self)] Failed to cast \(object) to \(BN.self)")
|
||||
return nil
|
||||
}
|
||||
|
||||
return betterNotification
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: -
|
||||
|
||||
public extension NotificationCenter {
|
||||
/// Returns an `AsyncSequence` of notifications of a specific `BetterNotification` type.
|
||||
|
|
@ -54,18 +74,18 @@ public extension NotificationCenter {
|
|||
for betterType: BN.Type
|
||||
) -> any AsyncSequence<BN, Never> {
|
||||
notifications(named: BN.notificationName)
|
||||
.compactMap { $0.userInfo?[BN.userInfoKey] as? BN }
|
||||
.compactMap { $0.better() }
|
||||
}
|
||||
|
||||
/// Returns a Combine publisher that emits `BetterNotification` values of a specific type.
|
||||
/// Returns a Combine publisher that emits `BetterNotification` values of the given type.
|
||||
///
|
||||
/// - Parameter betterType: The `BetterNotification` type to observe.
|
||||
/// - Returns: A publisher emitting `BetterNotification` values.
|
||||
func publisher<T: BetterNotification>(
|
||||
for betterType: T.Type
|
||||
) -> AnyPublisher<T, Never> {
|
||||
func publisher<BN: BetterNotification>(
|
||||
for betterType: BN.Type
|
||||
) -> AnyPublisher<BN, Never> {
|
||||
publisher(for: betterType.notificationName)
|
||||
.compactMap { $0.userInfo?[T.userInfoKey] as? T }
|
||||
.compactMap { $0.better() }
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,138 @@
|
|||
import Testing
|
||||
@testable import BetterNotification
|
||||
import Combine
|
||||
import Foundation
|
||||
import Testing
|
||||
|
||||
@Test func example() async throws {
|
||||
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
|
||||
struct BetterNotificationTests {
|
||||
@Test func notificationName() {
|
||||
#expect(HitchhikersNotification.notificationName.rawValue == "BetterNotification:HitchhikersNotification")
|
||||
}
|
||||
|
||||
@Test func userInfoKey() {
|
||||
#expect(HitchhikersNotification.userInfoKey == "BetterNotification:HitchhikersNotification")
|
||||
}
|
||||
|
||||
// MARK: - Notification extensions
|
||||
|
||||
@Test func buildNotification() {
|
||||
let hitchhikers = HitchhikersNotification(answer: 42)
|
||||
let notification = Notification.better(hitchhikers)
|
||||
#expect(notification.name == HitchhikersNotification.notificationName)
|
||||
#expect(notification.userInfo?.count == 1)
|
||||
let key = HitchhikersNotification.userInfoKey
|
||||
let userInfoValue = notification.userInfo?[key] as? HitchhikersNotification
|
||||
#expect(userInfoValue == hitchhikers)
|
||||
}
|
||||
|
||||
@Test func extractBetterPayload() {
|
||||
let hitchhikers = HitchhikersNotification(answer: 42)
|
||||
let notification = Notification.better(hitchhikers)
|
||||
let extracted: HitchhikersNotification? = notification.better()
|
||||
#expect(extracted == hitchhikers)
|
||||
}
|
||||
|
||||
@Test func extractBetterPayloadFailsOnWrongType() {
|
||||
let imposter = Notification(
|
||||
name: HitchhikersNotification.notificationName,
|
||||
object: nil,
|
||||
userInfo: [HitchhikersNotification.userInfoKey: "imposter"]
|
||||
)
|
||||
let extracted: HitchhikersNotification? = imposter.better()
|
||||
#expect(extracted == nil)
|
||||
}
|
||||
|
||||
@Test func extractBetterPayloadFailsOnMissingPayload() {
|
||||
let wrongNotification = Notification(name: HitchhikersNotification.notificationName)
|
||||
let extracted: HitchhikersNotification? = wrongNotification.better()
|
||||
#expect(extracted == nil)
|
||||
}
|
||||
|
||||
// MARK: NotificationCenter extensions
|
||||
|
||||
// It's important that the tests and the notification-observing tasks are not on the same actor,
|
||||
// so we make the tests @MainActor and observe notifications on another actor. Otherwise it's
|
||||
// a deadlock.
|
||||
|
||||
@Test(.timeLimit(.minutes(1)))
|
||||
@MainActor func notificationCenterAsyncSequence() async throws {
|
||||
let center = NotificationCenter()
|
||||
nonisolated(unsafe) var received = false
|
||||
Task {
|
||||
for await hitchhikers in center.notifications(for: HitchhikersNotification.self) {
|
||||
#expect(hitchhikers.answer == 42)
|
||||
received = true
|
||||
break
|
||||
}
|
||||
}
|
||||
await Task.yield()
|
||||
|
||||
let hitchhikers = HitchhikersNotification(answer: 42)
|
||||
center.post(.better(hitchhikers))
|
||||
while !received { await Task.yield() }
|
||||
}
|
||||
|
||||
@Test(.timeLimit(.minutes(1)))
|
||||
@MainActor func notificationCenterAsyncSequenceIgnoresInvalidPayloads() async throws {
|
||||
let center = NotificationCenter()
|
||||
nonisolated(unsafe) var received = false
|
||||
let task = Task {
|
||||
for await _ in center.notifications(for: HitchhikersNotification.self) {
|
||||
received = true
|
||||
}
|
||||
}
|
||||
defer { task.cancel() }
|
||||
await Task.yield()
|
||||
|
||||
// Post an invalid one that should be ignored.
|
||||
let imposter = Notification(
|
||||
name: HitchhikersNotification.notificationName,
|
||||
object: nil,
|
||||
userInfo: [HitchhikersNotification.userInfoKey: "imposter"]
|
||||
)
|
||||
center.post(imposter)
|
||||
|
||||
try await Task.sleep(for: .milliseconds(10))
|
||||
#expect(!received)
|
||||
}
|
||||
|
||||
@Test(.timeLimit(.minutes(1)))
|
||||
@MainActor func notificationCenterPublisher() async {
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
let center = NotificationCenter()
|
||||
nonisolated(unsafe) var received = false
|
||||
center.publisher(for: HitchhikersNotification.self)
|
||||
.sink { hitchhikers in
|
||||
#expect(hitchhikers.answer == 42)
|
||||
received = true
|
||||
}.store(in: &cancellables)
|
||||
await Task.yield()
|
||||
|
||||
let hitchhikers = HitchhikersNotification(answer: 42)
|
||||
center.post(.better(hitchhikers))
|
||||
while !received { await Task.yield() }
|
||||
}
|
||||
|
||||
@Test(.timeLimit(.minutes(1)))
|
||||
@MainActor func notificationCenterPublisherIgnoresInvalidPayloads() async throws {
|
||||
var cancellables = Set<AnyCancellable>()
|
||||
let center = NotificationCenter()
|
||||
nonisolated(unsafe) var received = false
|
||||
center.publisher(for: HitchhikersNotification.self)
|
||||
.sink { hitchhikers in
|
||||
#expect(hitchhikers.answer == 42)
|
||||
received = true
|
||||
}.store(in: &cancellables)
|
||||
await Task.yield()
|
||||
|
||||
// Post an invalid one that should be ignored.
|
||||
let imposter = Notification(
|
||||
name: HitchhikersNotification.notificationName,
|
||||
object: nil,
|
||||
userInfo: [HitchhikersNotification.userInfoKey: "imposter"]
|
||||
)
|
||||
center.post(imposter)
|
||||
|
||||
try await Task.sleep(for: .milliseconds(10))
|
||||
#expect(!received)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import BetterNotification
|
||||
|
||||
struct HitchhikersNotification: BetterNotification, Equatable {
|
||||
let answer: Int
|
||||
}
|
||||
Loading…
Reference in a new issue