Ctrl K
ComunidadGuías…

EchoUITests/ReplyActionsUITests.swift

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
import XCTest

/// Copying a reply says so, and a tool step opens onto what it returned. Taps are the point, so
/// these run in the app.
@MainActor
final class ReplyActionsUITests: XCTestCase {
    private func launchDemo() -> XCUIApplication {
        continueAfterFailure = false
        let app = XCUIApplication()
        app.launchArguments = ["-echo.demo", "-echo.demoShort"] + XCUIApplication.chatsSection + XCUIApplication.straightToChat
        app.launch()
        return app
    }


    func testCopySaysSo() {
        let app = launchDemo()
        XCTAssertTrue(app.text(containing: "I've added a reminder").waitForExistence(timeout: 15), "The demo reply didn't appear")
        // The actions sit under a finished reply without being asked for.
        XCTAssertTrue(app.buttons["Regenerate"].waitForExistence(timeout: 3), "No actions under the reply")

        app.buttons["Copy reply"].tap()
        let toast = app.staticTexts["Copied"]
        XCTAssertTrue(toast.waitForExistence(timeout: 3), "No toast after copying")
        XCTAssertTrue(app.buttons["Copied"].exists, "The copy button didn't turn into a check")
        let composer = app.textFields.firstMatch.exists ? app.textFields.firstMatch : app.textViews.firstMatch
        XCTAssertLessThan(toast.frame.maxY, composer.frame.minY, "The toast sits under the composer")
    }

    /// While a reply is being worked on its thinking is open, and a tap folds it all the same:
    /// it used to stay open, with a header that took no taps, until the reply was done.
    func testThinkingFoldsWhileTheReplyIsStillComing() {
        let app = XCUIApplication()
        app.launchArguments = ["-echo.demoStream"] + XCUIApplication.straightToChat
        app.launch()
        let hide = app.buttons["Hide thinking"]
        XCTAssertTrue(hide.waitForExistence(timeout: 20), "The thinking isn't open while the reply is live")
        hide.tap()
        XCTAssertTrue(app.buttons["Show thinking"].waitForExistence(timeout: 3), "The tap didn't fold the thinking")
        XCTAssertFalse(hide.exists)
        // And it stays as the person left it once the reply is done.
        XCTAssertTrue(app.buttons["Regenerate"].waitForExistence(timeout: 30), "The demo reply never finished")
        XCTAssertTrue(app.buttons["Show thinking"].exists)
    }

    func testAToolStepOpensOntoWhatItReturned() {
        let app = launchDemo()
        let steps = app.buttons["Show tool steps"]
        XCTAssertTrue(steps.waitForExistence(timeout: 15), "No steps card on the demo reply")
        steps.tap()

        let step = app.buttons["Tool calendar, completed"]
        XCTAssertTrue(step.waitForExistence(timeout: 3), "The card didn't open onto its steps")
        XCTAssertFalse(app.text(containing: "Standup").exists, "A step's output is showing before it is opened")
        step.tap()
        XCTAssertTrue(app.text(containing: "Standup").waitForExistence(timeout: 3), "The step didn't open onto its output")
        XCTAssertTrue(app.buttons["Open full output"].exists)

        app.buttons["Open full output"].tap()
        XCTAssertTrue(app.navigationBars["calendar"].waitForExistence(timeout: 3), "The full output didn't open")
    }

    /// GitHub issue 46: a long-press on a reply's text selects the word under the finger where
    /// it stands, with the selection's menu; "Ask about this" puts it in the composer as a quote.
    func testALongPressSelectsInPlaceAndAsksAboutIt() {
        let app = launchDemo()
        let reply = app.text(containing: "I've added a reminder")
        XCTAssertTrue(reply.waitForExistence(timeout: 15), "The demo reply didn't appear")
        reply.coordinate(withNormalizedOffset: CGVector(dx: 0.3, dy: 0.5)).press(forDuration: 0.8)

        let ask = app.menuItems["Ask about this"].exists ? app.menuItems["Ask about this"] : app.buttons["Ask about this"]
        XCTAssertTrue(ask.waitForExistence(timeout: 3), "The long-press didn't select with the selection's menu")
        XCTAssertFalse(app.navigationBars["Select text"].exists, "It opened the sheet, not a selection in place")
        XCTAssertFalse(app.buttons["Make This a Routine…"].exists || app.buttons["Watch This…"].exists, "The reply's own menu came up instead")
        let shot = XCTAttachment(screenshot: app.screenshot())
        shot.name = "inline-selection"
        shot.lifetime = .keepAlways
        add(shot)
        // Let go with a tap elsewhere, and press again: the same, the reply drawn as before between.
        app.staticTexts["Redde"].firstMatch.tap()
        XCTAssertTrue(ask.waitForNonExistence(timeout: 3), "A tap elsewhere didn't let go of the selection")
        XCTAssertTrue(reply.waitForExistence(timeout: 3), "The reply wasn't drawn as before")
        reply.coordinate(withNormalizedOffset: CGVector(dx: 0.6, dy: 0.5)).press(forDuration: 0.8)
        XCTAssertTrue(ask.waitForExistence(timeout: 3), "A second long-press didn't select")
        ask.tap()

        let composer = app.textFields.firstMatch.exists ? app.textFields.firstMatch : app.textViews.matching(NSPredicate(format: "value BEGINSWITH %@", "> ")).firstMatch
        let quoted = NSPredicate(format: "value BEGINSWITH %@", "> ")
        expectation(for: quoted, evaluatedWith: composer)
        waitForExpectations(timeout: 5)
    }

    /// The selection's handles move it, as in Notes: the end handle dragged along takes in more.
    func testDraggingAHandleGrowsTheSelection() {
        let app = launchDemo()
        let reply = app.text(containing: "I've added a reminder")
        XCTAssertTrue(reply.waitForExistence(timeout: 15), "The demo reply didn't appear")
        reply.coordinate(withNormalizedOffset: CGVector(dx: 0.2, dy: 0.5)).press(forDuration: 0.8)
        let selection = app.textViews.matching(NSPredicate(format: "identifier BEGINSWITH %@", "inline-selection|")).firstMatch
        XCTAssertTrue(selection.waitForExistence(timeout: 3), "The long-press didn't select in place")
        func state() -> (length: Int, end: CGPoint) {
            let parts = selection.identifier.split(separator: "|").compactMap { Int($0) }
            return (parts[0], CGPoint(x: parts[1], y: parts[2]))
        }
        let before = state()
        let first = XCTAttachment(screenshot: app.screenshot())
        first.name = "selected \(before.length) end \(Int(before.end.x)),\(Int(before.end.y))"
        first.lifetime = .keepAlways
        add(first)
        let origin = app.coordinate(withNormalizedOffset: .zero)
        let handle = origin.withOffset(CGVector(dx: before.end.x, dy: before.end.y + 4))
        handle.press(forDuration: 0.15, thenDragTo: origin.withOffset(CGVector(dx: min(before.end.x + 140, 380), dy: before.end.y + 4)))
        let shot = XCTAttachment(screenshot: app.screenshot())
        shot.name = "handle-dragged"
        shot.lifetime = .keepAlways
        add(shot)
        XCTAssertTrue(selection.waitForExistence(timeout: 2), "The selection ended when its handle was dragged")
        let after = state()
        XCTAssertGreaterThan(after.length, before.length, "Dragging the end handle didn't take in more text (\(before.length) → \(after.length))")
    }

    /// The reply's menu is on its ··· button now, "Select text" (the whole reply in a sheet) with it.
    func testTheMoreButtonStillHasTheRepliesMenu() {
        let app = launchDemo()
        XCTAssertTrue(app.buttons["Regenerate"].waitForExistence(timeout: 15), "No actions under the reply")
        // The reply's, not the header's.
        app.buttons.matching(NSPredicate(format: "label == %@", "More")).allElementsBoundByIndex.last?.tap()
        XCTAssertTrue(app.buttons["Select text"].waitForExistence(timeout: 3), "No Select text in the ··· menu")
        app.buttons["Select text"].tap()
        XCTAssertTrue(app.navigationBars["Select text"].waitForExistence(timeout: 5), "The selectable copy didn't open")
    }

    /// A double-tap on a reply brings up the reactions; one picked shows under it.
    func testADoubleTapReacts() {
        let app = launchDemo()
        let reply = app.text(containing: "I've added a reminder")
        XCTAssertTrue(reply.waitForExistence(timeout: 15), "The demo reply didn't appear")
        reply.doubleTap()
        let thumbs = app.buttons["React 👍"]
        XCTAssertTrue(thumbs.waitForExistence(timeout: 3), "The double-tap didn't bring up the reactions")
        thumbs.tap()
        XCTAssertTrue(app.buttons["You reacted 👍. Remove"].waitForExistence(timeout: 3), "The reaction doesn't show under the reply")
    }
}