import AppTypes import ComposableArchitecture import DependenciesTestSupport import Foundation import Testing @testable import AppNavigation @testable import EditSettingsNew_TopLevel @MainActor @Suite("AppNavigation Feature Tests", .dependencies { $0.defaultDatabase = try! $0.appDatabase() $0.uuid = .incrementing }) struct AppNavigationFeatureTests { @Test("initial state is settings screen") func initialState() async { let state = AppNavigationFeature.State() #expect(state.screen == .settings) } @Test("onAppear starts screen off monitoring") func onAppearStartsMonitoring() async { let clock = TestClock() let store = TestStore(initialState: AppNavigationFeature.State()) { AppNavigationFeature() } withDependencies: { $0.continuousClock = clock $0.date = .constant(Date()) $0.calendar = .current $0.screenControl.getBrightness = { 0.75 } $0.screenControl.setBrightness = { _ in } // onAppear starts the settings resolver; unauthorized access // makes its initial reconcile a no-op. $0.photoKitAlbums.libraryAccess = { .notDetermined } $0.calendarAsync.calendarAccess = { .notDetermined } } store.exhaustivity = .off await store.send(.onAppear) await store.receive(\.screenOffMonitor.startMonitoring) await store.finish() } @Test("onAppear with configured slideshow switches to dashboard") func onAppearWithConfiguredSlideshow() async { let clock = TestClock() var state = AppNavigationFeature.State() state.settingsState.albumPickerState.$selectedAlbum.withLock { $0 = AlbumLocalId("test-album") } let store = TestStore(initialState: state) { AppNavigationFeature() } withDependencies: { $0.continuousClock = clock $0.date = .constant(Date()) $0.calendar = .current $0.screenControl.getBrightness = { 0.75 } $0.screenControl.setBrightness = { _ in } // onAppear starts the settings resolver; unauthorized access // makes its initial reconcile a no-op. $0.photoKitAlbums.libraryAccess = { .notDetermined } $0.calendarAsync.calendarAccess = { .notDetermined } } store.exhaustivity = .off await store.send(.onAppear) { $0.screen = .dashboard $0.screenOffMonitorState.isSlideshowPlaying = true } await store.receive(\.screenOffMonitor.startMonitoring) await store.finish() } @Test("onAppear without configured slideshow stays on settings") func onAppearWithoutConfiguredSlideshow() async { let clock = TestClock() let store = TestStore(initialState: AppNavigationFeature.State()) { AppNavigationFeature() } withDependencies: { $0.continuousClock = clock $0.date = .constant(Date()) $0.calendar = .current $0.screenControl.getBrightness = { 0.75 } $0.screenControl.setBrightness = { _ in } // onAppear starts the settings resolver; unauthorized access // makes its initial reconcile a no-op. $0.photoKitAlbums.libraryAccess = { .notDetermined } $0.calendarAsync.calendarAccess = { .notDetermined } } store.exhaustivity = .off await store.send(.onAppear) await store.receive(\.screenOffMonitor.startMonitoring) await store.finish() } @Test("startSlideshow delegate switches to dashboard") func startSlideshowSwitchesToDashboard() async { let store = TestStore(initialState: AppNavigationFeature.State()) { AppNavigationFeature() } store.exhaustivity = .off await store.send(.settings(.startSlideshow)) await store.receive(\.settings.delegate.startSlideshow) { $0.screen = .dashboard $0.screenOffMonitorState.isSlideshowPlaying = true } } @Test("showDashboard switches screen to dashboard") func showDashboard() async { let store = TestStore(initialState: AppNavigationFeature.State()) { AppNavigationFeature() } await store.send(.showDashboard) { $0.screen = .dashboard $0.screenOffMonitorState.isSlideshowPlaying = true } } @Test("showSettings switches screen to settings") func showSettings() async { var state = AppNavigationFeature.State() state.screen = .dashboard state.screenOffMonitorState.isSlideshowPlaying = true let store = TestStore(initialState: state) { AppNavigationFeature() } await store.send(.showSettings) { $0.screen = .settings $0.screenOffMonitorState.isSlideshowPlaying = false } } @Test("dashboard returnToSettings delegate switches to settings") func dashboardReturnToSettings() async { var state = AppNavigationFeature.State() state.screen = .dashboard state.screenOffMonitorState.isSlideshowPlaying = true let store = TestStore(initialState: state) { AppNavigationFeature() } store.exhaustivity = .off await store.send(.dashboard(.delegate(.returnToSettings))) { $0.screen = .settings $0.screenOffMonitorState.isSlideshowPlaying = false } } @Test("settings action is forwarded without side effects") func settingsActionForwarded() async { let store = TestStore(initialState: AppNavigationFeature.State()) { AppNavigationFeature() } withDependencies: { $0.photoKitAlbums.libraryAccess = { .notDetermined } } await store.send(.settings(.albumPicker(.onAppear))) } @Test("startSlideshow sets isSlideshowPlaying on screen off monitor") func startSlideshowSetsSlideshowPlaying() async { let store = TestStore(initialState: AppNavigationFeature.State()) { AppNavigationFeature() } store.exhaustivity = .off await store.send(.settings(.startSlideshow)) await store.receive(\.settings.delegate.startSlideshow) { $0.screen = .dashboard $0.screenOffMonitorState.isSlideshowPlaying = true } } @Test("returnToSettings clears isSlideshowPlaying on screen off monitor") func returnToSettingsClearsSlideshowPlaying() async { var state = AppNavigationFeature.State() state.screen = .dashboard state.screenOffMonitorState.isSlideshowPlaying = true let store = TestStore(initialState: state) { AppNavigationFeature() } store.exhaustivity = .off await store.send(.dashboard(.delegate(.returnToSettings))) { $0.screen = .settings $0.screenOffMonitorState.isSlideshowPlaying = false } } @Test("tick updates hasLateReminders on screen off monitor") func lateTrackeesUpdatesHasLateReminders() async { var state = AppNavigationFeature.State() state.screen = .dashboard let store = TestStore(initialState: state) { AppNavigationFeature() } withDependencies: { $0.date = .constant(Date()) $0.calendar = .current } // With no late reminders, tick should set hasLateReminders to false await store.send(.dashboard(.alertLoader(.tick))) { $0.dashboardState.alertLoaderState.dayOfWeek = Calendar.current.weekdaySymbols[ Calendar.current.component(.weekday, from: Date()) - 1 ] } } @Test("screenOffMonitor action is forwarded without side effects") func screenOffMonitorActionForwarded() async { let clock = TestClock() let store = TestStore(initialState: AppNavigationFeature.State()) { AppNavigationFeature() } withDependencies: { $0.continuousClock = clock $0.date = .constant(Date()) $0.calendar = .current $0.screenControl.getBrightness = { 0.75 } $0.screenControl.setBrightness = { _ in } } store.exhaustivity = .off await store.send(.screenOffMonitor(.startMonitoring)) { $0.screenOffMonitorState.isMonitoring = true } await store.finish() } }