EchoUITests/AnswerCardsUITests.swift
Rama main · réplica de lectura del repositorio original.
import XCTest
/// The review and the checklist drawn to the approved designs (asked 2026-10-30), every part of
/// them working: a review's Cancel, Edit and its button; a checklist's rows, its ring and its
/// groups; each a finger's size, with a VoiceOver hint. `-echo.demoAsks` supplies the cards; the
/// answers and ticks start empty each run (`-cardAnswers`, `-cardTicks`).
@MainActor
final class AnswerCardsUITests: XCTestCase {
private func launch(_ extra: [String]) -> XCUIApplication {
continueAfterFailure = false
let app = XCUIApplication()
app.launchArguments = ["-echo.demoAsks"] + extra + ["-cardAnswers", "<dict/>", "-cardTicks", "<dict/>"]
+ XCUIApplication.chatsSection + XCUIApplication.straightToChat
app.launch()
return app
}
private func button(_ app: XCUIApplication, _ label: String) -> XCUIElement? {
app.buttons.matching(NSPredicate(format: "label == %@", label)).hittable(within: 10)
}
/// A checklist's row: a switch from iOS 27 (its `isToggle` trait), a button before.
private func row(_ app: XCUIApplication, _ text: String) -> XCUIElement {
app.descendants(matching: .any).matching(NSPredicate(format: "(elementType == %d OR elementType == %d) AND label BEGINSWITH %@",
XCUIElement.ElementType.switch.rawValue, XCUIElement.ElementType.button.rawValue, text)).firstMatch
}
private func assertFingerSized(_ element: XCUIElement, _ name: String) {
XCTAssertGreaterThanOrEqual(element.frame.height, 43.5, "\(name) is shorter than a finger")
XCTAssertGreaterThanOrEqual(element.frame.width, 43.5, "\(name) is narrower than a finger")
}
private func keep(_ name: String) {
let shot = XCTAttachment(screenshot: XCUIScreen.main.screenshot())
shot.name = name
shot.lifetime = .keepAlways
add(shot)
}
func testAReviewEditsThenSendsWhatItSays() throws {
let app = launch(["-echo.demoAsksReviewOnly"])
XCTAssertTrue(app.text(containing: "Email to send").waitForExistence(timeout: 20), "The review didn't appear")
XCTAssertTrue(app.text(containing: "Running a little late tonight").exists, "The subject isn't shown")
let edit = try XCTUnwrap(button(app, "Edit"), "No Edit")
let cancel = try XCTUnwrap(button(app, "Cancel"), "No Cancel")
let send = try XCTUnwrap(button(app, "Send"), "No Send")
for (element, name) in [(edit, "Edit"), (cancel, "Cancel"), (send, "Send")] { assertFingerSized(element, name) }
// Edit opens the sheet for changes; Done shows it as it will go.
edit.tap()
let subject = app.textFields["Subject"].exists ? app.textFields["Subject"] : app.textViews["Subject"]
XCTAssertTrue(subject.waitForExistence(timeout: 5), "Edit didn't open the fields")
keep("review-editing")
try XCTUnwrap(button(app, "Done"), "No Done while editing").tap()
XCTAssertFalse(app.textFields["Subject"].exists || app.textViews["Subject"].exists, "Done didn't close the fields")
// The button sends it, and the card says so.
send.tap()
XCTAssertTrue(app.text(containing: "Done: Send").waitForExistence(timeout: 10), "Send didn't answer the card")
XCTAssertFalse(app.text(containing: "Needs you").exists && app.buttons["Edit"].exists, "The answered card still asks")
keep("review-sent")
}
func testAReviewCancels() throws {
let app = launch(["-echo.demoAsksBooking"])
XCTAssertTrue(app.text(containing: "Table at Lupa Trattoria").waitForExistence(timeout: 20), "The booking didn't appear")
let cancels = app.buttons.matching(NSPredicate(format: "label == %@", "Cancel"))
// The booking's, the last card: on screen.
let cancel = try XCTUnwrap(cancels.hittable(within: 10), "No Cancel on screen")
cancel.tap()
XCTAssertTrue(app.text(containing: "Cancelled").waitForExistence(timeout: 10), "Cancel didn't answer the card")
}
func testAChecklistTicksLiveAndFolds() throws {
let app = launch(["-echo.demoAsksMore", "-echo.demoAsksListOnly"])
XCTAssertTrue(app.text(containing: "Cocoa Beach weekend").waitForExistence(timeout: 20), "The list didn't appear")
let ring = { (said: String) in app.descendants(matching: .any).matching(NSPredicate(format: "label == %@", said)).firstMatch }
XCTAssertTrue(ring("3 of 8 packed").exists, "The ring doesn't say how it stands")
// Each row ticks, and the ring follows.
let layer = row(app, "A warm layer")
XCTAssertTrue(layer.waitForExistence(timeout: 5), "No row for an item")
assertFingerSized(layer, "A row")
XCTAssertEqual(layer.value as? String, "Not done")
layer.tap()
XCTAssertTrue(ring("4 of 8 packed").waitForExistence(timeout: 5), "The ring didn't follow a tick")
XCTAssertEqual(layer.value as? String, "Done")
XCTAssertNotNil(button(app, "Send"), "A changed list has no Send")
keep("checklist-ticked")
layer.tap()
XCTAssertTrue(ring("3 of 8 packed").waitForExistence(timeout: 5), "The ring didn't follow an untick")
for text in ["Swimsuits", "Sandals", "Sunscreen", "Beach towels", "Umbrella", "Phone chargers", "Snacks for the drive"] {
XCTAssertTrue(row(app, text).isHittable, "\(text) can't be tapped")
}
// A group's name folds it away and back.
// Folded, it says how many are done too ("Clothes, 1/3").
let clothes = app.buttons.matching(NSPredicate(format: "label ==[c] %@ OR label BEGINSWITH[c] %@", "Clothes", "Clothes,")).firstMatch
XCTAssertTrue(clothes.exists, "A group's name isn't a button")
assertFingerSized(clothes, "A group's name")
clothes.tap()
XCTAssertFalse(row(app, "Sandals").waitForExistence(timeout: 2),
"The group didn't fold")
clothes.tap()
XCTAssertTrue(row(app, "Sandals").waitForExistence(timeout: 5),
"The group didn't open again")
let more = app.buttons["More for Cocoa Beach weekend"]
XCTAssertTrue(more.exists, "No menu")
assertFingerSized(more, "The menu")
}
}