Adds 3 new UI tests covering stadium visit manual entry, required field validation, and games history navigation. Includes accessibility IDs on StadiumVisitSheet/ProgressTabView and new page objects (StadiumVisitSheetScreen, GamesHistoryScreen) in the test framework. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
207 lines
7.1 KiB
Swift
207 lines
7.1 KiB
Swift
//
|
|
// ProgressTests.swift
|
|
// SportsTimeUITests
|
|
//
|
|
// Tests for the Progress tab (Pro-gated).
|
|
// QA Sheet: F-066, F-097, F-100, F-101, F-106, F-110
|
|
//
|
|
|
|
import XCTest
|
|
|
|
final class ProgressTests: BaseUITestCase {
|
|
|
|
/// F-066: Progress tab loads for Pro user — stadium quest and navigation visible.
|
|
@MainActor
|
|
func testF066_ProgressTabLoads() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.progressTab)
|
|
|
|
let progress = ProgressScreen(app: app)
|
|
progress.waitForLoad()
|
|
|
|
// Stadium Quest label should be visible (proves data loaded)
|
|
XCTAssertTrue(
|
|
progress.stadiumQuestLabel.waitForExistence(
|
|
timeout: BaseUITestCase.longTimeout),
|
|
"Stadium Quest label should appear on Progress tab"
|
|
)
|
|
|
|
captureScreenshot(named: "F066-ProgressTab-Loaded")
|
|
}
|
|
|
|
/// F-097: League/sport selector toggles between sports.
|
|
@MainActor
|
|
func testF097_LeagueSportSelector() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.progressTab)
|
|
|
|
let progress = ProgressScreen(app: app)
|
|
progress.waitForLoad()
|
|
|
|
// Sport buttons inherit the parent's identifier (progress.sportSelector),
|
|
// so find them by their accessibility label which includes the sport name.
|
|
let mlbButton = app.buttons.matching(NSPredicate(
|
|
format: "label BEGINSWITH 'MLB'"
|
|
)).firstMatch
|
|
XCTAssertTrue(mlbButton.waitForExistence(timeout: BaseUITestCase.longTimeout),
|
|
"MLB sport button should exist in sport selector")
|
|
mlbButton.tap()
|
|
|
|
captureScreenshot(named: "F097-SportSelector-MLB")
|
|
|
|
// Switch to NBA
|
|
let nbaButton = app.buttons.matching(NSPredicate(
|
|
format: "label BEGINSWITH 'NBA'"
|
|
)).firstMatch
|
|
if nbaButton.waitForExistence(timeout: BaseUITestCase.shortTimeout) {
|
|
nbaButton.tap()
|
|
captureScreenshot(named: "F097-SportSelector-NBA")
|
|
}
|
|
}
|
|
|
|
/// F-101: Add visit — Save button disabled until stadium is selected.
|
|
@MainActor
|
|
func testF101_AddVisitRequiredFields() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.progressTab)
|
|
|
|
let progress = ProgressScreen(app: app)
|
|
progress.waitForLoad()
|
|
progress.tapAddManualVisit()
|
|
|
|
let visitSheet = StadiumVisitSheetScreen(app: app)
|
|
visitSheet.waitForLoad()
|
|
|
|
// Save button should exist but be disabled (no stadium selected)
|
|
XCTAssertTrue(
|
|
visitSheet.saveButton.waitForExistence(timeout: BaseUITestCase.shortTimeout),
|
|
"Save button should exist"
|
|
)
|
|
XCTAssertFalse(
|
|
visitSheet.saveButton.isEnabled,
|
|
"Save button should be disabled without a stadium selected"
|
|
)
|
|
|
|
captureScreenshot(named: "F101-SaveDisabledNoStadium")
|
|
}
|
|
|
|
/// F-100: Add visit — full manual entry flow (select stadium, save).
|
|
@MainActor
|
|
func testF100_AddVisitManualEntry() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.progressTab)
|
|
|
|
let progress = ProgressScreen(app: app)
|
|
progress.waitForLoad()
|
|
progress.tapAddManualVisit()
|
|
|
|
let visitSheet = StadiumVisitSheetScreen(app: app)
|
|
visitSheet.waitForLoad()
|
|
|
|
// Pick a stadium
|
|
visitSheet.pickFirstStadium()
|
|
|
|
// Save button should now be enabled
|
|
XCTAssertTrue(
|
|
visitSheet.saveButton.waitForExistence(timeout: BaseUITestCase.shortTimeout),
|
|
"Save button should exist after picking a stadium"
|
|
)
|
|
XCTAssertTrue(
|
|
visitSheet.saveButton.isEnabled,
|
|
"Save button should be enabled after selecting a stadium"
|
|
)
|
|
|
|
// Save the visit
|
|
visitSheet.tapSave()
|
|
|
|
// Sheet should dismiss — nav bar disappears
|
|
visitSheet.navigationBar.waitForNonExistence(
|
|
timeout: BaseUITestCase.defaultTimeout,
|
|
"Visit sheet should dismiss after save"
|
|
)
|
|
|
|
captureScreenshot(named: "F100-ManualEntryComplete")
|
|
}
|
|
|
|
/// F-106: View Games History — add a visit, then navigate to history view.
|
|
@MainActor
|
|
func testF106_ViewGamesHistory() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.progressTab)
|
|
|
|
let progress = ProgressScreen(app: app)
|
|
progress.waitForLoad()
|
|
|
|
// Add a visit first (prerequisite: Recent Visits section only shows with visits)
|
|
progress.tapAddManualVisit()
|
|
let visitSheet = StadiumVisitSheetScreen(app: app)
|
|
visitSheet.waitForLoad()
|
|
visitSheet.pickFirstStadium()
|
|
visitSheet.tapSave()
|
|
visitSheet.navigationBar.waitForNonExistence(timeout: BaseUITestCase.defaultTimeout)
|
|
|
|
// Wait for data reload — Recent Visits section should appear in hierarchy
|
|
XCTAssertTrue(
|
|
progress.recentVisitsTitle.waitForExistence(timeout: BaseUITestCase.longTimeout),
|
|
"Recent Visits section should appear after adding a visit"
|
|
)
|
|
|
|
// Scroll to Recent Visits (same pattern as F-110 which uses app.swipeUp)
|
|
var scrollAttempts = 0
|
|
while !progress.recentVisitsTitle.isHittable && scrollAttempts < 20 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
|
|
// Find and tap "See All" — try button, link, then generic element
|
|
let seeAllPredicate = NSPredicate(format: "label CONTAINS[c] 'See All'")
|
|
let seeAllButton = app.buttons.matching(seeAllPredicate).firstMatch
|
|
let seeAllGeneric = app.descendants(matching: .any).matching(seeAllPredicate).firstMatch
|
|
|
|
if seeAllButton.exists && seeAllButton.isHittable {
|
|
seeAllButton.tap()
|
|
} else if seeAllGeneric.exists {
|
|
// One more scroll to ensure it's on screen
|
|
if !seeAllGeneric.isHittable {
|
|
app.swipeUp(velocity: .slow)
|
|
}
|
|
seeAllGeneric.tap()
|
|
} else {
|
|
XCTFail("Could not find 'See All' link in Recent Visits section")
|
|
}
|
|
|
|
// Verify Games History view loads
|
|
let gamesHistory = GamesHistoryScreen(app: app)
|
|
gamesHistory.waitForLoad()
|
|
|
|
captureScreenshot(named: "F106-GamesHistory")
|
|
}
|
|
|
|
/// F-110: Achievements gallery is visible with badge grid.
|
|
@MainActor
|
|
func testF110_AchievementsGalleryVisible() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.progressTab)
|
|
|
|
let progress = ProgressScreen(app: app)
|
|
progress.waitForLoad()
|
|
|
|
// Achievements section is below the fold — swipe up to find it
|
|
let achievementsTitle = progress.achievementsTitle
|
|
var scrollAttempts = 0
|
|
while !achievementsTitle.exists && scrollAttempts < 15 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
XCTAssertTrue(achievementsTitle.exists, "Achievements section should be visible")
|
|
|
|
captureScreenshot(named: "F110-AchievementsGallery")
|
|
}
|
|
}
|