UITests in Xcode 7 finds wrong 'Next' button - xcode

I have a test that looks like the following:
func testNextButtonDisabled() {
let app = XCUIApplication()
XCTAssertFalse(app.buttons["Next"].enabled)
}
This test fails because, in addition to my own "Next" button that I've created, the keyboard return button is labeled 'Next'. This test fails with the error:
UI Testing Failure - Multiple matches found
How can I differentiate between my own 'Next' button and the keyboard 'Next' button?

The specific solution to this problem is to look for elements that are descendants of the main window.
func testNextButtonDisabled() {
let app = XCUIApplication()
XCTAssertFalse(app.childrenMatchingType(.Window).elementBoundByIndex(0).buttons["Next"].enabled)
}
For a general solution to solve problems like this: In Xcode run the "Record UI Test" again to see how Xcode thinks you should be referencing the element in which you're interested.

Related

Why Cypress test is not able to trigger a script in a child window?

I have a pop up window containing a script in its head. And this script should be triggered by click on the window button. But after click on this button the script does not run. Could you suggest the reason?
Steps to reproduce:
Click on a button in the main window.
Child window containing a simple form is open.
Move to the child window, fill in the form fields and click on submit button.
Expected result: The click triggers a script located in the child window head. The script process the form and submit it.
Actual result: an error arises: "submitMyForm is not defined" (reference: submitMyForm() is a method containing in the script mentioned above).
So the problem is that the child window form fields can be populated and form button can be clicked but the script bounded to the button by means of the link <a href="javascript:submitMyForm();"> does not work as the function submitMyForm() is not found. Obviously the reason of the test failure is stubbing the child window which is performed to prevent having 2 windows at the same time. After this the child window is opening in the same browser tab as Cypress is not able to work with 2 windows open in the same time. But in this case the script stops working neither from the test nor by performing the click manually.
Here is a code snippet from the Cypress test:
const pop_url = `/dir1/dir2/file.php?id=${sectionId}`; // Here is a new window URL
cy.window().then( win => {
const stub = cy.stub(win, 'open').as('windowopen');
newSurveySectionListObj.AddNewBtnClick(); // Triggers form opening in the new window
cy.get('#windowopen').should('be.called.with', pop_url);
cy.window().then( $win => {
$win.location.href = pop_url
newSurveySectionListObj.typeDropdownSelect('Matrix'); // works fine
newSurveySectionListObj.modalDescriptionFldType('Cypress test string'); // works fine
newSurveySectionListObj.responseTypeDropdownSelect('Checkbox'); // works fine
cy.get('a[href="javascript:submitMyForm();"]').click(); // Does not work despite the button is clicked. An error "submitMyForm is // not defined" arises
});
});
You are going beyond what Cypress is capable of/designed for.
I would question why you are using a separate window in your app? You will run afoul of pop-up blockers that will prevent the new window being opened anyway. A percentage of users won't be able to work it out.
There are 2 approaches to this:
Use a modal dialog to achieve this (not great on mobile)
Use another route/page to display your form, return to the prev page when done
These have the advantage that they won't break with pop-up blockers, and your Cypress tests will work, as they are still in the same window.

How to write Xamarin.UITest case for Software Back Button on Android

I am writing UITest cases for my Xamarin forms project. Now, i am stuck at Navigation Part. I know using "app.Back()" we can navigate back but on our Project Hardware Back Button is disabled. Is there any way we can use Navigation Bar "Back button" ?
I tried to get elements in Page by using following code "AppResult[] results = app.Query();" but still i am not able to find any element which says barbackbutton or backbutton etc in the list.
Bharat, after reading this a couple times, I think what you are asking is "how do I find the automation ids / elements to target".
There's a couple different ways to do this. My preferred one is App.Repl(). Here's the Microsoft docs on it, but in short:
at the point in your test where you are on the application view that you want to find an element on, put in App.Repl()
[Test]
public void CanTapButton()
{
App.Repl();
}
Run the test. When the test gets to this point, a repl window will open and the test will pause. End the test if you want, but keep the command window. It will look like this:
Type into the command prompt tree, to see the full layout of the page visible on the device.
You can use the app query calls in the Repl window to draft queries. For example,
app.Query(x => x.Marked("cpgTitle"));
will return the cpgTitle element that you can see listed in the tree. You can then use that app query to interact with the element, using something like App.Tap(appQueryVariable).
AppQueries docs are here and overall, it's very similar to selenium-style selectors.

Xcode7 | Xcode UI Tests | How to handle location service alert?

I am writing UI Test Cases for one one of my app using the XCUIApplication, XCUIElement and XCUIElementQuery introduced in Xcode7/iOS 9.
I have hit a road block. One of the screens in test case requires iOS's Location Services. As expected the user is prompted about allowing use of location service with alert titled: Allow “App name” to access your location while you use the app? with Allow & Don't Allow buttons.
Problem is or so it seems that since the alert is presented by OS itself it is not present in Application's element sub-tree.
I have logged following:
print("XYZ:\(app.alerts.count)")//0
var existence = app.staticTexts["Allow “App Name” to access your location while you use the app?"].exists
print("XYZ:\(existence)")//false
existence = app.buttons["Allow"].exists
print("XYZ:\(existence)") //false
Even UI recording generated similar code:
XCUIApplication().alerts["Allow “App Name” to access your location while you use the app?"].collectionViews.buttons["Allow"].tap()
I have not found any API that can get me past this problem. For example:
Tap at a position on the screen
Get alerts outside the app
So how can I get past this? Is there a way to configure Test Targets so that Location Service Authorization is not required.
Xcode 9
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowBtn = springboard.buttons["Allow"]
if allowBtn.exists {
allowBtn.tap()
}
Xcode 8.3.3
_ = addUIInterruptionMonitor(withDescription: "Location Dialog") { (alert) -> Bool in
alert.buttons["Allow"].tap()
return true
}
app.buttons["Request Location"].tap()
app.tap() // need to interact with the app for the handler to fire
Note that it is a bit different as the method name now is addUIInterruptionMonitor and takes withDescription as an argument
Xcode 7.1
Xcode 7.1 has finally fixed a issue with system alerts. There are, however, two small gotchas.
First, you need to set up a "UI Interuption Handler" before presenting the alert. This is our way of telling the framework how to handle an alert when it appears.
Second, after presenting the alert you must interact with the interface. Simply tapping the app works just fine, but is required.
addUIInterruptionMonitorWithDescription("Location Dialog") { (alert) -> Bool in
alert.buttons["Allow"].tap()
return true
}
app.buttons["Request Location"].tap()
app.tap() // need to interact with the app for the handler to fire
The "Location Dialog" is just a string to help the developer identify which handler was accessed, it is not specific to the type of alert.
Xcode 7.0
The following will dismiss a single "system alert" in Xcode 7 Beta 6:
let app = XCUIApplication()
app.launch()
// trigger location permission dialog
app.alerts.element.collectionViews.buttons["Allow"].tap()
Beta 6 introduced a slew of fixes for UI Testing and I believe this was one of them.
Also note that I am calling -element directly on -alerts. Calling -element on an XCUIElementQuery forces the framework to choose the "one and only" matching element on the screen. This works great for alerts where you can only have one visible at a time. However, if you try this for a label and have two labels the framework will raise an exception.
This was the only thing that worked for me. Using Xcode 9 fwiw.
Also probably relevant that I was already using addUIInterruptionMonitor for a different alert. I tried reordering them and it didn't make a difference. Could be that it's a problem in 9 when you have two, or could be I was using them wrong. In any event the code below worked. :)
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowBtn = springboard.buttons["Allow"]
if allowBtn.exists {
allowBtn.tap()
}
If you want to check if the alert is showing, just check for the existence of the button:
if (app.alerts.element.collectionViews.buttons["Dismiss"].exists)
{
app.alerts.element.collectionViews.buttons["Dismiss"].tap()
}
it checks if the alert is showing, and if it's showing it will tap it
I got it to work with this on Xcode 9.4.1, the trick was to wait for the popup to appear.
// wait for location service popup to appear
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowBtn = springboard.buttons["Allow"]
expectation(for: NSPredicate(format: "exists == true"), evaluatedWith: allowBtn, handler: nil)
waitForExpectations(timeout: 10, handler: nil)
//allow location service
if allowBtn.exists {
allowBtn.tap()
}
On xcode 9.1, alerts are only being handled if the test device has iOS 11. Doesn't work on older iOS versions e.g 10.3 etc. Reference: https://forums.developer.apple.com/thread/86989
To handle alerts use this:
//Use this before the alerts appear. I am doing it before app.launch()
let allowButtonPredicate = NSPredicate(format: "label == 'Always Allow' || label == 'Allow'")
//1st alert
_ = addUIInterruptionMonitor(withDescription: "Allow to access your location?") { (alert) -> Bool in
let alwaysAllowButton = alert.buttons.matching(allowButtonPredicate).element.firstMatch
if alwaysAllowButton.exists {
alwaysAllowButton.tap()
return true
}
return false
}
// One interruption monitor is sufficient for multiple alerts
This works for all languages:
let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")
let allowBtn = springboard.buttons.element(boundBy: 1)
if allowBtn.exists {
allowBtn.tap()
}
To tap allow on location alert you can call
element.tap() where element is any element on your screen.
So after calling tap, accessibility will tap Allow on alert and than tap on your element
Here's what I did to accept a notifications permission alert in any language by tapping the second button found in the (two button) dialog. The Allow button is on the right, therefore index 1.
let handler = addUIInterruptionMonitor(withDescription: "System Dialog") { (alert) -> Bool in
alert.buttons.element(boundBy: 1).tap()
return true
}
app.tap()

Show ViewController on Extension info button Edit Mode

I am working on an Today Extension for Yosemite.
I would like to show a SettingsViewController instead of going into edit mode.
If I "presentViewControllerInWidget" on "widgetDidBeginEditing" it gets some weird glitches and the view controller is hiding and showing all the time.
Did anyone achieved to show an viewController on info button click or knows a workaround on that glitch?
func widgetDidBeginEditing() {
self.presentViewControllerInWidget(self.settingsViewController)
}
This worked for me:
func widgetDidBeginEditing() {
var delay = dispatch_time(DISPATCH_TIME_NOW, Int64(0.1 * Double(NSEC_PER_SEC)))
dispatch_after(delay, dispatch_get_main_queue()) {
self.presentViewControllerInWidget(self.settingsViewController)
}
}
I guess this is a bug that made it into the Yosemite release.
Documentation on widgets is very sketchy at best and it seems there are quite a few oddities in the framework.
When adding a symbolic breakpoint to widgetDidBeginEditing I get two hits when clicking the little edit button, and the edit Button becomes "Cancel". It is supposed to say "Done" though. Only after an "Add" action should it say "Cancel" (Just check out Apple's Weather widget)
Important to say: I am not using the template with the NCWidgetListViewController but my own list implementation.
If anyone finds a proper solution to this problem I'd be very happy!

UI Automation FrameWork for iPhone

I am exploring the newly exposed framework UI Automation in iphoneOS 4.0. Has anybody tested their application using this framework. I will appreciate any help.
I am trying to test a sample application that just contains a textfield and a button. I have written a script as
UIALogger.logStart("Starting Test");
var view = UIATarget.localTarget().frontMostApp().mainWindow().elements()[0];
var textfields = view.textFields();
if (textfields.length != 1) {
UIALogger.logFail("Wrong number of text fields");
} else {
UIALogger.logPass("Right number of text fields");
}
textfields[0].setValue("anurag");
view.buttons()[0].tap();
The problem is that the value of textfield is not getting set and no button is tapped. When I run the instruments only the view(with textfield and button) appears and then notting is happening.
There is a message in instruments "Something else has happened".
If your main window contains a button and a text field (in this order in the hierarchy) then your first line of code will return you the UIAButton element, so the next line is incorrect, because you're trying to call textFields() on a button.
The first part should look like this:
var view = UIATarget.localTarget().frontMostApp().mainWindow();
var textfields = view.textFields();
if (textfields.length != 1) {
UIALogger.logFail("Wrong number of text fields");
} else {
UIALogger.logPass("Right number of text fields");
}
And in that case I think there are two ways of testing the tap and text field. Like this:
textfields[0].setValue("anurag");
view.buttons()[0].tap();
or like this:
view.elements()[1].setValue("anurag");
view.elements()[0].tap();
And personally I prefer getting objects by using Accessibility Label instead of index. For more information look for a UIAElement Class Reference and take a look here:
UI Automation Reference Collection
All this stuff is gonna work only if the application is made with that accessibility thing (its own accessibility protocol: by tagging all its UI controls in Interface Builder with names, by setting the Accessability label to a unique value for the view). Or if you work with iPhone standard controls.
If the application doesn't contain anything like that, you won't be able to do much with UI Automation and will see only a 320x480 empty canvas.
You can check this link for some more details.
For example, I work on a OpenGL application that was not built with any accessibility tag and I cannot see anything through UI Automation besides a 320x480 empty form.

Resources