refactor(itinerary): extract reordering logic into pure functions
Extract all itinerary reordering logic from ItineraryTableViewController into ItineraryReorderingLogic.swift for testability. Key changes: - Add flattenDays, dayNumber, travelRow, simulateMove pure functions - Add calculateSortOrder with proper region classification (before/after games) - Add computeValidDestinationRowsProposed with simulation+validation pattern - Add coordinate space conversion helpers (proposedToOriginal, originalToProposed) - Fix DragZones coordinate space mismatch (was mixing proposed/original indices) - Add comprehensive documentation of coordinate space conventions Test coverage includes: - Row flattening order and semantic travel model - Sort order calculation for before/after games regions - Travel constraints validation - DragZones coordinate space correctness - Coordinate conversion helpers - Edge cases (empty days, multi-day trips) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
125
SportsTimeTests/Features/Trip/ItineraryTestHelpers.swift
Normal file
125
SportsTimeTests/Features/Trip/ItineraryTestHelpers.swift
Normal file
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// ItineraryTestHelpers.swift
|
||||
// SportsTimeTests
|
||||
//
|
||||
// Shared test fixtures and helpers for Itinerary tests.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@testable import SportsTime
|
||||
|
||||
/// Shared test fixtures for itinerary tests
|
||||
enum ItineraryTestHelpers {
|
||||
static let testTripId = UUID()
|
||||
static let testDate = Date()
|
||||
|
||||
// MARK: - Day Helpers
|
||||
|
||||
static func makeDays(count: Int, from baseDate: Date = testDate) -> [ItineraryDayData] {
|
||||
return (0..<count).map { i in
|
||||
ItineraryDayData(
|
||||
id: i + 1,
|
||||
dayNumber: i + 1,
|
||||
date: Calendar.current.date(byAdding: .day, value: i, to: baseDate)!,
|
||||
games: [],
|
||||
items: [],
|
||||
travelBefore: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
static func dayAfter(_ date: Date) -> Date {
|
||||
Calendar.current.date(byAdding: .day, value: 1, to: date)!
|
||||
}
|
||||
|
||||
// MARK: - Travel Helpers
|
||||
|
||||
static func makeTravelSegment(from: String, to: String) -> TravelSegment {
|
||||
TravelSegment(
|
||||
fromLocation: LocationInput(name: from, coordinate: nil),
|
||||
toLocation: LocationInput(name: to, coordinate: nil),
|
||||
travelMode: .drive,
|
||||
distanceMeters: 500_000,
|
||||
durationSeconds: 18000
|
||||
)
|
||||
}
|
||||
|
||||
static func makeTravelItem(from: String, to: String, day: Int, sortOrder: Double) -> ItineraryItem {
|
||||
ItineraryItem(
|
||||
tripId: testTripId,
|
||||
day: day,
|
||||
sortOrder: sortOrder,
|
||||
kind: .travel(TravelInfo(fromCity: from, toCity: to))
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Game Helpers
|
||||
|
||||
static func makeRichGame(city: String, hour: Int, baseDate: Date = testDate) -> RichGame {
|
||||
var dateComponents = Calendar.current.dateComponents([.year, .month, .day], from: baseDate)
|
||||
dateComponents.hour = hour
|
||||
let gameTime = Calendar.current.date(from: dateComponents)!
|
||||
|
||||
let game = Game(
|
||||
id: "game-\(city)-\(UUID().uuidString.prefix(4))",
|
||||
homeTeamId: "team-\(city)",
|
||||
awayTeamId: "team-visitor",
|
||||
stadiumId: "stadium-\(city)",
|
||||
dateTime: gameTime,
|
||||
sport: .mlb,
|
||||
season: "2026",
|
||||
isPlayoff: false
|
||||
)
|
||||
|
||||
let stadium = Stadium(
|
||||
id: "stadium-\(city)",
|
||||
name: "\(city) Stadium",
|
||||
city: city,
|
||||
state: "XX",
|
||||
latitude: 40.0,
|
||||
longitude: -80.0,
|
||||
capacity: 40000,
|
||||
sport: .mlb
|
||||
)
|
||||
|
||||
let homeTeam = Team(
|
||||
id: "team-\(city)",
|
||||
name: "\(city) Team",
|
||||
abbreviation: String(city.prefix(3)).uppercased(),
|
||||
sport: .mlb,
|
||||
city: city,
|
||||
stadiumId: "stadium-\(city)"
|
||||
)
|
||||
|
||||
let awayTeam = Team(
|
||||
id: "team-visitor",
|
||||
name: "Visitor Team",
|
||||
abbreviation: "VIS",
|
||||
sport: .mlb,
|
||||
city: "Visiting",
|
||||
stadiumId: "stadium-visitor"
|
||||
)
|
||||
|
||||
return RichGame(game: game, homeTeam: homeTeam, awayTeam: awayTeam, stadium: stadium)
|
||||
}
|
||||
|
||||
static func makeGameItem(city: String, day: Int, sortOrder: Double = 100) -> ItineraryItem {
|
||||
ItineraryItem(
|
||||
tripId: testTripId,
|
||||
day: day,
|
||||
sortOrder: sortOrder,
|
||||
kind: .game(gameId: "game-\(city)-\(UUID().uuidString.prefix(4))")
|
||||
)
|
||||
}
|
||||
|
||||
// MARK: - Custom Item Helpers
|
||||
|
||||
static func makeCustomItem(day: Int, sortOrder: Double, title: String) -> ItineraryItem {
|
||||
ItineraryItem(
|
||||
tripId: testTripId,
|
||||
day: day,
|
||||
sortOrder: sortOrder,
|
||||
kind: .custom(CustomInfo(title: title, icon: "🍽️"))
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user