feat: expand XCUITest coverage to 54 QA scenarios with accessibility IDs and fix test failures
Add 22 new UI tests across 8 test files covering Home, Schedule, Progress, Settings, TabNavigation, TripSaving, and TripOptions. Add accessibility identifiers to 11 view files for test element discovery. Fix sport chip assertion logic (all sports start selected, tap deselects), scroll container issues on iOS 26 nested ScrollViews, toggle interaction, and delete trip flow. Update QA coverage map from 32 to 54 automated test cases. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -3,64 +3,169 @@
|
||||
// SportsTimeUITests
|
||||
//
|
||||
// Tests the end-to-end trip saving flow: plan → select → save → verify in My Trips.
|
||||
// QA Sheet: F-064, F-065, F-077, F-078, F-079, F-080
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
final class TripSavingTests: BaseUITestCase {
|
||||
|
||||
/// Plans a trip, selects an option, saves it, and verifies it appears in My Trips.
|
||||
// MARK: - Helpers
|
||||
|
||||
/// Plans a trip, saves it, navigates back to My Trips, and returns the screens.
|
||||
@MainActor
|
||||
func testSaveTripAppearsInMyTrips() {
|
||||
let home = HomeScreen(app: app)
|
||||
home.waitForLoad()
|
||||
home.tapStartPlanning()
|
||||
private func planSaveAndReturnToMyTrips() -> (home: HomeScreen, myTrips: MyTripsScreen) {
|
||||
let (wizard, detail) = TestFlows.planAndSelectFirstTrip(app: app)
|
||||
|
||||
// Plan a trip using date range mode
|
||||
let wizard = TripWizardScreen(app: app)
|
||||
wizard.waitForLoad()
|
||||
wizard.selectDateRangeMode()
|
||||
|
||||
wizard.nextMonthButton.scrollIntoView(in: app.scrollViews.firstMatch)
|
||||
wizard.selectDateRange(
|
||||
targetMonth: "June",
|
||||
targetYear: "2026",
|
||||
startDay: "2026-06-11",
|
||||
endDay: "2026-06-16"
|
||||
)
|
||||
wizard.selectSport("mlb")
|
||||
wizard.selectRegion("central")
|
||||
wizard.tapPlanTrip()
|
||||
|
||||
// Select first trip option
|
||||
let options = TripOptionsScreen(app: app)
|
||||
options.waitForLoad()
|
||||
options.selectTrip(at: 0)
|
||||
|
||||
// Save the trip
|
||||
let detail = TripDetailScreen(app: app)
|
||||
detail.waitForLoad()
|
||||
detail.assertSaveState(isSaved: false)
|
||||
detail.tapFavorite()
|
||||
|
||||
// Allow save to persist
|
||||
detail.assertSaveState(isSaved: true)
|
||||
|
||||
captureScreenshot(named: "TripSaving-Favorited")
|
||||
|
||||
// Navigate back to My Trips tab
|
||||
// Dismiss the entire wizard sheet: Detail → Options → Wizard → Cancel
|
||||
app.navigationBars.buttons.firstMatch.tap() // Back from detail to options
|
||||
// Back from options to wizard
|
||||
// Navigate back: Detail → Options → Wizard → Cancel
|
||||
app.navigationBars.buttons.firstMatch.tap()
|
||||
let wizardBackBtn = app.navigationBars.buttons.firstMatch
|
||||
wizardBackBtn.waitUntilHittable(timeout: BaseUITestCase.shortTimeout).tap()
|
||||
// Cancel the wizard sheet
|
||||
wizard.tapCancel()
|
||||
// Now the tab bar is accessible
|
||||
|
||||
let home = HomeScreen(app: app)
|
||||
home.switchToTab(home.myTripsTab)
|
||||
|
||||
// Assert: Saved trip appears (empty state should NOT be visible)
|
||||
let myTrips = MyTripsScreen(app: app)
|
||||
return (home, myTrips)
|
||||
}
|
||||
|
||||
// MARK: - Save Trip (F-043)
|
||||
|
||||
/// F-043/F-044: Plans a trip, selects an option, saves it, and verifies it appears in My Trips.
|
||||
@MainActor
|
||||
func testF043_SaveTripAppearsInMyTrips() {
|
||||
let (_, myTrips) = planSaveAndReturnToMyTrips()
|
||||
myTrips.assertHasTrips()
|
||||
|
||||
captureScreenshot(named: "F043-TripSaving-InMyTrips")
|
||||
}
|
||||
|
||||
// MARK: - Save/Unsave Toggle (F-048, F-049)
|
||||
|
||||
/// F-048: Save trip — unsaved trip shows "Save to favorites", tap changes to "Remove from favorites".
|
||||
@MainActor
|
||||
func testF048_SaveTrip() {
|
||||
let (_, detail) = TestFlows.planAndSelectFirstTrip(app: app)
|
||||
|
||||
detail.assertSaveState(isSaved: false)
|
||||
detail.tapFavorite()
|
||||
detail.assertSaveState(isSaved: true)
|
||||
|
||||
captureScreenshot(named: "F048-TripSaved")
|
||||
}
|
||||
|
||||
/// F-049: Unsave trip — saved trip can be un-favorited by tapping again.
|
||||
@MainActor
|
||||
func testF049_UnsaveTrip() {
|
||||
let (_, detail) = TestFlows.planAndSelectFirstTrip(app: app)
|
||||
|
||||
// Save first
|
||||
detail.assertSaveState(isSaved: false)
|
||||
detail.tapFavorite()
|
||||
detail.assertSaveState(isSaved: true)
|
||||
|
||||
// Unsave
|
||||
detail.tapFavorite()
|
||||
detail.assertSaveState(isSaved: false)
|
||||
|
||||
captureScreenshot(named: "F049-TripUnsaved")
|
||||
}
|
||||
|
||||
// MARK: - Saved Trips List (F-059)
|
||||
|
||||
/// F-059: Saved trips list shows trip card after saving.
|
||||
@MainActor
|
||||
func testF059_SavedTripsList() {
|
||||
let (_, myTrips) = planSaveAndReturnToMyTrips()
|
||||
|
||||
myTrips.assertHasTrips()
|
||||
|
||||
// First trip card should exist
|
||||
let firstTrip = myTrips.tripCard(0)
|
||||
XCTAssertTrue(
|
||||
firstTrip.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
|
||||
"First saved trip card should be visible"
|
||||
)
|
||||
|
||||
captureScreenshot(named: "F059-SavedTripsList")
|
||||
}
|
||||
|
||||
// MARK: - Empty State (F-058)
|
||||
|
||||
/// F-058: My Trips empty state when no trips saved.
|
||||
@MainActor
|
||||
func testF058_MyTripsEmptyState() {
|
||||
let home = HomeScreen(app: app)
|
||||
home.waitForLoad()
|
||||
home.switchToTab(home.myTripsTab)
|
||||
|
||||
let myTrips = MyTripsScreen(app: app)
|
||||
myTrips.assertEmpty()
|
||||
|
||||
captureScreenshot(named: "F058-MyTrips-Empty")
|
||||
}
|
||||
|
||||
// MARK: - Tap Saved Trip Opens Detail (F-079)
|
||||
|
||||
/// F-079: Tapping a saved trip card opens the detail view.
|
||||
@MainActor
|
||||
func testF079_TapSavedTripOpensDetail() {
|
||||
let (_, myTrips) = planSaveAndReturnToMyTrips()
|
||||
myTrips.assertHasTrips()
|
||||
|
||||
// Tap the first saved trip
|
||||
myTrips.tapTrip(at: 0)
|
||||
|
||||
// Trip detail should open
|
||||
let detail = TripDetailScreen(app: app)
|
||||
detail.waitForLoad()
|
||||
detail.assertItineraryVisible()
|
||||
|
||||
captureScreenshot(named: "F079-TapSavedTripOpensDetail")
|
||||
}
|
||||
|
||||
// MARK: - Remove Saved Trip (F-080)
|
||||
|
||||
/// F-080: Unfavoriting a trip removes it from My Trips.
|
||||
@MainActor
|
||||
func testF080_DeleteSavedTrip() {
|
||||
let (_, myTrips) = planSaveAndReturnToMyTrips()
|
||||
myTrips.assertHasTrips()
|
||||
|
||||
// Tap into the saved trip detail
|
||||
myTrips.tapTrip(at: 0)
|
||||
|
||||
let detail = TripDetailScreen(app: app)
|
||||
detail.waitForLoad()
|
||||
|
||||
// Unfavorite to remove from saved trips
|
||||
detail.assertSaveState(isSaved: true)
|
||||
detail.tapFavorite()
|
||||
detail.assertSaveState(isSaved: false)
|
||||
|
||||
// Navigate back to My Trips
|
||||
app.navigationBars.buttons.firstMatch.tap()
|
||||
|
||||
// After unfavoriting, should show empty state
|
||||
myTrips.assertEmpty()
|
||||
|
||||
captureScreenshot(named: "F080-DeleteSavedTrip")
|
||||
}
|
||||
|
||||
// MARK: - Stats Row (F-061)
|
||||
|
||||
/// F-061: Trip detail stats row shows city count, game count, distance, driving time.
|
||||
@MainActor
|
||||
func testF061_StatsRowDisplaysCorrectly() {
|
||||
let (_, detail) = TestFlows.planAndSelectFirstTrip(app: app)
|
||||
|
||||
detail.assertStatsRowVisible()
|
||||
|
||||
captureScreenshot(named: "F061-StatsRow")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user