- F-015: Featured trips refresh button works without crash - F-076: Trip detail loads correctly with single-stop trip - F-094: Schedule diagnostics sheet opens from filter menu Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
223 lines
7.7 KiB
Swift
223 lines
7.7 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-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: - 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")
|
|
}
|
|
}
|