Files
Sportstime/SportsTimeUITests/Tests/TripSavingTests.swift
2026-02-20 00:06:38 -06:00

219 lines
6.7 KiB
Swift

//
// TripSavingTests.swift
// SportsTimeUITests
//
// Tests the end-to-end trip saving flow: plan select save verify in My Trips.
// QA Sheet: F-060, F-062, F-064, F-065, F-077, F-078, F-079, F-080
//
import XCTest
final class TripSavingTests: BaseUITestCase {
// MARK: - Helpers
/// Plans a trip, saves it, navigates back to My Trips, and returns the screens.
@MainActor
private func planSaveAndReturnToMyTrips() -> (home: HomeScreen, myTrips: MyTripsScreen) {
let (wizard, detail) = TestFlows.planAndSelectFirstTrip(app: app)
detail.assertSaveState(isSaved: false)
detail.tapFavorite()
detail.assertSaveState(isSaved: true)
// Navigate back: Detail Options Wizard Cancel
app.navigationBars.buttons.firstMatch.tap()
let wizardBackBtn = app.navigationBars.buttons.firstMatch
wizardBackBtn.waitUntilHittable(timeout: BaseUITestCase.shortTimeout).tap()
wizard.tapCancel()
let home = HomeScreen(app: app)
home.switchToTab(home.myTripsTab)
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")
}
// MARK: - Trip Detail Loads with Map (F-060)
/// F-060: Trip detail shows the hero map and itinerary after opening.
@MainActor
func testF060_TripDetailLoadsWithMap() {
let (_, detail) = TestFlows.planAndSelectFirstTrip(app: app)
// Favorite button sits on the map overlay its presence proves the map loaded
XCTAssertTrue(
detail.favoriteButton.exists,
"Favorite button (map overlay) should be visible, proving map loaded"
)
// Itinerary section should also be visible below the map
detail.assertItineraryVisible()
captureScreenshot(named: "F060-TripDetailWithMap")
}
// MARK: - Itinerary Day Cards (F-062)
/// F-062: Scrolling through the itinerary reveals day cards (Day 1, Day 2, etc.).
@MainActor
func testF062_ItineraryDayCardsVisible() {
let (_, _) = TestFlows.planAndSelectFirstTrip(app: app)
// Day 1 should exist in the itinerary
let day1 = app.staticTexts["Day 1"]
var scrollAttempts = 0
while !day1.exists && scrollAttempts < 15 {
app.swipeUp(velocity: .slow)
scrollAttempts += 1
}
XCTAssertTrue(day1.exists, "Day 1 card should be visible in itinerary")
// Day 2 should also exist (planned trips have at least 2 days)
let day2 = app.staticTexts["Day 2"]
scrollAttempts = 0
while !day2.exists && scrollAttempts < 10 {
app.swipeUp(velocity: .slow)
scrollAttempts += 1
}
XCTAssertTrue(day2.exists, "Day 2 card should be visible in itinerary")
captureScreenshot(named: "F062-ItineraryDayCards")
}
}