Rafael C.
Rafael C.

Reputation: 2345

How to delete/reset an app from iOS 13 with XCTest?

Recently I started testing an iOS app using XCTest but I found some difficulties, the main difficulty was deleting or resetting the app content in each test class.

I'm currently using XCode 11 and trying to delete/reset an app from iOS 13 for each test class, I've already tried:

This step is really important in my tests because in each test I need to create a profile and log in, so in the next test I need to have the app just installed from scratch

Upvotes: 12

Views: 7005

Answers (7)

Naqeeb
Naqeeb

Reputation: 1431

you can simply do this via springboard same as you uninstall build by yourself. Here is the class with deleteApp function which will uninstall build at anytime you need.

 class Springboard {
    
    static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    
    class func deleteApp () {
        //terminate app and activate Springboard
        XCUIApplication().terminate()
        springboard.activate()
        
        //tap on app icon
        let appIcon = springboard.icons.matching(identifier: "App Display Name").firstMatch
        
        if appIcon.exists {
            
        appIcon.press(forDuration: 2.0)
            
           //Access first alert button (Remove App)
        let _ = springboard.alerts.buttons["Remove App"].waitForExistence(timeout: 1.0)
        springboard.buttons["Remove App"].tap()
        
        //Access second alert button (Delete App)
        let _ = springboard.alerts.buttons["Delete App"].waitForExistence(timeout: 1.0)
        springboard.buttons["Delete App"].tap()
        
        //Access second alert button (Delete)
        let _ = springboard.alerts.buttons["Delete"].waitForExistence(timeout: 1.0)
        springboard.buttons["Delete"].tap()

        }
    }
}

Usgae:

 func test_YourTestName() {
    Springboard.deleteApp()
}

Upvotes: 1

Mohamed Wasiq
Mohamed Wasiq

Reputation: 587

iOS 14 and below

func deleteMyApp() {
XCUIApplication().terminate()

let bundleDisplayName = "App Name"

XCUIDevice.shared.press(.home)

let icon = springboard.icons[bundleDisplayName]

if icon.waitForExistence(timeout: 5) {
    
    XCUIDevice.shared.press(.home)

    
    let value = springboard.pageIndicators.element(boundBy: 0).value as? String
    
    let f = value!.last?.wholeNumberValue
    for _ in (1...f!){
        if(icon.isHittable){
            break
        }
        else{
            springboard.swipeLeft()
        }
    }


    let systemVersion = UIDevice.current.systemVersion.prefix(2)

    switch systemVersion {
    case "13":
        icon.press(forDuration: 1)

        let rrd = springboard.buttons["Delete App"]
        XCTAssert(rrd.waitForExistence(timeout: 5))
        rrd.tap()
        let rrd2 = springboard.buttons["Delete"]
        XCTAssert(rrd2.waitForExistence(timeout: 5))
        rrd2.tap()
        
    case "14":
        icon.press(forDuration: 1)

        let rrd = springboard.buttons["Remove App"]
        XCTAssert(rrd.waitForExistence(timeout: 5))
        rrd.tap()
        let rrd2 = springboard.buttons["Delete App"]
        XCTAssert(rrd2.waitForExistence(timeout: 5))
        rrd2.tap()
        let rrd3 = springboard.buttons["Delete"]
        XCTAssert(rrd3.waitForExistence(timeout: 5))
        rrd3.tap()
        
    default:
        XCTFail("Did not handle")
    }
   
}}

Upvotes: -1

Nik
Nik

Reputation: 9452

iOS 14

The working solution for iOS 14

import XCTest

let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")

func deleteMyApp() {
    XCUIApplication().terminate()

    let bundleDisplayName = "MyApp"

    let icon = springboard.icons[bundleDisplayName]
    if icon.exists {
        icon.press(forDuration: 1)

        let buttonRemoveApp = springboard.buttons["Remove App"]
        if buttonRemoveApp.waitForExistence(timeout: 5) {
            buttonRemoveApp.tap()
        } else {
            XCTFail("Button \"Remove App\" not found")
        }

        let buttonDeleteApp = springboard.alerts.buttons["Delete App"]
        if buttonDeleteApp.waitForExistence(timeout: 5) {
            buttonDeleteApp.tap()
        }
        else {
            XCTFail("Button \"Delete App\" not found")
        }

        let buttonDelete = springboard.alerts.buttons["Delete"]
        if buttonDelete.waitForExistence(timeout: 5) {
            buttonDelete.tap()
        }
        else {
            XCTFail("Button \"Delete\" not found")
        }
    }
}

class HomeUITests: XCTestCase {
    override func setUpWithError() throws {
        // Put setup code here. This method is called before the invocation of each test method in the class.

        // In UI tests it is usually best to stop immediately when a failure occurs.
        continueAfterFailure = false

        // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
    }

    override func tearDownWithError() throws {
        // Put teardown code here. This method is called after the invocation of each test method in the class.
    }

    func testExample() throws {
        deleteMyApp()

        // UI tests must launch the application that they test.
        let app = XCUIApplication()
        app.launch()

        // Use recording to get started writing UI tests.
        // Use XCTAssert and related functions to verify your tests produce the correct results.
    }

    func testLaunchPerformance() throws {
        if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
            // This measures how long it takes to launch your application.
            measure(metrics: [XCTApplicationLaunchMetric()]) {
                XCUIApplication().launch()
            }
        }
    }
}

Upvotes: 14

Dorian Roy
Dorian Roy

Reputation: 3104

This is a quick and dirty solution for iOS 14:

let appName = "You app name as it appears on the home screen"

// Put the app in the background
XCUIDevice.shared.press(XCUIDevice.Button.home)

let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
if springboard.icons[appName].waitForExistence(timeout: 5) {
    springboard.icons[appName].press(forDuration: 1.5);
}

if springboard.collectionViews.buttons["Remove App"].waitForExistence(timeout: 5) {
    springboard.collectionViews.buttons["Remove App"].tap()
}

if springboard.alerts["Remove “\(appName)”?"].scrollViews.otherElements.buttons["Delete App"].waitForExistence(timeout: 5) {
    springboard.alerts["Remove “\(appName)”?"].scrollViews.otherElements.buttons["Delete App"].tap()
}

if springboard.alerts["Delete “\(appName)”?"].scrollViews.otherElements.buttons["Delete"].waitForExistence(timeout: 5) {
    springboard.alerts["Delete “\(appName)”?"].scrollViews.otherElements.buttons["Delete"].tap()
}

Upvotes: 0

Gen Takeda
Gen Takeda

Reputation: 21

Updated Roman's answer to support iOS 13 on iPad.

By inserting otherElements["Home screen icons"], the code works both of iPhone and iPad.

class Springboard {

    static let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")

    class func deleteMyApp() {
        XCUIApplication().terminate()

         // Insert otherElements["Home screen icons"] here
        let icon = springboard.otherElements["Home screen icons"].icons["Workoutimer"]
        if icon.exists {
            let iconFrame = icon.frame
            let springboardFrame = springboard.frame
            icon.press(forDuration: 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()

            springboard.alerts.buttons["Delete"].tap()
            
            XCUIDevice.shared.press(XCUIDevice.Button.home)
        }
    }
 }

Misc.

On iPad, "recent launched app" will be added to the right side of the Dock. This iPad specific behavior leads XCUITest finds two elements if you search icons only by its identifier.

Your app will be added here, too.

To handle that, specify otherElements["Home screen icons"] and it excludes elements of the "Dock".

Upvotes: 2

DmitrievichR
DmitrievichR

Reputation: 119

enum Springboard {

    static let springboardApp = XCUIApplication(bundleIdentifier: "com.apple.springboard")
    static let appName = "appName"

    static func deleteApp() {
        XCUIApplication().terminate()

        let icon = springboardApp.icons[appName]
        if icon.exists {
            icon.press(forDuration: 3)
            icon.buttons["DeleteButton"].tap()

            springboardApp.alerts.buttons["Delete"].tap()
        }
    }
 }

Upvotes: 1

Roman Zakharov
Roman Zakharov

Reputation: 2273

Try to press the app icon a little longer than in previous iOS versions.

    let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")

    func deleteMyApp() {
        XCUIApplication().terminate()

        let icon = springboard.icons["YourAppName"]
        if icon.exists {
            let iconFrame = icon.frame
            let springboardFrame = springboard.frame
            icon.press(forDuration: 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()

            springboard.alerts.buttons["Delete"].tap()
        }
    }

Upvotes: 3

Related Questions