general: test redux
This commit is contained in:
@@ -13,7 +13,7 @@ import os.log
|
||||
@MainActor
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
@Published var timerEngine: TimerEngine?
|
||||
private let settingsManager: SettingsManager = .shared
|
||||
private let serviceContainer: ServiceContainer
|
||||
private let windowManager: WindowManaging
|
||||
private var updateManager: UpdateManager?
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
@@ -21,19 +21,21 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
|
||||
// Logging manager
|
||||
private let logger = LoggingManager.shared
|
||||
|
||||
// Smart Mode services
|
||||
private var fullscreenService: FullscreenDetectionService?
|
||||
private var idleService: IdleMonitoringService?
|
||||
private var usageTrackingService: UsageTrackingService?
|
||||
|
||||
// Convenience accessor for settings
|
||||
private var settingsManager: any SettingsProviding {
|
||||
serviceContainer.settingsManager
|
||||
}
|
||||
|
||||
override init() {
|
||||
self.serviceContainer = ServiceContainer.shared
|
||||
self.windowManager = WindowManager.shared
|
||||
super.init()
|
||||
}
|
||||
|
||||
/// Initializer for testing with injectable dependencies
|
||||
init(windowManager: WindowManaging) {
|
||||
init(serviceContainer: ServiceContainer, windowManager: WindowManaging) {
|
||||
self.serviceContainer = serviceContainer
|
||||
self.windowManager = windowManager
|
||||
super.init()
|
||||
}
|
||||
@@ -46,9 +48,11 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
logger.configureLogging()
|
||||
logger.appLogger.info("🚀 Application did finish launching")
|
||||
|
||||
timerEngine = TimerEngine(settingsManager: settingsManager)
|
||||
// Get timer engine from service container
|
||||
timerEngine = serviceContainer.timerEngine
|
||||
|
||||
setupSmartModeServices()
|
||||
// Setup smart mode services through container
|
||||
serviceContainer.setupSmartModeServices()
|
||||
|
||||
// Initialize update manager after onboarding is complete
|
||||
if settingsManager.settings.hasCompletedOnboarding {
|
||||
@@ -64,37 +68,21 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
private func setupSmartModeServices() {
|
||||
fullscreenService = FullscreenDetectionService()
|
||||
idleService = IdleMonitoringService(
|
||||
idleThresholdMinutes: settingsManager.settings.smartMode.idleThresholdMinutes
|
||||
)
|
||||
usageTrackingService = UsageTrackingService(
|
||||
resetThresholdMinutes: settingsManager.settings.smartMode.usageResetAfterMinutes
|
||||
)
|
||||
|
||||
if let idleService = idleService {
|
||||
usageTrackingService?.setupIdleMonitoring(idleService)
|
||||
}
|
||||
|
||||
// Connect services to timer engine
|
||||
timerEngine?.setupSmartMode(
|
||||
fullscreenService: fullscreenService,
|
||||
idleService: idleService
|
||||
)
|
||||
|
||||
// Observe smart mode settings changes
|
||||
settingsManager.$settings
|
||||
// Note: Smart mode setup is now handled by ServiceContainer
|
||||
// Keeping this method for settings change observation
|
||||
private func observeSmartModeSettings() {
|
||||
settingsManager.settingsPublisher
|
||||
.map { $0.smartMode }
|
||||
.removeDuplicates()
|
||||
.sink { [weak self] smartMode in
|
||||
self?.idleService?.updateThreshold(minutes: smartMode.idleThresholdMinutes)
|
||||
self?.usageTrackingService?.updateResetThreshold(
|
||||
guard let self = self else { return }
|
||||
self.serviceContainer.idleService?.updateThreshold(minutes: smartMode.idleThresholdMinutes)
|
||||
self.serviceContainer.usageTrackingService?.updateResetThreshold(
|
||||
minutes: smartMode.usageResetAfterMinutes)
|
||||
|
||||
// Force state check when settings change to apply immediately
|
||||
self?.fullscreenService?.forceUpdate()
|
||||
self?.idleService?.forceUpdate()
|
||||
self.serviceContainer.fullscreenService?.forceUpdate()
|
||||
self.serviceContainer.idleService?.forceUpdate()
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
}
|
||||
@@ -117,7 +105,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
}
|
||||
|
||||
private func observeSettingsChanges() {
|
||||
settingsManager.$settings
|
||||
settingsManager.settingsPublisher
|
||||
.sink { [weak self] settings in
|
||||
if settings.hasCompletedOnboarding && self?.hasStartedTimers == false {
|
||||
self?.startTimers()
|
||||
@@ -129,6 +117,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, ObservableObject {
|
||||
}
|
||||
}
|
||||
.store(in: &cancellables)
|
||||
|
||||
// Also observe smart mode settings
|
||||
observeSmartModeSettings()
|
||||
}
|
||||
|
||||
func applicationWillTerminate(_ notification: Notification) {
|
||||
|
||||
@@ -10,6 +10,8 @@ import SwiftUI
|
||||
@main
|
||||
struct GazeApp: App {
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
// Note: SettingsManager.shared is used directly here for SwiftUI view updates
|
||||
// AppDelegate uses ServiceContainer for dependency injection
|
||||
@StateObject private var settingsManager = SettingsManager.shared
|
||||
|
||||
init() {
|
||||
|
||||
160
Gaze/Services/MockWindowManager.swift
Normal file
160
Gaze/Services/MockWindowManager.swift
Normal file
@@ -0,0 +1,160 @@
|
||||
//
|
||||
// MockWindowManager.swift
|
||||
// Gaze
|
||||
//
|
||||
// Mock implementation of WindowManaging for testing purposes.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
/// Mock window manager that tracks window operations without creating actual windows.
|
||||
/// Useful for unit testing UI flows and state management.
|
||||
@MainActor
|
||||
final class MockWindowManager: WindowManaging {
|
||||
|
||||
// MARK: - State Tracking
|
||||
|
||||
private(set) var isOverlayReminderVisible = false
|
||||
private(set) var isSubtleReminderVisible = false
|
||||
|
||||
// MARK: - Operation History
|
||||
|
||||
struct WindowOperation {
|
||||
let timestamp: Date
|
||||
let operation: Operation
|
||||
|
||||
enum Operation {
|
||||
case showOverlayReminder
|
||||
case showSubtleReminder
|
||||
case dismissOverlayReminder
|
||||
case dismissSubtleReminder
|
||||
case dismissAllReminders
|
||||
case showSettings(initialTab: Int)
|
||||
case showOnboarding
|
||||
}
|
||||
}
|
||||
|
||||
private(set) var operations: [WindowOperation] = []
|
||||
|
||||
// MARK: - Callbacks for Testing
|
||||
|
||||
var onShowOverlayReminder: (() -> Void)?
|
||||
var onShowSubtleReminder: (() -> Void)?
|
||||
var onDismissOverlayReminder: (() -> Void)?
|
||||
var onDismissSubtleReminder: (() -> Void)?
|
||||
var onShowSettings: ((Int) -> Void)?
|
||||
var onShowOnboarding: (() -> Void)?
|
||||
|
||||
// MARK: - WindowManaging Implementation
|
||||
|
||||
func showReminderWindow<Content: View>(_ content: Content, windowType: ReminderWindowType) {
|
||||
let operation: WindowOperation.Operation
|
||||
|
||||
switch windowType {
|
||||
case .overlay:
|
||||
isOverlayReminderVisible = true
|
||||
operation = .showOverlayReminder
|
||||
onShowOverlayReminder?()
|
||||
case .subtle:
|
||||
isSubtleReminderVisible = true
|
||||
operation = .showSubtleReminder
|
||||
onShowSubtleReminder?()
|
||||
}
|
||||
|
||||
operations.append(WindowOperation(timestamp: Date(), operation: operation))
|
||||
}
|
||||
|
||||
func dismissOverlayReminder() {
|
||||
isOverlayReminderVisible = false
|
||||
operations.append(WindowOperation(timestamp: Date(), operation: .dismissOverlayReminder))
|
||||
onDismissOverlayReminder?()
|
||||
}
|
||||
|
||||
func dismissSubtleReminder() {
|
||||
isSubtleReminderVisible = false
|
||||
operations.append(WindowOperation(timestamp: Date(), operation: .dismissSubtleReminder))
|
||||
onDismissSubtleReminder?()
|
||||
}
|
||||
|
||||
func dismissAllReminders() {
|
||||
isOverlayReminderVisible = false
|
||||
isSubtleReminderVisible = false
|
||||
operations.append(WindowOperation(timestamp: Date(), operation: .dismissAllReminders))
|
||||
onDismissOverlayReminder?()
|
||||
onDismissSubtleReminder?()
|
||||
}
|
||||
|
||||
func showSettings(settingsManager: any SettingsProviding, initialTab: Int) {
|
||||
operations.append(WindowOperation(timestamp: Date(), operation: .showSettings(initialTab: initialTab)))
|
||||
onShowSettings?(initialTab)
|
||||
}
|
||||
|
||||
func showOnboarding(settingsManager: any SettingsProviding) {
|
||||
operations.append(WindowOperation(timestamp: Date(), operation: .showOnboarding))
|
||||
onShowOnboarding?()
|
||||
}
|
||||
|
||||
// MARK: - Test Helpers
|
||||
|
||||
/// Resets all state for a fresh test
|
||||
func reset() {
|
||||
isOverlayReminderVisible = false
|
||||
isSubtleReminderVisible = false
|
||||
operations.removeAll()
|
||||
onShowOverlayReminder = nil
|
||||
onShowSubtleReminder = nil
|
||||
onDismissOverlayReminder = nil
|
||||
onDismissSubtleReminder = nil
|
||||
onShowSettings = nil
|
||||
onShowOnboarding = nil
|
||||
}
|
||||
|
||||
/// Returns the number of times a specific operation was performed
|
||||
func operationCount(_ operationType: WindowOperation.Operation) -> Int {
|
||||
operations.filter { operation in
|
||||
switch (operation.operation, operationType) {
|
||||
case (.showOverlayReminder, .showOverlayReminder),
|
||||
(.showSubtleReminder, .showSubtleReminder),
|
||||
(.dismissOverlayReminder, .dismissOverlayReminder),
|
||||
(.dismissSubtleReminder, .dismissSubtleReminder),
|
||||
(.dismissAllReminders, .dismissAllReminders),
|
||||
(.showOnboarding, .showOnboarding):
|
||||
return true
|
||||
case (.showSettings(let tab1), .showSettings(let tab2)):
|
||||
return tab1 == tab2
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}.count
|
||||
}
|
||||
|
||||
/// Returns true if the operation was performed at least once
|
||||
func didPerformOperation(_ operationType: WindowOperation.Operation) -> Bool {
|
||||
operationCount(operationType) > 0
|
||||
}
|
||||
|
||||
/// Returns the last operation performed, if any
|
||||
var lastOperation: WindowOperation? {
|
||||
operations.last
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - Equatable Conformance for Testing
|
||||
|
||||
extension MockWindowManager.WindowOperation.Operation: Equatable {
|
||||
static func == (lhs: MockWindowManager.WindowOperation.Operation, rhs: MockWindowManager.WindowOperation.Operation) -> Bool {
|
||||
switch (lhs, rhs) {
|
||||
case (.showOverlayReminder, .showOverlayReminder),
|
||||
(.showSubtleReminder, .showSubtleReminder),
|
||||
(.dismissOverlayReminder, .dismissOverlayReminder),
|
||||
(.dismissSubtleReminder, .dismissSubtleReminder),
|
||||
(.dismissAllReminders, .dismissAllReminders),
|
||||
(.showOnboarding, .showOnboarding):
|
||||
return true
|
||||
case (.showSettings(let tab1), .showSettings(let tab2)):
|
||||
return tab1 == tab2
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user