diff --git a/Shared/UITestMode.swift b/Shared/UITestMode.swift index f64533d..a75e76c 100644 --- a/Shared/UITestMode.swift +++ b/Shared/UITestMode.swift @@ -66,11 +66,9 @@ enum UITestMode { GroupUserDefaults.groupDefaults.set(false, forKey: UserDefaultsStore.Keys.needsOnboarding.rawValue) } - if bypassSubscription { - #if DEBUG - IAPManager.shared.bypassSubscription = true - #endif - } + #if DEBUG + IAPManager.shared.bypassSubscription = bypassSubscription + #endif if expireTrial { // Set firstLaunchDate to 31 days ago so the 30-day trial is expired @@ -93,8 +91,8 @@ enum UITestMode { if let bundleId = Bundle.main.bundleIdentifier { defaults.removePersistentDomain(forName: bundleId) } - // Reset key defaults explicitly - defaults.set(false, forKey: UserDefaultsStore.Keys.needsOnboarding.rawValue) + // Reset key defaults explicitly (true = fresh install state where onboarding is needed) + defaults.set(true, forKey: UserDefaultsStore.Keys.needsOnboarding.rawValue) defaults.set(0, forKey: UserDefaultsStore.Keys.votingLayoutStyle.rawValue) // horizontal defaults.synchronize() diff --git a/Shared/Views/EmptyView.swift b/Shared/Views/EmptyView.swift index b80bf86..fc56da1 100644 --- a/Shared/Views/EmptyView.swift +++ b/Shared/Views/EmptyView.swift @@ -39,7 +39,6 @@ struct EmptyHomeView: View { } } } - .accessibilityIdentifier(AccessibilityID.DayView.emptyState) } .fixedSize(horizontal: false, vertical: true) .cornerRadius(Constants.viewsCornerRaidus, corners: [.topLeft, .topRight, .bottomLeft, .bottomRight]) diff --git a/Tests iOS/Screens/EntryDetailScreen.swift b/Tests iOS/Screens/EntryDetailScreen.swift index 17b411f..6123edb 100644 --- a/Tests iOS/Screens/EntryDetailScreen.swift +++ b/Tests iOS/Screens/EntryDetailScreen.swift @@ -18,9 +18,9 @@ struct EntryDetailScreen { var moodGrid: XCUIElement { app.otherElements["entry_detail_mood_grid"] } /// Mood buttons inside the detail sheet's mood grid. - /// These use accessibilityLabel (the mood name text), not identifiers. - func moodButton(label: String) -> XCUIElement { - app.buttons.matching(NSPredicate(format: "label CONTAINS[cd] %@", label)).firstMatch + /// Match by the mood_button_ identifier prefix to avoid matching entry rows. + func moodButton(for mood: MoodChoice) -> XCUIElement { + app.buttons["mood_button_\(mood.rawValue)"] } // MARK: - Actions @@ -30,7 +30,7 @@ struct EntryDetailScreen { } func selectMood(_ mood: MoodChoice) { - let button = moodButton(label: mood.rawValue.capitalized) + let button = moodButton(for: mood) button.tapWhenReady() } diff --git a/Tests iOS/Screens/SettingsScreen.swift b/Tests iOS/Screens/SettingsScreen.swift index b183bfe..76e1f09 100644 --- a/Tests iOS/Screens/SettingsScreen.swift +++ b/Tests iOS/Screens/SettingsScreen.swift @@ -14,7 +14,6 @@ struct SettingsScreen { var settingsHeader: XCUIElement { app.staticTexts["settings_header"] } var customizeSegment: XCUIElement { app.buttons["Customize"] } - var settingsSegment: XCUIElement { app.buttons["Settings"] } var upgradeBanner: XCUIElement { app.otherElements["upgrade_banner"] } var subscribeButton: XCUIElement { app.buttons["subscribe_button"] } var whyUpgradeButton: XCUIElement { app.buttons["why_upgrade_button"] } @@ -26,24 +25,44 @@ struct SettingsScreen { // MARK: - Actions func tapCustomizeTab() { - customizeSegment.tapWhenReady() + let segment = customizeSegment + _ = segment.waitForExistence(timeout: 5) + segment.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() } func tapSettingsTab() { - settingsSegment.tapWhenReady() + // Find the "Settings" segment in the segmented control (not the tab bar button). + // Try segmentedControls first, then fall back to finding by exclusion. + let segCtrl = app.segmentedControls.buttons["Settings"] + if segCtrl.waitForExistence(timeout: 3) { + segCtrl.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + return + } + // Fallback: find a "Settings" button that is NOT the tab bar button + let candidates = app.buttons.matching(NSPredicate(format: "label == 'Settings'")).allElementsBoundByIndex + let tabBarBtn = app.tabBars.buttons["Settings"] + for candidate in candidates where candidate.frame != tabBarBtn.frame { + candidate.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + return + } } func tapClearData() { - clearDataButton.tapWhenReady() + let button = clearDataButton + if button.exists && !button.isHittable { + app.swipeUp() + } + _ = button.waitForExistence(timeout: 5) + button.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() } func tapAnalyticsToggle() { - // Scroll down to find the toggle if needed - let toggle = app.descendants(matching: .any).matching(identifier: "settings_analytics_toggle").firstMatch - if !toggle.isHittable { + let toggle = analyticsToggle + if toggle.exists && !toggle.isHittable { app.swipeUp() } - toggle.tapWhenReady() + _ = toggle.waitForExistence(timeout: 5) + toggle.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() } // MARK: - Assertions diff --git a/Tests iOS/Screens/TabBarScreen.swift b/Tests iOS/Screens/TabBarScreen.swift index e7d4f40..c4ba1d2 100644 --- a/Tests iOS/Screens/TabBarScreen.swift +++ b/Tests iOS/Screens/TabBarScreen.swift @@ -22,31 +22,31 @@ struct TabBarScreen { @discardableResult func tapDay() -> DayScreen { - dayTab.tapWhenReady() + tapTab(dayTab) return DayScreen(app: app) } @discardableResult func tapMonth() -> TabBarScreen { - monthTab.tapWhenReady() + tapTab(monthTab) return self } @discardableResult func tapYear() -> TabBarScreen { - yearTab.tapWhenReady() + tapTab(yearTab) return self } @discardableResult func tapInsights() -> TabBarScreen { - insightsTab.tapWhenReady() + tapTab(insightsTab) return self } @discardableResult func tapSettings() -> SettingsScreen { - settingsTab.tapWhenReady() + tapTab(settingsTab) return SettingsScreen(app: app) } @@ -59,4 +59,13 @@ struct TabBarScreen { func assertTabBarVisible() { XCTAssertTrue(dayTab.waitForExistence(timeout: 5), "Tab bar should be visible") } + + // MARK: - Private + + /// Tap a tab bar button. Uses coordinate tap to avoid iOS 26 Liquid Glass + /// overlay elements reporting buttons as not hittable. + private func tapTab(_ tab: XCUIElement) { + _ = tab.waitForExistence(timeout: 5) + tab.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + } }