Stabilize unit and UI tests for SportsTime
This commit is contained in:
48
SportsTimeTests/Helpers/TestClock.swift
Normal file
48
SportsTimeTests/Helpers/TestClock.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// TestClock.swift
|
||||
// SportsTimeTests
|
||||
//
|
||||
// Centralized time utilities for deterministic tests.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum TestClock {
|
||||
static let timeZone = TimeZone.current
|
||||
static let locale = Locale(identifier: "en_US_POSIX")
|
||||
|
||||
static let calendar: Calendar = {
|
||||
var calendar = Calendar.current
|
||||
calendar.timeZone = timeZone
|
||||
calendar.locale = locale
|
||||
return calendar
|
||||
}()
|
||||
|
||||
static let baseDate: Date = {
|
||||
let components = DateComponents(
|
||||
calendar: calendar,
|
||||
timeZone: timeZone,
|
||||
year: 2026,
|
||||
month: 1,
|
||||
day: 15,
|
||||
hour: 12,
|
||||
minute: 0,
|
||||
second: 0
|
||||
)
|
||||
return calendar.date(from: components) ?? Date(timeIntervalSince1970: 0)
|
||||
}()
|
||||
|
||||
static var now: Date { baseDate }
|
||||
|
||||
static func startOfDay(for date: Date = baseDate) -> Date {
|
||||
calendar.startOfDay(for: date)
|
||||
}
|
||||
|
||||
static func addingDays(_ days: Int, to date: Date = baseDate) -> Date {
|
||||
calendar.date(byAdding: .day, value: days, to: date) ?? date
|
||||
}
|
||||
|
||||
static func addingHours(_ hours: Int, to date: Date = baseDate) -> Date {
|
||||
calendar.date(byAdding: .hour, value: hours, to: date) ?? date
|
||||
}
|
||||
}
|
||||
@@ -86,13 +86,15 @@ enum TestFixtures {
|
||||
season: String = "2026",
|
||||
isPlayoff: Bool = false
|
||||
) -> Game {
|
||||
let actualDateTime = dateTime ?? Calendar.current.date(byAdding: .day, value: 1, to: Date())!
|
||||
let actualDateTime = dateTime ?? TestClock.calendar.date(byAdding: .day, value: 1, to: TestClock.now)!
|
||||
let homeId = homeTeamId ?? "team_\(sport.rawValue.lowercased())_\(city.lowercased().replacingOccurrences(of: " ", with: "_"))"
|
||||
let awayId = awayTeamId ?? "team_\(sport.rawValue.lowercased())_visitor"
|
||||
let stadId = stadiumId ?? "stadium_\(sport.rawValue.lowercased())_\(city.lowercased().replacingOccurrences(of: " ", with: "_"))"
|
||||
|
||||
let formatter = DateFormatter()
|
||||
formatter.dateFormat = "MMdd"
|
||||
formatter.timeZone = TestClock.timeZone
|
||||
formatter.locale = TestClock.locale
|
||||
let dateStr = formatter.string(from: actualDateTime)
|
||||
|
||||
let actualId = id ?? "game_\(sport.rawValue.lowercased())_\(season)_\(awayId.split(separator: "_").last ?? "vis")_\(homeId.split(separator: "_").last ?? "home")_\(dateStr)"
|
||||
@@ -119,12 +121,12 @@ enum TestFixtures {
|
||||
count: Int,
|
||||
sport: Sport = .mlb,
|
||||
cities: [String] = ["New York", "Boston", "Chicago", "Los Angeles"],
|
||||
startDate: Date = Date(),
|
||||
startDate: Date = TestClock.now,
|
||||
daySpread: Int = 1
|
||||
) -> [Game] {
|
||||
(0..<count).map { i in
|
||||
let city = cities[i % cities.count]
|
||||
let gameDate = Calendar.current.date(byAdding: .day, value: i * daySpread, to: startDate)!
|
||||
let gameDate = TestClock.calendar.date(byAdding: .day, value: i * daySpread, to: startDate)!
|
||||
return game(sport: sport, city: city, dateTime: gameDate)
|
||||
}
|
||||
}
|
||||
@@ -132,12 +134,12 @@ enum TestFixtures {
|
||||
/// Creates games for same-day conflict testing.
|
||||
static func sameDayGames(
|
||||
cities: [String],
|
||||
date: Date = Date(),
|
||||
date: Date = TestClock.now,
|
||||
sport: Sport = .mlb
|
||||
) -> [Game] {
|
||||
cities.enumerated().map { index, city in
|
||||
// Stagger times by 3 hours
|
||||
let time = Calendar.current.date(byAdding: .hour, value: 13 + (index * 3), to: Calendar.current.startOfDay(for: date))!
|
||||
let time = TestClock.calendar.date(byAdding: .hour, value: 13 + (index * 3), to: TestClock.calendar.startOfDay(for: date))!
|
||||
return game(sport: sport, city: city, dateTime: time)
|
||||
}
|
||||
}
|
||||
@@ -241,8 +243,8 @@ enum TestFixtures {
|
||||
) -> TripStop {
|
||||
let coordinate = coordinates[city]
|
||||
let actualState = state ?? states[city] ?? "NY"
|
||||
let arrival = arrivalDate ?? Date()
|
||||
let departure = departureDate ?? Calendar.current.date(byAdding: .day, value: 1, to: arrival)!
|
||||
let arrival = arrivalDate ?? TestClock.now
|
||||
let departure = departureDate ?? TestClock.calendar.date(byAdding: .day, value: 1, to: arrival)!
|
||||
|
||||
return TripStop(
|
||||
stopNumber: stopNumber,
|
||||
@@ -259,14 +261,14 @@ enum TestFixtures {
|
||||
/// Creates a sequence of trip stops for a multi-city trip.
|
||||
static func tripStops(
|
||||
cities: [String],
|
||||
startDate: Date = Date(),
|
||||
startDate: Date = TestClock.now,
|
||||
daysPerStop: Int = 1
|
||||
) -> [TripStop] {
|
||||
var stops: [TripStop] = []
|
||||
var currentDate = startDate
|
||||
|
||||
for (index, city) in cities.enumerated() {
|
||||
let departure = Calendar.current.date(byAdding: .day, value: daysPerStop, to: currentDate)!
|
||||
let departure = TestClock.calendar.date(byAdding: .day, value: daysPerStop, to: currentDate)!
|
||||
stops.append(tripStop(
|
||||
stopNumber: index + 1,
|
||||
city: city,
|
||||
@@ -317,8 +319,8 @@ enum TestFixtures {
|
||||
needsEVCharging: Bool = false,
|
||||
maxDrivingHoursPerDriver: Double? = nil
|
||||
) -> TripPreferences {
|
||||
let start = startDate ?? Date()
|
||||
let end = endDate ?? Calendar.current.date(byAdding: .day, value: 7, to: start)!
|
||||
let start = startDate ?? TestClock.now
|
||||
let end = endDate ?? TestClock.calendar.date(byAdding: .day, value: 7, to: start)!
|
||||
|
||||
return TripPreferences(
|
||||
planningMode: mode,
|
||||
@@ -416,12 +418,12 @@ enum TestFixtures {
|
||||
components.hour = hour
|
||||
components.minute = minute
|
||||
components.timeZone = TimeZone(identifier: "America/New_York")
|
||||
return Calendar.current.date(from: components)!
|
||||
return TestClock.calendar.date(from: components)!
|
||||
}
|
||||
|
||||
/// Creates dates for a range of days.
|
||||
static func dateRange(start: Date = Date(), days: Int) -> (start: Date, end: Date) {
|
||||
let end = Calendar.current.date(byAdding: .day, value: days, to: start)!
|
||||
static func dateRange(start: Date = TestClock.now, days: Int) -> (start: Date, end: Date) {
|
||||
let end = TestClock.calendar.date(byAdding: .day, value: days, to: start)!
|
||||
return (start, end)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user