diff --git a/Tests iOS/Screens/SettingsScreen.swift b/Tests iOS/Screens/SettingsScreen.swift index 663afa1..a800779 100644 --- a/Tests iOS/Screens/SettingsScreen.swift +++ b/Tests iOS/Screens/SettingsScreen.swift @@ -38,45 +38,69 @@ struct SettingsScreen { } private func tapSegment(identifier: String, fallbackLabel: String, file: StaticString, line: UInt) { - // Try accessibility ID on the descendant element (SwiftUI puts IDs on Text inside Picker) - let byID = app.element(identifier) - if byID.waitForExistence(timeout: defaultTimeout) { - if byID.isHittable { - byID.tap() - return - } - // Element exists but not hittable — try coordinate tap - byID.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() - return - } - // Fallback: segmented control button by label + // On iOS 26, segmented controls may expose buttons by label or by ID. + // Try multiple strategies in order of reliability. + + // Strategy 1: Segmented control button by label (most reliable) let segButton = app.segmentedControls.buttons[fallbackLabel] - if segButton.waitForExistence(timeout: defaultTimeout) { + if segButton.waitForExistence(timeout: defaultTimeout) && segButton.isHittable { segButton.tap() return } - // Last fallback: find buttons matching label that are NOT in tab bar - let allButtons = app.buttons.matching(NSPredicate(format: "label == %@", fallbackLabel)).allElementsBoundByIndex + + // Strategy 2: Find a non-tab-bar button with matching label let tabBarButton = app.tabBars.buttons[fallbackLabel] + let allButtons = app.buttons.matching(NSPredicate(format: "label == %@", fallbackLabel)).allElementsBoundByIndex for button in allButtons { - if button.frame != tabBarButton.frame && button.isHittable { + if button.isHittable && button.frame != tabBarButton.frame { button.tap() return } } + + // Strategy 3: Accessibility ID with coordinate tap fallback + let byID = app.element(identifier) + if byID.waitForExistence(timeout: defaultTimeout) { + byID.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap() + return + } + XCTFail("Could not find segment '\(fallbackLabel)' by ID or label", file: file, line: line) } func tapClearData(file: StaticString = #filePath, line: UInt = #line) { - clearDataButton.scrollIntoView(in: app, direction: .up, maxSwipes: 5, file: file, line: line) + scrollToSettingsElement(clearDataButton, maxSwipes: 20, file: file, line: line) clearDataButton.forceTap(file: file, line: line) } func tapAnalyticsToggle(file: StaticString = #filePath, line: UInt = #line) { - analyticsToggle.scrollIntoView(in: app, direction: .up, maxSwipes: 5, file: file, line: line) + scrollToSettingsElement(analyticsToggle, maxSwipes: 15, file: file, line: line) analyticsToggle.forceTap(file: file, line: line) } + /// Scroll within the settings content to find a deeply nested element. + /// Uses aggressive swipes on the main app surface since the ScrollView + /// hierarchy varies by iOS version. + private func scrollToSettingsElement( + _ element: XCUIElement, + maxSwipes: Int, + file: StaticString, + line: UInt + ) { + if element.exists && element.isHittable { return } + + for _ in 0..