219 lines
6.7 KiB
Swift
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")
|
|
}
|
|
}
|