152 lines
6.3 KiB
Swift
152 lines
6.3 KiB
Swift
import Testing
|
||
@testable import bookstax
|
||
import Foundation
|
||
|
||
// MARK: - DonationPurchaseState Enum
|
||
|
||
@Suite("DonationPurchaseState – Computed Properties")
|
||
struct DonationPurchaseStateTests {
|
||
|
||
@Test("activePurchasingID returns id when purchasing")
|
||
func activePurchasingID() {
|
||
let state = DonationPurchaseState.purchasing(productID: "donatebook")
|
||
#expect(state.activePurchasingID == "donatebook")
|
||
}
|
||
|
||
@Test("activePurchasingID returns nil for non-purchasing states")
|
||
@available(iOS 18.0, *)
|
||
func activePurchasingIDNil() {
|
||
#expect(DonationPurchaseState.idle.activePurchasingID == nil)
|
||
#expect(DonationPurchaseState.thankYou(productID: "x").activePurchasingID == nil)
|
||
#expect(DonationPurchaseState.pending(productID: "x").activePurchasingID == nil)
|
||
#expect(DonationPurchaseState.failed(productID: "x", message: "err").activePurchasingID == nil)
|
||
}
|
||
|
||
@Test("thankYouID returns id when in thankYou state")
|
||
func thankYouID() {
|
||
let state = DonationPurchaseState.thankYou(productID: "doneatepage")
|
||
#expect(state.thankYouID == "doneatepage")
|
||
}
|
||
|
||
@Test("thankYouID returns nil for other states")
|
||
func thankYouIDNil() {
|
||
#expect(DonationPurchaseState.idle.thankYouID == nil)
|
||
#expect(DonationPurchaseState.purchasing(productID: "x").thankYouID == nil)
|
||
}
|
||
|
||
@Test("pendingID returns id when in pending state")
|
||
func pendingID() {
|
||
let state = DonationPurchaseState.pending(productID: "donateencyclopaedia")
|
||
#expect(state.pendingID == "donateencyclopaedia")
|
||
}
|
||
|
||
@Test("pendingID returns nil for other states")
|
||
func pendingIDNil() {
|
||
#expect(DonationPurchaseState.idle.pendingID == nil)
|
||
#expect(DonationPurchaseState.thankYou(productID: "x").pendingID == nil)
|
||
}
|
||
|
||
@Test("errorMessage returns message when failed for matching id")
|
||
func errorMessageMatch() {
|
||
let state = DonationPurchaseState.failed(productID: "donatebook", message: "Payment declined")
|
||
#expect(state.errorMessage(for: "donatebook") == "Payment declined")
|
||
}
|
||
|
||
@Test("errorMessage returns nil for wrong product id")
|
||
func errorMessageNoMatch() {
|
||
let state = DonationPurchaseState.failed(productID: "donatebook", message: "error")
|
||
#expect(state.errorMessage(for: "doneatepage") == nil)
|
||
}
|
||
|
||
@Test("errorMessage returns nil for non-failed states")
|
||
func errorMessageNotFailed() {
|
||
#expect(DonationPurchaseState.idle.errorMessage(for: "x") == nil)
|
||
#expect(DonationPurchaseState.purchasing(productID: "x").errorMessage(for: "x") == nil)
|
||
}
|
||
|
||
@Test("isIdle returns true only for .idle")
|
||
func isIdleCheck() {
|
||
#expect(DonationPurchaseState.idle.isIdle == true)
|
||
#expect(DonationPurchaseState.purchasing(productID: "x").isIdle == false)
|
||
#expect(DonationPurchaseState.thankYou(productID: "x").isIdle == false)
|
||
#expect(DonationPurchaseState.failed(productID: "x", message: "e").isIdle == false)
|
||
}
|
||
|
||
@Test("Equatable: same purchasing states are equal")
|
||
func equatablePurchasing() {
|
||
#expect(DonationPurchaseState.purchasing(productID: "a") == DonationPurchaseState.purchasing(productID: "a"))
|
||
#expect(DonationPurchaseState.purchasing(productID: "a") != DonationPurchaseState.purchasing(productID: "b"))
|
||
}
|
||
|
||
@Test("Equatable: idle equals idle")
|
||
func equatableIdle() {
|
||
#expect(DonationPurchaseState.idle == DonationPurchaseState.idle)
|
||
#expect(DonationPurchaseState.idle != DonationPurchaseState.purchasing(productID: "x"))
|
||
}
|
||
}
|
||
|
||
// MARK: - shouldShowNudge Timing
|
||
|
||
@Suite("DonationService – shouldShowNudge", .serialized)
|
||
@MainActor
|
||
struct DonationServiceNudgeTests {
|
||
|
||
private let installKey = "bookstax.installDate"
|
||
private let nudgeKey = "bookstax.lastNudgeDate"
|
||
private let historyKey = "bookstax.donationHistory"
|
||
|
||
init() {
|
||
// Clean UserDefaults before each test so DonationService reads fresh state
|
||
UserDefaults.standard.removeObject(forKey: installKey)
|
||
UserDefaults.standard.removeObject(forKey: nudgeKey)
|
||
UserDefaults.standard.removeObject(forKey: historyKey)
|
||
}
|
||
|
||
@Test("Within 3-day grace period: nudge should not show")
|
||
func gracePeriodPreventsNudge() {
|
||
let recentInstall = Date().addingTimeInterval(-(2 * 24 * 3600)) // 2 days ago
|
||
UserDefaults.standard.set(recentInstall, forKey: installKey)
|
||
#expect(DonationService.shared.shouldShowNudge == false)
|
||
}
|
||
|
||
@Test("After 3-day grace period: nudge should show")
|
||
func afterGracePeriodNudgeShows() {
|
||
let oldInstall = Date().addingTimeInterval(-(4 * 24 * 3600)) // 4 days ago
|
||
UserDefaults.standard.set(oldInstall, forKey: installKey)
|
||
#expect(DonationService.shared.shouldShowNudge == true)
|
||
}
|
||
|
||
@Test("No install date recorded: nudge should not show (safe fallback)")
|
||
func noInstallDateFallback() {
|
||
// No install date stored — graceful fallback
|
||
#expect(DonationService.shared.shouldShowNudge == false)
|
||
}
|
||
|
||
@Test("Nudge recently dismissed: should not show again")
|
||
func recentNudgeHidden() {
|
||
let oldInstall = Date().addingTimeInterval(-(90 * 24 * 3600))
|
||
let recentNudge = Date().addingTimeInterval(-(10 * 24 * 3600)) // dismissed 10 days ago
|
||
UserDefaults.standard.set(oldInstall, forKey: installKey)
|
||
UserDefaults.standard.set(recentNudge, forKey: nudgeKey)
|
||
#expect(DonationService.shared.shouldShowNudge == false)
|
||
}
|
||
|
||
@Test("Nudge dismissed ~6 months ago: should show again")
|
||
func sixMonthsLaterShowAgain() {
|
||
let oldInstall = Date().addingTimeInterval(-(200 * 24 * 3600))
|
||
let oldNudge = Date().addingTimeInterval(-(183 * 24 * 3600)) // ~6 months ago
|
||
UserDefaults.standard.set(oldInstall, forKey: installKey)
|
||
UserDefaults.standard.set(oldNudge, forKey: nudgeKey)
|
||
#expect(DonationService.shared.shouldShowNudge == true)
|
||
}
|
||
|
||
@Test("Nudge not yet 6 months: should not show")
|
||
func beforeSixMonthsHidden() {
|
||
let oldInstall = Date().addingTimeInterval(-(200 * 24 * 3600))
|
||
let recentNudge = Date().addingTimeInterval(-(100 * 24 * 3600)) // only 100 days
|
||
UserDefaults.standard.set(oldInstall, forKey: installKey)
|
||
UserDefaults.standard.set(recentNudge, forKey: nudgeKey)
|
||
#expect(DonationService.shared.shouldShowNudge == false)
|
||
}
|
||
}
|