Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GREYInteraction atIndex doesn't throw an exception if index is larger than number of matched elements #419

Closed
jblack10101 opened this issue Feb 3, 2017 · 10 comments

Comments

@jblack10101
Copy link

jblack10101 commented Feb 3, 2017

The GREYInteraction atIndex documentation says, "In case of the index being over the number of matched elements, it throws an exception."

But the Swift declaration of atIndex doesn't indicate that it is possible to throw an exception, and it in fact doesn't throw an exception.

public func atIndex(index: UInt) -> Self!

From my testing, atIndex will always return a GREYInteraction, so how is it possible to know if the GREYInteraction that is returned is actually pointing to a valid UI element?

I'm using EarlGrey v1.7.0 with Swift 2.3.

@tirodkar
Copy link
Collaborator

tirodkar commented Feb 3, 2017

Quick question - could you show us how you're using atIndex in your test?selectElementWithMatcher and atIndex will not do anything until an interaction is performed on the GREYInteraction object i.e. by doing performAction, assert etc.

@jblack10101
Copy link
Author

So, I'm essentially just trying to determine the number of elements of a certain type that are currently displayed. The latest version of my method (which doesn't work) is:

static func count(collection: GREYInteraction) -> UInt {
    let errorConstant = NSError(domain: "", code: 1, userInfo: [:])
    var error: NSError? = errorConstant
    
    var index: UInt = 0
    collection.atIndex(index).assertWithMatcher(grey_notNil(), error: &error)
    
    while (error == errorConstant) {
        index += 1
        collection.atIndex(index).assertWithMatcher(grey_notNil(), error: &error)
    }
    
    return index
}

@tirodkar
Copy link
Collaborator

tirodkar commented Feb 3, 2017

That seems like a bug in EarlGrey. Also, I notice you're using atIndex to get the count of elements. This seems to be a behavior many devs are using.

We're planning to cut a release by today, this will surely be added in the next one. Thanks for reporting this.

@tirodkar
Copy link
Collaborator

@jblack10101 I created this test for our main table view -

  func testCountOfTableViewCells() {
    var error:NSError? = nil
    var index: UInt = 0
    while (true) {
      EarlGrey.select(elementWithMatcher: grey_kindOfClass(UITableViewCell.self)).atIndex(index).assert(with: grey_notNil(), error: &error)
      if ((error) != nil) {
        break
      } else {
        index = index + 1
      }
    }
    GREYAssert(index > 1, reason: "There are more than one cells present.")
    let outOfBoundsIndex: UInt = index + 1
    EarlGrey.select(elementWithMatcher: grey_kindOfClass(UITableViewCell.self)).atIndex(outOfBoundsIndex).assert(with: grey_notNil(), error: &error)
    GREYAssert(error?.code == GREYInteractionErrorCode.matchedElementIndexOutOfBoundsErrorCode.rawValue,
               reason: "The Interaction element's index being used was over the count of matched elements available.")
  }

From my testing, atIndex will always return a GREYInteraction, so how is it possible to know if the GREYInteraction that is returned is actually pointing to a valid UI element?

A GREYInteraction is just showing an intent to perform an interaction but does not perform the interaction itself. You need to call the action for it to actually go and search the hierarchy and find if an element exists. Simply creating a GREYInteraction doesn't do anything processing. This is done so that you can have the same GREYInteraction object utilized in different places for different actions. For eg.

let interaction: GREYInteraction! = EarlGrey.select(elementWithMatcher: grey_foo())
interaction.assert(with: grey_sufficientlyVisible())
// Perform an action on another GREYInteraction object.
interaction.perform(grey_tap())

would be completely out of sync.

The GREYInteraction atIndex documentation says, "In case of the index being over the number of matched elements, it throws an exception."

But the Swift declaration of atIndex doesn't indicate that it is possible to throw an exception, and it in fact doesn't throw an exception.

public func atIndex(index: UInt) -> Self!

Is there a @throws or so annotation that we could use for that here? As before, the atIndex can be used only in conjunction with an action / assertion. The reason why you're not seeing any exception being thrown is because you're passing an error. The error will soak the exception and allow you to handle it yourself. In the above test I've added, if you remove the passed error, then you'll see an issue with the last statement.

Is there something I'm missing from this? I'll add the test I showed in our Contribs project as a method so that you'd have a ready reference when you wish to get the count.

I'm using EarlGrey v1.7.0 with Swift 2.3.

@tirodkar
Copy link
Collaborator

Closing this for now. @jblack10101 do reopen and tell us if anything else is required.

@minuscorp
Copy link

Reopening here (tell me if it is not the place to talk about this) but is related to the count method. It does not get count elements that are hidden under the scroll, so I'm wondering how would it be possible to get all of the unique occurrences of a UITableView or UIScrollView even when they're not visible because a scroll is being needed to get them @tirodkar

@tirodkar
Copy link
Collaborator

We don't do a check for visibility when we try to get the count of cells, so visibility shouldn't impact the result. We go through the hierarchy and check the count. Can you ensure using [GREYElementHierarchy hierarchyStringForAllUIWindows]; that the UITableView or UIScrollView is indeed present?

@minuscorp
Copy link

Counting instances of UITableViewCell works just fine, but counting elements inside all cells (like counting how many labels of a certain accessibility identifier are there present in the table or scroll this method does not work as intended, it seems just count visible ones (on the screen) but not outside the frame of the screen itself

@minuscorp
Copy link

So I figured out what the issue is, the labels that I'm counting seems to have an alpha=1 even though their container is hidden (an UIStackView), so they are being counted when they're not visible enough.

@minuscorp
Copy link

minuscorp commented Dec 17, 2018

I've tried to make a more in-depth query about this and got the following result using:

let participationCells = grey_allOf([
            grey_accessibilityID("BonusRecommendationRedeemedTitleID"),
            GREYMatchers.matcher(forAncestor: grey_allOf([grey_kindOfClass(UIStackView.self), grey_sufficientlyVisible()]))
            ])
            .occurrences

That query finds and counts that an element has a visible UIStackView instance as an ancestor, but I'm doubting how many ancestors it is counting or how can I make it to just take the first ancestor that matches the GREYMatcher.

EDIT: occurrences is just the Swift implementation of the example count(GREYMatcher):

extension GREYMatcher {

    /// The number of elements that satisfies the matcher.
    public var occurrences: Int {
        var error: NSError?
        var index: Int = 0
        let countMatcher: GREYElementMatcherBlock =
            GREYElementMatcherBlock.matcher(matchesBlock: { (element: Any) -> Bool in
                if self.matches(element) {
                    index += 1
                }
                return false
                // swiftlint:disable:next multiple_closures_with_trailing_closure
            }) { (description: AnyObject?) in
                // swiftlint:disable:next force_cast
                let greyDescription: GREYDescription = description as! GREYDescription
                greyDescription.appendText("Count of Matcher")
            }
        EarlGreyImpl
            .invoked(fromFile: #file, lineNumber: #line)
            .selectElement(with: countMatcher)
            .assert(grey_notNil(), error: &error)
        return index
    }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants