Version bump to v0.4.0
This commit is contained in:
@@ -424,7 +424,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 5;
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -438,7 +438,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 0.3.0;
|
MARKETING_VERSION = 0.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
@@ -460,7 +460,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
CURRENT_PROJECT_VERSION = 5;
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
ENABLE_APP_SANDBOX = YES;
|
ENABLE_APP_SANDBOX = YES;
|
||||||
ENABLE_HARDENED_RUNTIME = YES;
|
ENABLE_HARDENED_RUNTIME = YES;
|
||||||
@@ -474,7 +474,7 @@
|
|||||||
"@executable_path/../Frameworks",
|
"@executable_path/../Frameworks",
|
||||||
);
|
);
|
||||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||||
MARKETING_VERSION = 0.3.0;
|
MARKETING_VERSION = 0.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
REGISTER_APP_GROUPS = YES;
|
REGISTER_APP_GROUPS = YES;
|
||||||
@@ -492,11 +492,11 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 26.2;
|
MACOSX_DEPLOYMENT_TARGET = 26.2;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 0.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
@@ -513,11 +513,11 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MACOSX_DEPLOYMENT_TARGET = 26.2;
|
MACOSX_DEPLOYMENT_TARGET = 26.2;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 0.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeTests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeTests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
@@ -533,10 +533,10 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 0.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeUITests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeUITests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
@@ -552,10 +552,10 @@
|
|||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 1;
|
CURRENT_PROJECT_VERSION = 6;
|
||||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
MARKETING_VERSION = 1.0;
|
MARKETING_VERSION = 0.4.0;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeUITests;
|
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeUITests;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||||
|
|||||||
@@ -37,10 +37,12 @@ struct SettingsWindowView: View {
|
|||||||
Label("Posture", systemImage: "figure.stand")
|
Label("Posture", systemImage: "figure.stand")
|
||||||
}
|
}
|
||||||
|
|
||||||
UserTimersView(userTimers: Binding(
|
UserTimersView(
|
||||||
|
userTimers: Binding(
|
||||||
get: { settingsManager.settings.userTimers },
|
get: { settingsManager.settings.userTimers },
|
||||||
set: { settingsManager.settings.userTimers = $0 }
|
set: { settingsManager.settings.userTimers = $0 }
|
||||||
))
|
)
|
||||||
|
)
|
||||||
.tag(3)
|
.tag(3)
|
||||||
.tabItem {
|
.tabItem {
|
||||||
Label("User Timers", systemImage: "plus.circle")
|
Label("User Timers", systemImage: "plus.circle")
|
||||||
|
|||||||
@@ -32,9 +32,9 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
||||||
XCTAssertNotNil(timerEngine.timerStates[.lookAway])
|
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.lookAway)])
|
||||||
XCTAssertNotNil(timerEngine.timerStates[.blink])
|
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||||
XCTAssertNotNil(timerEngine.timerStates[.posture])
|
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.posture)])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDisabledTimersNotInitialized() {
|
func testDisabledTimersNotInitialized() {
|
||||||
@@ -43,16 +43,16 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
XCTAssertEqual(timerEngine.timerStates.count, 2)
|
XCTAssertEqual(timerEngine.timerStates.count, 2)
|
||||||
XCTAssertNotNil(timerEngine.timerStates[.lookAway])
|
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.lookAway)])
|
||||||
XCTAssertNil(timerEngine.timerStates[.blink])
|
XCTAssertNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||||
XCTAssertNotNil(timerEngine.timerStates[.posture])
|
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.posture)])
|
||||||
}
|
}
|
||||||
|
|
||||||
func testTimerStateInitialValues() {
|
func testTimerStateInitialValues() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
let lookAwayState = timerEngine.timerStates[.lookAway]!
|
let lookAwayState = timerEngine.timerStates[.builtIn(.lookAway)]!
|
||||||
XCTAssertEqual(lookAwayState.type, .lookAway)
|
XCTAssertEqual(lookAwayState.identifier, .builtIn(.lookAway))
|
||||||
XCTAssertEqual(lookAwayState.remainingSeconds, 20 * 60)
|
XCTAssertEqual(lookAwayState.remainingSeconds, 20 * 60)
|
||||||
XCTAssertFalse(lookAwayState.isPaused)
|
XCTAssertFalse(lookAwayState.isPaused)
|
||||||
XCTAssertTrue(lookAwayState.isActive)
|
XCTAssertTrue(lookAwayState.isActive)
|
||||||
@@ -81,33 +81,33 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
settingsManager.settings.lookAwayTimer.intervalSeconds = 60
|
settingsManager.settings.lookAwayTimer.intervalSeconds = 60
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
timerEngine.timerStates[.lookAway]?.remainingSeconds = 10
|
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 10
|
||||||
|
|
||||||
timerEngine.skipNext(type: .lookAway)
|
timerEngine.skipNext(identifier: .builtIn(.lookAway))
|
||||||
|
|
||||||
XCTAssertEqual(timerEngine.timerStates[.lookAway]?.remainingSeconds, 60)
|
XCTAssertEqual(timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds, 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetTimeRemaining() {
|
func testGetTimeRemaining() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
let timeRemaining = timerEngine.getTimeRemaining(for: .lookAway)
|
let timeRemaining = timerEngine.getTimeRemaining(for: .builtIn(.lookAway))
|
||||||
XCTAssertEqual(timeRemaining, TimeInterval(20 * 60))
|
XCTAssertEqual(timeRemaining, TimeInterval(20 * 60))
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetFormattedTimeRemaining() {
|
func testGetFormattedTimeRemaining() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
timerEngine.timerStates[.lookAway]?.remainingSeconds = 125
|
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 125
|
||||||
|
|
||||||
let formatted = timerEngine.getFormattedTimeRemaining(for: .lookAway)
|
let formatted = timerEngine.getFormattedTimeRemaining(for: .builtIn(.lookAway))
|
||||||
XCTAssertEqual(formatted, "2:05")
|
XCTAssertEqual(formatted, "2:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetFormattedTimeRemainingWithHours() {
|
func testGetFormattedTimeRemainingWithHours() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
timerEngine.timerStates[.lookAway]?.remainingSeconds = 3665
|
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 3665
|
||||||
|
|
||||||
let formatted = timerEngine.getFormattedTimeRemaining(for: .lookAway)
|
let formatted = timerEngine.getFormattedTimeRemaining(for: .builtIn(.lookAway))
|
||||||
XCTAssertEqual(formatted, "1:01:05")
|
XCTAssertEqual(formatted, "1:01:05")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -121,13 +121,13 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
|
|
||||||
func testDismissReminderResetsTimer() {
|
func testDismissReminderResetsTimer() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
timerEngine.timerStates[.blink]?.remainingSeconds = 0
|
timerEngine.timerStates[.builtIn(.blink)]?.remainingSeconds = 0
|
||||||
timerEngine.activeReminder = .blinkTriggered
|
timerEngine.activeReminder = .blinkTriggered
|
||||||
|
|
||||||
timerEngine.dismissReminder()
|
timerEngine.dismissReminder()
|
||||||
|
|
||||||
XCTAssertNil(timerEngine.activeReminder)
|
XCTAssertNil(timerEngine.activeReminder)
|
||||||
XCTAssertEqual(timerEngine.timerStates[.blink]?.remainingSeconds, 5 * 60)
|
XCTAssertEqual(timerEngine.timerStates[.builtIn(.blink)]?.remainingSeconds, 5 * 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDismissLookAwayResumesTimers() {
|
func testDismissLookAwayResumesTimers() {
|
||||||
@@ -145,7 +145,7 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
func testTriggerReminderForLookAway() {
|
func testTriggerReminderForLookAway() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
timerEngine.triggerReminder(for: .lookAway)
|
timerEngine.triggerReminder(for: .builtIn(.lookAway))
|
||||||
|
|
||||||
XCTAssertNotNil(timerEngine.activeReminder)
|
XCTAssertNotNil(timerEngine.activeReminder)
|
||||||
if case .lookAwayTriggered(let countdown) = timerEngine.activeReminder {
|
if case .lookAwayTriggered(let countdown) = timerEngine.activeReminder {
|
||||||
@@ -162,7 +162,7 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
func testTriggerReminderForBlink() {
|
func testTriggerReminderForBlink() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
timerEngine.triggerReminder(for: .blink)
|
timerEngine.triggerReminder(for: .builtIn(.blink))
|
||||||
|
|
||||||
XCTAssertNotNil(timerEngine.activeReminder)
|
XCTAssertNotNil(timerEngine.activeReminder)
|
||||||
if case .blinkTriggered = timerEngine.activeReminder {
|
if case .blinkTriggered = timerEngine.activeReminder {
|
||||||
@@ -175,7 +175,7 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
func testTriggerReminderForPosture() {
|
func testTriggerReminderForPosture() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
timerEngine.triggerReminder(for: .posture)
|
timerEngine.triggerReminder(for: .builtIn(.posture))
|
||||||
|
|
||||||
XCTAssertNotNil(timerEngine.activeReminder)
|
XCTAssertNotNil(timerEngine.activeReminder)
|
||||||
if case .postureTriggered = timerEngine.activeReminder {
|
if case .postureTriggered = timerEngine.activeReminder {
|
||||||
@@ -186,58 +186,58 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func testGetTimeRemainingForNonExistentTimer() {
|
func testGetTimeRemainingForNonExistentTimer() {
|
||||||
let timeRemaining = timerEngine.getTimeRemaining(for: .lookAway)
|
let timeRemaining = timerEngine.getTimeRemaining(for: .builtIn(.lookAway))
|
||||||
XCTAssertEqual(timeRemaining, 0)
|
XCTAssertEqual(timeRemaining, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetFormattedTimeRemainingZeroSeconds() {
|
func testGetFormattedTimeRemainingZeroSeconds() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
timerEngine.timerStates[.lookAway]?.remainingSeconds = 0
|
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 0
|
||||||
|
|
||||||
let formatted = timerEngine.getFormattedTimeRemaining(for: .lookAway)
|
let formatted = timerEngine.getFormattedTimeRemaining(for: .builtIn(.lookAway))
|
||||||
XCTAssertEqual(formatted, "0:00")
|
XCTAssertEqual(formatted, "0:00")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetFormattedTimeRemainingLessThanMinute() {
|
func testGetFormattedTimeRemainingLessThanMinute() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
timerEngine.timerStates[.lookAway]?.remainingSeconds = 45
|
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 45
|
||||||
|
|
||||||
let formatted = timerEngine.getFormattedTimeRemaining(for: .lookAway)
|
let formatted = timerEngine.getFormattedTimeRemaining(for: .builtIn(.lookAway))
|
||||||
XCTAssertEqual(formatted, "0:45")
|
XCTAssertEqual(formatted, "0:45")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testGetFormattedTimeRemainingExactHour() {
|
func testGetFormattedTimeRemainingExactHour() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
timerEngine.timerStates[.lookAway]?.remainingSeconds = 3600
|
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 3600
|
||||||
|
|
||||||
let formatted = timerEngine.getFormattedTimeRemaining(for: .lookAway)
|
let formatted = timerEngine.getFormattedTimeRemaining(for: .builtIn(.lookAway))
|
||||||
XCTAssertEqual(formatted, "1:00:00")
|
XCTAssertEqual(formatted, "1:00:00")
|
||||||
}
|
}
|
||||||
|
|
||||||
func testMultipleStartCallsResetTimers() {
|
func testMultipleStartCallsResetTimers() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
timerEngine.timerStates[.lookAway]?.remainingSeconds = 100
|
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 100
|
||||||
|
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
XCTAssertEqual(timerEngine.timerStates[.lookAway]?.remainingSeconds, 20 * 60)
|
XCTAssertEqual(timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds, 20 * 60)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSkipNextPreservesPausedState() {
|
func testSkipNextPreservesPausedState() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
timerEngine.pause()
|
timerEngine.pause()
|
||||||
|
|
||||||
timerEngine.skipNext(type: .lookAway)
|
timerEngine.skipNext(identifier: .builtIn(.lookAway))
|
||||||
|
|
||||||
XCTAssertTrue(timerEngine.timerStates[.lookAway]?.isPaused ?? false)
|
XCTAssertTrue(timerEngine.timerStates[.builtIn(.lookAway)]?.isPaused ?? false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSkipNextPreservesActiveState() {
|
func testSkipNextPreservesActiveState() {
|
||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
timerEngine.skipNext(type: .lookAway)
|
timerEngine.skipNext(identifier: .builtIn(.lookAway))
|
||||||
|
|
||||||
XCTAssertTrue(timerEngine.timerStates[.lookAway]?.isActive ?? false)
|
XCTAssertTrue(timerEngine.timerStates[.builtIn(.lookAway)]?.isActive ?? false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func testDismissReminderWithNoActiveReminder() {
|
func testDismissReminderWithNoActiveReminder() {
|
||||||
@@ -279,8 +279,8 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
||||||
for timerType in TimerType.allCases {
|
for builtInTimer in TimerType.allCases {
|
||||||
XCTAssertNotNil(timerEngine.timerStates[timerType])
|
XCTAssertNotNil(timerEngine.timerStates[.builtIn(builtInTimer)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -302,8 +302,8 @@ final class TimerEngineTests: XCTestCase {
|
|||||||
timerEngine.start()
|
timerEngine.start()
|
||||||
|
|
||||||
XCTAssertEqual(timerEngine.timerStates.count, 2)
|
XCTAssertEqual(timerEngine.timerStates.count, 2)
|
||||||
XCTAssertNotNil(timerEngine.timerStates[.lookAway])
|
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.lookAway)])
|
||||||
XCTAssertNil(timerEngine.timerStates[.blink])
|
XCTAssertNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||||
XCTAssertNotNil(timerEngine.timerStates[.posture])
|
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.posture)])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
175
GazeUITests/EnhancedOnboardingUITests.swift
Normal file
175
GazeUITests/EnhancedOnboardingUITests.swift
Normal file
@@ -0,0 +1,175 @@
|
|||||||
|
//
|
||||||
|
// EnhancedOnboardingUITests.swift
|
||||||
|
// GazeUITests
|
||||||
|
//
|
||||||
|
// Created by Gaze Team on 1/13/26.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
@MainActor
|
||||||
|
final class EnhancedOnboardingUITests: 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 testOnboardingCompleteFlowWithUserTimers() throws {
|
||||||
|
// Navigate through the complete onboarding flow
|
||||||
|
let continueButtons = app.buttons.matching(identifier: "Continue")
|
||||||
|
let nextButtons = app.buttons.matching(identifier: "Next")
|
||||||
|
|
||||||
|
var currentStep = 0
|
||||||
|
let maxSteps = 15
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verify onboarding completed successfully
|
||||||
|
XCTAssertLessThan(currentStep, maxSteps, "Onboarding flow should complete")
|
||||||
|
|
||||||
|
// Verify main application UI is visible (menubar should be active)
|
||||||
|
XCTAssertTrue(app.menuBarItems.firstMatch.exists, "Menubar should be available after onboarding")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testUserTimerCreationInOnboarding() throws {
|
||||||
|
// Reset to fresh onboarding state
|
||||||
|
app.terminate()
|
||||||
|
app = XCUIApplication()
|
||||||
|
app.launchArguments.append("--reset-onboarding")
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
// Navigate to user timer setup section (assumes it's at the end)
|
||||||
|
let continueButtons = app.buttons.matching(identifier: "Continue")
|
||||||
|
let nextButtons = app.buttons.matching(identifier: "Next")
|
||||||
|
|
||||||
|
// Skip through initial screens
|
||||||
|
var currentStep = 0
|
||||||
|
while currentStep < 8 && (continueButtons.firstMatch.exists || nextButtons.firstMatch.exists) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Look for timer creation UI or related elements
|
||||||
|
let timerSetupElement = app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'Timer' OR label CONTAINS 'Custom'")).firstMatch
|
||||||
|
XCTAssertTrue(timerSetupElement.exists, "User timer setup section should be available during onboarding")
|
||||||
|
|
||||||
|
// If we can create a timer in onboarding, test that flow
|
||||||
|
if app.buttons["Add Timer"].exists {
|
||||||
|
app.buttons["Add Timer"].tap()
|
||||||
|
|
||||||
|
// Fill out timer details - this would be specific to the actual UI structure
|
||||||
|
let titleField = app.textFields["Timer Title"]
|
||||||
|
if titleField.exists {
|
||||||
|
titleField.typeText("Test Timer")
|
||||||
|
}
|
||||||
|
|
||||||
|
let intervalField = app.textFields["Interval (minutes)"]
|
||||||
|
if intervalField.exists {
|
||||||
|
intervalField.typeText("10")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Submit the timer
|
||||||
|
app.buttons["Save"].tap()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testSettingsPersistenceAfterOnboarding() throws {
|
||||||
|
// Reset to fresh onboarding state
|
||||||
|
app.terminate()
|
||||||
|
app = XCUIApplication()
|
||||||
|
app.launchArguments.append("--reset-onboarding")
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
// Complete onboarding flow
|
||||||
|
let continueButtons = app.buttons.matching(identifier: "Continue")
|
||||||
|
let nextButtons = app.buttons.matching(identifier: "Next")
|
||||||
|
|
||||||
|
while continueButtons.firstMatch.exists || nextButtons.firstMatch.exists {
|
||||||
|
if continueButtons.firstMatch.exists && continueButtons.firstMatch.isHittable {
|
||||||
|
continueButtons.firstMatch.tap()
|
||||||
|
sleep(1)
|
||||||
|
} else if nextButtons.firstMatch.exists && nextButtons.firstMatch.isHittable {
|
||||||
|
nextButtons.firstMatch.tap()
|
||||||
|
sleep(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get to the end and complete onboarding
|
||||||
|
app.buttons["Get Started"].tap()
|
||||||
|
|
||||||
|
// Verify that settings are properly initialized
|
||||||
|
let menuBar = app.menuBarItems.firstMatch
|
||||||
|
XCTAssertTrue(menuBar.exists, "Menubar should exist after onboarding")
|
||||||
|
|
||||||
|
// Re-launch the app to verify settings persistence
|
||||||
|
app.terminate()
|
||||||
|
let newApp = XCUIApplication()
|
||||||
|
newApp.launchArguments.append("--skip-onboarding")
|
||||||
|
newApp.launch()
|
||||||
|
|
||||||
|
XCTAssertTrue(newApp.menuBarItems.firstMatch.exists, "Application should maintain state after restart")
|
||||||
|
newApp.terminate()
|
||||||
|
}
|
||||||
|
|
||||||
|
func testOnboardingNavigationEdgeCases() throws {
|
||||||
|
// Test that navigation buttons work properly at each step
|
||||||
|
let continueButton = app.buttons["Continue"]
|
||||||
|
if continueButton.waitForExistence(timeout: 2) {
|
||||||
|
continueButton.tap()
|
||||||
|
|
||||||
|
// Verify we moved to the next screen
|
||||||
|
let nextScreen = app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'Setup' OR label CONTAINS 'Configure'")).firstMatch
|
||||||
|
XCTAssertTrue(nextScreen.exists, "Should navigate to next screen on Continue")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test back navigation
|
||||||
|
let backButton = app.buttons["Back"]
|
||||||
|
if backButton.waitForExistence(timeout: 1) {
|
||||||
|
backButton.tap()
|
||||||
|
|
||||||
|
// Should return to previous screen
|
||||||
|
XCTAssertTrue(app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'Welcome'")).firstMatch.exists)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that we can go forward again
|
||||||
|
let continueButton2 = app.buttons["Continue"]
|
||||||
|
if continueButton2.waitForExistence(timeout: 1) {
|
||||||
|
continueButton2.tap()
|
||||||
|
XCTAssertTrue(app.staticTexts.containing(NSPredicate(format: "label CONTAINS 'Setup'")).firstMatch.exists)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
17
build_dmg
17
build_dmg
@@ -111,11 +111,18 @@ ask_version_bump() {
|
|||||||
|
|
||||||
echo "New version: v${NEW_VERSION}"
|
echo "New version: v${NEW_VERSION}"
|
||||||
|
|
||||||
|
# Get current build number for display
|
||||||
|
DISPLAY_CURRENT_BUILD=$(grep -A 1 "CURRENT_PROJECT_VERSION" Gaze.xcodeproj/project.pbxproj | grep -o '[0-9]\+' | head -1)
|
||||||
|
if [ -z "$DISPLAY_CURRENT_BUILD" ]; then
|
||||||
|
DISPLAY_CURRENT_BUILD=0
|
||||||
|
fi
|
||||||
|
DISPLAY_NEW_BUILD=$((DISPLAY_CURRENT_BUILD + 1))
|
||||||
|
|
||||||
# Ask for confirmation to proceed with version bumping
|
# Ask for confirmation to proceed with version bumping
|
||||||
echo ""
|
echo ""
|
||||||
echo "This will:"
|
echo "This will:"
|
||||||
echo " 1. Update project.pbxproj → MARKETING_VERSION = ${NEW_VERSION}"
|
echo " 1. Update project.pbxproj → MARKETING_VERSION = ${NEW_VERSION}"
|
||||||
echo " 2. Update project.pbxproj → CURRENT_PROJECT_VERSION = $(($(date +%Y%m%d)))"
|
echo " 2. Update project.pbxproj → CURRENT_PROJECT_VERSION = ${DISPLAY_NEW_BUILD} (currently ${DISPLAY_CURRENT_BUILD})"
|
||||||
echo " 3. Create git tag v${NEW_VERSION}"
|
echo " 3. Create git tag v${NEW_VERSION}"
|
||||||
echo ""
|
echo ""
|
||||||
read -p "Proceed with version bump? (y/n) " -n 1 -r
|
read -p "Proceed with version bump? (y/n) " -n 1 -r
|
||||||
@@ -132,8 +139,12 @@ ask_version_bump() {
|
|||||||
sed -i.bak "s/MARKETING_VERSION = [0-9.]*;/MARKETING_VERSION = ${NEW_VERSION};/" Gaze.xcodeproj/project.pbxproj
|
sed -i.bak "s/MARKETING_VERSION = [0-9.]*;/MARKETING_VERSION = ${NEW_VERSION};/" Gaze.xcodeproj/project.pbxproj
|
||||||
rm -f Gaze.xcodeproj/project.pbxproj.bak
|
rm -f Gaze.xcodeproj/project.pbxproj.bak
|
||||||
|
|
||||||
# Update CURRENT_PROJECT_VERSION (build number)
|
# Update CURRENT_PROJECT_VERSION (build number) - increment by 1
|
||||||
BUILD_NUMBER=$(date +%Y%m%d)
|
CURRENT_BUILD=$(grep -A 1 "CURRENT_PROJECT_VERSION" Gaze.xcodeproj/project.pbxproj | grep -o '[0-9]\+' | head -1)
|
||||||
|
if [ -z "$CURRENT_BUILD" ]; then
|
||||||
|
CURRENT_BUILD=0
|
||||||
|
fi
|
||||||
|
BUILD_NUMBER=$((CURRENT_BUILD + 1))
|
||||||
sed -i.bak "s/CURRENT_PROJECT_VERSION = [0-9]*;/CURRENT_PROJECT_VERSION = ${BUILD_NUMBER};/" Gaze.xcodeproj/project.pbxproj
|
sed -i.bak "s/CURRENT_PROJECT_VERSION = [0-9]*;/CURRENT_PROJECT_VERSION = ${BUILD_NUMBER};/" Gaze.xcodeproj/project.pbxproj
|
||||||
rm -f Gaze.xcodeproj/project.pbxproj.bak
|
rm -f Gaze.xcodeproj/project.pbxproj.bak
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user