Ctrl K
ComunidadGuías…

EchoUITests/UITestSupport.swift

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

Archivo original ↗Preguntar a KiBot
import XCTest

/// What the UI tests share: the launch arguments nearly all of them start from, finding a piece
/// of text on screen, and the conversation list's rows.
extension XCUIApplication {
    /// A launch that lands on the chat: setup done, no voice mode at launch, no app lock.
    static let straightToChat = ["-setupDone", "YES", "-openToVoiceScreen", "NO", "-listenOnOpen", "NO", "-requireBiometrics", "NO"]
    /// The conversation list on Chats; without it, it reopens on whichever section was used last.
    static let chatsSection = ["-conversations.section", "sessions"]

    /// The first text on screen that contains `fragment`.
    func text(containing fragment: String) -> XCUIElement {
        staticTexts.containing(NSPredicate(format: "label CONTAINS %@", fragment)).firstMatch
    }
}

extension XCUIElementQuery {
    /// The first match that can be tapped, once there is one. A conversation's title is also the
    /// header of the open conversation, pushed off screen behind the iPhone's list, and a row's
    /// Delete is also a swipe action laid out off screen: the one wanted is the one on screen.
    func hittable(within seconds: TimeInterval) -> XCUIElement? {
        let deadline = Date().addingTimeInterval(seconds)
        repeat {
            if let found = (0 ..< count).map({ element(boundBy: $0) }).first(where: \.isHittable) { return found }
            RunLoop.current.run(until: Date().addingTimeInterval(0.2))
        } while Date() < deadline
        return nil
    }
}

extension XCUIApplication {
    /// Deletes a conversation through its row's menu in the list (open already). A swipe to the
    /// left on the iPhone closes the list instead. False when there was no such row.
    @discardableResult
    func deleteConversation(titled title: String) -> Bool {
        let rows = staticTexts.matching(identifier: title)
        guard let row = rows.hittable(within: 10) else { return false }
        let before = rows.count
        row.press(forDuration: 1.0)
        guard let delete = buttons.matching(NSPredicate(format: "label == %@", "Delete")).hittable(within: 5) else { return false }
        delete.tap()
        let deadline = Date().addingTimeInterval(5)
        while rows.count >= before, Date() < deadline { RunLoop.current.run(until: Date().addingTimeInterval(0.2)) }
        return rows.count < before
    }
}

extension XCUIApplication {
    /// Opens one of Settings' pages (Appearance, Writing, Voice, Siri, Agent) from its first
    /// screen, which is open already.
    func openSettingsPage(_ title: String, file: StaticString = #filePath, line: UInt = #line) {
        let row = buttons.matching(NSPredicate(format: "label == %@", title)).firstMatch
        for _ in 0 ..< 6 where !(row.exists && row.isHittable) { swipeUp() }
        XCTAssertTrue(row.exists && row.isHittable, "No \(title) in Settings", file: file, line: line)
        row.tap()
        XCTAssertTrue(navigationBars[title].waitForExistence(timeout: 5), "Settings → \(title) didn't open", file: file, line: line)
    }
}