Aggiunge UI test sul simulatore (hub, lingue, wizard, diretta) e identificatori di accessibilità per poterli ripetere su collaudo e produzione. Co-authored-by: Cursor <cursoragent@cursor.com>
561 lines
23 KiB
Swift
561 lines
23 KiB
Swift
import XCTest
|
|
|
|
/// Pilota il simulatore come un utente (backend da API_BASE_URL).
|
|
final class HubFlowsUITests: XCTestCase {
|
|
private var app: XCUIApplication!
|
|
private let shotDir = "/tmp/mltv-uitest"
|
|
private var apiBaseUrl = "http://localhost:3000"
|
|
|
|
override func setUpWithError() throws {
|
|
continueAfterFailure = false
|
|
addUIInterruptionMonitor(withDescription: "Dialog di sistema") { alert in
|
|
for label in [
|
|
"Non ora", "Not Now", "Not now",
|
|
"Consenti", "OK", "Allow", "Allow Once", "Consenti una volta",
|
|
"Allow While Using App", "Consenti durante l'utilizzo",
|
|
"Allow While Using the App"
|
|
] {
|
|
if alert.buttons[label].exists {
|
|
alert.buttons[label].tap()
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
try FileManager.default.createDirectory(atPath: shotDir, withIntermediateDirectories: true)
|
|
app = XCUIApplication()
|
|
// L'URL API arriva dall'Info.plist dell'app ($(API_BASE_URL) di xcodebuild).
|
|
// Non iniettare un default localhost: maschererebbe il collaudo.
|
|
if let fromEnv = ProcessInfo.processInfo.environment["API_BASE_URL"]?.trimmingCharacters(in: .whitespacesAndNewlines),
|
|
!fromEnv.isEmpty {
|
|
apiBaseUrl = fromEnv
|
|
app.launchEnvironment["API_BASE_URL"] = fromEnv
|
|
}
|
|
app.launchArguments = ["--uitesting-reset", "-AppleLanguages", "(it)", "-AppleLocale", "it_IT"]
|
|
app.launch()
|
|
}
|
|
|
|
func testLanguagesOnLogin() throws {
|
|
waitLogin()
|
|
dismissKeyboard()
|
|
capture("lang-00-it-login")
|
|
XCTAssertTrue(
|
|
app.staticTexts["ACCEDI"].exists || app.buttons["ACCEDI"].exists,
|
|
"Login italiano atteso (ACCEDI)"
|
|
)
|
|
XCTAssertTrue(any("login.forgot").exists || app.buttons["Password dimenticata?"].exists)
|
|
|
|
assertLanguageSwitch(
|
|
code: "en",
|
|
loginTitle: "LOG IN",
|
|
forgot: "Forgot password?",
|
|
shot: "lang-01-en-login"
|
|
)
|
|
assertLanguageSwitch(
|
|
code: "fr",
|
|
loginTitle: "CONNEXION",
|
|
forgot: "Mot de passe oublié ?",
|
|
shot: "lang-02-fr-login"
|
|
)
|
|
assertLanguageSwitch(
|
|
code: "de",
|
|
loginTitle: "ANMELDEN",
|
|
forgot: "Passwort vergessen?",
|
|
shot: "lang-03-de-login"
|
|
)
|
|
assertLanguageSwitch(
|
|
code: "es",
|
|
loginTitle: "ACCEDER",
|
|
forgot: "¿Olvidaste la contraseña?",
|
|
shot: "lang-04-es-login"
|
|
)
|
|
assertLanguageSwitch(
|
|
code: "it",
|
|
loginTitle: "ACCEDI",
|
|
forgot: "Password dimenticata?",
|
|
shot: "lang-05-it-login"
|
|
)
|
|
}
|
|
|
|
func testCoachFlows() throws {
|
|
waitLogin()
|
|
dismissKeyboard()
|
|
capture("01-login")
|
|
|
|
let forgot = firstExisting(["login.forgot"], labels: ["Password dimenticata?", "Forgot password?"])
|
|
XCTAssertTrue(forgot.waitForExistence(timeout: 8), "Link password dimenticata assente")
|
|
tapHittable(forgot)
|
|
XCTAssertTrue(any("forgot.back").waitForExistence(timeout: 8), "Schermata password dimenticata assente")
|
|
capture("02-forgot-password")
|
|
|
|
let mail = app.textFields.firstMatch
|
|
XCTAssertTrue(mail.waitForExistence(timeout: 4))
|
|
tapHittable(mail)
|
|
mail.typeText("coach@matchlivetv.test")
|
|
dismissKeyboard()
|
|
tapHittable(firstExisting(["forgot.submit"], labels: ["Invia link di reset", "Send reset link"]))
|
|
XCTAssertTrue(
|
|
app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'link'")).firstMatch.waitForExistence(timeout: 12)
|
|
|| app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'registrat'")).firstMatch.exists,
|
|
"Manca il messaggio di esito del reset password.\n\(app.debugDescription)"
|
|
)
|
|
capture("02b-forgot-submitted")
|
|
tapHittable(app.buttons["forgot.back"])
|
|
|
|
loginIfNeeded()
|
|
waitHub()
|
|
dismissSystemDialogs()
|
|
capture("03-hub-dopo-login")
|
|
|
|
openLanguagePicker(from: "matches.language")
|
|
tapHittable(any("language.en"))
|
|
XCTAssertTrue(
|
|
app.buttons["NEW MATCH"].waitForExistence(timeout: 8) || any("matches.new").waitForExistence(timeout: 4),
|
|
"Hub non passato in inglese"
|
|
)
|
|
capture("03b-hub-en")
|
|
openLanguagePicker(from: "matches.language")
|
|
tapHittable(any("language.it"))
|
|
XCTAssertTrue(
|
|
app.buttons["NUOVA PARTITA"].waitForExistence(timeout: 8) || any("matches.new").waitForExistence(timeout: 4),
|
|
"Hub non tornato in italiano"
|
|
)
|
|
|
|
openAccountAndBack()
|
|
capture("04-hub-dopo-account")
|
|
|
|
scheduleMatchShowsConfigureDialog()
|
|
capture("05-dopo-programmazione")
|
|
}
|
|
|
|
func testPasswordRejectAndLogout() throws {
|
|
loginIfNeeded()
|
|
waitHub()
|
|
dismissSystemDialogs()
|
|
tapHittable(firstExisting(["matches.account"], labels: ["Account"]))
|
|
XCTAssertTrue(any("account.back").waitForExistence(timeout: 8), "Account non aperto")
|
|
app.swipeUp()
|
|
app.swipeUp()
|
|
let secures = app.secureTextFields
|
|
XCTAssertTrue(secures.element(boundBy: 0).waitForExistence(timeout: 6), "Campi password assenti")
|
|
tapHittable(secures.element(boundBy: 0))
|
|
secures.element(boundBy: 0).typeText("sbagliata")
|
|
tapHittable(secures.element(boundBy: 1))
|
|
secures.element(boundBy: 1).typeText("NuovaPass123!")
|
|
tapHittable(secures.element(boundBy: 2))
|
|
secures.element(boundBy: 2).typeText("NuovaPass123!")
|
|
dismissKeyboard()
|
|
tapHittable(firstExisting(["account.save.password"], labels: ["Aggiorna password"]))
|
|
sleep(2)
|
|
capture("13-password-reject")
|
|
XCTAssertTrue(
|
|
app.staticTexts.matching(NSPredicate(format: "label CONTAINS[c] 'password' OR label CONTAINS[c] 'non valid' OR label CONTAINS[c] 'errat' OR label CONTAINS[c] 'incorrect'")).firstMatch.waitForExistence(timeout: 8)
|
|
|| any("account.save.password").exists,
|
|
"Atteso un errore sul cambio password con password attuale sbagliata"
|
|
)
|
|
tapHittable(any("account.logout"))
|
|
XCTAssertTrue(
|
|
any("login.submit").waitForExistence(timeout: 12) || app.staticTexts["ACCEDI"].waitForExistence(timeout: 4),
|
|
"Logout non ha riportato al login"
|
|
)
|
|
capture("14-dopo-logout")
|
|
}
|
|
|
|
func testWizardNetworkAndLive() throws {
|
|
loginIfNeeded()
|
|
waitHub()
|
|
dismissSystemDialogs()
|
|
tapNewMatch()
|
|
let quick = firstExisting(["sheet.quick.option"], labels: ["Avvia subito"])
|
|
XCTAssertTrue(quick.waitForExistence(timeout: 8))
|
|
tapHittable(quick)
|
|
XCTAssertTrue(
|
|
app.staticTexts["01 · Partita"].waitForExistence(timeout: 25),
|
|
"Wizard step 1 assente"
|
|
)
|
|
capture("06-wizard-step1")
|
|
|
|
let opponent = any("wizard.opponent").exists ? any("wizard.opponent") : app.textFields.firstMatch
|
|
if opponent.waitForExistence(timeout: 8) {
|
|
tapHittable(opponent)
|
|
if let current = opponent.value as? String, !current.isEmpty {
|
|
let deletes = String(repeating: XCUIKeyboardKey.delete.rawValue, count: current.count)
|
|
opponent.typeText(deletes)
|
|
}
|
|
opponent.typeText("UI Wizard")
|
|
dismissKeyboard()
|
|
}
|
|
if any("wizard.cover.change").waitForExistence(timeout: 3)
|
|
|| app.buttons["Cambia copertina per questa partita"].waitForExistence(timeout: 2) {
|
|
let cover = firstExisting(["wizard.cover.change"], labels: ["Cambia copertina per questa partita"])
|
|
tapHittable(cover)
|
|
sleep(1)
|
|
capture("06b-cover-picker")
|
|
let spring = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
for label in ["Annulla", "Cancel", "Chiudi", "Close"] {
|
|
if spring.buttons[label].exists {
|
|
spring.buttons[label].tap()
|
|
break
|
|
}
|
|
if app.buttons[label].exists {
|
|
tapHittable(app.buttons[label])
|
|
break
|
|
}
|
|
}
|
|
dismissSystemDialogs()
|
|
}
|
|
scrollToId("wizard.next")
|
|
tapHittable(any("wizard.next"))
|
|
XCTAssertTrue(
|
|
app.staticTexts["02 · Trasmissione"].waitForExistence(timeout: 20)
|
|
|| app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'Trasmissione' OR label CONTAINS 'Transmission'")).firstMatch.waitForExistence(timeout: 8),
|
|
"Wizard step 2 (trasmissione) non aperto.\n\(app.debugDescription)"
|
|
)
|
|
capture("07-wizard-step2")
|
|
if app.buttons["NON IN ELENCO"].waitForExistence(timeout: 3) {
|
|
tapHittable(app.buttons["NON IN ELENCO"])
|
|
}
|
|
if app.staticTexts["YouTube Live"].waitForExistence(timeout: 3) {
|
|
tapHittable(app.staticTexts["YouTube Live"])
|
|
sleep(1)
|
|
capture("07b-youtube-tap")
|
|
if app.alerts.firstMatch.exists {
|
|
app.alerts.buttons.firstMatch.tap()
|
|
}
|
|
}
|
|
scrollToId("wizard.next")
|
|
tapHittable(any("wizard.next"))
|
|
XCTAssertTrue(
|
|
app.staticTexts["03 · Test rete"].waitForExistence(timeout: 20)
|
|
|| any("wizard.network.test").waitForExistence(timeout: 10),
|
|
"Wizard step 3 (rete) non aperto.\n\(app.debugDescription)"
|
|
)
|
|
capture("08-wizard-step3")
|
|
|
|
scrollToId("wizard.network.test")
|
|
tapHittable(any("wizard.network.test"))
|
|
XCTAssertTrue(
|
|
app.staticTexts["PRONTO PER ANDARE IN DIRETTA"].waitForExistence(timeout: 20)
|
|
|| app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'PRONTO' OR label CONTAINS 'READY'")).firstMatch.waitForExistence(timeout: 10),
|
|
"Test rete non completato.\n\(app.debugDescription)"
|
|
)
|
|
capture("08b-network-ready")
|
|
|
|
let start = any("wizard.start")
|
|
XCTAssertTrue(start.waitForExistence(timeout: 6))
|
|
tapHittable(start)
|
|
dismissSystemDialogs()
|
|
let liveVisible =
|
|
any("broadcast.terminate").waitForExistence(timeout: 25)
|
|
|| any("broadcast.pause").waitForExistence(timeout: 4)
|
|
|| app.buttons["CONCEDI PERMESSI"].waitForExistence(timeout: 4)
|
|
|| app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'camera' OR label CONTAINS 'microfono' OR label CONTAINS 'Permessi'")).firstMatch.waitForExistence(timeout: 4)
|
|
capture("09-broadcast")
|
|
XCTAssertTrue(liveVisible, "Dopo INIZIA non si apre la diretta né la richiesta permessi.\n\(app.debugDescription)")
|
|
|
|
if any("broadcast.mute").waitForExistence(timeout: 3) {
|
|
tapHittable(any("broadcast.mute"))
|
|
sleep(1)
|
|
capture("09b-mute-confirm")
|
|
if app.buttons["Conferma"].waitForExistence(timeout: 2) || app.alerts.buttons.element(boundBy: 1).exists {
|
|
let confirm = app.buttons["Conferma"].exists ? app.buttons["Conferma"] : app.alerts.buttons.element(boundBy: 1)
|
|
tapHittable(confirm)
|
|
}
|
|
capture("09c-muted")
|
|
}
|
|
|
|
if any("broadcast.score.home.plus").waitForExistence(timeout: 3) {
|
|
tapHittable(any("broadcast.score.home.plus"))
|
|
sleep(1)
|
|
capture("09c2-score-plus")
|
|
}
|
|
if any("broadcast.abr").waitForExistence(timeout: 2) {
|
|
tapHittable(any("broadcast.abr"))
|
|
sleep(1)
|
|
capture("09c3-abr")
|
|
if app.buttons["Annulla"].waitForExistence(timeout: 2) {
|
|
tapHittable(app.buttons["Annulla"])
|
|
}
|
|
}
|
|
if any("broadcast.share").waitForExistence(timeout: 2) {
|
|
tapHittable(any("broadcast.share"))
|
|
sleep(1)
|
|
if app.buttons["Conferma"].waitForExistence(timeout: 3) {
|
|
tapHittable(app.buttons["Conferma"])
|
|
}
|
|
sleep(1)
|
|
capture("09c4-share")
|
|
let spring = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
for label in ["Chiudi", "Close", "Annulla", "Cancel"] {
|
|
if spring.buttons[label].exists {
|
|
spring.buttons[label].tap()
|
|
break
|
|
}
|
|
if app.buttons[label].exists {
|
|
tapHittable(app.buttons[label])
|
|
break
|
|
}
|
|
}
|
|
}
|
|
|
|
if any("broadcast.terminate").waitForExistence(timeout: 3) {
|
|
tapHittable(any("broadcast.terminate"))
|
|
sleep(1)
|
|
capture("09d-terminate-confirm")
|
|
for label in ["TERMINA", "END", "BEENDEN", "TERMINAR", "TERMINER"] {
|
|
if app.buttons[label].exists {
|
|
tapHittable(app.buttons[label])
|
|
break
|
|
}
|
|
}
|
|
} else if any("wizard.close").waitForExistence(timeout: 2) {
|
|
tapHittable(any("wizard.close"))
|
|
}
|
|
_ = any("matches.new").waitForExistence(timeout: 12)
|
|
capture("09e-back-hub")
|
|
}
|
|
|
|
func testResumeLiveSheet() throws {
|
|
loginIfNeeded()
|
|
waitHub()
|
|
dismissSystemDialogs()
|
|
capture("10-hub-con-banner")
|
|
|
|
let banner = app.buttons["matches.resume.banner"]
|
|
if !banner.waitForExistence(timeout: 10) {
|
|
throw XCTSkip("Nessuna sessione attiva da riprendere su \(apiBaseUrl)")
|
|
}
|
|
tapHittable(banner)
|
|
sleep(1)
|
|
capture("11-dopo-tap-banner")
|
|
|
|
let sheetVisible =
|
|
any("resume.sheet").waitForExistence(timeout: 4)
|
|
|| any("resume.sheet.panel").waitForExistence(timeout: 2)
|
|
|| any("resume.camera").waitForExistence(timeout: 2)
|
|
|| app.staticTexts["Diretta in corso"].waitForExistence(timeout: 2)
|
|
XCTAssertTrue(sheetVisible, "Lo sheet di ripresa non è comparso.\n\(app.debugDescription)")
|
|
capture("12-resume-sheet")
|
|
|
|
let cancel = firstExisting(["resume.cancel"], labels: ["Annulla", "Cancel"])
|
|
if cancel.waitForExistence(timeout: 3) {
|
|
tapHittable(cancel)
|
|
}
|
|
}
|
|
|
|
private func assertLanguageSwitch(code: String, loginTitle: String, forgot: String, shot: String) {
|
|
openLanguagePicker(from: "login.language")
|
|
let row = any("language.\(code)")
|
|
XCTAssertTrue(row.waitForExistence(timeout: 6), "Voce lingua \(code) assente")
|
|
tapHittable(row)
|
|
XCTAssertTrue(
|
|
app.staticTexts[loginTitle].waitForExistence(timeout: 8) || app.buttons[loginTitle].waitForExistence(timeout: 2),
|
|
"Dopo lingua \(code) manca il titolo \(loginTitle).\n\(app.debugDescription)"
|
|
)
|
|
XCTAssertTrue(
|
|
any("login.forgot").waitForExistence(timeout: 4)
|
|
|| app.buttons[forgot].waitForExistence(timeout: 2)
|
|
|| app.staticTexts[forgot].waitForExistence(timeout: 1),
|
|
"Dopo lingua \(code) manca '\(forgot)'"
|
|
)
|
|
capture(shot)
|
|
}
|
|
|
|
private func openLanguagePicker(from id: String) {
|
|
let globe = any(id)
|
|
XCTAssertTrue(globe.waitForExistence(timeout: 8), "Bottone lingua \(id) assente")
|
|
tapHittable(globe)
|
|
XCTAssertTrue(
|
|
any("language.it").waitForExistence(timeout: 8) || app.staticTexts["Italiano"].waitForExistence(timeout: 4),
|
|
"Picker lingua non aperto"
|
|
)
|
|
}
|
|
|
|
private func loginIfNeeded() {
|
|
if hubVisible() { return }
|
|
waitLogin()
|
|
dismissKeyboard()
|
|
let email = app.textFields.firstMatch
|
|
XCTAssertTrue(email.waitForExistence(timeout: 8))
|
|
tapHittable(email)
|
|
email.typeText("coach@matchlivetv.test")
|
|
let password = app.secureTextFields.firstMatch
|
|
XCTAssertTrue(password.waitForExistence(timeout: 4))
|
|
tapHittable(password)
|
|
password.typeText("Password123")
|
|
dismissKeyboard()
|
|
let submit = firstExisting(["login.submit"], labels: ["ACCEDI", "LOG IN", "CONNEXION", "ANMELDEN", "ACCEDER"])
|
|
XCTAssertTrue(submit.waitForExistence(timeout: 4))
|
|
tapHittable(submit)
|
|
dismissSystemDialogs()
|
|
}
|
|
|
|
private func waitLogin() {
|
|
let deadline = Date().addingTimeInterval(20)
|
|
while Date() < deadline {
|
|
if any("login.submit").exists || app.textFields.firstMatch.exists {
|
|
return
|
|
}
|
|
if hubVisible() { return }
|
|
RunLoop.current.run(until: Date().addingTimeInterval(0.3))
|
|
}
|
|
capture("fail-login")
|
|
XCTFail("Login non comparso. Gerarchia:\n\(app.debugDescription)")
|
|
}
|
|
|
|
private func waitHub() {
|
|
XCTAssertTrue(
|
|
any("matches.new").waitForExistence(timeout: 25)
|
|
|| app.buttons["NUOVA PARTITA"].waitForExistence(timeout: 5)
|
|
|| app.buttons["NEW MATCH"].waitForExistence(timeout: 2),
|
|
"Hub partite non raggiunto dopo login su \(apiBaseUrl).\n\(app.debugDescription)"
|
|
)
|
|
dismissKeyboard()
|
|
}
|
|
|
|
private func hubVisible() -> Bool {
|
|
app.buttons["matches.new"].exists || app.buttons["NUOVA PARTITA"].exists || app.buttons["NEW MATCH"].exists
|
|
}
|
|
|
|
private func openAccountAndBack() {
|
|
let account = firstExisting(["matches.account"], labels: ["Account"])
|
|
XCTAssertTrue(account.waitForExistence(timeout: 6), "Bottone Account assente")
|
|
tapHittable(account)
|
|
XCTAssertTrue(
|
|
any("account.title").waitForExistence(timeout: 8) || any("account.back").waitForExistence(timeout: 2),
|
|
"Schermata Account non aperta.\n\(app.debugDescription)"
|
|
)
|
|
capture("04a-account")
|
|
XCTAssertTrue(any("account.save.profile").waitForExistence(timeout: 4), "Manca Salva profilo")
|
|
tapHittable(any("account.save.profile"))
|
|
sleep(2)
|
|
capture("04b-account-saved")
|
|
tapHittable(app.buttons["account.back"])
|
|
XCTAssertTrue(any("matches.new").waitForExistence(timeout: 8))
|
|
}
|
|
|
|
private func scheduleMatchShowsConfigureDialog() {
|
|
tapNewMatch()
|
|
capture("05a-nuova-partita")
|
|
let schedule = firstExisting(["sheet.schedule.option"], labels: ["Programma partita"])
|
|
XCTAssertTrue(schedule.waitForExistence(timeout: 8), "Opzione Programma partita assente")
|
|
tapHittable(schedule)
|
|
let opponent = app.textFields["Avversario"].exists ? app.textFields["Avversario"] : app.textFields.firstMatch
|
|
XCTAssertTrue(opponent.waitForExistence(timeout: 8), "Campo avversario assente")
|
|
tapHittable(opponent)
|
|
opponent.typeText("UI Test Programmata")
|
|
dismissKeyboard()
|
|
let save = app.buttons["SALVA IN PROGRAMMA"]
|
|
XCTAssertTrue(save.waitForExistence(timeout: 4))
|
|
tapHittable(save)
|
|
XCTAssertTrue(
|
|
app.staticTexts["Configurare ora?"].waitForExistence(timeout: 12) || app.alerts.firstMatch.waitForExistence(timeout: 4),
|
|
"Manca il dialog Configura/Più tardi"
|
|
)
|
|
capture("05b-dialog-configura")
|
|
let later = app.buttons["Più tardi"].exists ? app.buttons["Più tardi"] : app.alerts.buttons.element(boundBy: 0)
|
|
tapHittable(later)
|
|
XCTAssertTrue(any("matches.new").waitForExistence(timeout: 8))
|
|
XCTAssertTrue(
|
|
app.staticTexts["UI Test Programmata"].waitForExistence(timeout: 8)
|
|
|| app.staticTexts.matching(NSPredicate(format: "label CONTAINS 'Programmata'")).firstMatch.exists,
|
|
"La partita programmata non compare in hub"
|
|
)
|
|
}
|
|
|
|
private func tapNewMatch() {
|
|
let neu = firstExisting(["matches.new"], labels: ["NUOVA PARTITA", "NEW MATCH"])
|
|
XCTAssertTrue(neu.waitForExistence(timeout: 8))
|
|
tapHittable(neu)
|
|
}
|
|
|
|
private func scrollToId(_ id: String) {
|
|
let el = any(id)
|
|
for _ in 0..<8 {
|
|
if el.exists && el.isHittable { return }
|
|
app.swipeUp()
|
|
}
|
|
}
|
|
|
|
private func any(_ id: String) -> XCUIElement {
|
|
app.descendants(matching: .any)[id]
|
|
}
|
|
|
|
private func firstExisting(_ ids: [String], labels: [String] = []) -> XCUIElement {
|
|
for id in ids {
|
|
let el = any(id)
|
|
if el.exists { return el }
|
|
}
|
|
for label in labels {
|
|
let button = app.buttons[label]
|
|
if button.exists { return button }
|
|
let text = app.staticTexts[label]
|
|
if text.exists { return text }
|
|
}
|
|
if let firstId = ids.first {
|
|
return any(firstId)
|
|
}
|
|
return app.buttons[labels.first ?? ""]
|
|
}
|
|
|
|
private func tapHittable(_ element: XCUIElement) {
|
|
if element.isHittable {
|
|
element.tap()
|
|
return
|
|
}
|
|
element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
|
|
}
|
|
|
|
private func dismissSystemDialogs() {
|
|
let spring = XCUIApplication(bundleIdentifier: "com.apple.springboard")
|
|
_ = spring.buttons["Non ora"].waitForExistence(timeout: 3)
|
|
|| spring.buttons["Not Now"].waitForExistence(timeout: 1)
|
|
|| app.alerts.buttons["Non ora"].waitForExistence(timeout: 1)
|
|
let labels = ["Non ora", "Not Now", "Not now", "OK", "Consenti", "Allow"]
|
|
for _ in 0..<8 {
|
|
var tapped = false
|
|
for label in labels {
|
|
if spring.buttons[label].exists {
|
|
spring.buttons[label].tap()
|
|
tapped = true
|
|
break
|
|
}
|
|
if app.alerts.buttons[label].exists {
|
|
app.alerts.buttons[label].tap()
|
|
tapped = true
|
|
break
|
|
}
|
|
}
|
|
if !tapped { break }
|
|
RunLoop.current.run(until: Date().addingTimeInterval(0.4))
|
|
}
|
|
app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.08)).tap()
|
|
}
|
|
|
|
private func dismissKeyboard() {
|
|
if !app.keyboards.firstMatch.exists { return }
|
|
for name in ["Fine", "Done", "Return", "Invio"] {
|
|
let key = app.keyboards.buttons[name]
|
|
if key.exists {
|
|
key.tap()
|
|
return
|
|
}
|
|
}
|
|
app.swipeDown()
|
|
sleep(1)
|
|
if app.keyboards.firstMatch.exists {
|
|
app.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.12)).tap()
|
|
}
|
|
}
|
|
|
|
private func capture(_ name: String) {
|
|
let shot = app.screenshot()
|
|
try? shot.pngRepresentation.write(to: URL(fileURLWithPath: "\(shotDir)/\(name).png"))
|
|
let attachment = XCTAttachment(screenshot: shot)
|
|
attachment.name = name
|
|
attachment.lifetime = .keepAlways
|
|
add(attachment)
|
|
try? app.debugDescription.write(toFile: "\(shotDir)/\(name).txt", atomically: true, encoding: .utf8)
|
|
}
|
|
}
|