Files
Sportstime/SportsTimeUITests/Tests/ScheduleTests.swift
Trey t 4e6d8ff37f Add F-037, F-038, F-091 UI tests; mark F-016 red (no accessibility ID)
- F-037: Route preference cards (Direct/Scenic/Balanced) selectable
- F-038: Allow repeat cities toggle buttons work
- F-091: Date range filter sheet opens and applies correctly
- F-016: Marked RED - featured trip card buttons lack accessibility IDs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 01:16:25 -06:00

265 lines
9.1 KiB
Swift

//
// ScheduleTests.swift
// SportsTimeUITests
//
// Verifies the Schedule tab loads and displays content.
// QA Sheet: F-085, F-086, F-087, F-088, F-089, F-090, F-091, F-092, F-094
//
import XCTest
final class ScheduleTests: BaseUITestCase {
/// F-047: Schedule tab loads and shows filter button.
@MainActor
func testF047_ScheduleTabLoads() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
captureScreenshot(named: "F047-Schedule-Loaded")
}
/// F-055: Sport filter chips are visible and tappable.
@MainActor
func testF055_SportFilterChips() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
// Verify at least MLB chip is present and tappable
let mlbChip = schedule.sportChip("mlb")
XCTAssertTrue(
mlbChip.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
"MLB sport filter chip should exist"
)
// All sports start selected. Tap MLB chip to DESELECT it.
mlbChip.tap()
// After tap, MLB is deselected (removed from selectedSports)
XCTAssertEqual(mlbChip.value as? String, "Not selected",
"MLB chip should be deselected after tap (starts selected, tap toggles off)")
captureScreenshot(named: "F055-SportChip-MLB-Selected")
}
/// F-087: Multiple sport filter chips can be selected simultaneously.
@MainActor
func testF087_MultipleSportFilters() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
// All sports start selected. Tap MLB to DESELECT it.
let mlbChip = schedule.sportChip("mlb")
XCTAssertTrue(mlbChip.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
"MLB chip should exist")
mlbChip.tap()
XCTAssertEqual(mlbChip.value as? String, "Not selected",
"MLB chip should be deselected after tap")
// Also deselect NBA
let nbaChip = schedule.sportChip("nba")
if nbaChip.waitForExistence(timeout: BaseUITestCase.shortTimeout) {
nbaChip.tap()
XCTAssertEqual(nbaChip.value as? String, "Not selected",
"NBA chip should be deselected after tap")
}
// MLB should still be deselected (independent toggle)
XCTAssertEqual(mlbChip.value as? String, "Not selected",
"MLB chip should remain deselected when NBA is also toggled")
captureScreenshot(named: "F087-MultipleSportFilters")
}
/// F-088: Clear/reset filters returns schedule to default state.
@MainActor
func testF088_ClearAllFilters() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
// All sports start selected. Tap MLB to deselect it.
let mlbChip = schedule.sportChip("mlb")
XCTAssertTrue(mlbChip.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
"MLB chip should exist")
mlbChip.tap()
XCTAssertEqual(mlbChip.value as? String, "Not selected",
"MLB chip should be deselected after first tap")
// Tap again to re-select it (restoring to original state)
mlbChip.tap()
// Chip should be back to "Selected"
XCTAssertEqual(mlbChip.value as? String, "Selected",
"MLB chip should be re-selected after second tap")
captureScreenshot(named: "F088-FiltersCleared")
}
/// F-089: Search by team name filters schedule results.
@MainActor
func testF089_SearchByTeamName() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
// Tap search field and type team name
let searchField = schedule.searchField
XCTAssertTrue(searchField.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
"Search field should exist")
searchField.tap()
searchField.typeText("Yankees")
// Wait for results to filter
sleep(1)
captureScreenshot(named: "F089-SearchByTeam")
}
/// F-090: Search by venue name filters schedule results.
@MainActor
func testF090_SearchByVenueName() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
// Tap search field and type venue name
let searchField = schedule.searchField
XCTAssertTrue(searchField.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
"Search field should exist")
searchField.tap()
searchField.typeText("Wrigley")
// Wait for results to filter
sleep(1)
captureScreenshot(named: "F090-SearchByVenue")
}
/// F-092: Empty state appears when filters match no games.
@MainActor
func testF092_ScheduleEmptyState() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
// Type a nonsensical search term to get no results
let searchField = schedule.searchField
XCTAssertTrue(searchField.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
"Search field should exist")
searchField.tap()
searchField.typeText("ZZZZNONEXISTENTTEAMZZZZ")
// Wait for empty state
sleep(1)
// Empty state or "no results" text should appear
let emptyState = schedule.emptyState
let noResults = app.staticTexts.matching(NSPredicate(
format: "label CONTAINS[c] 'no' AND label CONTAINS[c] 'game'"
)).firstMatch
let hasEmptyIndicator = emptyState.waitForExistence(timeout: BaseUITestCase.shortTimeout)
|| noResults.waitForExistence(timeout: BaseUITestCase.shortTimeout)
XCTAssertTrue(hasEmptyIndicator,
"Empty state should appear when no games match search")
captureScreenshot(named: "F092-ScheduleEmptyState")
}
// MARK: - Date Range Filter (F-091)
/// F-091: Date range filter sheet opens and applies.
@MainActor
func testF091_DateRangeFilter() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
// Open filter menu
schedule.filterButton.tap()
// Tap "Date Range" in the menu
let dateRangeButton = app.buttons["Date Range"]
XCTAssertTrue(dateRangeButton.waitForExistence(timeout: BaseUITestCase.shortTimeout),
"Date Range menu item should exist")
dateRangeButton.tap()
// Date range picker sheet should appear
let sheetTitle = app.staticTexts["Select Dates"]
XCTAssertTrue(sheetTitle.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
"Date range picker sheet should appear")
// Tap "Next 7 Days" quick option
let next7Days = app.buttons["Next 7 Days"]
XCTAssertTrue(next7Days.exists, "Next 7 Days button should exist")
next7Days.tap()
// Apply the filter
let applyButton = app.buttons["Apply"]
XCTAssertTrue(applyButton.exists, "Apply button should exist")
applyButton.tap()
// Schedule should still function after applying date filter
schedule.assertLoaded()
captureScreenshot(named: "F091-DateRangeFilter")
}
// MARK: - Diagnostics (F-094)
/// F-094: Diagnostics button opens the diagnostics sheet.
@MainActor
func testF094_ScheduleDiagnostics() {
let home = HomeScreen(app: app)
home.waitForLoad()
home.switchToTab(home.scheduleTab)
let schedule = ScheduleScreen(app: app)
schedule.assertLoaded()
// Tap the filter menu button to open the menu
schedule.filterButton.tap()
// Tap "Diagnostics" in the menu
let diagnosticsButton = app.buttons["Diagnostics"]
XCTAssertTrue(diagnosticsButton.waitForExistence(timeout: BaseUITestCase.shortTimeout),
"Diagnostics menu item should exist")
diagnosticsButton.tap()
// Diagnostics sheet should appear with game count info
let sheetContent = app.navigationBars.firstMatch
XCTAssertTrue(sheetContent.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
"Diagnostics sheet should appear")
captureScreenshot(named: "F094-ScheduleDiagnostics")
}
}