// // SettingsScreen.swift // Tests iOS // // Screen object for the Settings tab (Customize + Settings sub-tabs). // import XCTest struct SettingsScreen { let app: XCUIApplication private let defaultTimeout: TimeInterval = 2 private let navigationTimeout: TimeInterval = 5 // MARK: - Elements var settingsHeader: XCUIElement { app.element(UITestID.Settings.header) } var segmentedPicker: XCUIElement { app.element(UITestID.Settings.segmentedPicker) } var upgradeBanner: XCUIElement { app.element(UITestID.Settings.upgradeBanner) } var subscribeButton: XCUIElement { app.element(UITestID.Settings.subscribeButton) } var whyUpgradeButton: XCUIElement { app.element(UITestID.Settings.whyUpgradeButton) } var browseThemesButton: XCUIElement { app.element(UITestID.Settings.browseThemesButton) } var clearDataButton: XCUIElement { app.element(UITestID.Settings.clearDataButton) } var analyticsToggle: XCUIElement { app.element(UITestID.Settings.analyticsToggle) } var eulaButton: XCUIElement { app.element(UITestID.Settings.eulaButton) } var privacyPolicyButton: XCUIElement { app.element(UITestID.Settings.privacyPolicyButton) } // MARK: - Actions func tapCustomizeTab(file: StaticString = #filePath, line: UInt = #line) { tapSegment(label: "Customize", file: file, line: line) } func tapSettingsTab(file: StaticString = #filePath, line: UInt = #line) { tapSegment(label: "Settings", file: file, line: line) } /// Tap a segmented control button by label, scoped to the settings picker /// to avoid collision with the tab bar's "Settings" button. private func tapSegment(label: String, file: StaticString, line: UInt) { // Find the segmented picker by its accessibility ID, then find the button within it let picker = segmentedPicker if picker.waitForExistence(timeout: defaultTimeout) { let button = picker.buttons[label] if button.waitForExistence(timeout: defaultTimeout) && button.isHittable { button.tap() return } } // Fallback: segmented control element type let segButton = app.segmentedControls.buttons[label] if segButton.waitForExistence(timeout: defaultTimeout) && segButton.isHittable { segButton.tap() return } XCTFail("Could not find segment '\(label)' in settings picker", file: file, line: line) } func tapClearData(file: StaticString = #filePath, line: UInt = #line) { scrollToSettingsElement(clearDataButton, maxSwipes: 20, file: file, line: line) clearDataButton.forceTap(file: file, line: line) } func tapAnalyticsToggle(file: StaticString = #filePath, line: UInt = #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. /// Swipes in the center band of the screen (between header and tab bar). private func scrollToSettingsElement( _ element: XCUIElement, maxSwipes: Int, file: StaticString, line: UInt ) { if element.exists && element.isHittable { return } for _ in 0.. SettingsScreen { settingsHeader.waitForExistenceOrFail(timeout: navigationTimeout, message: "Settings header should be visible", file: file, line: line) return self } func assertUpgradeBannerVisible(file: StaticString = #filePath, line: UInt = #line) { upgradeBanner.waitForExistenceOrFail(timeout: defaultTimeout, message: "Upgrade banner should be visible", file: file, line: line) } func assertUpgradeBannerHidden(file: StaticString = #filePath, line: UInt = #line) { upgradeBanner.waitForNonExistence(timeout: navigationTimeout, message: "Upgrade banner should be hidden (subscribed)", file: file, line: line) } }