How to locate element on system action sheet in iOS UI Test

1.7k views Asked by At

Our app will pop up a system action sheet on the bottom of iPhone screen as below screenshots illustrated. I want to locate and tap the "Call..." and "Cancel" button on that action sheet in UI Test.

First, I tried to add an UI interruption handler, and put a break point into the handler closure but it is not triggered when action sheet showed.

        // Add an UI interruption handler to handle system alert.
        addUIInterruptionMonitor(withDescription: "Notification permisson request") { alert in
            let notifyAllowBtn = alert.buttons["Allow"]
            let callBtn = alert.buttons["Call 8663xxxxx"]
            
            if notifyAllowBtn.exists {
                notifyAllowBtn.tap()
                return true
            }
            
            if callBtn.exists {
                callBtn.tap()
                return true
            }
            
            // A placeholder for other alerts
            return true
        }
        // need to interact with the app again for the handler to fire
        app.swipeUp()

Also I have another try with SpringBoard, still without luck. Need help here, how could I locate the element on system action sheet?

        let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
        let alertCallButton = springboard.buttons["Call 8663xxxxx"]
        XCTAssert(alertCallButton.waitForExistence(timeout: 5))

enter image description here

1

There are 1 answers

0
Zhou Haibo On

As @stackich said, I add a 30 seconds delay to find elements on Springboard. And it works. I put my test in below in case anyone meet the same problem in the future.

Worth to mention that, for our app it is not Action sheet actually, instead it is text associated with UITextView, and UITextViewDelegate method textView(_ textView: UITextView, shouldInteractWith URL:... is fired when a user click a URL.

UI Test, the call button is actually a staticText/Label. The cancel button is button. So be sure to give them the correct type.

    func testPhoneCall() {
        app = XCUIApplication()
        springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
        app.launch()
        
        app.links["888-111-2222"].tap()
        sleep(1)
        
        let callLabel = springboard.staticTexts["Call (888) 111 2222"]
        
        print("  \(springboard.debugDescription)")
        
        // Add a 30 seconds waiting for springboard available
        XCTAssert(callLabel.waitForExistence(timeout: 30))
//        callLabel.tap()

    }

Minimum test app

import UIKit

class ViewController: UIViewController, UITextViewDelegate {
    override func viewDidLoad() {
        super.viewDidLoad()
        self.navigationItem.title = "Main View"
        
        addUITextViews()
    }
    
    func addUITextViews(){
        
        // lauout for the View
        let myTextView3 = UITextView()
        myTextView3.translatesAutoresizingMaskIntoConstraints = false
        self.view.addSubview(myTextView3)
        
        let g = view.safeAreaLayoutGuide
        NSLayoutConstraint.activate([
            myTextView3.leadingAnchor.constraint(equalTo: g.leadingAnchor, constant: 40),
            myTextView3.trailingAnchor.constraint(equalTo: g.trailingAnchor, constant: 40),
            myTextView3.centerXAnchor.constraint(equalTo: view.centerXAnchor),
            myTextView3.centerYAnchor.constraint(equalTo: view.centerYAnchor),
            myTextView3.widthAnchor.constraint(equalToConstant: 400),
            myTextView3.heightAnchor.constraint(equalToConstant: 400),
        ])
        
        // set Phone# and Email Address for the UITextView
        myTextView3.font = UIFont.systemFont(ofSize: 24)
        myTextView3.isEditable = false
        myTextView3.isSelectable = true
        myTextView3.text = "Phone: 888-111-2222 \nEmail: [email protected]"
        myTextView3.dataDetectorTypes = .all
        myTextView3.delegate = self
    }
    
    func textView(_ textView: UITextView, shouldInteractWith URL: URL, in characterRange: NSRange, interaction: UITextItemInteraction) -> Bool {

        guard let currentScheme = URL.scheme else { return false }

        switch currentScheme {
        case "sms", "tel":
            print("It is a call!")
        case "mailto":
            print("send email")
        default: break
        }
        return true
    }
}