Version bump to v0.4.0
This commit is contained in:
@@ -424,7 +424,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -438,7 +438,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
MARKETING_VERSION = 0.4.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
@@ -460,7 +460,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = Gaze/Gaze.entitlements;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 5;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||
ENABLE_APP_SANDBOX = YES;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
@@ -474,7 +474,7 @@
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 13.0;
|
||||
MARKETING_VERSION = 0.3.0;
|
||||
MARKETING_VERSION = 0.4.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.Gaze;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
REGISTER_APP_GROUPS = YES;
|
||||
@@ -492,11 +492,11 @@
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.2;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MARKETING_VERSION = 0.4.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
@@ -513,11 +513,11 @@
|
||||
buildSettings = {
|
||||
BUNDLE_LOADER = "$(TEST_HOST)";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 26.2;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MARKETING_VERSION = 0.4.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeTests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
@@ -533,10 +533,10 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MARKETING_VERSION = 0.4.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
@@ -552,10 +552,10 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
CURRENT_PROJECT_VERSION = 6;
|
||||
DEVELOPMENT_TEAM = 6GK4F9L62V;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MARKETING_VERSION = 1.0;
|
||||
MARKETING_VERSION = 0.4.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = com.mikefreno.GazeUITests;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
STRING_CATALOG_GENERATE_SYMBOLS = NO;
|
||||
|
||||
@@ -37,14 +37,16 @@ struct SettingsWindowView: View {
|
||||
Label("Posture", systemImage: "figure.stand")
|
||||
}
|
||||
|
||||
UserTimersView(userTimers: Binding(
|
||||
get: { settingsManager.settings.userTimers },
|
||||
set: { settingsManager.settings.userTimers = $0 }
|
||||
))
|
||||
.tag(3)
|
||||
.tabItem {
|
||||
Label("User Timers", systemImage: "plus.circle")
|
||||
}
|
||||
UserTimersView(
|
||||
userTimers: Binding(
|
||||
get: { settingsManager.settings.userTimers },
|
||||
set: { settingsManager.settings.userTimers = $0 }
|
||||
)
|
||||
)
|
||||
.tag(3)
|
||||
.tabItem {
|
||||
Label("User Timers", systemImage: "plus.circle")
|
||||
}
|
||||
|
||||
GeneralSetupView(
|
||||
settingsManager: settingsManager,
|
||||
@@ -60,10 +62,10 @@ struct SettingsWindowView: View {
|
||||
|
||||
HStack {
|
||||
#if DEBUG
|
||||
Button("Retrigger Onboarding") {
|
||||
retriggerOnboarding()
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
Button("Retrigger Onboarding") {
|
||||
retriggerOnboarding()
|
||||
}
|
||||
.buttonStyle(.bordered)
|
||||
#endif
|
||||
|
||||
Spacer()
|
||||
@@ -96,19 +98,19 @@ struct SettingsWindowView: View {
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
private func retriggerOnboarding() {
|
||||
// Close settings window first
|
||||
closeWindow()
|
||||
private func retriggerOnboarding() {
|
||||
// Close settings window first
|
||||
closeWindow()
|
||||
|
||||
// Get AppDelegate and open onboarding
|
||||
if let appDelegate = NSApplication.shared.delegate as? AppDelegate {
|
||||
// Reset onboarding state so it shows as fresh
|
||||
settingsManager.settings.hasCompletedOnboarding = false
|
||||
// Get AppDelegate and open onboarding
|
||||
if let appDelegate = NSApplication.shared.delegate as? AppDelegate {
|
||||
// Reset onboarding state so it shows as fresh
|
||||
settingsManager.settings.hasCompletedOnboarding = false
|
||||
|
||||
// Open onboarding window
|
||||
appDelegate.openOnboarding()
|
||||
// Open onboarding window
|
||||
appDelegate.openOnboarding()
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -32,9 +32,9 @@ final class TimerEngineTests: XCTestCase {
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
||||
XCTAssertNotNil(timerEngine.timerStates[.lookAway])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.blink])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.posture])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.lookAway)])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.posture)])
|
||||
}
|
||||
|
||||
func testDisabledTimersNotInitialized() {
|
||||
@@ -43,16 +43,16 @@ final class TimerEngineTests: XCTestCase {
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates.count, 2)
|
||||
XCTAssertNotNil(timerEngine.timerStates[.lookAway])
|
||||
XCTAssertNil(timerEngine.timerStates[.blink])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.posture])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.lookAway)])
|
||||
XCTAssertNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.posture)])
|
||||
}
|
||||
|
||||
func testTimerStateInitialValues() {
|
||||
timerEngine.start()
|
||||
|
||||
let lookAwayState = timerEngine.timerStates[.lookAway]!
|
||||
XCTAssertEqual(lookAwayState.type, .lookAway)
|
||||
let lookAwayState = timerEngine.timerStates[.builtIn(.lookAway)]!
|
||||
XCTAssertEqual(lookAwayState.identifier, .builtIn(.lookAway))
|
||||
XCTAssertEqual(lookAwayState.remainingSeconds, 20 * 60)
|
||||
XCTAssertFalse(lookAwayState.isPaused)
|
||||
XCTAssertTrue(lookAwayState.isActive)
|
||||
@@ -81,33 +81,33 @@ final class TimerEngineTests: XCTestCase {
|
||||
settingsManager.settings.lookAwayTimer.intervalSeconds = 60
|
||||
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() {
|
||||
timerEngine.start()
|
||||
|
||||
let timeRemaining = timerEngine.getTimeRemaining(for: .lookAway)
|
||||
let timeRemaining = timerEngine.getTimeRemaining(for: .builtIn(.lookAway))
|
||||
XCTAssertEqual(timeRemaining, TimeInterval(20 * 60))
|
||||
}
|
||||
|
||||
func testGetFormattedTimeRemaining() {
|
||||
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")
|
||||
}
|
||||
|
||||
func testGetFormattedTimeRemainingWithHours() {
|
||||
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")
|
||||
}
|
||||
|
||||
@@ -121,13 +121,13 @@ final class TimerEngineTests: XCTestCase {
|
||||
|
||||
func testDismissReminderResetsTimer() {
|
||||
timerEngine.start()
|
||||
timerEngine.timerStates[.blink]?.remainingSeconds = 0
|
||||
timerEngine.timerStates[.builtIn(.blink)]?.remainingSeconds = 0
|
||||
timerEngine.activeReminder = .blinkTriggered
|
||||
|
||||
timerEngine.dismissReminder()
|
||||
|
||||
XCTAssertNil(timerEngine.activeReminder)
|
||||
XCTAssertEqual(timerEngine.timerStates[.blink]?.remainingSeconds, 5 * 60)
|
||||
XCTAssertEqual(timerEngine.timerStates[.builtIn(.blink)]?.remainingSeconds, 5 * 60)
|
||||
}
|
||||
|
||||
func testDismissLookAwayResumesTimers() {
|
||||
@@ -145,7 +145,7 @@ final class TimerEngineTests: XCTestCase {
|
||||
func testTriggerReminderForLookAway() {
|
||||
timerEngine.start()
|
||||
|
||||
timerEngine.triggerReminder(for: .lookAway)
|
||||
timerEngine.triggerReminder(for: .builtIn(.lookAway))
|
||||
|
||||
XCTAssertNotNil(timerEngine.activeReminder)
|
||||
if case .lookAwayTriggered(let countdown) = timerEngine.activeReminder {
|
||||
@@ -162,7 +162,7 @@ final class TimerEngineTests: XCTestCase {
|
||||
func testTriggerReminderForBlink() {
|
||||
timerEngine.start()
|
||||
|
||||
timerEngine.triggerReminder(for: .blink)
|
||||
timerEngine.triggerReminder(for: .builtIn(.blink))
|
||||
|
||||
XCTAssertNotNil(timerEngine.activeReminder)
|
||||
if case .blinkTriggered = timerEngine.activeReminder {
|
||||
@@ -175,7 +175,7 @@ final class TimerEngineTests: XCTestCase {
|
||||
func testTriggerReminderForPosture() {
|
||||
timerEngine.start()
|
||||
|
||||
timerEngine.triggerReminder(for: .posture)
|
||||
timerEngine.triggerReminder(for: .builtIn(.posture))
|
||||
|
||||
XCTAssertNotNil(timerEngine.activeReminder)
|
||||
if case .postureTriggered = timerEngine.activeReminder {
|
||||
@@ -186,58 +186,58 @@ final class TimerEngineTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testGetTimeRemainingForNonExistentTimer() {
|
||||
let timeRemaining = timerEngine.getTimeRemaining(for: .lookAway)
|
||||
let timeRemaining = timerEngine.getTimeRemaining(for: .builtIn(.lookAway))
|
||||
XCTAssertEqual(timeRemaining, 0)
|
||||
}
|
||||
|
||||
func testGetFormattedTimeRemainingZeroSeconds() {
|
||||
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")
|
||||
}
|
||||
|
||||
func testGetFormattedTimeRemainingLessThanMinute() {
|
||||
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")
|
||||
}
|
||||
|
||||
func testGetFormattedTimeRemainingExactHour() {
|
||||
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")
|
||||
}
|
||||
|
||||
func testMultipleStartCallsResetTimers() {
|
||||
timerEngine.start()
|
||||
timerEngine.timerStates[.lookAway]?.remainingSeconds = 100
|
||||
timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds = 100
|
||||
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates[.lookAway]?.remainingSeconds, 20 * 60)
|
||||
XCTAssertEqual(timerEngine.timerStates[.builtIn(.lookAway)]?.remainingSeconds, 20 * 60)
|
||||
}
|
||||
|
||||
func testSkipNextPreservesPausedState() {
|
||||
timerEngine.start()
|
||||
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() {
|
||||
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() {
|
||||
@@ -279,8 +279,8 @@ final class TimerEngineTests: XCTestCase {
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates.count, 3)
|
||||
for timerType in TimerType.allCases {
|
||||
XCTAssertNotNil(timerEngine.timerStates[timerType])
|
||||
for builtInTimer in TimerType.allCases {
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(builtInTimer)])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -302,8 +302,8 @@ final class TimerEngineTests: XCTestCase {
|
||||
timerEngine.start()
|
||||
|
||||
XCTAssertEqual(timerEngine.timerStates.count, 2)
|
||||
XCTAssertNotNil(timerEngine.timerStates[.lookAway])
|
||||
XCTAssertNil(timerEngine.timerStates[.blink])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.posture])
|
||||
XCTAssertNotNil(timerEngine.timerStates[.builtIn(.lookAway)])
|
||||
XCTAssertNil(timerEngine.timerStates[.builtIn(.blink)])
|
||||
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}"
|
||||
|
||||
# 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
|
||||
echo ""
|
||||
echo "This will:"
|
||||
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 ""
|
||||
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
|
||||
rm -f Gaze.xcodeproj/project.pbxproj.bak
|
||||
|
||||
# Update CURRENT_PROJECT_VERSION (build number)
|
||||
BUILD_NUMBER=$(date +%Y%m%d)
|
||||
# Update CURRENT_PROJECT_VERSION (build number) - increment by 1
|
||||
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
|
||||
rm -f Gaze.xcodeproj/project.pbxproj.bak
|
||||
|
||||
|
||||
Reference in New Issue
Block a user