Есть ли способ перезагрузить приложение между тестами в Swift XCTest UI?
Есть ли вызов API в XCTest, который я могу поместить в setUP() или tearDown() для сброса приложения между тестами? Я посмотрел в точечный синтаксис XCUIApplication, и все, что я видел, было.launch()
ИЛИ есть ли способ вызвать сценарий оболочки в Swift? Затем я мог бы вызвать промежуточные методы тестирования xcrun для сброса симулятора.
19 ответов
Вы можете добавить фазу "Выполнить сценарий", чтобы построить фазы в тестовой цели, чтобы удалить приложение перед запуском юнит-тестов , хотя, к сожалению, это не между тестовыми случаями.
/usr/bin/xcrun simctl uninstall booted com.mycompany.bundleId
Обновить
Между тестами вы можете удалить приложение через Springboard на этапе tearDown. Хотя это требует использования частного заголовка от XCTest. (Дамп заголовка доступен из WebDriverAgent Facebook здесь.)
Вот пример кода из класса Springboard для удаления приложения из Springboard с помощью нажатия и удержания:
import XCTest
class Springboard {
static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")
/**
Terminate and delete the app via springboard
*/
class func deleteMyApp() {
XCUIApplication().terminate()
// Resolve the query for the springboard rather than launching it
springboard.resolve()
// Force delete the app from the springboard
let icon = springboard.icons["MyAppName"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.pressForDuration(1.3)
// Tap the little "X" button at approximately where it is. The X is not exposed directly
springboard.coordinateWithNormalizedOffset(CGVectorMake((iconFrame.minX + 3) / springboardFrame.maxX, (iconFrame.minY + 3) / springboardFrame.maxY)).tap()
springboard.alerts.buttons["Delete"].tap()
}
}
}
А потом:
override func tearDown() {
Springboard.deleteMyApp()
super.tearDown()
}
Частные заголовки были импортированы в заголовок моста Swift. Вам нужно будет импортировать:
// Private headers from XCTest
#import "XCUIApplication.h"
#import "XCUIElement.h"
В это время общедоступный API в Xcode 7 & 8 и симуляторе не появляется, имеет какой-либо метод, вызываемый из setUp()
а также tearDown()
XCText
подклассы "Сбросить содержимое и настройки" для симулятора.
Есть и другие возможные подходы, которые используют публичные API:
Код приложения. Добавь немного
myResetApplication()
код приложения для перевода приложения в известное состояние. Однако управление состоянием устройства (симулятора) ограничено изолированной программной средой приложения... которая не сильно помогает за пределами приложения. Этот подход подходит для очистки управляемой приложением персистентности.Shell Script. Запустите тесты из сценария оболочки. использование
xcrun simctl erase all
или жеxcrun simctl uninstall <device> <app identifier>
или подобное между каждым тестовым прогоном для сброса симулятора (или удаления приложения). см. Stackru: "Как я могу сбросить симулятор iOS из командной строки?"
macos> xcrun simctl --help
# can uninstall a single application
macos> xcrun simctl uninstall --help
# Usage: simctl uninstall <device> <app identifier>
- Действие схемы XCode. добавлять
xcrun simctl erase all
(или жеxcrun simctl erase <DEVICE_UUID>
) или аналогично разделу "Проверка схемы". Выберите меню "Продукт"> "Схема"> "Редактировать схему…". Разверните раздел "Проверка схемы". Выберите "Предварительные действия" в разделе "Тест". Нажмите (+), чтобы добавить "New Run Script Action". Командаxcrun simctl erase all
можно вводить напрямую, не требуя какого-либо внешнего скрипта.
Варианты вызова 1. Код приложения для сброса приложения:
А. Интерфейс приложения. [UI Test] Предоставьте кнопку сброса или другое действие пользовательского интерфейса, которое сбрасывает приложение. Элемент пользовательского интерфейса может быть реализован через XCUIApplication
в XCTest
подпрограммы setUp()
, tearDown()
или же testSomething()
,
Б. Запустите параметр. [UI Test] Как отметил Виктор Ронин, аргумент может быть передан из теста setUp()
...
class AppResetUITests: XCTestCase {
override func setUp() {
// ...
let app = XCUIApplication()
app.launchArguments = ["MY_UI_TEST_MODE"]
app.launch()
... быть принятым AppDelegate
...
class AppDelegate: UIResponder, UIApplicationDelegate {
func application( …didFinishLaunchingWithOptions… ) -> Bool {
// ...
let args = NSProcessInfo.processInfo().arguments
if args.contains("MY_UI_TEST_MODE") {
myResetApplication()
}
C. Параметр схемы XCode. [UI Test, Unit Test] Выберите меню "Продукт"> "Схема"> "Редактировать схему…". Разверните раздел "Выполнение схемы". (+) Добавить некоторый параметр, например MY_UI_TEST_MODE
, Параметр будет доступен в NSProcessInfo.processInfo()
,
// ... in application
let args = NSProcessInfo.processInfo().arguments
if args.contains("MY_UI_TEST_MODE") {
myResetApplication()
}
Z. Прямой звонок. [Unit Test] Пакеты модульных тестов внедряются в работающее приложение и могут напрямую вызывать некоторые myResetApplication()
рутина в приложении. Предостережение: модульные тесты по умолчанию запускаются после загрузки основного экрана. см. Последовательность тестовой загрузки. Однако комплекты тестов пользовательского интерфейса выполняются как процесс, внешний по отношению к тестируемому приложению. Итак, то, что работает в модульном тесте, дает ошибку ссылки в тесте пользовательского интерфейса.
class AppResetUnitTests: XCTestCase {
override func setUp() {
// ... Unit Test: runs. UI Test: link error.
myResetApplication() // visible code implemented in application
Обновлено для swift 3.1 / xcode 8.3
создать промежуточный заголовок в тестовой цели:
#import <XCTest/XCUIApplication.h>
#import <XCTest/XCUIElement.h>
@interface XCUIApplication (Private)
- (id)initPrivateWithPath:(NSString *)path bundleID:(NSString *)bundleID;
- (void)resolve;
@end
обновленный класс трамплина
class Springboard {
static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")!
static let settings = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.Preferences")!
/**
Terminate and delete the app via springboard
*/
class func deleteMyApp() {
XCUIApplication().terminate()
// Resolve the query for the springboard rather than launching it
springboard.resolve()
// Force delete the app from the springboard
let icon = springboard.icons["{MyAppName}"] /// change to correct app name
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.press(forDuration: 1.3)
// Tap the little "X" button at approximately where it is. The X is not exposed directly
springboard.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + 3) / springboardFrame.maxX, dy: (iconFrame.minY + 3) / springboardFrame.maxY)).tap()
springboard.alerts.buttons["Delete"].tap()
// Press home once make the icons stop wiggling
XCUIDevice.shared().press(.home)
// Press home again to go to the first page of the springboard
XCUIDevice.shared().press(.home)
// Wait some time for the animation end
Thread.sleep(forTimeInterval: 0.5)
let settingsIcon = springboard.icons["Settings"]
if settingsIcon.exists {
settingsIcon.tap()
settings.tables.staticTexts["General"].tap()
settings.tables.staticTexts["Reset"].tap()
settings.tables.staticTexts["Reset Location & Privacy"].tap()
settings.buttons["Reset Warnings"].tap()
settings.terminate()
}
}
}
}
Решение для iOS 13.2
final class Springboard {
private static var springboardApp = XCUIApplication(bundleIdentifier: "com.apple.springboard")
class func deleteApp(name: String) {
XCUIApplication().terminate()
springboardApp.activate()
sleep(1)
let appIcon = springboardApp.icons.matching(identifier: name).firstMatch
appIcon.press(forDuration: 1.3)
sleep(1)
springboardApp.buttons["Delete App"].tap()
let deleteButton = springboardApp.alerts.buttons["Delete"].firstMatch
if deleteButton.waitForExistence(timeout: 5) {
deleteButton.tap()
}
}
}
Вы можете попросить ваше приложение "очистить" себя
- Ты используешь
XCUIApplication.launchArguments
установить какой-то флаг В AppDelegate вы проверяете
if NSProcessInfo.processInfo().arguments.contains("YOUR_FLAG_NAME_HERE") { // Выполнить очистку здесь}
Я вижу много ответов на удаление вашего приложения в setUp
или tearDown
вашего теста.
Но вы можете легко удалить свое приложение перед запуском тестов, добавив этап сценария запуска в свою тестовую цель.
Для этого:
- Выберите проект Xcode вашего приложения
- Выберите свою тестовую цель
- Выберите "Фазы сборки"
- Нажмите на "+" и "Фаза нового запуска сценария".
Затем замените заполнитель # Type a script or drag a script file from your workspace to insert its path.
командой:
xcrun simctl boot ${TARGET_DEVICE_IDENTIFIER}
xcrun simctl uninstall ${TARGET_DEVICE_IDENTIFIER} YOUR_APP_BUNDLE
Я использовал ответ @ odm, но изменил его, чтобы он работал для Swift 4. Примечание: некоторые ответы S/O не дифференцируют версии Swift, которые иногда имеют довольно фундаментальные различия. Я проверил это на симуляторе iPhone 7 и iPad Air в портретной ориентации, и это сработало для моего приложения.
Swift 4
import XCTest
import Foundation
class Springboard {
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let settings = XCUIApplication(bundleIdentifier: "com.apple.Preferences")
/**
Terminate and delete the app via springboard
*/
func deleteMyApp() {
XCUIApplication().terminate()
// Resolve the query for the springboard rather than launching it
springboard.activate()
// Rotate back to Portrait, just to ensure repeatability here
XCUIDevice.shared.orientation = UIDeviceOrientation.portrait
// Sleep to let the device finish its rotation animation, if it needed rotating
sleep(2)
// Force delete the app from the springboard
// Handle iOS 11 iPad 'duplication' of icons (one nested under "Home screen icons" and the other nested under "Multitasking Dock"
let icon = springboard.otherElements["Home screen icons"].scrollViews.otherElements.icons["YourAppName"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.press(forDuration: 2.5)
// Tap the little "X" button at approximately where it is. The X is not exposed directly
springboard.coordinate(withNormalizedOffset: CGVector(dx: ((iconFrame.minX + 3) / springboardFrame.maxX), dy:((iconFrame.minY + 3) / springboardFrame.maxY))).tap()
// Wait some time for the animation end
Thread.sleep(forTimeInterval: 0.5)
//springboard.alerts.buttons["Delete"].firstMatch.tap()
springboard.buttons["Delete"].firstMatch.tap()
// Press home once make the icons stop wiggling
XCUIDevice.shared.press(.home)
// Press home again to go to the first page of the springboard
XCUIDevice.shared.press(.home)
// Wait some time for the animation end
Thread.sleep(forTimeInterval: 0.5)
// Handle iOS 11 iPad 'duplication' of icons (one nested under "Home screen icons" and the other nested under "Multitasking Dock"
let settingsIcon = springboard.otherElements["Home screen icons"].scrollViews.otherElements.icons["Settings"]
if settingsIcon.exists {
settingsIcon.tap()
settings.tables.staticTexts["General"].tap()
settings.tables.staticTexts["Reset"].tap()
settings.tables.staticTexts["Reset Location & Privacy"].tap()
// Handle iOS 11 iPad difference in error button text
if UIDevice.current.userInterfaceIdiom == .pad {
settings.buttons["Reset"].tap()
}
else {
settings.buttons["Reset Warnings"].tap()
}
settings.terminate()
}
}
}
}
Начиная с Xcode 11.4, если все, что вам нужно, это сбросить разрешения, вы можете использовать resetAuthorizationStatus(for:)
по примеру XCUIApplication
см. https://developer.apple.com/documentation/xctest/xcuiapplication/3526066-resetauthorizationstatusforresou
Вы также можете использовать simctl
при необходимости, цитируется из Xcode 11.4 Release Notes:
simctl теперь поддерживает изменение разрешений конфиденциальности. Вы можете изменить разрешения конфиденциальности, чтобы создать известные состояния для целей тестирования. Например, чтобы разрешить приложению-примеру получить доступ к библиотеке фотографий без каких-либо запросов:
xcrun simctl privacy <device> grant photos com.example.app
Чтобы сбросить все разрешения до значений по умолчанию, как если бы приложение никогда раньше не устанавливалось:
xcrun simctl privacy <device> reset all com.example.app
.
Я использовал ответ @Chase Holland и обновил класс Springboard, следуя тому же подходу, чтобы сбросить содержимое и настройки с помощью приложения "Настройки". Это полезно, когда вам нужно сбросить разрешения диалогов.
import XCTest
class Springboard {
static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")
static let settings = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.Preferences")
/**
Terminate and delete the app via springboard
*/
class func deleteMyApp() {
XCUIApplication().terminate()
// Resolve the query for the springboard rather than launching it
springboard.resolve()
// Force delete the app from the springboard
let icon = springboard.icons["MyAppName"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.pressForDuration(1.3)
// Tap the little "X" button at approximately where it is. The X is not exposed directly
springboard.coordinateWithNormalizedOffset(CGVectorMake((iconFrame.minX + 3) / springboardFrame.maxX, (iconFrame.minY + 3) / springboardFrame.maxY)).tap()
springboard.alerts.buttons["Delete"].tap()
// Press home once make the icons stop wiggling
XCUIDevice.sharedDevice().pressButton(.Home)
// Press home again to go to the first page of the springboard
XCUIDevice.sharedDevice().pressButton(.Home)
// Wait some time for the animation end
NSThread.sleepForTimeInterval(0.5)
let settingsIcon = springboard.icons["Settings"]
if settingsIcon.exists {
settingsIcon.tap()
settings.tables.staticTexts["General"].tap()
settings.tables.staticTexts["Reset"].tap()
settings.tables.staticTexts["Reset Location & Privacy"].tap()
settings.buttons["Reset Warnings"].tap()
settings.terminate()
}
}
}
}
Рабочее решение для iOS14
final class Springboard {
private static var springboardApp = XCUIApplication(bundleIdentifier: "com.apple.springboard")
class func deleteApp(name: String) {
XCUIApplication().terminate()
springboardApp.activate()
sleep(1)
let appIcon = springboardApp.icons.matching(identifier: name).firstMatch
appIcon.press(forDuration: 1.3)
sleep(1)
springboardApp.buttons["Remove App"].tap()
let deleteButton = springboardApp.alerts.buttons["Delete App"].firstMatch
if deleteButton.waitForExistence(timeout: 5) {
deleteButton.tap()
springboardApp.alerts.buttons["Delete"].tap()
}
}
}
Есть так много вариантов ответа, даже не уверен, стоит ли добавлять к нему, но на случай, если кому-то понадобится универсальное решение:
iOS 14.6 и 15 бета
class func deleteApp() {
XCUIApplication().terminate()
// Force delete the app from the springboard
let icon = springboard.icons["APP_NAME"]
if icon.exists {
icon.press(forDuration: 1.3)
springboard.buttons["Remove App"].tap()
springboard.alerts.buttons["Delete App"].tap()
springboard.alerts.buttons["Delete"].tap()
// Press home once to make the icons stop wiggling
XCUIDevice.shared.press(.home)
}
}
Для iOS 11 sims up я сделал очень маленькую модификацию, чтобы нажать значок "x" и место, где мы нажимаем в соответствии с исправлением, предложенным Monkey @Code. Фикс хорошо работает как на 10.3, так и на 11.2 телефонных симках. Для записи, я использую Swift 3. Думаю, что я прошел через некоторый код, чтобы скопировать и вставить, чтобы найти исправление немного проще.:)
import XCTest
class Springboard {
static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")
class func deleteMyApp() {
XCUIApplication().terminate()
// Resolve the query for the springboard rather than launching it
springboard!.resolve()
// Force delete the app from the springboard
let icon = springboard!.icons["My Test App"]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard!.frame
icon.press(forDuration: 1.3)
springboard!.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + 3 * UIScreen.main.scale) / springboardFrame.maxX, dy: (iconFrame.minY + 3 * UIScreen.main.scale) / springboardFrame.maxY)).tap()
springboard!.alerts.buttons["Delete"].tap()
}
}
}
Кажется, это работает для меня на iOS 12.1 и симулятор
class func deleteApp(appName: String) {
XCUIApplication().terminate()
// Force delete the app from the springboard
let icon = springboard.icons[appName]
if icon.exists {
icon.press(forDuration: 2.0)
icon.buttons["DeleteButton"].tap()
sleep(2)
springboard.alerts["Delete “\(appName)”?"].buttons["Delete"].tap()
sleep(2)
XCUIDevice.shared.press(.home)
}
}
Удаление iOS 13.1/Swift 5.1 на основе пользовательского интерфейса
static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")!
class func deleteApp() {
XCUIApplication().terminate()
XCUIDevice.shared.press(.home)
XCUIDevice.shared.press(.home)
let icon = springboard.icons["YourApplication"]
if !icon.exists { return }
springboard.swipeLeft()
springboard.activate()
Thread.sleep(forTimeInterval: 1.0)
icon.press(forDuration: 1.3)
springboard.buttons["Rearrange Apps"].eventuallyExists().tap()
icon.buttons["DeleteButton"].eventuallyExists().tap()
springboard.alerts.buttons["Delete"].eventuallyExists().tap()
XCUIDevice.shared.press(.home)
XCUIDevice.shared.press(.home)
}
Опираясь на ответы Chase Holland и odm, я смог избежать длинных нажатий и +3 смещений bs, удалив приложение в настройках, таких как dis:
import XCTest
class Springboard {
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
static let settings = XCUIApplication(bundleIdentifier: "com.apple.Preferences")
static let isiPad = UIScreen.main.traitCollection.userInterfaceIdiom == .pad
class func deleteApp(name: String) {
XCUIApplication().terminate()
if !springboard.icons[name].firstMatch.exists { return }
settings.launch()
goToRootSetting(settings)
settings.tables.staticTexts["General"].tap()
settings.tables.staticTexts[(isiPad ? "iPad" : "iPhone") + " Storage"].tap()
while settings.tables.activityIndicators["In progress"].exists { sleep(1) }
let appTableCellElementQuery = settings.tables.staticTexts.matching(identifier: name)
appTableCellElementQuery.element(boundBy: appTableCellElementQuery.count - 1).tap()
settings.tables.staticTexts["Delete App"].tap()
isiPad ? settings.alerts.buttons["Delete App"].tap() : settings.buttons["Delete App"].tap()
settings.terminate()
}
/**
You may not want to do this cuz it makes you re-trust your computer and device.
**/
class func resetLocationAndPrivacySetting(passcode: String?) {
settings.launch()
goToRootSetting(settings)
settings.tables.staticTexts["General"].tap()
settings.tables.staticTexts["Reset"].tap()
settings.tables.staticTexts["Reset Location & Privacy"].tap()
passcode?.forEach({ char in
settings.keys[String(char)].tap()
})
isiPad ? settings.alerts.buttons["Reset"].tap() : settings.buttons["Reset Settings"].tap()
}
class func goToRootSetting(_ settings: XCUIApplication) {
let navBackButton = settings.navigationBars.buttons.element(boundBy: 0)
while navBackButton.exists {
navBackButton.tap()
}
}
}
Использование:
Springboard.deleteApp(name: "AppName")
Springboard.resetLocationAndPrivacySetting()
Обновление Craig Fishers отвечает за Swift 4. Обновлен для iPad в альбомной ориентации, вероятно, работает только для оставленной альбомной ориентации.
импортировать XCTest
трамплин класса {
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
class func deleteMyApp(name: String) {
// Force delete the app from the springboard
let icon = springboard.icons[name]
if icon.exists {
let iconFrame = icon.frame
let springboardFrame = springboard.frame
icon.press(forDuration: 2.0)
var portaitOffset = 0.0 as CGFloat
if XCUIDevice.shared.orientation != .portrait {
portaitOffset = iconFrame.size.width - 2 * 3 * UIScreen.main.scale
}
let coord = springboard.coordinate(withNormalizedOffset: CGVector(dx: (iconFrame.minX + portaitOffset + 3 * UIScreen.main.scale) / springboardFrame.maxX, dy: (iconFrame.minY + 3 * UIScreen.main.scale) / springboardFrame.maxY))
coord.tap()
let _ = springboard.alerts.buttons["Delete"].waitForExistence(timeout: 5)
springboard.alerts.buttons["Delete"].tap()
XCUIDevice.shared.press(.home)
}
}
}
После некоторых экспериментов я закончил реализацию более четкого решения, охватывающего разные версии iOS:
import XCTest
private enum Constants {
static let springboardBundleIdentifier = "com.apple.springboard"
static let appIconPressShortDuration: TimeInterval = 2.0
static let appIconPressLongDuration: TimeInterval = 3.0
static let deleteAppButton = "Delete App"
static let removeAppButton = "Remove App"
static let deleteButton = "Delete"
static let deleteButtonVectorOffset: CGFloat = 3.0
}
final class Springboard {
static let springboard = XCUIApplication(bundleIdentifier: Constants.springboardBundleIdentifier)
static func deleteApp() {
let app = XCUIApplication()
if app.state != .notRunning {
app.terminate()
}
let appIcon = self.springboard.icons[app.label]
guard appIcon.exists else {
return
}
appIcon.press(forDuration: Constants.appIconPressShortDuration)
if let deleteListButton = self.deleteListButton() {
deleteListButton.tap()
self.pressDeleteAlertButtons()
} else {
appIcon.press(forDuration: Constants.appIconPressLongDuration)
self.pressDeleteTopLeftButton(for: appIcon)
self.pressDeleteAlertButtons()
}
}
}
private extension Springboard {
static func pressDeleteAlertButtons() {
self.pressDeleteAlertButton(self.deleteAppAlertButton())
self.pressDeleteAlertButton(self.deleteAlertButton())
}
static func pressDeleteAlertButton(_ button: XCUIElement?) {
guard let button = button,
button.waitForExistence(timeout: TestConstants.minWaitingTime)
else {
return
}
button.tap()
}
static func pressDeleteTopLeftButton(for appIcon: XCUIElement) {
let iconFrame = appIcon.frame
let springboardFrame = self.springboard.frame
let deleteButtonVector = CGVector(
dx: (iconFrame.minX + Constants.deleteButtonVectorOffset) / springboardFrame.maxX,
dy: (iconFrame.minY + Constants.deleteButtonVectorOffset) / springboardFrame.maxY)
let deleteButtonCoordinate = self.springboard.coordinate(withNormalizedOffset: deleteButtonVector)
deleteButtonCoordinate.tap()
}
}
private extension Springboard {
static func deleteListButton() -> XCUIElement? {
let removeListButton = self.springboard.buttons[Constants.removeAppButton]
let deleteListButton = self.springboard.buttons[Constants.deleteAppButton]
if removeListButton.exists {
return removeListButton
} else if deleteListButton.exists {
return deleteListButton
}
return nil
}
static func deleteAppAlertButton() -> XCUIElement? {
let deleteAppButton = self.springboard.alerts.buttons[Constants.deleteAppButton]
if deleteAppButton.exists {
return deleteAppButton
}
return nil
}
static func deleteAlertButton() -> XCUIElement? {
let deleteButton = self.springboard.alerts.buttons[Constants.deleteButton]
if deleteButton.exists {
return deleteButton
}
return nil
}
}
Вот версия Objective C вышеперечисленных ответов для удаления приложения и сброса предупреждений (проверено на iOS 11 и 12):
- (void)uninstallAppNamed:(NSString *)appName {
[[[XCUIApplication alloc] init] terminate];
XCUIApplication *springboard = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.springboard"];
[springboard activate];
XCUIElement *icon = springboard.otherElements[@"Home screen icons"].scrollViews.otherElements.icons[appName];
if (icon.exists) {
[icon pressForDuration:2.3];
[icon.buttons[@"DeleteButton"] tap];
sleep(2);
[[springboard.alerts firstMatch].buttons[@"Delete"] tap];
sleep(2);
[[XCUIDevice sharedDevice] pressButton:XCUIDeviceButtonHome];
sleep(2);
}
}
..
- (void)resetWarnings {
XCUIApplication *settings = [[XCUIApplication alloc] initWithBundleIdentifier:@"com.apple.Preferences"];
[settings activate];
sleep(2);
[settings.tables.staticTexts[@"General"] tap];
[settings.tables.staticTexts[@"Reset"] tap];
[settings.tables.staticTexts[@"Reset Location & Privacy"] tap];
if (UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPad) {
[settings.buttons[@"Reset"] tap];
} else {
[settings.buttons[@"Reset Warnings"] tap];
}
sleep(2);
[settings terminate];
}
Это работает для меня во всех версиях ОС (iOS11,12 и 13)
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
func deleteApp() {
XCUIApplication().terminate()
springboard.activate()
let icon = springboard.icons[appName]
if icon.exists {
icon.firstMatch.press(forDuration: 5)
icon.buttons["DeleteButton"].tap()
let deleteConfirmation = springboard.alerts["Delete “\(appName)”?"].buttons["Delete"]
XCTAssertTrue(deleteConfirmation.waitForExistence(timeout: 5), "Delete confirmation not shown")
deleteConfirmation.tap()
}
}