Fix custom item edit refresh and stabilize F069 UI test
This commit is contained in:
@@ -55,7 +55,7 @@ struct ItineraryTableViewWrapper<HeaderContent: View>: UIViewControllerRepresent
|
|||||||
var headerHostingController: UIHostingController<HeaderContent>?
|
var headerHostingController: UIHostingController<HeaderContent>?
|
||||||
var lastStopCount: Int = 0
|
var lastStopCount: Int = 0
|
||||||
var lastGameIDsHash: Int = 0
|
var lastGameIDsHash: Int = 0
|
||||||
var lastItemCount: Int = 0
|
var lastItineraryItemsHash: Int = 0
|
||||||
var lastOverrideCount: Int = 0
|
var lastOverrideCount: Int = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -118,20 +118,20 @@ struct ItineraryTableViewWrapper<HeaderContent: View>: UIViewControllerRepresent
|
|||||||
// Diff inputs before rebuilding to avoid unnecessary reloads
|
// Diff inputs before rebuilding to avoid unnecessary reloads
|
||||||
let currentStopCount = trip.stops.count
|
let currentStopCount = trip.stops.count
|
||||||
let currentGameIDsHash = games.map(\.game.id).hashValue
|
let currentGameIDsHash = games.map(\.game.id).hashValue
|
||||||
let currentItemCount = itineraryItems.count
|
let currentItineraryItemsHash = itineraryItemsHash(itineraryItems)
|
||||||
let currentOverrideCount = travelOverrides.count
|
let currentOverrideCount = travelOverrides.count
|
||||||
|
|
||||||
let coord = context.coordinator
|
let coord = context.coordinator
|
||||||
guard currentStopCount != coord.lastStopCount ||
|
guard currentStopCount != coord.lastStopCount ||
|
||||||
currentGameIDsHash != coord.lastGameIDsHash ||
|
currentGameIDsHash != coord.lastGameIDsHash ||
|
||||||
currentItemCount != coord.lastItemCount ||
|
currentItineraryItemsHash != coord.lastItineraryItemsHash ||
|
||||||
currentOverrideCount != coord.lastOverrideCount else {
|
currentOverrideCount != coord.lastOverrideCount else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
coord.lastStopCount = currentStopCount
|
coord.lastStopCount = currentStopCount
|
||||||
coord.lastGameIDsHash = currentGameIDsHash
|
coord.lastGameIDsHash = currentGameIDsHash
|
||||||
coord.lastItemCount = currentItemCount
|
coord.lastItineraryItemsHash = currentItineraryItemsHash
|
||||||
coord.lastOverrideCount = currentOverrideCount
|
coord.lastOverrideCount = currentOverrideCount
|
||||||
|
|
||||||
let (days, validRanges, allItemsForConstraints, travelSegmentIndices) = buildItineraryData()
|
let (days, validRanges, allItemsForConstraints, travelSegmentIndices) = buildItineraryData()
|
||||||
@@ -143,6 +143,36 @@ struct ItineraryTableViewWrapper<HeaderContent: View>: UIViewControllerRepresent
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func itineraryItemsHash(_ items: [ItineraryItem]) -> Int {
|
||||||
|
var hasher = Hasher()
|
||||||
|
for item in items.sorted(by: { $0.id.uuidString < $1.id.uuidString }) {
|
||||||
|
hasher.combine(item.id)
|
||||||
|
hasher.combine(item.day)
|
||||||
|
hasher.combine(item.sortOrder)
|
||||||
|
hasher.combine(item.modifiedAt)
|
||||||
|
|
||||||
|
switch item.kind {
|
||||||
|
case .game(let gameId, let city):
|
||||||
|
hasher.combine("game")
|
||||||
|
hasher.combine(gameId)
|
||||||
|
hasher.combine(city)
|
||||||
|
case .travel(let info):
|
||||||
|
hasher.combine("travel")
|
||||||
|
hasher.combine(info.fromCity)
|
||||||
|
hasher.combine(info.toCity)
|
||||||
|
hasher.combine(info.segmentIndex ?? -1)
|
||||||
|
case .custom(let info):
|
||||||
|
hasher.combine("custom")
|
||||||
|
hasher.combine(info.title)
|
||||||
|
hasher.combine(info.icon)
|
||||||
|
hasher.combine(info.latitude ?? 0)
|
||||||
|
hasher.combine(info.longitude ?? 0)
|
||||||
|
hasher.combine(info.address ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hasher.finalize()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: - Build Itinerary Data
|
// MARK: - Build Itinerary Data
|
||||||
|
|
||||||
private func buildItineraryData() -> ([ItineraryDayData], [String: ClosedRange<Int>], [ItineraryItem], [UUID: Int]) {
|
private func buildItineraryData() -> ([ItineraryDayData], [String: ClosedRange<Int>], [ItineraryItem], [UUID: Int]) {
|
||||||
|
|||||||
@@ -898,9 +898,22 @@ struct QuickAddItemSheetScreen {
|
|||||||
func clearAndTypeTitle(_ text: String) {
|
func clearAndTypeTitle(_ text: String) {
|
||||||
let field = titleField
|
let field = titleField
|
||||||
field.waitUntilHittable().tap()
|
field.waitUntilHittable().tap()
|
||||||
// Triple-tap to select all existing text
|
|
||||||
field.tap(withNumberOfTaps: 3, numberOfTouches: 1)
|
// Primary path: command-A replacement is generally the most reliable on simulator.
|
||||||
|
field.typeKey("a", modifierFlags: .command)
|
||||||
field.typeText(text)
|
field.typeText(text)
|
||||||
|
|
||||||
|
// Fallback path: if replacement didn't stick, try explicit select-all and replace.
|
||||||
|
let currentValue = (field.value as? String) ?? ""
|
||||||
|
if !currentValue.localizedCaseInsensitiveContains(text) {
|
||||||
|
field.tap(withNumberOfTaps: 3, numberOfTouches: 1)
|
||||||
|
if app.menuItems["Select All"].waitForExistence(timeout: BaseUITestCase.shortTimeout) {
|
||||||
|
app.menuItems["Select All"].tap()
|
||||||
|
} else {
|
||||||
|
field.typeKey("a", modifierFlags: .command)
|
||||||
|
}
|
||||||
|
field.typeText(text)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func tapSave() {
|
func tapSave() {
|
||||||
|
|||||||
@@ -104,13 +104,25 @@ final class TripCustomItemTests: BaseUITestCase {
|
|||||||
// Sheet should dismiss
|
// Sheet should dismiss
|
||||||
editSheet.waitForDismiss()
|
editSheet.waitForDismiss()
|
||||||
|
|
||||||
// Verify updated title is visible
|
// Verify edited item still exists in itinerary
|
||||||
let updatedItem = app.staticTexts["Updated Title"]
|
|
||||||
XCTAssertTrue(
|
XCTAssertTrue(
|
||||||
updatedItem.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
|
detail.customItemCell.waitForExistence(timeout: BaseUITestCase.defaultTimeout),
|
||||||
"Updated custom item title should be visible in itinerary"
|
"Custom item should remain visible after editing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Re-open to verify updated title persisted (more reliable than global static text lookup).
|
||||||
|
detail.tapCustomItem()
|
||||||
|
let verifySheet = QuickAddItemSheetScreen(app: app)
|
||||||
|
verifySheet.waitForLoad()
|
||||||
|
|
||||||
|
let persistedTitle = (verifySheet.titleField.value as? String) ?? ""
|
||||||
|
XCTAssertTrue(
|
||||||
|
persistedTitle.localizedCaseInsensitiveContains("Updated Title"),
|
||||||
|
"Updated custom item title should persist after saving; found: '\(persistedTitle)'"
|
||||||
|
)
|
||||||
|
verifySheet.tapCancel()
|
||||||
|
verifySheet.waitForDismiss()
|
||||||
|
|
||||||
captureScreenshot(named: "F069-CustomItemEdited")
|
captureScreenshot(named: "F069-CustomItemEdited")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user