Files
nahbar/nahbar/nahbarTests/Tour/TourSeenStoreTests.swift
T
sven b214bb6c50 Fix #23: Spotlight-Tour-System – Onboarding + Update-Touren
Neue wiederverwendbare Tour-Komponente (11 Swift-Dateien):

Model:
- TourID, TourTargetID, TourStep, Tour (max 6 Steps per precondition)
- TourCatalog: statische Registry; initiale Onboarding-Tour mit 6 Steps

State:
- TourSeenStore: UserDefaults-backed, injizierbar für Tests
- TourCoordinator: @Observable, Pending-Queue, Auto-Start-Logik
  · checkForPendingTours() startet .autoOnUpdate-Touren bei App-Update
  · .manualOrFirstLaunch-Touren werden explizit per start(_:) gestartet

UI:
- SpotlightShape: Even-Odd-Fill-Shape mit animatableData
- TourCardView: Progress-Dots, Navigation, Haptic-Feedback
- TourOverlayView: Spotlight-Cutout via thinMaterial + SpotlightShape eoFill,
  Coral-Glow-Ring, automatische Card-Positionierung (above/below/center)
- TourViewModifiers: .tourTarget() + .tourPresenter()

Integration:
- NahbarApp: TourCoordinator via @State + .environment()
- OnboardingContainerView: FeatureTourView ersetzt durch TourCoordinator.start(.onboarding);
  .tourPresenter() im fullScreenCover-Kontext; OnboardingPrivacyView bleibt erhalten
- ContentView: .tourPresenter() für main-app Update-Touren + checkForPendingTours()
- SettingsView: neue "App-Touren"-Sektion zum manuellen Neu-Starten

Tests (40 Tests in 4 Dateien):
- TourCatalogTests, TourSeenStoreTests, TourCoordinatorTests, AutoStartLogicTests

Lokalisierung: DE + EN für alle Tour-Strings in Localizable.xcstrings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-22 19:06:56 +02:00

110 lines
3.3 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Testing
import Foundation
@testable import nahbar
// MARK: - TourSeenStore Tests
@Suite("TourSeenStore Grundfunktionen")
struct TourSeenStoreTests {
/// Creates an isolated UserDefaults for each test to avoid state leakage.
private func makeStore() -> TourSeenStore {
let suiteName = "test.tour.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
return TourSeenStore(defaults: defaults)
}
@Test("hasSeen gibt false zurück bevor markSeen aufgerufen wurde")
func hasSeenFalseInitially() {
let store = makeStore()
#expect(!store.hasSeen(.onboarding))
}
@Test("markSeen setzt hasSeen auf true")
func markSeenSetsHasSeen() {
let store = makeStore()
store.markSeen(.onboarding)
#expect(store.hasSeen(.onboarding))
}
@Test("markSeen für eine ID beeinflusst andere IDs nicht")
func markSeenDoesNotAffectOtherIDs() {
let store = makeStore()
store.markSeen(.onboarding)
#expect(!store.hasSeen(.v1_2_besuchsfragebogen))
#expect(!store.hasSeen(.v1_3_personality))
}
@Test("Mehrfaches markSeen ist idempotent")
func markSeenIsIdempotent() {
let store = makeStore()
store.markSeen(.onboarding)
store.markSeen(.onboarding)
#expect(store.hasSeen(.onboarding))
}
@Test("reset setzt hasSeen zurück auf false")
func resetClearsSeenIDs() {
let store = makeStore()
store.markSeen(.onboarding)
store.reset()
#expect(!store.hasSeen(.onboarding))
}
@Test("reset löscht auch lastSeenAppVersion")
func resetClearsLastSeenVersion() {
let store = makeStore()
store.lastSeenAppVersion = "1.2"
store.reset()
#expect(store.lastSeenAppVersion == nil)
}
@Test("lastSeenAppVersion ist initial nil")
func lastSeenVersionIsInitiallyNil() {
let store = makeStore()
#expect(store.lastSeenAppVersion == nil)
}
@Test("lastSeenAppVersion wird korrekt geschrieben und gelesen")
func lastSeenVersionPersists() {
let store = makeStore()
store.lastSeenAppVersion = "1.3"
#expect(store.lastSeenAppVersion == "1.3")
}
}
@Suite("TourSeenStore Persistenz")
struct TourSeenStorePersistenceTests {
@Test("markSeen persistiert über Store-Neuinstanzierung")
func persistenceAcrossInstances() {
let suiteName = "test.tour.persist.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
let store1 = TourSeenStore(defaults: defaults)
store1.markSeen(.onboarding)
// New instance, same defaults
let store2 = TourSeenStore(defaults: defaults)
#expect(store2.hasSeen(.onboarding))
// Cleanup
defaults.removePersistentDomain(forName: suiteName)
}
@Test("lastSeenAppVersion persistiert über Store-Neuinstanzierung")
func versionPersistenceAcrossInstances() {
let suiteName = "test.tour.version.\(UUID().uuidString)"
let defaults = UserDefaults(suiteName: suiteName)!
let store1 = TourSeenStore(defaults: defaults)
store1.lastSeenAppVersion = "2.0"
let store2 = TourSeenStore(defaults: defaults)
#expect(store2.lastSeenAppVersion == "2.0")
// Cleanup
defaults.removePersistentDomain(forName: suiteName)
}
}