Xcode 7 UI Testing XCUIElementQuery Randomly Not Updating Properly - ios

I have a UI Test that dismisses an alert, checks to see if the alert had been dismissed (if not it attempts to dismiss it again) and then continues on with the rest of the test.
Most of the time the test passes just fine, but sometimes seemingly randomly (though more often in the simulator on a machine with older hardware), the test will do the following:
check for the alert
tap on the button that dismisses the alert
the alert is dismissed
check for the alert (and still find it - I have logged the query at this point and it is indeed finding the same alert that was just dismissed)
attempt to tap the button that dismisses the alert
complain that it can't find the alert and fail
A couple of notes:
I have not, as of yet, observed this behaviour when stepping through the test with the debugger.
Forcing the test to wait a bit after dismissing the alert (using things like usleep()) can still cause this behaviour.
Why is the query still finding the alert after it has been dismissed? Why is this behaviour seemingly random?

Have you tried interacting with the alert via a UI Interruption handler?
addUIInterruptionMonitorWithDescription("Alert") { (alert) -> Bool in
alert.buttons["Allow"].tap()
return true
}

Related

XCUITest: Auto-Accepting System-Alerts. Need more fine grained control

I have read that addUIInterruptionMonitorWithDescription could be used to accept / tap on a particular button in a system alert. On recently trying some test code out, I was doing the following:
Adding a Photos Alert
Adding a Calendar Alert
Adding a Location Alert.
On the simulator, I was surprised to find that the Calendar and Location prompt automatically had their "Allow" buttons tapped. For the Photos prompt, the "Don't Allow" was hit. My question is - is there no need for addUIInterruptionMonitorWithDescription anymore? I tried using it for tapping on the dialogs but it didn't do anything. Even when I tried to hit another button on the alerts, I didn't see it working. How do I tap on the individual buttons on a system alert her?
If there is an alert on screen, and none of your interruption handlers handle it, XCTest will dismiss it for you if you are using Xcode <9.1.
To gain control of the alerts, you should create an interruption handler for each alert, returning true from the closure when (and only when) you have handled the alert that handler was intended for.
If the test tries all your alert handlers or receives a true return value from any of your handlers, and there is still an alert on screen, XCTest will handle the alert itself.

"UI Testing Failure: Did not receive view did disappear notification within 2.0s" error

I am trying to record a UI test case and run it back, but I am running into this error. Basically, in my application when I land on the home screen, the user is asked to allow use of notification and location. After I record these events and try to segue to another VC, it records normally like so.
[app.alerts[#"\u201cSampleApp\u201d Would Like to Send You Notifications"].collectionViews.buttons[#"Don\u2019t Allow"] tap];
[app.alerts[#"Allow \u201cSampleApp\u201d to access your location while you use the app?"].collectionViews.buttons[#"Allow"] tap];
//segue to VC2
But when I play it back, it fails with the error in the title.
Did not receive view did disappear notification within 2.0s
I suspect that by the time the alerts are cleared, the segue button is already tapped and while it expects the home VC to disappear, it does not. Is this understanding right?. If yes, how can I delay the expectation, if no, please help.
System level alerts should be handled by addUIInterruptionMonitorWithDescription API here is the documentation from apple Link and sample code in swift below:
addUIInterruptionMonitorWithDescription("First Dialog") { (alert) -> Bool in
alert.buttons["Allow"].tap()
return true
}
XCUIApplication().tap()

Dismissing location and notifcation settings Xcode Ui testing

I am writing UI tests for my app. I have two alerts, location and notifications. I am struggling to find a solution in order to dismiss these alerts.
Currently I am using
systemAlertMonitorToken = addUIInterruptionMonitorWithDescription(systemAlertHandlerDescription) { (alert) -> Bool in
if alert.buttons.matchingIdentifier("OK").count > 0 {
alert.buttons["OK"].tap()
return true
} else {
return false
}
}
and
let notifications = self.app.alerts.element.collectionViews.buttons["OK"]
if notifications.exists {
notifications.tap()
}
however both functions are not allowing me to dismiss the alerts.
EDIT
Now I have added
app.buttons["OK"].tap()
app.tap()
to my code, but it means my tests are failing due to the XCT looking for the button "OK" straight away when it isnt a notification what pops up straight away.
I only want the alert OK to be dismissed when it pops up not on the first thing for launch.
The interruption monitor will only trigger the next time you try to interact with the app, so you need to have code after the interruption monitor is registered to do whatever you want to do after dismissing the alert, e.g tapping another button in your UI.
When the code gets to the part where you next interact with the UI, the handler for the completion handler will be executed and the system alert will be dealt with.

Delaying application while UIAlertView is active

I added an Alert view, asking for user input, at the start of my app. The app works fine without the Alert view. But with the code for the Alert view added, part of the UI is blacked out after hitting the 'ok' button on the alert.
I'm not well versed in ios, but is there a good way to delay the app from running until the Alert (text input) is completed (ok button pressed). This might avoid whatever is causing the screen to go black in one section. Apparently the app is executing while the alert is active, and the alert is affecting the UI. Basically, I am asking the user to input their phone number via an alert that will be used later in the app.
When alert view is shown on screen, getting back ground dimmed (reduced alpha) is a normal thing and is practiced by iOS.
However, if you feel some part of the code you want to run only on tap on OK button on alert, move that method call to OK button action handler.

UI Automation onAlert method not being called on simulator

Upon initial launch of my app, I get a permissions alert asking if I will allow the app to use my current location. My onAlert method successfully dismisses the alert on my device. When I run it on the simulator, it never gets called. Other internal alerts are handled by the onAlert method on the simulator. The permission alert coming from SpringBoard is not handled on the simulator. Any ideas?
UIATarget.onAlert = function onAlert(alert)
{
var title = alert.name();
UIALogger.logMessage(title);
return false;
}
This problem happens because the alert you're seeing comes from the system itself -- before the app actually launches and your automation environment is initialized.
To see this happen, add a debug line before the function definition for UIATarget.onAlert:
UIALogger.logDebug("Now setting up the alert function");
UIATarget.onAlert = function onAlert(alert) {}
Next, Reset Content and Settings... on your simulator and re-run your automation. You should notice that the debug line will not appear until after you manually dismiss the alert about using the current location.
I do not see how this would be fixable from javascript code. You have to delay the alert until the app has properly launched, or follow the example shown in this answer.
If default handler is not working for you, then you can simply use 'return true' instead of the 'return false' so that you can manually dismiss the popover.
Before 'return true' statement you can write some statement for tapping the button (dismiss button) you wish to.
I had the same problem with an app that presents an alert immediately after launching. When I logged the element tree, I could see the additional alert window, and I could let UIAutomation tap the OK button in the alert. But the alert handler was never called.
The reason was that the alert appeared before UIAutomation was set up properly to handle it. If I delayed the presentation of the alert, UIAutomation did catch it.

Resources