testing:like all fail
This commit is contained in:
@@ -154,7 +154,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
showReminderWindow(contentView)
|
showReminderWindow(contentView)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func showReminderWindow(_ content: AnyView) {
|
private func showReminderWindow(_ content: AnyView) {
|
||||||
guard let screen = NSScreen.main else { return }
|
guard let screen = NSScreen.main else { return }
|
||||||
|
|
||||||
let window = NSWindow(
|
let window = NSWindow(
|
||||||
@@ -169,9 +169,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
window.backgroundColor = .clear
|
window.backgroundColor = .clear
|
||||||
window.contentView = NSHostingView(rootView: content)
|
window.contentView = NSHostingView(rootView: content)
|
||||||
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
window.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
|
||||||
|
// Ensure this window can receive key events
|
||||||
|
window.acceptsMouseMovedEvents = true
|
||||||
|
window.makeFirstResponder(window.contentView)
|
||||||
|
|
||||||
let windowController = NSWindowController(window: window)
|
let windowController = NSWindowController(window: window)
|
||||||
windowController.showWindow(nil)
|
windowController.showWindow(nil)
|
||||||
|
// Make sure the window is brought to front and made key for key events
|
||||||
|
window.makeKeyAndOrderFront(nil)
|
||||||
|
|
||||||
reminderWindowController = windowController
|
reminderWindowController = windowController
|
||||||
}
|
}
|
||||||
|
|||||||
176
GazeTests/IntegrationTests.swift
Normal file
176
GazeTests/IntegrationTests.swift
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
//
|
||||||
|
// IntegrationTests.swift
|
||||||
|
// GazeTests
|
||||||
|
//
|
||||||
|
// Created by Mike Freno on 1/8/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import Gaze
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class IntegrationTests: XCTestCase {
|
||||||
|
|
||||||
|
var settingsManager: SettingsManager!
|
||||||
|
var timerEngine: TimerEngine!
|
||||||
|
|
||||||
|
override func setUp() async throws {
|
||||||
|
try await super.setUp()
|
||||||
|
settingsManager = SettingsManager.shared
|
||||||
|
UserDefaults.standard.removeObject(forKey: "gazeAppSettings")
|
||||||
|
settingsManager.load()
|
||||||
|
timerEngine = TimerEngine(settingsManager: settingsManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDown() async throws {
|
||||||
|
timerEngine.stop()
|
||||||
|
UserDefaults.standard.removeObject(forKey: "gazeAppSettings")
|
||||||
|
try await super.tearDown()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingsChangePropagateToTimerEngine() {
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
let originalInterval = timerEngine.timerStates[.lookAway]?.remainingSeconds
|
||||||
|
XCTAssertEqual(originalInterval, 20 * 60)
|
||||||
|
|
||||||
|
let newConfig = TimerConfiguration(enabled: true, intervalSeconds: 10 * 60)
|
||||||
|
settingsManager.updateTimerConfiguration(for: .lookAway, configuration: newConfig)
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
let newInterval = timerEngine.timerStates[.lookAway]?.remainingSeconds
|
||||||
|
XCTAssertEqual(newInterval, 10 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDisablingTimerRemovesFromEngine() {
|
||||||
|
timerEngine.start()
|
||||||
|
XCTAssertNotNil(timerEngine.timerStates[.blink])
|
||||||
|
|
||||||
|
var config = TimerConfiguration(enabled: false, intervalSeconds: 5 * 60)
|
||||||
|
settingsManager.updateTimerConfiguration(for: .blink, configuration: config)
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
XCTAssertNil(timerEngine.timerStates[.blink])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testEnablingTimerAddsToEngine() {
|
||||||
|
settingsManager.settings.postureTimer.enabled = false
|
||||||
|
timerEngine.start()
|
||||||
|
XCTAssertNil(timerEngine.timerStates[.posture])
|
||||||
|
|
||||||
|
let config = TimerConfiguration(enabled: true, intervalSeconds: 30 * 60)
|
||||||
|
settingsManager.updateTimerConfiguration(for: .posture, configuration: config)
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
XCTAssertNotNil(timerEngine.timerStates[.posture])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingsPersistAcrossEngineLifecycle() {
|
||||||
|
let config = TimerConfiguration(enabled: false, intervalSeconds: 15 * 60)
|
||||||
|
settingsManager.updateTimerConfiguration(for: .lookAway, configuration: config)
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
timerEngine.stop()
|
||||||
|
|
||||||
|
let newEngine = TimerEngine(settingsManager: settingsManager)
|
||||||
|
newEngine.start()
|
||||||
|
|
||||||
|
XCTAssertNil(newEngine.timerStates[.lookAway])
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMultipleTimerConfigurationUpdates() {
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
let configs = [
|
||||||
|
(TimerType.lookAway, TimerConfiguration(enabled: true, intervalSeconds: 600)),
|
||||||
|
(TimerType.blink, TimerConfiguration(enabled: true, intervalSeconds: 300)),
|
||||||
|
(TimerType.posture, TimerConfiguration(enabled: true, intervalSeconds: 1800))
|
||||||
|
]
|
||||||
|
|
||||||
|
for (type, config) in configs {
|
||||||
|
settingsManager.updateTimerConfiguration(for: type, configuration: config)
|
||||||
|
}
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
XCTAssertEqual(timerEngine.timerStates[.lookAway]?.remainingSeconds, 600)
|
||||||
|
XCTAssertEqual(timerEngine.timerStates[.blink]?.remainingSeconds, 300)
|
||||||
|
XCTAssertEqual(timerEngine.timerStates[.posture]?.remainingSeconds, 1800)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testResetToDefaultsAffectsTimerEngine() {
|
||||||
|
let config = TimerConfiguration(enabled: false, intervalSeconds: 5 * 60)
|
||||||
|
settingsManager.updateTimerConfiguration(for: .blink, configuration: config)
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
XCTAssertNil(timerEngine.timerStates[.blink])
|
||||||
|
|
||||||
|
settingsManager.resetToDefaults()
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
XCTAssertNotNil(timerEngine.timerStates[.blink])
|
||||||
|
XCTAssertEqual(timerEngine.timerStates[.blink]?.remainingSeconds, 5 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTimerEngineRespectsDisabledTimers() {
|
||||||
|
settingsManager.settings.lookAwayTimer.enabled = false
|
||||||
|
settingsManager.settings.blinkTimer.enabled = false
|
||||||
|
settingsManager.settings.postureTimer.enabled = false
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
XCTAssertTrue(timerEngine.timerStates.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testCompleteWorkflow() {
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
||||||
|
|
||||||
|
timerEngine.pause()
|
||||||
|
for (_, state) in timerEngine.timerStates {
|
||||||
|
XCTAssertTrue(state.isPaused)
|
||||||
|
}
|
||||||
|
|
||||||
|
timerEngine.resume()
|
||||||
|
for (_, state) in timerEngine.timerStates {
|
||||||
|
XCTAssertFalse(state.isPaused)
|
||||||
|
}
|
||||||
|
|
||||||
|
timerEngine.skipNext(type: .lookAway)
|
||||||
|
XCTAssertEqual(timerEngine.timerStates[.lookAway]?.remainingSeconds, 20 * 60)
|
||||||
|
|
||||||
|
timerEngine.stop()
|
||||||
|
XCTAssertTrue(timerEngine.timerStates.isEmpty)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testReminderWorkflow() {
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
timerEngine.triggerReminder(for: .lookAway)
|
||||||
|
XCTAssertNotNil(timerEngine.activeReminder)
|
||||||
|
|
||||||
|
for (_, state) in timerEngine.timerStates {
|
||||||
|
XCTAssertTrue(state.isPaused)
|
||||||
|
}
|
||||||
|
|
||||||
|
timerEngine.dismissReminder()
|
||||||
|
XCTAssertNil(timerEngine.activeReminder)
|
||||||
|
|
||||||
|
for (_, state) in timerEngine.timerStates {
|
||||||
|
XCTAssertFalse(state.isPaused)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingsAutoSaveIntegration() {
|
||||||
|
let config = TimerConfiguration(enabled: false, intervalSeconds: 900)
|
||||||
|
settingsManager.updateTimerConfiguration(for: .lookAway, configuration: config)
|
||||||
|
|
||||||
|
settingsManager.load()
|
||||||
|
|
||||||
|
let loadedConfig = settingsManager.timerConfiguration(for: .lookAway)
|
||||||
|
XCTAssertEqual(loadedConfig.intervalSeconds, 900)
|
||||||
|
XCTAssertFalse(loadedConfig.enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -74,7 +74,7 @@ final class SettingsManagerTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testUpdateTimerConfiguration() {
|
func testUpdateTimerConfiguration() {
|
||||||
var newConfig = TimerConfiguration(enabled: false, intervalSeconds: 10 * 60)
|
let newConfig = TimerConfiguration(enabled: false, intervalSeconds: 10 * 60)
|
||||||
settingsManager.updateTimerConfiguration(for: .lookAway, configuration: newConfig)
|
settingsManager.updateTimerConfiguration(for: .lookAway, configuration: newConfig)
|
||||||
|
|
||||||
let retrieved = settingsManager.timerConfiguration(for: .lookAway)
|
let retrieved = settingsManager.timerConfiguration(for: .lookAway)
|
||||||
|
|||||||
@@ -141,4 +141,169 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
XCTAssertFalse(state.isPaused)
|
XCTAssertFalse(state.isPaused)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func testTriggerReminderForLookAway() {
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
timerEngine.triggerReminder(for: .lookAway)
|
||||||
|
|
||||||
|
XCTAssertNotNil(timerEngine.activeReminder)
|
||||||
|
if case .lookAwayTriggered(let countdown) = timerEngine.activeReminder {
|
||||||
|
XCTAssertEqual(countdown, settingsManager.settings.lookAwayCountdownSeconds)
|
||||||
|
} else {
|
||||||
|
XCTFail("Expected lookAwayTriggered reminder")
|
||||||
|
}
|
||||||
|
|
||||||
|
for (_, state) in timerEngine.timerStates {
|
||||||
|
XCTAssertTrue(state.isPaused)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTriggerReminderForBlink() {
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
timerEngine.triggerReminder(for: .blink)
|
||||||
|
|
||||||
|
XCTAssertNotNil(timerEngine.activeReminder)
|
||||||
|
if case .blinkTriggered = timerEngine.activeReminder {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} else {
|
||||||
|
XCTFail("Expected blinkTriggered reminder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testTriggerReminderForPosture() {
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
timerEngine.triggerReminder(for: .posture)
|
||||||
|
|
||||||
|
XCTAssertNotNil(timerEngine.activeReminder)
|
||||||
|
if case .postureTriggered = timerEngine.activeReminder {
|
||||||
|
XCTAssertTrue(true)
|
||||||
|
} else {
|
||||||
|
XCTFail("Expected postureTriggered reminder")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGetTimeRemainingForNonExistentTimer() {
|
||||||
|
let timeRemaining = timerEngine.getTimeRemaining(for: .lookAway)
|
||||||
|
XCTAssertEqual(timeRemaining, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGetFormattedTimeRemainingZeroSeconds() {
|
||||||
|
timerEngine.start()
|
||||||
|
timerEngine.timerStates[.lookAway]?.remainingSeconds = 0
|
||||||
|
|
||||||
|
let formatted = timerEngine.getFormattedTimeRemaining(for: .lookAway)
|
||||||
|
XCTAssertEqual(formatted, "0:00")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGetFormattedTimeRemainingLessThanMinute() {
|
||||||
|
timerEngine.start()
|
||||||
|
timerEngine.timerStates[.lookAway]?.remainingSeconds = 45
|
||||||
|
|
||||||
|
let formatted = timerEngine.getFormattedTimeRemaining(for: .lookAway)
|
||||||
|
XCTAssertEqual(formatted, "0:45")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testGetFormattedTimeRemainingExactHour() {
|
||||||
|
timerEngine.start()
|
||||||
|
timerEngine.timerStates[.lookAway]?.remainingSeconds = 3600
|
||||||
|
|
||||||
|
let formatted = timerEngine.getFormattedTimeRemaining(for: .lookAway)
|
||||||
|
XCTAssertEqual(formatted, "1:00:00")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMultipleStartCallsResetTimers() {
|
||||||
|
timerEngine.start()
|
||||||
|
timerEngine.timerStates[.lookAway]?.remainingSeconds = 100
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
XCTAssertEqual(timerEngine.timerStates[.lookAway]?.remainingSeconds, 20 * 60)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSkipNextPreservesPausedState() {
|
||||||
|
timerEngine.start()
|
||||||
|
timerEngine.pause()
|
||||||
|
|
||||||
|
timerEngine.skipNext(type: .lookAway)
|
||||||
|
|
||||||
|
XCTAssertTrue(timerEngine.timerStates[.lookAway]?.isPaused ?? false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSkipNextPreservesActiveState() {
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
timerEngine.skipNext(type: .lookAway)
|
||||||
|
|
||||||
|
XCTAssertTrue(timerEngine.timerStates[.lookAway]?.isActive ?? false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDismissReminderWithNoActiveReminder() {
|
||||||
|
timerEngine.start()
|
||||||
|
XCTAssertNil(timerEngine.activeReminder)
|
||||||
|
|
||||||
|
timerEngine.dismissReminder()
|
||||||
|
|
||||||
|
XCTAssertNil(timerEngine.activeReminder)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDismissBlinkReminderDoesNotResumeTimers() {
|
||||||
|
timerEngine.start()
|
||||||
|
timerEngine.activeReminder = .blinkTriggered
|
||||||
|
|
||||||
|
timerEngine.dismissReminder()
|
||||||
|
|
||||||
|
for (_, state) in timerEngine.timerStates {
|
||||||
|
XCTAssertFalse(state.isPaused)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testDismissPostureReminderDoesNotResumeTimers() {
|
||||||
|
timerEngine.start()
|
||||||
|
timerEngine.activeReminder = .postureTriggered
|
||||||
|
|
||||||
|
timerEngine.dismissReminder()
|
||||||
|
|
||||||
|
for (_, state) in timerEngine.timerStates {
|
||||||
|
XCTAssertFalse(state.isPaused)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAllTimersStartWhenEnabled() {
|
||||||
|
settingsManager.settings.lookAwayTimer.enabled = true
|
||||||
|
settingsManager.settings.blinkTimer.enabled = true
|
||||||
|
settingsManager.settings.postureTimer.enabled = true
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
||||||
|
for timerType in TimerType.allCases {
|
||||||
|
XCTAssertNotNil(timerEngine.timerStates[timerType])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAllTimersDisabled() {
|
||||||
|
settingsManager.settings.lookAwayTimer.enabled = false
|
||||||
|
settingsManager.settings.blinkTimer.enabled = false
|
||||||
|
settingsManager.settings.postureTimer.enabled = false
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
XCTAssertEqual(timerEngine.timerStates.count, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPartialTimersEnabled() {
|
||||||
|
settingsManager.settings.lookAwayTimer.enabled = true
|
||||||
|
settingsManager.settings.blinkTimer.enabled = false
|
||||||
|
settingsManager.settings.postureTimer.enabled = true
|
||||||
|
|
||||||
|
timerEngine.start()
|
||||||
|
|
||||||
|
XCTAssertEqual(timerEngine.timerStates.count, 2)
|
||||||
|
XCTAssertNotNil(timerEngine.timerStates[.lookAway])
|
||||||
|
XCTAssertNil(timerEngine.timerStates[.blink])
|
||||||
|
XCTAssertNotNil(timerEngine.timerStates[.posture])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
86
GazeUITests/AccessibilityUITests.swift
Normal file
86
GazeUITests/AccessibilityUITests.swift
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
//
|
||||||
|
// AccessibilityUITests.swift
|
||||||
|
// GazeUITests
|
||||||
|
//
|
||||||
|
// Created by Mike Freno on 1/8/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class AccessibilityUITests: XCTestCase {
|
||||||
|
|
||||||
|
var app: XCUIApplication!
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
continueAfterFailure = false
|
||||||
|
app = XCUIApplication()
|
||||||
|
app.launchArguments.append("--skip-onboarding")
|
||||||
|
app.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
app = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMenuBarAccessibilityLabels() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
for button in app.buttons.allElementsBoundByIndex {
|
||||||
|
XCTAssertFalse(button.label.isEmpty, "Button should have accessibility label")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testKeyboardNavigation() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
app.typeKey(XCUIKeyboardKey.tab, modifierFlags: [])
|
||||||
|
|
||||||
|
let focusedElement = app.descendants(matching: .any).element(matching: NSPredicate(format: "hasKeyboardFocus == true"))
|
||||||
|
XCTAssertTrue(focusedElement.exists || app.buttons.count > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAllButtonsAreHittable() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
|
let buttons = app.buttons.allElementsBoundByIndex
|
||||||
|
for button in buttons where button.exists && button.isEnabled {
|
||||||
|
XCTAssertTrue(button.isHittable || !button.isEnabled, "Enabled button should be hittable: \(button.label)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testVoiceOverElementsHaveLabels() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let staticTexts = app.staticTexts.allElementsBoundByIndex
|
||||||
|
for text in staticTexts where text.exists {
|
||||||
|
XCTAssertFalse(text.label.isEmpty, "Static text should have label")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testImagesHaveAccessibilityTraits() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let images = app.images.allElementsBoundByIndex
|
||||||
|
for image in images where image.exists {
|
||||||
|
XCTAssertFalse(image.label.isEmpty, "Image should have accessibility label")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
102
GazeUITests/MenuBarUITests.swift
Normal file
102
GazeUITests/MenuBarUITests.swift
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
//
|
||||||
|
// MenuBarUITests.swift
|
||||||
|
// GazeUITests
|
||||||
|
//
|
||||||
|
// Created by Mike Freno on 1/8/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class MenuBarUITests: XCTestCase {
|
||||||
|
|
||||||
|
var app: XCUIApplication!
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
continueAfterFailure = false
|
||||||
|
app = XCUIApplication()
|
||||||
|
app.launchArguments.append("--skip-onboarding")
|
||||||
|
app.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
app = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMenuBarExtraExists() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
XCTAssertTrue(menuBar.waitForExistence(timeout: 5))
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMenuBarCanBeOpened() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let gazeTitle = app.staticTexts["Gaze"]
|
||||||
|
XCTAssertTrue(gazeTitle.waitForExistence(timeout: 2) || app.staticTexts.count > 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMenuBarHasTimerStatus() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let activeTimersText = app.staticTexts["Active Timers"]
|
||||||
|
let hasTimerInfo = activeTimersText.exists || app.staticTexts.count > 3
|
||||||
|
|
||||||
|
XCTAssertTrue(hasTimerInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMenuBarHasPauseResumeControl() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let pauseButton = app.buttons.containing(NSPredicate(format: "label CONTAINS 'Pause' OR label CONTAINS 'Resume'")).firstMatch
|
||||||
|
XCTAssertTrue(pauseButton.waitForExistence(timeout: 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMenuBarHasSettingsButton() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let settingsButton = app.buttons["Settings"]
|
||||||
|
let settingsMenuItem = app.menuItems["Settings"]
|
||||||
|
|
||||||
|
XCTAssertTrue(settingsButton.exists || settingsMenuItem.exists)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMenuBarHasQuitButton() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let quitButton = app.buttons.containing(NSPredicate(format: "label CONTAINS 'Quit'")).firstMatch
|
||||||
|
XCTAssertTrue(quitButton.waitForExistence(timeout: 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPauseResumeToggle() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let pauseButton = app.buttons.containing(NSPredicate(format: "label CONTAINS 'Pause'")).firstMatch
|
||||||
|
let resumeButton = app.buttons.containing(NSPredicate(format: "label CONTAINS 'Resume'")).firstMatch
|
||||||
|
|
||||||
|
if pauseButton.exists && pauseButton.isHittable {
|
||||||
|
pauseButton.tap()
|
||||||
|
XCTAssertTrue(resumeButton.waitForExistence(timeout: 2))
|
||||||
|
} else if resumeButton.exists && resumeButton.isHittable {
|
||||||
|
resumeButton.tap()
|
||||||
|
XCTAssertTrue(pauseButton.waitForExistence(timeout: 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
92
GazeUITests/OnboardingUITests.swift
Normal file
92
GazeUITests/OnboardingUITests.swift
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
//
|
||||||
|
// OnboardingUITests.swift
|
||||||
|
// GazeUITests
|
||||||
|
//
|
||||||
|
// Created by Mike Freno on 1/8/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class OnboardingUITests: XCTestCase {
|
||||||
|
|
||||||
|
var app: XCUIApplication!
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
continueAfterFailure = false
|
||||||
|
app = XCUIApplication()
|
||||||
|
app.launchArguments.append("--reset-onboarding")
|
||||||
|
app.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
app = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOnboardingWelcomeScreen() throws {
|
||||||
|
XCTAssertTrue(app.staticTexts["Welcome to Gaze"].exists || app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'Welcome'")).firstMatch.exists)
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOnboardingNavigationFromWelcome() throws {
|
||||||
|
let continueButton = app.buttons["Continue"]
|
||||||
|
|
||||||
|
if continueButton.waitForExistence(timeout: 2) {
|
||||||
|
continueButton.tap()
|
||||||
|
|
||||||
|
let nextScreen = app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'Setup' OR label CONTAINS 'Configure'")).firstMatch
|
||||||
|
XCTAssertTrue(nextScreen.waitForExistence(timeout: 2))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOnboardingBackNavigation() throws {
|
||||||
|
let continueButton = app.buttons["Continue"]
|
||||||
|
if continueButton.waitForExistence(timeout: 2) {
|
||||||
|
continueButton.tap()
|
||||||
|
|
||||||
|
let backButton = app.buttons["Back"]
|
||||||
|
if backButton.waitForExistence(timeout: 1) {
|
||||||
|
backButton.tap()
|
||||||
|
|
||||||
|
XCTAssertTrue(app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'Welcome'")).firstMatch.waitForExistence(timeout: 1))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOnboardingCompleteFlow() throws {
|
||||||
|
let continueButtons = app.buttons.matching(identifier: "Continue")
|
||||||
|
let nextButtons = app.buttons.matching(identifier: "Next")
|
||||||
|
|
||||||
|
var currentStep = 0
|
||||||
|
let maxSteps = 10
|
||||||
|
|
||||||
|
while currentStep < maxSteps {
|
||||||
|
if continueButtons.firstMatch.exists && continueButtons.firstMatch.isHittable {
|
||||||
|
continueButtons.firstMatch.tap()
|
||||||
|
currentStep += 1
|
||||||
|
sleep(1)
|
||||||
|
} else if nextButtons.firstMatch.exists && nextButtons.firstMatch.isHittable {
|
||||||
|
nextButtons.firstMatch.tap()
|
||||||
|
currentStep += 1
|
||||||
|
sleep(1)
|
||||||
|
} else if app.buttons["Get Started"].exists {
|
||||||
|
app.buttons["Get Started"].tap()
|
||||||
|
break
|
||||||
|
} else if app.buttons["Done"].exists {
|
||||||
|
app.buttons["Done"].tap()
|
||||||
|
break
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
XCTAssertLessThan(currentStep, maxSteps, "Onboarding flow should complete")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOnboardingHasRequiredElements() throws {
|
||||||
|
let hasText = app.staticTexts.count > 0
|
||||||
|
let hasButtons = app.buttons.count > 0
|
||||||
|
|
||||||
|
XCTAssertTrue(hasText, "Onboarding should have text elements")
|
||||||
|
XCTAssertTrue(hasButtons, "Onboarding should have buttons")
|
||||||
|
}
|
||||||
|
}
|
||||||
88
GazeUITests/PerformanceUITests.swift
Normal file
88
GazeUITests/PerformanceUITests.swift
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
//
|
||||||
|
// PerformanceUITests.swift
|
||||||
|
// GazeUITests
|
||||||
|
//
|
||||||
|
// Created by Mike Freno on 1/8/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class PerformanceUITests: XCTestCase {
|
||||||
|
|
||||||
|
var app: XCUIApplication!
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
continueAfterFailure = false
|
||||||
|
app = XCUIApplication()
|
||||||
|
app.launchArguments.append("--skip-onboarding")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
app = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAppLaunchPerformance() throws {
|
||||||
|
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||||
|
app.launch()
|
||||||
|
app.terminate()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMenuBarOpenPerformance() throws {
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
measure {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
_ = app.staticTexts["Gaze"].waitForExistence(timeout: 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingsWindowOpenPerformance() throws {
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
measure {
|
||||||
|
let settingsButton = app.menuItems["Settings"]
|
||||||
|
if settingsButton.waitForExistence(timeout: 2) {
|
||||||
|
settingsButton.click()
|
||||||
|
|
||||||
|
let settingsWindow = app.windows["Settings"]
|
||||||
|
_ = settingsWindow.waitForExistence(timeout: 3)
|
||||||
|
|
||||||
|
if settingsWindow.exists {
|
||||||
|
let closeButton = settingsWindow.buttons[XCUIIdentifierCloseWindow]
|
||||||
|
if closeButton.exists {
|
||||||
|
closeButton.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
menuBar.click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testMemoryUsageDuringOperation() throws {
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.waitForExistence(timeout: 5) {
|
||||||
|
measure(metrics: [XCTMemoryMetric()]) {
|
||||||
|
for _ in 0..<5 {
|
||||||
|
menuBar.click()
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
|
menuBar.click()
|
||||||
|
sleep(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
83
GazeUITests/SettingsUITests.swift
Normal file
83
GazeUITests/SettingsUITests.swift
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
//
|
||||||
|
// SettingsUITests.swift
|
||||||
|
// GazeUITests
|
||||||
|
//
|
||||||
|
// Created by Mike Freno on 1/8/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class SettingsUITests: XCTestCase {
|
||||||
|
|
||||||
|
var app: XCUIApplication!
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
continueAfterFailure = false
|
||||||
|
app = XCUIApplication()
|
||||||
|
app.launchArguments.append("--skip-onboarding")
|
||||||
|
app.launch()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
app = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOpenSettingsWindow() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.exists {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let settingsButton = app.menuItems["Settings"]
|
||||||
|
if settingsButton.waitForExistence(timeout: 2) {
|
||||||
|
settingsButton.click()
|
||||||
|
|
||||||
|
let settingsWindow = app.windows["Settings"]
|
||||||
|
XCTAssertTrue(settingsWindow.waitForExistence(timeout: 3))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingsWindowHasTimerControls() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.exists {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let settingsButton = app.menuItems["Settings"]
|
||||||
|
if settingsButton.waitForExistence(timeout: 2) {
|
||||||
|
settingsButton.click()
|
||||||
|
|
||||||
|
sleep(1)
|
||||||
|
|
||||||
|
let hasSliders = app.sliders.count > 0
|
||||||
|
let hasTextFields = app.textFields.count > 0
|
||||||
|
let hasSwitches = app.switches.count > 0
|
||||||
|
|
||||||
|
let hasControls = hasSliders || hasTextFields || hasSwitches
|
||||||
|
XCTAssertTrue(hasControls, "Settings should have timer controls")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingsWindowCanBeClosed() throws {
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
if menuBar.exists {
|
||||||
|
menuBar.click()
|
||||||
|
|
||||||
|
let settingsButton = app.menuItems["Settings"]
|
||||||
|
if settingsButton.waitForExistence(timeout: 2) {
|
||||||
|
settingsButton.click()
|
||||||
|
|
||||||
|
let settingsWindow = app.windows["Settings"]
|
||||||
|
if settingsWindow.waitForExistence(timeout: 3) {
|
||||||
|
let closeButton = settingsWindow.buttons[XCUIIdentifierCloseWindow]
|
||||||
|
if closeButton.exists {
|
||||||
|
closeButton.click()
|
||||||
|
|
||||||
|
XCTAssertFalse(settingsWindow.exists)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user