Files
Sportstime/SportsTimeUITests/Tests/ScheduleTests.swift
Trey t dc142bd14b feat: expand XCUITest coverage to 54 QA scenarios with accessibility IDs and fix test failures
Add 22 new UI tests across 8 test files covering Home, Schedule, Progress,
Settings, TabNavigation, TripSaving, and TripOptions. Add accessibility
identifiers to 11 view files for test element discovery. Fix sport chip
assertion logic (all sports start selected, tap deselects), scroll container
issues on iOS 26 nested ScrollViews, toggle interaction, and delete trip flow.
Update QA coverage map from 32 to 54 automated test cases.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 19:44:22 -06:00

171 lines
5.9 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-092
//
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-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")
}
}