211 lines
7.1 KiB
Swift
211 lines
7.1 KiB
Swift
//
|
|
// TripOptionsTests.swift
|
|
// SportsTimeUITests
|
|
//
|
|
// Tests the Trip Options results screen: sorting, selection, navigation.
|
|
// QA Sheet: F-051, F-052, F-053, F-054, F-055, F-056, F-057, F-058, F-059, F-076
|
|
//
|
|
|
|
import XCTest
|
|
|
|
final class TripOptionsTests: BaseUITestCase {
|
|
|
|
// MARK: - Helpers
|
|
|
|
/// Plans a trip and returns the options screen ready for sorting tests.
|
|
@MainActor
|
|
private func planTripAndGetOptions() -> TripOptionsScreen {
|
|
let (_, options) = TestFlows.planDateRangeTrip(app: app)
|
|
options.assertHasResults()
|
|
return options
|
|
}
|
|
|
|
// MARK: - Results Display (F-051)
|
|
|
|
/// F-051: Trip options listed with city count, game count, distance, and preview.
|
|
@MainActor
|
|
func testF051_ResultsDisplayAfterPlanning() {
|
|
let options = planTripAndGetOptions()
|
|
|
|
// At least one trip card should be visible
|
|
let firstCard = options.tripCard(0)
|
|
XCTAssertTrue(firstCard.exists, "First trip card should be visible")
|
|
|
|
// Sort dropdown should be visible (proves results header rendered)
|
|
XCTAssertTrue(options.sortDropdown.exists, "Sort dropdown should be visible on results screen")
|
|
|
|
captureScreenshot(named: "F051-ResultsDisplay")
|
|
}
|
|
|
|
// MARK: - Sort Options (F-052, F-053, F-054, F-055)
|
|
|
|
/// F-052: Sort by Recommended reorders trip options.
|
|
@MainActor
|
|
func testF052_SortByRecommended() {
|
|
let options = planTripAndGetOptions()
|
|
|
|
// Sort option IDs are rawValue.lowercased() with spaces removed
|
|
options.sort(by: "recommended")
|
|
|
|
// Results should still exist after sorting
|
|
options.assertHasResults()
|
|
|
|
captureScreenshot(named: "F052-SortByRecommended")
|
|
}
|
|
|
|
/// F-053: Sort by Most Games reorders trip options.
|
|
@MainActor
|
|
func testF053_SortByMostGames() {
|
|
let options = planTripAndGetOptions()
|
|
|
|
options.sort(by: "mostgames")
|
|
|
|
options.assertHasResults()
|
|
|
|
captureScreenshot(named: "F053-SortByMostGames")
|
|
}
|
|
|
|
/// F-054: Sort by Least Miles reorders trip options.
|
|
@MainActor
|
|
func testF054_SortByLeastMiles() {
|
|
let options = planTripAndGetOptions()
|
|
|
|
options.sort(by: "leastmiles")
|
|
|
|
options.assertHasResults()
|
|
|
|
captureScreenshot(named: "F054-SortByLeastMiles")
|
|
}
|
|
|
|
/// F-055: Sort by Best Efficiency reorders trip options.
|
|
@MainActor
|
|
func testF055_SortByBestEfficiency() {
|
|
let options = planTripAndGetOptions()
|
|
|
|
options.sort(by: "bestefficiency")
|
|
|
|
options.assertHasResults()
|
|
|
|
captureScreenshot(named: "F055-SortByBestEfficiency")
|
|
}
|
|
|
|
// MARK: - Select Trip (F-058)
|
|
|
|
/// F-058: Tapping a trip option card opens TripDetailView with full itinerary.
|
|
@MainActor
|
|
func testF058_SelectTripOpensDetail() {
|
|
let options = planTripAndGetOptions()
|
|
|
|
// Tap first trip card
|
|
options.selectTrip(at: 0)
|
|
|
|
// Trip detail should open with itinerary
|
|
let detail = TripDetailScreen(app: app)
|
|
detail.waitForLoad()
|
|
detail.assertItineraryVisible()
|
|
|
|
captureScreenshot(named: "F058-SelectTripOpensDetail")
|
|
}
|
|
|
|
// MARK: - Pace Filter (F-056)
|
|
|
|
/// F-056: Pace filter dropdown selects Packed/Moderate/Relaxed.
|
|
@MainActor
|
|
func testF056_PaceFilter() {
|
|
_ = planTripAndGetOptions()
|
|
|
|
// The pace filter is a Menu. The default label shows "All".
|
|
// Find and tap the pace filter menu button.
|
|
let paceMenu = app.buttons.matching(NSPredicate(
|
|
format: "label CONTAINS 'All' OR label CONTAINS 'Packed' OR label CONTAINS 'Moderate' OR label CONTAINS 'Relaxed'"
|
|
)).firstMatch
|
|
|
|
XCTAssertTrue(paceMenu.waitForExistence(timeout: BaseUITestCase.shortTimeout),
|
|
"Pace filter menu should exist")
|
|
paceMenu.tap()
|
|
|
|
// Select "Packed" from the menu
|
|
let packedOption = app.buttons["Packed"]
|
|
XCTAssertTrue(packedOption.waitForExistence(timeout: BaseUITestCase.shortTimeout),
|
|
"Packed option should exist in pace menu")
|
|
packedOption.tap()
|
|
|
|
// Results should update (may show fewer or same results)
|
|
// The pace filter label should now show "Packed"
|
|
let updatedMenu = app.buttons.matching(NSPredicate(
|
|
format: "label CONTAINS 'Packed'"
|
|
)).firstMatch
|
|
XCTAssertTrue(updatedMenu.waitForExistence(timeout: BaseUITestCase.shortTimeout),
|
|
"Pace filter should show 'Packed' after selection")
|
|
|
|
captureScreenshot(named: "F056-PaceFilter-Packed")
|
|
}
|
|
|
|
// MARK: - Cities Filter (F-057)
|
|
|
|
/// F-057: Cities filter limits results to selected max cities.
|
|
@MainActor
|
|
func testF057_CitiesFilter() {
|
|
_ = planTripAndGetOptions()
|
|
|
|
// Cities filter buttons are labeled with numbers: "No Limit", "15", "10", "5", etc.
|
|
// Find and tap the "5" cities filter button
|
|
let fiveCitiesButton = app.buttons["5"]
|
|
fiveCitiesButton.scrollIntoView(in: app.scrollViews.firstMatch)
|
|
|
|
XCTAssertTrue(fiveCitiesButton.waitForExistence(timeout: BaseUITestCase.shortTimeout),
|
|
"'5' cities filter button should exist")
|
|
fiveCitiesButton.tap()
|
|
|
|
let firstTrip = app.buttons.matching(NSPredicate(format: "identifier BEGINSWITH 'tripOptions.trip.'")).firstMatch
|
|
XCTAssertTrue(
|
|
firstTrip.waitForExistence(timeout: BaseUITestCase.shortTimeout),
|
|
"Trip results should remain visible after applying the cities filter"
|
|
)
|
|
|
|
captureScreenshot(named: "F057-CitiesFilter-5")
|
|
}
|
|
|
|
// MARK: - Trip Detail Variants (F-076)
|
|
|
|
/// F-076: Trip detail works correctly with a single-stop (minimal) trip.
|
|
@MainActor
|
|
func testF076_TripDetailWithSingleStop() {
|
|
let options = planTripAndGetOptions()
|
|
|
|
// Select the first trip option
|
|
options.selectTrip(at: 0)
|
|
|
|
// Trip detail should load with itinerary
|
|
let detail = TripDetailScreen(app: app)
|
|
detail.waitForLoad()
|
|
detail.assertItineraryVisible()
|
|
|
|
// Verify the detail screen is functional (favorite button exists)
|
|
XCTAssertTrue(detail.favoriteButton.waitForExistence(
|
|
timeout: BaseUITestCase.shortTimeout),
|
|
"Favorite button should exist on trip detail")
|
|
|
|
captureScreenshot(named: "F076-TripDetailSingleStop")
|
|
}
|
|
|
|
// MARK: - Back Navigation (F-059)
|
|
|
|
/// F-059: Back button on Trip Options returns to wizard with selections preserved.
|
|
@MainActor
|
|
func testF059_BackToWizardFromOptions() {
|
|
_ = planTripAndGetOptions()
|
|
|
|
// Tap back button to return to wizard
|
|
app.navigationBars.buttons.firstMatch.tap()
|
|
|
|
// Wizard should reappear with its navigation title or planning mode buttons
|
|
let wizard = TripWizardScreen(app: app)
|
|
let wizardVisible = wizard.navigationTitle.waitForExistence(timeout: BaseUITestCase.defaultTimeout)
|
|
|| wizard.planningModeButton("dateRange").waitForExistence(timeout: BaseUITestCase.defaultTimeout)
|
|
XCTAssertTrue(wizardVisible, "Wizard should be visible after tapping back from options")
|
|
|
|
captureScreenshot(named: "F059-BackToWizard")
|
|
}
|
|
}
|