Swift XCTest UI에서 테스트 사이에 앱을 재설정할 수 있는 방법이 있습니까?
테스트 사이에 앱을 재설정하기 위해 setUP() 또는 tearDown()에 넣을 수 있는 XCT 테스트 내 API 호출이 있습니까?저는 XCUIA Application의 dot 구문을 살펴보았는데 .launch()만 보였습니다.
아니면 스위프트에서 셸 스크립트를 호출하는 방법이 있습니까?그러면 xcrun in-between test methods를 호출하여 시뮬레이터를 리셋할 수 있습니다.
"스크립트 실행" 단계를 추가하여 테스트 대상에 단계를 만들어 앱에 대한 단위 테스트를 실행하기 전에 앱을 제거할 수 있습니다. 하지만 안타깝게도 이 단계는 테스트 사례 사이에 있는 것은 아닙니다.
/usr/bin/xcrun simctl uninstall booted com.mycompany.bundleId
갱신하다
테스트 사이에 tearDown 단계에서 Springboard를 통해 앱을 삭제할 수 있습니다.그러나 XCTest의 개인 헤더를 사용해야 합니다. (헤더 덤프는 여기 Facebook의 WebDriverAgent에서 사용할 수 있습니다.)
Springboard 클래스에서 탭을 누른 상태에서 Springboard에서 앱을 삭제하기 위한 샘플 코드는 다음과 같습니다.
#스위프트4:
import XCTest
class Springboard {
static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
/**
Terminate and delete the app via springboard
*/
class func deleteMyApp() {
XCUIApplication().terminate()
// Force delete the app from the springboard
let icon = springboard.icons["Citizen"]
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()
}
}
}
#스위프트 3-:
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)
if #available(iOS 13.0, *) {
springboard.buttons["Remove App"].tap()
springboard.alerts.buttons["Delete App"].tap()
springboard.alerts.buttons["Delete"].tap()
} else {
// Tap the little "X" button at approximately where it is. The X is not exposed directly
let xPosition = CGVector(dx: (iconFrame.minX + 3) / springboardFrame.maxX,
dy: (iconFrame.minY + 3) / springboardFrame.maxY)
springboard.coordinate(withNormalizedOffset: xPosition).tap()
springboard.alerts.buttons["Delete"].tap()
}
}
}
}
그 다음은:
override func tearDown() {
Springboard.deleteMyApp()
super.tearDown()
}
개인 헤더를 Swift bridging 헤더로 가져왔습니다.다음을 가져와야 합니다.
// Private headers from XCTest
#import "XCUIApplication.h"
#import "XCUIElement.h"
참고: Xcode 10 기준,XCUIApplication(bundleIdentifier:)
이제 Apple에 의해 노출되므로 개인 헤더가 더 이상 필요하지 않습니다.
현재 Xcode, Simulator 및 Swift Package Manager에서 제공하는 공개 API는 호출할 수 있는 방법이 없습니다.setUp()
그리고.tearDown()
XCText
subclasses to "Reset Contents and Settings"(시뮬레이터 내용 및 설정 재설정)으로 이동합니다.
공용 API를 사용하는 다른 가능한 접근 방식이 있습니다.
응용 프로그램 코드.추가하기
myResetApplication()
응용 프로그램을 알려진 상태로 만들기 위한 응용 프로그램 코드입니다.그러나 장치(시뮬레이터) 상태 제어는 애플리케이션 샌드박스에 의해 제한됩니다...응용 프로그램 밖에서는 큰 도움이 되지 않습니다.이 방법은 응용프로그램 제어 가능한 저항을 제거하는 데 적합합니다.셸 스크립트.셸 스크립트에서 테스트를 실행합니다.
xcrun simctl erase all
아니면xcrun simctl uninstall <device> <app identifier>
또는 시뮬레이터를 재설정(또는 앱 제거)하기 위한 각 테스트 실행 간에 유사합니다.StackOverflow: "명령줄에서 iOS 시뮬레이터를 재설정하려면 어떻게 해야 합니까?"를 참조하십시오.
xcrun simctl --help
# Uninstall a single application
xcrun simctl uninstall --help
xcrun simctl uninstall <device> <app identifier>
# Erase a device's contents and settings.
xcrun simctl erase <device>
xcrun simctl erase all # all existing devices
# Grant, revoke, or reset privacy and permissions
simctl privacy <device> <action> <service> [<bundle identifier>]
- Xcode 스키마 스크립트 작업. 추가
xcrun simctl erase all
(또는xcrun simctl erase <DEVICE_UUID>
테스트 또는 빌드 섹션과 같은 Xcode Scheme 섹션과 유사한 명령입니다.Product > Scheme > Edit Scheme … 메뉴를 선택합니다.Scheme Test 섹션을 펼칩니다.테스트 섹션 아래에 있는 Pre-actions(사전 작업)을 선택합니다.(+) "New Run Script Action"을 추가합니다.명령어xcrun simctl erase all
외부 스크립트 없이 직접 입력할 수 있습니다.
1을 호출하기 위한 옵션. 응용 프로그램을 재설정하기 위한 응용 프로그램 코드:
가. 어플리케이션 UI [UI Test] 어플리케이션을 리셋하는 리셋 버튼 또는 기타 UI 동작을 제공합니다.UI 요소는 다음을 통해 실행할 수 있습니다.XCUIApplication
인에XCTest
일상적인setUp()
,tearDown()
아니면testSomething()
.
B. 매개변수를 실행합니다.[UI Test] Victor Ronin이 언급한 바와 같이, 그 테스트에서 주장이 통과될 수 있습니다.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 = ProcessInfo.processInfo.arguments
if args.contains("MY_UI_TEST_MODE") {
myResetApplication()
}
C. Xcode Scheme 매개 변수[UI Test, Unit Test] Product > Scheme > Edit Scheme … 메뉴를 선택합니다.구성표 실행 섹션을 펼칩니다. (+) 다음과 같은 매개변수 추가MY_UI_TEST_MODE
. 매개 변수는 다음에서 사용할 수 있습니다.ProcessInfo.processInfo
.
// ... in application
let args = ProcessInfo.processInfo.arguments
if args.contains("MY_UI_TEST_MODE") {
myResetApplication()
}
D. 직접 통화.[Unit Test] Unit Test 번들은 실행 중인 어플리케이션에 주입되어 일부를 직접 호출할 수 있습니다.myResetApplication()
지원서의 일상적인 작업주의 사항:기본 단위 테스트는 주 화면이 로드된 후에 실행됩니다.테스트 로드 시퀀스를 참조하십시오. 그러나 UI 테스트 번들은 테스트 대상 응용 프로그램 외부의 프로세스로 실행됩니다.따라서 단위 테스트에서 작동하는 것은 UI 테스트에서 링크 오류를 발생시킵니다.
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에서 다음을 확인합니다.
NSProcessInfo.processInfo().arguments.에 포함된 경우("YOUR_FLAG_NAME_HERE") { // 여기서 정리하기 }
@ODM 답변을 사용했는데 스위프트 4에서 작동하도록 수정했습니다.NB: 일부 S/O 답변은 Swift 버전을 차별화하지 않으며, 때로는 상당히 근본적인 차이가 있습니다.아이폰7 시뮬레이터와 아이패드 에어 시뮬레이터를 세로 방향으로 테스트해봤는데 제 앱에 잘 맞았습니다.
스위프트 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()
}
}
}
}
@Chase Holland 답변을 사용하고 Settings 앱을 사용하여 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()
}
}
}
}
앱을 제거하기 위한 많은 답변이 있습니다.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
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
.
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 11 sims an up의 경우 "x" 아이콘과 @Code Monkey가 제안한 수정 사항을 탭하는 곳을 약간 수정했습니다.수정은 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 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 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 UI 기반 삭제
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)
}
스위프트 4에 대한 크레이그 피셔의 답변 업데이트 중입니다.iPad용으로 조경용으로 업데이트되었으며, 아마 남은 조경용으로만 작동할 것입니다.
가져오기 XCT 테스트
클래스 스프링보드 {
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)
}
}
}
앱 삭제 및 경고 재설정을 위한 위 답변의 목표 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];
}
이것은 모든 OS 버전(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()
}
}
몇 가지 실험 끝에 다양한 iOS 버전을 포함하는 보다 명확한 솔루션 구현을 마쳤습니다.
import XCTest
private enum Constants {
static let springboardBundleIdentifier = "com.apple.springboard"
static let appIconName = "Your App Name"
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 SpringboardManager {
private static let springboard = XCUIApplication(bundleIdentifier: Constants.springboardBundleIdentifier)
static func deleteApp(_ app: XCUIApplication) {
if app.exists && app.isHittable {
XCUIDevice.shared.press(.home)
}
app.terminate()
self.deleteAppIfNeeded(with: Constants.appIconName)
sleep(1)
}
private static func deleteAppIfNeeded(with iconName: String) {
let appIcon = self.springboard.icons[iconName]
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 SpringboardManager {
static func pressDeleteAlertButtons() {
self.pressDeleteAlertButton(self.deleteAppAlertButton())
self.pressDeleteAlertButton(self.deleteAlertButton())
}
static func pressDeleteAlertButton(_ button: XCUIElement?) {
guard let button = button 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 SpringboardManager {
static func deleteListButton() -> XCUIElement? {
sleep(1)
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? {
sleep(1)
let deleteAppButton = self.springboard.alerts.buttons[Constants.deleteAppButton]
if deleteAppButton.exists {
return deleteAppButton
}
return nil
}
static func deleteAlertButton() -> XCUIElement? {
sleep(1)
let deleteButton = self.springboard.alerts.buttons[Constants.deleteButton]
if deleteButton.exists {
return deleteButton
}
return nil
}
}
언급URL : https://stackoverflow.com/questions/33107731/is-there-a-way-to-reset-the-app-between-tests-in-swift-xctest-ui
'source' 카테고리의 다른 글
R: RMySQL을 사용하여 MySQL 오류에 테이블 쓰기 (0) | 2023.11.02 |
---|---|
static_assert를 사용하여 매크로에 전달된 유형을 확인합니다. (0) | 2023.11.02 |
jquery는 Safari에서만 마진 문제 생성 (0) | 2023.11.02 |
Angularjs에서 문자열 보간 내에서 줄 바꿈을 얻는 방법 (0) | 2023.11.02 |
서버 표준 시간대 값 'CEST'을(를) 인식할 수 없습니다. (0) | 2023.11.02 |