Xcode7 | Тесты пользовательского интерфейса Xcode | Как обрабатывать оповещения службы определения местоположения?

Я пишу тестовые примеры пользовательского интерфейса для одного из моих приложений, используя XCUIApplication, XCUIElement и XCUIElementQuery, представленные в Xcode7/iOS 9.

Я наткнулся на блокпост. Для одного из экранов в тестовом примере требуются службы определения местоположения iOS. Как и ожидалось, пользователю предлагается разрешить использование службы определения местоположения с предупреждением под названием: Allow “App name” to access your location while you use the app? с кнопками Allow и Don't Allow.

Проблема в том, что кажется, что, поскольку предупреждение представлено самой ОС, оно отсутствует в поддереве элементов приложения.

Я зарегистрировал следующее:

print("XYZ:\(app.alerts.count)")//0
var existence = app.staticTexts["Allow “App Name” to access your location while you use the app?"].exists
print("XYZ:\(existence)")//false
existence  = app.buttons["Allow"].exists
print("XYZ:\(existence)") //false

Даже запись пользовательского интерфейса генерировала аналогичный код:

XCUIApplication().alerts["Allow “App Name” to access your location while you use the app?"].collectionViews.buttons["Allow"].tap()

Я не нашел никакого API, который мог бы решить эту проблему. Например:

  • Коснитесь места на экране
  • Получайте оповещения вне приложения

Итак, как я могу пройти мимо этого? Есть ли способ настроить тестовые цели, чтобы авторизация службы определения местоположения не требовалась.


person Taha Samad    schedule 31.07.2015    source источник
comment
Для цели C вы можете найти правильный путь здесь, stackoverflow.com/a/42810747/1084174   -  person Sazzad Hissain Khan    schedule 15.03.2017
comment
Вы можете взглянуть на мою заметку github.com/onmyway133/blog/issues/48   -  person onmyway133    schedule 02.06.2017


Ответы (8)


Xкод 9

    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    let allowBtn = springboard.buttons["Allow"]
    if allowBtn.exists {
        allowBtn.tap()
    }

Xcode 8.3.3

    _ = addUIInterruptionMonitor(withDescription: "Location Dialog") { (alert) -> Bool in
        alert.buttons["Allow"].tap()
        return true
    }
    app.buttons["Request Location"].tap()
    app.tap() // need to interact with the app for the handler to fire

Обратите внимание, что это немного отличается, так как имя метода теперь addUIInterruptionMonitor и принимает withDescription в качестве аргумента.

Xcode 7.1

В Xcode 7.1 наконец-то исправлена ​​проблема с системными оповещениями. Однако есть два небольших подводных камня.

Во-первых, вам нужно настроить «обработчик прерываний пользовательского интерфейса», прежде чем выводить оповещение. Это наш способ сообщить платформе, как обрабатывать предупреждение, когда оно появляется.

Во-вторых, после представления предупреждения вы должны взаимодействовать с интерфейсом. Простое нажатие на приложение работает нормально, но необходимо.

addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
    alert.buttons["Allow"].tap()
    return true
}

app.buttons["Request Location"].tap()
app.tap() // need to interact with the app for the handler to fire

«Диалог местоположения» — это просто строка, которая помогает разработчику определить, к какому обработчику был осуществлен доступ, он не относится к типу предупреждения.

Xкод 7.0

Следующее отклонит одно «системное предупреждение» в Xcode 7 Beta 6:

let app = XCUIApplication()
app.launch()
// trigger location permission dialog

app.alerts.element.collectionViews.buttons["Allow"].tap()

Бета 6 представила множество исправлений для тестирования пользовательского интерфейса, и я думаю, что это было одним из них.

Также обратите внимание, что я звоню -element прямо на -alerts. Вызов -element для XCUIElementQuery заставляет фреймворк выбрать «один и единственный» соответствующий элемент на экране. Это отлично работает для предупреждений, когда вы можете видеть только одно уведомление за раз. Однако, если вы попробуете это для метки и у вас будет две метки, фреймворк вызовет исключение.

person Joe Masilotti    schedule 26.08.2015
comment
Кажется, в Xcode 7.1 происходит сбой. Я подал отчет об ошибке, rdar://22498241. Я предлагаю всем, кто испытывает это, дублировать это. - person Joe Masilotti; 27.10.2015
comment
Я столкнулся с предупреждением о push-уведомлении, и его НЕТ в списке, когда я набираю po XCUIApplicaction() Есть идеи? Я использую Xcode 7.1 - person blackjacx; 09.11.2015
comment
@blackjacx Я обновил свой ответ для Xcode 7.1, который больше не приводит к сбою набора тестов. - person Joe Masilotti; 13.11.2015
comment
Да, вот как я это сделал, и я могу подтвердить, что это работает! - person blackjacx; 13.11.2015
comment
Это отлично работает для меня. Все, что мне нужно было сделать, это заменить addUIInterruptionMonitorWithDescription("APNS Alert") { (alert) -> Bool in alert.buttons["OK"].tap() return true} на то место, где раньше было app.alerts.collectionViews.buttons["OK"].tap(). - person Matt Bearson; 28.06.2016
comment
Есть ли обновления для этого на 8.2? Я заметил, что теперь мне даже не нужно было отклонять оповещение. Он разрешает автоматически. Кто-нибудь еще это замечает? - person gran_profaci; 13.03.2017
comment
Мне пришлось использовать описанный выше метод на 8.2.1, чтобы отклонить предупреждение об уведомлениях. Все еще работает, но сигнатура функции немного изменилась на addUIInterruptionMonitor(withDescription - person Aleksander Niedziolko; 15.03.2017
comment
Обнаружено, что при использовании xcode 9.1 оповещения обрабатываются только в том случае, если на тестовом устройстве установлена ​​iOS 11. Не работает на более старых версиях iOS, например 10.3 и т. д. Ссылка: forums.developer.apple.com/thread/86989 - person Hasaan Ali; 20.12.2017

Это было единственное, что сработало для меня. Используя Xcode 9 между прочим.

Также, вероятно, важно, что я уже использовал addUIInterruptionMonitor для другого оповещения. Я пробовал их переупорядочивать, и это не имело значения. Может быть, это проблема в 9, когда у вас их два, или, может быть, я использовал их неправильно. В любом случае приведенный ниже код работал. :)

let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowBtn = springboard.buttons["Allow"]
if allowBtn.exists {
    allowBtn.tap()
}
person Joe Susnick    schedule 02.11.2017
comment
Как здорово, это работает для меня как шарм. Старые решения больше не работают в Xcode 9. Это лучшее, что я нашел за многие часы поиска ответов. Это решение должно быть на высоте. :) Спасибо Джо. - person Rhusfer; 22.11.2017
comment
Это работает отлично - это должно быть помечено как правильный ответ, так как apparently правильный ответ - нет! - person RichAppz; 21.03.2018
comment
Это сработало для меня как чемпион в XCode 9.1, хотя в XCode 9.3 это кажется сломанным. - person dps; 09.04.2018

Если вы хотите проверить, отображается ли предупреждение, просто проверьте наличие кнопки:

if (app.alerts.element.collectionViews.buttons["Dismiss"].exists)
{
app.alerts.element.collectionViews.buttons["Dismiss"].tap()
}

он проверяет, отображается ли предупреждение, и если оно отображается, оно нажимает на него.

person Joao_dche    schedule 02.11.2015

Я заставил его работать с этим на Xcode 9.4.1, хитрость заключалась в том, чтобы дождаться появления всплывающего окна.

// wait for location service popup to appear
    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    let allowBtn = springboard.buttons["Allow"]
    expectation(for: NSPredicate(format: "exists == true"), evaluatedWith: allowBtn, handler: nil)
    waitForExpectations(timeout: 10, handler: nil)

    //allow location service
    if allowBtn.exists {
      allowBtn.tap()
    }
person null    schedule 07.08.2018

В xcode 9.1 оповещения обрабатываются только в том случае, если на тестовом устройстве установлена ​​iOS 11. Не работает на более старых версиях iOS, например 10.3 и т. д. Ссылка: https://forums.developer.apple.com/thread/86989

Для обработки предупреждений используйте это:

//Use this before the alerts appear. I am doing it before app.launch()

let allowButtonPredicate = NSPredicate(format: "label == 'Always Allow' || label == 'Allow'")
//1st alert
_ = addUIInterruptionMonitor(withDescription: "Allow to access your location?") { (alert) -> Bool in
    let alwaysAllowButton = alert.buttons.matching(allowButtonPredicate).element.firstMatch
    if alwaysAllowButton.exists {
        alwaysAllowButton.tap()
        return true
    }
    return false
}
// One interruption monitor is sufficient for multiple alerts
person Hasaan Ali    schedule 20.12.2017

Это работает для всех языков:

let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowBtn = springboard.buttons.element(boundBy: 1)
if allowBtn.exists {
    allowBtn.tap()
}
person Mobile Developer    schedule 18.08.2018
comment
У меня это работает, если я использую индекс 1. Мне кажется странным, что у кого-то будет 3 кнопки - person Daniel; 01.02.2019

Чтобы нажать «Разрешить оповещение о местоположении», вы можете вызвать element.tap(), где element — это любой элемент на вашем экране. Таким образом, после вызова tap доступность будет касаться «Разрешить при предупреждении», а затем касаться вашего элемента.

person Andrey    schedule 27.10.2015

Вот что я сделал, чтобы принять предупреждение о разрешении уведомлений на любом языке, нажав вторую кнопку в диалоговом окне (две кнопки). Кнопка «Разрешить» находится справа, поэтому индекс 1.

let handler = addUIInterruptionMonitor(withDescription: "System Dialog") { (alert) -> Bool in
    alert.buttons.element(boundBy: 1).tap()
    return true
}
app.tap()
person Robbie Trencheny    schedule 22.06.2019