Stabilize iOS UI test foundation and fix flaky suites
This commit is contained in:
@@ -12,61 +12,41 @@ struct SettingsScreen {
|
||||
|
||||
// MARK: - Elements
|
||||
|
||||
var settingsHeader: XCUIElement { app.staticTexts["settings_header"] }
|
||||
var customizeSegment: XCUIElement { app.buttons["Customize"] }
|
||||
var settingsHeader: XCUIElement { app.element(UITestID.Settings.header) }
|
||||
var customizeSegment: XCUIElement { app.element(UITestID.Settings.customizeTab) }
|
||||
var settingsSegment: XCUIElement { app.element(UITestID.Settings.settingsTab) }
|
||||
var upgradeBanner: XCUIElement {
|
||||
app.descendants(matching: .any).matching(identifier: "upgrade_banner").firstMatch
|
||||
app.element(UITestID.Settings.upgradeBanner)
|
||||
}
|
||||
var subscribeButton: XCUIElement {
|
||||
app.descendants(matching: .any).matching(identifier: "subscribe_button").firstMatch
|
||||
app.element(UITestID.Settings.subscribeButton)
|
||||
}
|
||||
var whyUpgradeButton: XCUIElement { app.buttons["why_upgrade_button"] }
|
||||
var browseThemesButton: XCUIElement { app.buttons["browse_themes_button"] }
|
||||
var clearDataButton: XCUIElement { app.buttons["settings_clear_data"].firstMatch }
|
||||
var analyticsToggle: XCUIElement { app.descendants(matching: .any).matching(identifier: "settings_analytics_toggle").firstMatch }
|
||||
var showOnboardingButton: XCUIElement { app.buttons["settings_show_onboarding"].firstMatch }
|
||||
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 showOnboardingButton: XCUIElement { app.buttons["settings_show_onboarding"] }
|
||||
|
||||
// MARK: - Actions
|
||||
|
||||
func tapCustomizeTab() {
|
||||
let segment = customizeSegment
|
||||
_ = segment.waitForExistence(timeout: 5)
|
||||
segment.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
|
||||
tapSegment(identifier: UITestID.Settings.customizeTab, fallbackLabel: "Customize")
|
||||
}
|
||||
|
||||
func tapSettingsTab() {
|
||||
// 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
|
||||
}
|
||||
tapSegment(identifier: UITestID.Settings.settingsTab, fallbackLabel: "Settings")
|
||||
}
|
||||
|
||||
func tapClearData() {
|
||||
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()
|
||||
_ = app.swipeUntilExists(button, direction: .up, maxSwipes: 6)
|
||||
button.tapWhenReady(timeout: 5)
|
||||
}
|
||||
|
||||
func tapAnalyticsToggle() {
|
||||
let toggle = analyticsToggle
|
||||
if toggle.exists && !toggle.isHittable {
|
||||
app.swipeUp()
|
||||
}
|
||||
_ = toggle.waitForExistence(timeout: 5)
|
||||
toggle.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
|
||||
_ = app.swipeUntilExists(toggle, direction: .up, maxSwipes: 6)
|
||||
toggle.tapWhenReady(timeout: 5)
|
||||
}
|
||||
|
||||
// MARK: - Assertions
|
||||
@@ -94,4 +74,26 @@ struct SettingsScreen {
|
||||
file: file, line: line
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Private
|
||||
|
||||
private func tapSegment(identifier: String, fallbackLabel: String) {
|
||||
let byID = app.element(identifier)
|
||||
if byID.waitForExistence(timeout: 2) {
|
||||
byID.tapWhenReady()
|
||||
return
|
||||
}
|
||||
|
||||
let segmentedButton = app.segmentedControls.buttons[fallbackLabel]
|
||||
if segmentedButton.waitForExistence(timeout: 2) {
|
||||
segmentedButton.tapWhenReady()
|
||||
return
|
||||
}
|
||||
|
||||
let candidates = app.buttons.matching(NSPredicate(format: "label == %@", fallbackLabel)).allElementsBoundByIndex
|
||||
let tabBarButton = app.tabBars.buttons[fallbackLabel]
|
||||
if let nonTabButton = candidates.first(where: { $0.frame != tabBarButton.frame }) {
|
||||
nonTabButton.tapWhenReady()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user