Final batch: F-082 (create poll with 2+ saved trips) and F-099 (progress percentage updates after stadium visit). Also marks 9 more impossible tests RED (F-016, F-039, F-048, F-050, F-107, F-108, F-111, F-129, F-130). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
332 lines
11 KiB
Swift
332 lines
11 KiB
Swift
//
|
|
// HomeTests.swift
|
|
// SportsTimeUITests
|
|
//
|
|
// Tests for the Home tab: hero card, start planning, toolbar button, recent trips, polls.
|
|
// QA Sheet: F-012, F-013, F-014, F-015, F-017, F-018, F-019, F-020, F-081, F-082, F-083, F-084
|
|
//
|
|
|
|
import XCTest
|
|
|
|
final class HomeTests: BaseUITestCase {
|
|
|
|
/// F-012: Hero card displays — "Adventure Awaits" text visible, Start Planning tappable.
|
|
@MainActor
|
|
func testF012_HeroCardDisplaysCorrectly() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
|
|
XCTAssertTrue(home.adventureAwaitsText.exists,
|
|
"Hero card should display 'Adventure Awaits'")
|
|
XCTAssertTrue(home.startPlanningButton.isHittable,
|
|
"Start Planning button should be tappable")
|
|
|
|
captureScreenshot(named: "F012-HeroCard")
|
|
}
|
|
|
|
/// F-013: Start Planning opens wizard sheet with "Plan a Trip" title.
|
|
@MainActor
|
|
func testF013_StartPlanningOpensWizard() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.tapStartPlanning()
|
|
|
|
let wizard = TripWizardScreen(app: app)
|
|
wizard.waitForLoad()
|
|
|
|
XCTAssertTrue(wizard.navigationTitle.exists,
|
|
"Wizard should show 'Plan a Trip' title")
|
|
|
|
captureScreenshot(named: "F013-WizardOpened")
|
|
}
|
|
|
|
/// F-020: Create trip toolbar button (+) opens the same wizard sheet.
|
|
@MainActor
|
|
func testF020_CreateTripToolbarButtonOpensWizard() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
|
|
home.createTripToolbarButton.waitUntilHittable(
|
|
timeout: BaseUITestCase.shortTimeout
|
|
).tap()
|
|
|
|
let wizard = TripWizardScreen(app: app)
|
|
wizard.waitForLoad()
|
|
|
|
XCTAssertTrue(wizard.navigationTitle.exists,
|
|
"Toolbar '+' button should open trip wizard")
|
|
|
|
captureScreenshot(named: "F020-ToolbarCreateTrip")
|
|
}
|
|
|
|
/// F-015: Featured trips refresh button reloads suggestions.
|
|
@MainActor
|
|
func testF015_FeaturedTripsRefreshButton() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
|
|
// Wait for featured trips section to load
|
|
let section = home.featuredTripsSection
|
|
if !section.waitForExistence(timeout: BaseUITestCase.longTimeout) {
|
|
section.scrollIntoView(in: app.scrollViews.firstMatch, maxScrolls: 15)
|
|
}
|
|
|
|
guard section.exists else {
|
|
XCTFail("Featured trips section not found — cannot test refresh")
|
|
return
|
|
}
|
|
|
|
// Find the refresh button by accessibility label
|
|
let refreshButton = app.buttons["Refresh trips"]
|
|
refreshButton.scrollIntoView(in: app.scrollViews.firstMatch)
|
|
XCTAssertTrue(refreshButton.exists,
|
|
"Refresh trips button should exist in featured section")
|
|
|
|
// Tap refresh and verify no crash
|
|
refreshButton.tap()
|
|
|
|
// Wait briefly for reload
|
|
sleep(2)
|
|
|
|
// Featured section should still exist after refresh
|
|
XCTAssertTrue(section.exists,
|
|
"Featured trips section should remain after refresh")
|
|
|
|
captureScreenshot(named: "F015-FeaturedTripsRefresh")
|
|
}
|
|
|
|
/// F-014: Featured trips carousel loads and is visible.
|
|
@MainActor
|
|
func testF014_FeaturedTripsCarouselLoads() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
|
|
// Featured trips load asynchronously — wait for them to appear
|
|
// Then scroll to make them visible. Use the app itself as scroll target
|
|
// since NavigationStack + ScrollView can nest scroll containers.
|
|
let section = home.featuredTripsSection
|
|
if !section.waitForExistence(timeout: BaseUITestCase.longTimeout) {
|
|
// Try scrolling to find it
|
|
section.scrollIntoView(in: app.scrollViews.firstMatch, maxScrolls: 15)
|
|
}
|
|
XCTAssertTrue(section.exists,
|
|
"Featured trips section should be visible")
|
|
|
|
captureScreenshot(named: "F014-FeaturedTrips")
|
|
}
|
|
|
|
/// F-017: Recent trips section shows saved trips with "See All" link on Home tab.
|
|
@MainActor
|
|
func testF017_RecentTripsSectionWithSavedTrips() {
|
|
// Plan a trip and select the first option
|
|
let (_, detail) = TestFlows.planAndSelectFirstTrip(app: app)
|
|
|
|
// Save the trip
|
|
detail.assertSaveState(isSaved: false)
|
|
detail.tapFavorite()
|
|
detail.assertSaveState(isSaved: true)
|
|
|
|
captureScreenshot(named: "F017-TripSaved")
|
|
|
|
// Navigate back: Detail → Options → Wizard → Cancel (dismiss sheet)
|
|
app.navigationBars.buttons.firstMatch.tap()
|
|
app.navigationBars.buttons.firstMatch
|
|
.waitUntilHittable(timeout: BaseUITestCase.shortTimeout).tap()
|
|
TripWizardScreen(app: app).tapCancel()
|
|
|
|
// We're back on Home tab. Wait for it to load.
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
|
|
// Scroll to find the recent trips section
|
|
let recentTrips = home.recentTripsSection
|
|
var scrollAttempts = 0
|
|
while !recentTrips.exists && scrollAttempts < 15 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
|
|
XCTAssertTrue(recentTrips.exists,
|
|
"Recent trips section should appear after saving a trip")
|
|
|
|
// Verify "See All" link is visible
|
|
let seeAllText = app.staticTexts["See All"]
|
|
XCTAssertTrue(seeAllText.exists,
|
|
"'See All' link should be visible in recent trips section")
|
|
|
|
captureScreenshot(named: "F017-RecentTripsSection")
|
|
}
|
|
|
|
/// F-018: Fresh launch with no saved trips — Recent Trips section should not appear.
|
|
@MainActor
|
|
func testF018_RecentTripsSectionHiddenNoSavedTrips() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
|
|
// Scroll down to check the full home tab
|
|
var scrollAttempts = 0
|
|
while scrollAttempts < 5 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
|
|
// Recent trips section should NOT exist on a fresh launch
|
|
XCTAssertFalse(
|
|
home.recentTripsSection.exists,
|
|
"Recent trips section should not appear when no trips are saved"
|
|
)
|
|
|
|
captureScreenshot(named: "F018-NoRecentTrips")
|
|
}
|
|
|
|
/// F-019: Planning tips section is visible at bottom of home tab.
|
|
@MainActor
|
|
func testF019_PlanningTipsSectionVisible() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
|
|
// Tips section is at the very bottom — need to scroll far down.
|
|
// The home screen has nested scroll views; swipe on the main view.
|
|
let section = home.tipsSection
|
|
var scrollAttempts = 0
|
|
while !section.exists && scrollAttempts < 15 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
XCTAssertTrue(section.exists,
|
|
"Planning tips section should be visible")
|
|
|
|
captureScreenshot(named: "F019-PlanningTips")
|
|
}
|
|
|
|
// MARK: - Polls Section (F-081, F-083)
|
|
|
|
/// F-081: Group Polls section is visible on My Trips tab.
|
|
@MainActor
|
|
func testF081_PollsSectionVisible() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.myTripsTab)
|
|
|
|
// Scroll to find "Group Polls" text
|
|
let groupPollsText = app.staticTexts["Group Polls"]
|
|
var scrollAttempts = 0
|
|
while !groupPollsText.exists && scrollAttempts < 10 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
XCTAssertTrue(groupPollsText.exists,
|
|
"Group Polls section should be visible on My Trips tab")
|
|
|
|
captureScreenshot(named: "F081-PollsSectionVisible")
|
|
}
|
|
|
|
/// F-082: Create poll button appears when 2+ trips are saved.
|
|
@MainActor
|
|
func testF082_CreatePollButtonWith2PlusTrips() {
|
|
// Save first trip using the standard flow
|
|
let (wizard1, detail1) = TestFlows.planAndSelectFirstTrip(app: app)
|
|
detail1.assertSaveState(isSaved: false)
|
|
detail1.tapFavorite()
|
|
detail1.assertSaveState(isSaved: true)
|
|
|
|
// Navigate back: Detail → Options → Wizard → Cancel
|
|
app.navigationBars.buttons.firstMatch.tap()
|
|
app.navigationBars.buttons.firstMatch
|
|
.waitUntilHittable(timeout: BaseUITestCase.shortTimeout).tap()
|
|
wizard1.tapCancel()
|
|
|
|
// Save second trip using a different date range
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
|
|
let (wizard2, options2) = TestFlows.planDateRangeTrip(
|
|
app: app,
|
|
month: "July",
|
|
year: "2026",
|
|
startDay: "2026-07-01",
|
|
endDay: "2026-07-07"
|
|
)
|
|
options2.selectTrip(at: 0)
|
|
let detail2 = TripDetailScreen(app: app)
|
|
detail2.waitForLoad()
|
|
detail2.assertSaveState(isSaved: false)
|
|
detail2.tapFavorite()
|
|
detail2.assertSaveState(isSaved: true)
|
|
|
|
// Navigate back: Detail → Options → Wizard → Cancel
|
|
app.navigationBars.buttons.firstMatch.tap()
|
|
app.navigationBars.buttons.firstMatch
|
|
.waitUntilHittable(timeout: BaseUITestCase.shortTimeout).tap()
|
|
wizard2.tapCancel()
|
|
|
|
// Navigate to My Trips tab
|
|
home.waitForLoad()
|
|
home.switchToTab(home.myTripsTab)
|
|
|
|
// Scroll to find the Create poll button
|
|
let createPollButton = app.buttons["Create poll"]
|
|
var scrollAttempts = 0
|
|
while !createPollButton.exists && scrollAttempts < 10 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
|
|
XCTAssertTrue(createPollButton.exists,
|
|
"Create poll button should appear with 2+ saved trips")
|
|
|
|
captureScreenshot(named: "F082-CreatePollWith2Trips")
|
|
}
|
|
|
|
/// F-083: Create poll button hidden when fewer than 2 trips saved.
|
|
@MainActor
|
|
func testF083_CreatePollButtonHiddenLessThan2Trips() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.myTripsTab)
|
|
|
|
// With 0 saved trips, create poll button should not exist
|
|
let createPollButton = app.buttons["Create poll"]
|
|
var scrollAttempts = 0
|
|
while scrollAttempts < 5 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
XCTAssertFalse(createPollButton.exists,
|
|
"Create poll button should not appear with fewer than 2 saved trips")
|
|
|
|
captureScreenshot(named: "F083-CreatePollHidden")
|
|
}
|
|
|
|
// MARK: - Pull to Refresh (F-084)
|
|
|
|
/// F-084: Pull to refresh on My Trips tab doesn't crash.
|
|
@MainActor
|
|
func testF084_PullToRefresh() {
|
|
let home = HomeScreen(app: app)
|
|
home.waitForLoad()
|
|
home.switchToTab(home.myTripsTab)
|
|
|
|
// Wait briefly for My Trips content to load
|
|
sleep(1)
|
|
|
|
// Pull down to refresh
|
|
app.swipeDown(velocity: .slow)
|
|
|
|
// Wait for any refresh to complete
|
|
sleep(2)
|
|
|
|
// Verify the tab is still functional (no crash)
|
|
let groupPolls = app.staticTexts["Group Polls"]
|
|
var scrollAttempts = 0
|
|
while !groupPolls.exists && scrollAttempts < 5 {
|
|
app.swipeUp(velocity: .slow)
|
|
scrollAttempts += 1
|
|
}
|
|
XCTAssertTrue(groupPolls.exists,
|
|
"My Trips tab should remain functional after pull to refresh")
|
|
|
|
captureScreenshot(named: "F084-PullToRefresh")
|
|
}
|
|
}
|