Files
Sportstime/SportsTimeTests/Trip/TripWizardViewModelTests.swift
Trey t 22772fa57f feat(store): add In-App Purchase system with Pro subscription
Implement freemium model with StoreKit 2:
- StoreManager singleton for purchase/restore/entitlements
- ProFeature enum defining gated features
- PaywallView and OnboardingPaywallView for upsell UI
- ProGate view modifier and ProBadge component

Feature gating:
- Trip saving: 1 free trip, then requires Pro
- PDF export: Pro only with badge indicator
- Progress tab: Shows ProLockedView for free users
- Settings: Subscription management section

Also fixes pre-existing test issues with StadiumVisit
and ItineraryOption model signature changes.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 11:41:40 -06:00

187 lines
5.6 KiB
Swift

//
// TripWizardViewModelTests.swift
// SportsTimeTests
//
import XCTest
@testable import SportsTime
final class TripWizardViewModelTests: XCTestCase {
// MARK: - Visibility Tests
func test_initialState_stepsNotVisible() {
let viewModel = TripWizardViewModel()
XCTAssertFalse(viewModel.areStepsVisible)
}
func test_selectingPlanningMode_revealsAllSteps() {
let viewModel = TripWizardViewModel()
viewModel.planningMode = .dateRange
XCTAssertTrue(viewModel.areStepsVisible)
}
// MARK: - canPlanTrip Validation Tests
func test_canPlanTrip_initiallyFalse() {
let viewModel = TripWizardViewModel()
XCTAssertFalse(viewModel.canPlanTrip)
}
func test_canPlanTrip_requiresAllFields() {
let viewModel = TripWizardViewModel()
// None set
XCTAssertFalse(viewModel.canPlanTrip)
// Only planning mode
viewModel.planningMode = .dateRange
XCTAssertFalse(viewModel.canPlanTrip)
// Add dates
viewModel.hasSetDates = true
XCTAssertFalse(viewModel.canPlanTrip)
// Add sports
viewModel.selectedSports = [.mlb]
XCTAssertFalse(viewModel.canPlanTrip)
// Add regions
viewModel.selectedRegions = [.east]
XCTAssertFalse(viewModel.canPlanTrip)
// Add route preference
viewModel.hasSetRoutePreference = true
XCTAssertFalse(viewModel.canPlanTrip)
// Add repeat cities - now all required fields are set
viewModel.hasSetRepeatCities = true
XCTAssertTrue(viewModel.canPlanTrip)
}
func test_canPlanTrip_trueWhenAllFieldsSet() {
let viewModel = TripWizardViewModel()
viewModel.planningMode = .dateRange
viewModel.hasSetDates = true
viewModel.selectedSports = [.mlb, .nba]
viewModel.selectedRegions = [.east, .central]
viewModel.hasSetRoutePreference = true
viewModel.hasSetRepeatCities = true
XCTAssertTrue(viewModel.canPlanTrip)
}
func test_canPlanTrip_mustStopsOptional() {
let viewModel = TripWizardViewModel()
viewModel.planningMode = .dateRange
viewModel.hasSetDates = true
viewModel.selectedSports = [.nba]
viewModel.selectedRegions = [.west]
viewModel.hasSetRoutePreference = true
viewModel.hasSetRepeatCities = true
// canPlanTrip should be true even without must stops
XCTAssertTrue(viewModel.canPlanTrip)
XCTAssertTrue(viewModel.mustStopLocations.isEmpty)
}
// MARK: - Reset Behavior Tests
func test_changingPlanningMode_resetsAllSelections() {
let viewModel = TripWizardViewModel()
// Set up full wizard state
viewModel.planningMode = .dateRange
viewModel.selectedSports = [.mlb, .nba]
viewModel.hasSetDates = true
viewModel.selectedRegions = [.east, .central]
viewModel.hasSetRoutePreference = true
viewModel.hasSetRepeatCities = true
viewModel.mustStopLocations = [LocationInput(name: "Test", coordinate: nil)]
// Change planning mode
viewModel.planningMode = .locations
// Verify all state is reset
XCTAssertTrue(viewModel.selectedSports.isEmpty)
XCTAssertFalse(viewModel.hasSetDates)
XCTAssertTrue(viewModel.selectedRegions.isEmpty)
XCTAssertFalse(viewModel.hasSetRoutePreference)
XCTAssertFalse(viewModel.hasSetRepeatCities)
XCTAssertTrue(viewModel.mustStopLocations.isEmpty)
}
func test_settingSamePlanningMode_doesNotResetState() {
let viewModel = TripWizardViewModel()
viewModel.planningMode = .dateRange
viewModel.selectedSports = [.mlb]
viewModel.hasSetDates = true
// Re-set the same mode
viewModel.planningMode = .dateRange
// State should NOT be reset
XCTAssertEqual(viewModel.selectedSports, [.mlb])
XCTAssertTrue(viewModel.hasSetDates)
}
// MARK: - Sport Availability Tests
func test_canSelectSport_defaultsToTrue() {
let viewModel = TripWizardViewModel()
XCTAssertTrue(viewModel.canSelectSport(.mlb))
XCTAssertTrue(viewModel.canSelectSport(.nba))
XCTAssertTrue(viewModel.canSelectSport(.nhl))
}
func test_canSelectSport_respectsAvailability() {
let viewModel = TripWizardViewModel()
viewModel.sportAvailability = [.mlb: true, .nba: false, .nhl: true]
XCTAssertTrue(viewModel.canSelectSport(.mlb))
XCTAssertFalse(viewModel.canSelectSport(.nba))
XCTAssertTrue(viewModel.canSelectSport(.nhl))
}
// MARK: - Multi-Selection Tests
func test_multipleSports_canBeSelected() {
let viewModel = TripWizardViewModel()
viewModel.selectedSports = [.mlb, .nba, .nhl]
XCTAssertEqual(viewModel.selectedSports.count, 3)
}
func test_multipleRegions_canBeSelected() {
let viewModel = TripWizardViewModel()
viewModel.selectedRegions = [.east, .central, .west]
XCTAssertEqual(viewModel.selectedRegions.count, 3)
}
// MARK: - Planning State Tests
func test_isPlanning_defaultsToFalse() {
let viewModel = TripWizardViewModel()
XCTAssertFalse(viewModel.isPlanning)
}
func test_mustStopLocations_canBeAdded() {
let viewModel = TripWizardViewModel()
let location = LocationInput(name: "Chicago, IL", coordinate: nil)
viewModel.mustStopLocations.append(location)
XCTAssertEqual(viewModel.mustStopLocations.count, 1)
XCTAssertEqual(viewModel.mustStopLocations.first?.name, "Chicago, IL")
}
}