Accessing buttons on a UIActivityViewController in an iOS 13 UI test causes a crash - ios

Is anyone else having problems running Xcode UI tests with Xcode 11 targeting an iOS 13 simulator or device where looking for the buttons on an UIActivityViewController causes a crash?
I have multiple UI tests that verify the buttons that appear in a UIActivityViewController. They do the expected setup work and then look for the button with something like:
XCTAssertTrue(app.buttons["Copy"].exists)
The tests have run fine iOS 10, 11, and 12. If I try to run the same test on an iOS 13 simulator or device, the moment the code attempts to access app.buttons, execution stops and I'll get a Thread 1: signal SIGABRT followed by Failed to get matching snapshots: Lost connection to the application (pid 33047). at the line where I try to access app.buttons.
Adding a wait or even an old-school sleep does nothing. I've tried to dig around some of the other queries hanging off of XCUIElementTypeQueryProvider to find the elements with no luck.
If I debug the test and put a breakpoint before the test accesses app.buttons, and I try to print out what it contains, I get a different error message.
po app.buttons
t = 49.37s Requesting snapshot of accessibility hierarchy for app with pid 37576
expression produced error: error: /var/folders/f2/zhwz28mn1hd815pc78kg02q80000gp/T/expr5-3b2971..swift:1:72: error: 'XCUIElementQuery' is not a member type of 'XCTest'
Swift._DebuggerSupport.stringForPrintObject(Swift.UnsafePointer<XCTest.XCUIElementQuery>(bitPattern: 0x10c73f4d0)!.pointee)
This sure feels like an Xcode bug. Has anyone else run into this?
Here's a bit of code if anyone else wants to try it out.
From a view controller:
#IBAction func showPressed(_ sender: Any) {
let text = "I have something to share."
let vc = UIActivityViewController(activityItems: [text], applicationActivities: nil)
vc.popoverPresentationController?.sourceView = self.view
self.present(vc, animated: true, completion: nil)
}
The UI test:
func testActivityViewController() {
let app = XCUIApplication()
app.launch()
app.buttons["Show AVC"].tap()
let buttons = app.buttons
let copy = buttons["Copy"]
sleep(2) // Just keeping things simple for the example.
XCTAssertTrue(copy.exists)
}

I'm not sure exactly in which version this was fixed, but the UI elements are available in a slightly different configuration as of Xcode version 11.2.1 (11B500). Here's how you might access the Copy button from UIActivityViewController now:
XCUIApplication().otherElements["ActivityListView"].cells.containing(.label(equals: "Copy")).firstMatch
HT to https://stackoverflow.com/a/48450562/19626 for the otherElements selector.
The close/cancel button moved, too. Here's where I found it:
XCUIApplication().otherElements["ActivityListView"].buttons["Close"].tap()

I'm seeing this behavior with Xcode 11 too, and I think your assumption that it's an Xcode bug is correct.
The crash appears to be fixed with Xcode Version 11.2 beta 2 (11B44), but querying for buttons in a UIActivityViewController is still broken. (I never see the element resolve.) Hopefully Apple will fix element lookup in the near future.

Related

iOS 15 DualSense is opening App Library on PS button long press

On iOS 15 a long press on the PS Button of the DualSense controller is opening the App Library and I don't receive a callback via the valueChangedHandler function. The App library which will be opened looks like this
This is how I handle all controller inputs:
func handleController(controller: GCController) {
controller.extendedGamepad?.valueChangedHandler = { [weak self] (gamepad: GCExtendedGamepad, element: GCControllerElement) in
guard let self = self else {
return
}
// no feedback received when performing a long press on the PS button
}
Can the game library be suppressed somehow? Sony's PS Remote Play app somehow manages to suppress it, but I don't know how, nor can I find anything in Apple's official API documentation.
Edit: Seems this problem only occurs on iPads, on iPhones this problem doesn't exist. Is there some API or anything on iPads to suppress this behaviour? I assume the most majority of users don't want to open the App Library in the middle of the game.
If someone ever faces the same problem you can actually disable system gestures for the Home button.
In Swift all you have to add is this line (controller is a GCController object)
controller.physicalInputProfile.buttons[GCInputButtonHome]?.preferredSystemGestureState = .disabled
In ObjectiveC it would work like this
controller.physicalInputProfile.buttons[GCInputButtonHome].preferredSystemGestureState = GCSystemGestureStateDisabled;
Thanks to the Apple employee who helped me here
https://developer.apple.com/forums/thread/711905
Edit: on tvOS this isn't working as the PS button (menu button) of a controller always have to act as home event
https://developer.apple.com/forums/thread/715012

How to acknowledge system alerts on a device with KIF testing framework?

I found out how to acknowledge system alerts while on a simulator from this post using this line of code:
self.viewTester.acknowledgeSystemAlert()
Unfortunately, the KIF code has #if TARGET_IPHONE_SIMULATOR wrapped around it, so it won't work on a device. How can I get around permission alerts on a device during automated testing?
I had same issue and here the solution I found:
its right than this KIF function doesn't work on device, its only for simulators! so, You can have a UITest in the UITarget and just a single Test case in it that will add a UIMonitors like this:
// ask for all the permission from users then :
_ = addUIInterruptionMonitor(withDescription: "") { alert -> Bool in
let confirmLabels = ["Allow", "OK"]
for (_, label) in confirmLabels.enumerated() {
let allow = alert.buttons[label]
if allow.exists {
allow.tap()
break
}
}
return true
}
// do some UI interaction here like tapping on some view in app
So you can call this UITest each time before running your UnitTests and that will prepare your app to have all the permissions.
btw, if anyone has better solution please provide cause I wanna know, too ;)

Can not access the programatically added UIView in XCTest

Below is my recorded XCTest
let app = XCUIApplication()
let tablesQuery = app.tables
tablesQuery.staticTexts["Video"].tap()
tablesQuery.staticTexts["\tWindowed"].tap()
app.buttons["Launch"].tap()
app.buttons["Popout Video"].tap()
app.children(matching: .window).element(boundBy: 0).children(matching: .other).element(boundBy: 1).tap()
When I am trying to run the test the last part that is:
app.children(matching: .window).element(boundBy: 0).children(matching: .other).element(boundBy: 1).tap()
is not accessible. It does not throw any error but the last line of code is not executed.
I have tried solving the issue by referring to the following stackoverflow question :
Xcode UI Tests can't find views that are added programatically
Also , I have referred to the following Apple Documentations: https://developer.apple.com/library/content/documentation/UserExperience/Conceptual/iPhoneAccessibility/Making_Application_Accessible/Making_Application_Accessible.html#//apple_ref/doc/uid/TP40008785-CH102-SW2
https://developer.apple.com/reference/uikit/uiview
But all these doesn't seem to solve the issue. Any help will be greatly appreciated.
The line in question is very brittle. It's possible that your app's views are not always available in the same order every time the app launches, depending on race conditions, so what was recorded in your recording session does not necessarily work for all launches of the app. You'll probably find that the code is actually running, but isn't tapping the element you expected.
To make your code less brittle, add an accessibility identifier to the UIView in your app code, and use the otherElements query to find the UIView.
// app code
let view: UIView!
view.accessibilityIdentifier = "myAccessibilityIdentifier"
// UI test code
app.otherElements["myAccessibilityIdentifier"].tap()

Element not found in UI Test Case using XCTestCase Xcode 7.3

I am trying to create UI test Cases using Xcode 7 and I am facing an issue that Ui elements such as buttons, tables are detected randomly when I run the test cases and most of the times it gives an error saying that "Failed to find the element". It is not resolved even after adding delays to the same. Can anyone please help.
The code for the same is
XCUIDevice.sharedDevice().orientation = .Portrait
let app = XCUIApplication()
app.buttons["Login"].tap()
let app2 = app
self.waitForHittable(app.tables.cells.staticTexts["Login with Email"], waitSeconds: 30)
app2.tables.cells.staticTexts["Login with Email"].tap()
app.textFields["Email address"].tap()
app.textFields["Email address"].typeText("anil#gmail.com")
UIPasteboard.generalPasteboard().string = "anil1234"
app.secureTextFields["Password"].doubleTap()
app.menuItems["Paste"].tap()
app.buttons["Login with Email"].tap()
self.waitForHittable(app.navigationBars["HomeView"].buttons["ic menu"], waitSeconds: 60)
app.navigationBars["HomeView"].buttons["ic menu"].tap()
If I write the code app.buttons["Login"].tap()
twice, it works or else it wont be able to find the subsequest control elements.

How to launch system apps in an iOS Xcode UI test case

I've got an app whose main purpose is to enter data into HealthKit. I'd like to write some Xcode UI tests to verify that it's writing this data successfully, but I'm having some difficulty verifying the data in the Health app.
When I initially recorded my test, it skipped my simulated Home button press, but it was recording as I swiped over to the first home screen and navigated into the Health app to show the data points.
I searched for how to press the Home button, and found this (which works):
XCUIDevice.shared.press(.home)
However, none of the other calls it recorded actually work for navigation outside of the app. The recorded code for swiping on the home screen obviously looks wrong, and also doesn't work when I replace tap() with a swipeRight() or swipeLeft():
app.childrenMatchingType(.Window).elementBoundByIndex(1).childrenMatchingType(.Other).elementBoundByIndex(1).childrenMatchingType(.Other).element.childrenMatchingType(.Other).element.childrenMatchingType(.Other).elementBoundByIndex(0).childrenMatchingType(.ScrollView).element.tap()
The next couple of lines, for launching an app on the home screen, don't even work for an app icon that's on the currently visible page:
let elementsQuery = app.scrollViews.otherElements
elementsQuery.icons["Health"].tap()
Is there any way to achieve what I'm trying to do, or will I need to wait to verify end-to-end testing until I add the ability to read from HealthKit to my app?
Xcode 9
Here's the solution using Xcode 9
let messageApp = XCUIApplication(bundleIdentifier: "com.apple.MobileSMS")
messageApp.activate()
You can find a list of bundle identifier for the system apps in this post
Xcode 8
For Xcode 8 it's a little bit more complicated
In order to launch an application from the Springboard you need to import the following headers
https://github.com/facebook/WebDriverAgent/blob/master/PrivateHeaders/XCTest/XCUIElement.h
https://github.com/facebook/WebDriverAgent/blob/master/PrivateHeaders/XCTest/XCUIApplication.h
Then use the following (for example with Health)
Objective-C
#interface Springboard : NSObject
+ (void)launchHealth;
#end
#implementation Springboard
+ (void)launchHealth
{
XCUIApplication *springboard = [[XCUIApplication alloc] initPrivateWithPath:nil bundleID:#"com.apple.springboard"];
[springboard resolve];
XCUIElement *icon = springboard.icons[#"Health"];
if (icon.exists) {
[icon tap];
// To query elements in the Health app
XCUIApplication *health = [[XCUIApplication alloc] initPrivateWithPath:nil bundleID:#"com.apple.Health"];
}
}
#end
Swift
class Springboard {
static let springboard = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")
class func launchHealth() {
springboard.resolve()
let icon = springboard.icons["Health"]
if icon.exists {
icon.tap()
// To query elements in the Health app
let health = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.Health")
}
}
}
Swift 4
let app = XCUIApplication(bundleIdentifier: "com.apple.springboard")
you bounded your UI tests with your application and the moment you press home button and move out of the application UI you can not perform UI operations as app variable in your code is pointing to your application.
you may be having code like
let app = XCUIApplication()
So you should modify that XCUIApplication() line.
let app = XCUIApplication(privateWithPath: nil, bundleID: "com.apple.springboard")
now you can move out of application.
According to my knowledge it is good to have and independent UITestAgent app and initiate its UiTestcases with springboard bundle id so you can test any application with the help of that app not like i coded some test case inside product XYZ code base and for next product ABC I will write tests inside ABC product's code base!

Resources