Vinpai
Vinpai

Reputation: 1759

Xcode UI test - UI Testing Failure - Failed to scroll to visible (by AX action) when tap on Search field "Cancel' button

I am trying to dismiss the search field by tapping 'Cancel' button in search bar.

The test case is failing to find the cancel button. It was working fine in Xcode 7.0.1

I have added predicate to wait for button to appear. The test case is failing when we tap of "cancel" button

let button = app.buttons[“Cancel”]
let existsPredicate = NSPredicate(format: "exists == 1")

expectationForPredicate(existsPredicate, evaluatedWithObject: button, handler: nil)
waitForExpectationsWithTimeout(5, handler: nil)

button.tap() // Failing here

logs:

    t =     7.21s     Tap SearchField
t =     7.21s         Wait for app to idle
t =     7.29s         Find the SearchField
t =     7.29s             Snapshot accessibility hierarchy for com.test.mail
t =     7.49s             Find: Descendants matching type SearchField
t =     7.49s             Find: Element at index 0
t =     7.49s             Wait for app to idle
t =     7.55s         Synthesize event
t =     7.84s         Wait for app to idle
t =     8.97s     Type '[email protected]' into
t =     8.97s         Wait for app to idle
t =     9.03s         Find the "Search" SearchField
t =     9.03s             Snapshot accessibility hierarchy for com.test.mail
t =     9.35s             Find: Descendants matching type SearchField
t =     9.35s             Find: Element at index 0
t =     9.36s             Wait for app to idle
t =     9.42s         Synthesize event
t =    10.37s         Wait for app to idle
t =    10.44s     Check predicate `exists == 1` against object `"Cancel" Button`
t =    10.44s         Snapshot accessibility hierarchy for com.test.mail
t =    10.58s         Find: Descendants matching type Button
t =    10.58s         Find: Elements matching predicate '"Cancel" IN identifiers'
t =    10.58s     Tap "Cancel" Button
t =    10.58s         Wait for app to idle
t =    10.64s         Find the "Cancel" Button
t =    10.64s             Snapshot accessibility hierarchy for com.test.mail
t =    10.78s             Find: Descendants matching type Button
t =    10.78s             Find: Elements matching predicate '"Cancel" IN identifiers'
t =    10.79s             Wait for app to idle
t =    11.08s         Synthesize event
t =    11.13s             Scroll element to visible
t =    11.14s             Assertion Failure: UI Testing Failure - Failed to scroll to visible (by AX action) Button 0x7f7fcaebde40: traits: 8589934593, {{353.0, 26.0}, {53.0, 30.0}}, label: 'Cancel', error: Error -25204 performing AXAction 2003

Upvotes: 110

Views: 38148

Answers (10)

Kukiwon
Kukiwon

Reputation: 1332

I had this issue because I set a toolbar (and ToolbarItem) on a GeometryReader:

GeometryReader { proxy in
    ZStack { ... }
}
.toolbar {
    ToolbarItem(placement: .navigationBarTrailing) {
        Button(action: { ... }) {
            Text(Localizable.Global.login.localized)
        }
        .accessibilityIdentifier("welcomeViewLoginButton")
    }
}

After setting the toolbar on the ZStack instead, the toolbar item button was hittable again.

GeometryReader { proxy in
    ZStack {
        ...
    }
    .toolbar {
        ToolbarItem(placement: .navigationBarTrailing) {
            Button(action: { ... }) {
                Text(Localizable.Global.login.localized)
            }
            .accessibilityIdentifier("welcomeViewLoginButton")
        }
    }
}

Upvotes: 0

Sandy
Sandy

Reputation: 3131

I guess here "Cancel" button returns false for hittable property, that is preventing it from tapping.

If you see tap() in documentation it says

/*!
 * Sends a tap event to a hittable point computed for the element.
 */
- (void)tap;

It seems things are broken with Xcode 7.1. To keep myself (and you too ;)) unblocked from these issues I wrote an extension on XCUIElement that allows tap on an element even if it is not hittable. Following can help you.

/*Sends a tap event to a hittable/unhittable element.*/
extension XCUIElement {
    func forceTapElement() {
        if self.hittable {
            self.tap()
        }
        else {
            let coordinate: XCUICoordinate = self.coordinateWithNormalizedOffset(CGVectorMake(0.0, 0.0))
            coordinate.tap()
        }
    }
}

Now you can call as

button.forceTapElement()

Update - For Swift 3 use the following:

extension XCUIElement {
    func forceTapElement() {
        if self.isHittable {
            self.tap()
        }
        else {
            let coordinate: XCUICoordinate = self.coordinate(withNormalizedOffset: CGVector(dx:0.0, dy:0.0))
            coordinate.tap()
        }
    }
}

Upvotes: 176

Nuno Ferro
Nuno Ferro

Reputation: 1279

Try this:

if !button.isHittable {
     let coordinate: XCUICoordinate = button.coordinate(withNormalizedOffset: CGVector(dx:0.0, dy:0.0))
     coordinate.tap()
}

Upvotes: 1

Jeroen Vannevel
Jeroen Vannevel

Reputation: 44459

In the spirit of things that can cover your element, I had the RN debugger partially overlayed on top of my icon:

enter image description here

Upvotes: 0

Aistė Stikliūtė
Aistė Stikliūtė

Reputation: 41

The workaround of Sandy seemed help for a while but then no more - I then changed it like this:

func waitAndForceTap(timeout: UInt32 = 5000) {
    XCTAssert(waitForElement(timeout: timeout))
    coordinate(withNormalizedOffset: CGVector(dx:0.5, dy:0.5)).tap()
}

Main point being that as the issue is that isHittable check throws an exception, I don't do this check at all and go straight for coordinates after the element is found.

Upvotes: 4

Jessedc
Jessedc

Reputation: 12460

This question ranks well for Google queries around the term "Failed to scroll to visible (by AX action) Button". Given the age of the question I was inclined to think this was no longer an issue with the XCUITest framework as the accepted answer suggests.

I found this issue was due to the XCElement existing, but being hidden behind the software keyboard. The error is emitted by the framework since it is unable to scroll a view that exists into view to be tappable. In my case the button in question was behind the software keyboard sometimes.

I found the iOS Simulator's software keyboard may be toggled off in some cases (eg: on your machine) and toggled on in others (eg: on your CI). In my case I had toggled the software keyboard off on one machine, and by default it was toggled on on others.

Solution: Dismiss the keyboard before attempting to tap buttons that may be behind it.

I found tapping somewhere that explicitly dismissed the keyboard before tapping on the button solved my problem in all environments.

I added add some actions to get the current responder to resignFirstResponder. The views behind my text views will force the first responder to resign, so I tap somewhere just underneath the last text area.

 /// The keyboard may be up, dismiss it by tapping just below the password field
let pointBelowPassword = passwordSecureTextField.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 1))
pointBelowPassword.press(forDuration: 0.1)

Upvotes: 8

fdelam
fdelam

Reputation: 11

If you're using the AppCenter simulator to run the tests, you should make sure that you're running the tests on the same device version than your local simulator. I lost 3 days of work because of this.

Upvotes: 0

PruitIgoe
PruitIgoe

Reputation: 6384

In my case it was having a programmatically added UI element covering the button.

Upvotes: 0

Nagaraj
Nagaraj

Reputation: 802

Please check the trait of the element, i was facing the same issue with TableViewSectionHeader, i was trying to tap but it was failing at every point

enter image description here

Upvotes: 2

dogsgod
dogsgod

Reputation: 6387

For me, the root cause was that the objects I wanted to tap

  • have been set to hidden (and back)
  • have been removed and re-attached

In both cases the isAccessibilityElement property was false afterwards. Setting it back to true fixed it.

Upvotes: 14

Related Questions