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

Android Matchers Need To Match More Than Just Visible Elements #1185

Open
RyanThomas73 opened this issue Feb 27, 2019 · 31 comments
Open

Android Matchers Need To Match More Than Just Visible Elements #1185

RyanThomas73 opened this issue Feb 27, 2019 · 31 comments

Comments

@RyanThomas73
Copy link
Contributor

Description

Android matchers only match visible elements. This deviates from the expected behavior and from the behavior when running on iOS.

Steps to Reproduce

Setup a sample app that has an element that will be in the view hierarchy but not considered visible. For example a view that is covered by an overlay with partial transparency.

Run the detox test -c <config> command for an android config with an example test that has an expectation for the 'non visible' element to exist.

Expected Behavior: The test passes because the element exists in the view hierarchy.

Actual Behavior: The test fails on android because the matchers in DetoxMatcher.java only select elements with effective visibility VISIBLE.

Detox, Node, Device, Xcode and macOS Versions

  • Detox: 10.0.10
  • React Native: 0.57.8
  • Node: 11.6.0
  • Device: Android Emulator
  • Xcode: N/A
  • macOS: N/A
  • Windows: Windows 10 Pro v1703

Device and verbose Detox logs

Skipping full logs as they're not relevant. The specific error message when matching by test id:

Failed: [Error: Error: No views in hierarchy found matching: (with tag value: is "<Test ID>" and view has effective visibility=VISIBLE)
@LeoNatan
Copy link
Contributor

@amitdwix @rotemmiz I think this is a bug.

@stale
Copy link

stale bot commented Mar 30, 2019

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs.
If you believe the issue is still relevant, please test on the latest Detox and report back.
Thank you for your contributions.

@stale stale bot added the 🏚 stale label Mar 30, 2019
@danielgreane
Copy link

+1 confirmed, tested with 12.1.3

@stale stale bot removed the 🏚 stale label Apr 5, 2019
@rotemmiz
Copy link
Member

Hey @RyanThomas73,

I am trying to find an example to the issue you described. The Detox test suite has something similar (a list view with only a few items visible, but all exist in view hierarchy), this test passes on RN57, but fails in 58 and 59 AFAIK (please correct me if I'm wrong @d4vidi).
The fact that you're using RN57 and your test is failing is what confuses me, can you provide more details or example project?

The issue on later versions of RN is related to facebook/react-native#23870 and needs to be addressed in react native itself.

@RyanThomas73
Copy link
Contributor Author

Hi @rotemmiz

The most common example I have of this that are problematic to me are custom dialog components that use absolute positioning to cover the screen. I'll reach a point in my test where I need to confirm that my view hierarchy is in a certain state (e.g. the login form is in the hierarchy) but all of the views in the screen are completely covered by the absolute positioned views that compose the dialog.

When I have some available time I'll retest using RN 0.59 and Detox v12 to see if I still encounter the same problems. I suspect I will since the detox matcher logic is still coded to only return views with effective visibility of Visible.

https://github.com/wix/Detox/blob/master/detox/android/detox/src/main/java/com/wix/detox/espresso/DetoxMatcher.java

It looks like they were set to search for views with only effective visibility of Visible in this PR:

b9a832f#diff-3ceb6941bb5fa2cc651630b78f284949

@fancychimp
Copy link

@RyanThomas73 is there a workaround you've found in the meantime?

@RyanThomas73
Copy link
Contributor Author

@fancychimp Sorry, no work around that I've found. We currently have a gap in our test coverage that we left open until this is resolved for Android.

@fancychimp
Copy link

Thanks for your reply, same here 😢 Hopefully this will be addressed soon.

@d4vidi
Copy link
Collaborator

d4vidi commented Nov 17, 2019

@RyanThomas73 I'd like to better understand your use case, if that's ok -- in what exact way do you have some of your views invisible, or partly visible? Also, what's the reasoning behind inspecting views that are hidden by a dialog - wouldn't it make sense to first complete the interaction with the dialog and then go back to the origin screen (e.g. login form)?

I believe a picture's worth a thousand words in this case.

@fancychimp
Copy link

@d4vidi, I'm not @RyanThomas73 but I'll share my use case:

In our app, we have a screen that contains an input for a phone number. Once that input is tapped, the number keyboard is brought up. The exact step is await element(by.id('phoneNumber')).typeText('111-111-1111');, and it fails on typeText. The interaction cannot be completed here because of the visibility bug.

Screen Shot 2019-11-18 at 11 30 09 AM

@d4vidi
Copy link
Collaborator

d4vidi commented Nov 19, 2019

@fancychimp thanks for the input! To be sure I'm not missing something here - when the keyboard pops up, it usually pops up over the phone number input - or alternatively, pushes it away from the visible viewport? (meaning, the screenshot was taken only after you've manually scrolled back up...)

@fancychimp
Copy link

@d4vidi it does not. This is a screenshot at the await element(by.id('phoneNumber')).typeText('111-111-1111'); or await element(by.id('phoneNumber')).tap(); step(s), where I have not scrolled. To my knowledge, nothing should be blocking the input at this point.

@d4vidi
Copy link
Collaborator

d4vidi commented Nov 19, 2019

@fancychimp I agree, yet it seems then that your problem is different seeing that the numbers input is fully visible.

@fancychimp
Copy link

@d4vidi even if I try to precisely click on the input's coordinates using tapAtPoint I still get this same error. I don't see an obvious workaround.

@losikov
Copy link

losikov commented Apr 16, 2020

Using RN 0.62 & react-navigation 5.X
After I do a push/navigate to next screen with stack navigator, all elements on main view or in navigation header located on root screen are still visible: return true to toBeVisible, and false to toBeNotVisible.

androidx.test.espresso.base.DefaultFailureHandler$AssertionFailedWithCauseError: 'not is displayed on the screen to the user' doesn't match the selected view.
Expected: not is displayed on the screen to the user
Got: "ReactViewGroup{id=769, desc=Main Menu, visibility=VISIBLE, width=134, height=111, has-focus=false, has-focusable=true, has-window-focus=true, is-clickable=true, is-enabled=true, is-focused=false, is-focusable=true, is-layout-requested=false, is-selected=false, layout-params=android.view.ViewGroup$LayoutParams@63dae5f, tag=OpenDrawerAccount, root-is-layout-requested=false, has-input-connection=false, x=0.0, y=18.0, child-count=1}"

No issues on iOS. In our case it is used only for convenience, for smart navigation between screens. But it would be nice to get it finally fixed.
facebook/react-native#23870 is older than a year. Is it only dependency?

@rikur
Copy link

rikur commented Apr 25, 2020

+1

I have a horizontal and paging FlatList and list items that are not visible are considered visible on Android. The toBeNotVisible() works great on iOS however.

Quick note: iOS uses matcherForSufficientlyVisible(>=0.750000) where as Android seems to use isVisible() (at least on the toBeNotVisible() matcher). Maybe a simple switch to isDisplayingAtLeast(75) would fix this? I think it would at least fix my case with FlatList:

Returns a matcher which accepts a view so long as a given percentage of that view's area is not obscured by any parent view and is thus visible to the user.

If not, at least the feature would be on parity on both platforms.

HTH

@iMaupin
Copy link

iMaupin commented Jan 19, 2022

Is this bug fix still in the works?
In the meantime are there any recommended workarounds?

@Alaa-Ben
Copy link

Alaa-Ben commented Oct 4, 2022

+1

1 similar comment
@axeldix
Copy link

axeldix commented Nov 1, 2022

+1

@andy-lawler-sbg
Copy link

Any updates on this? Still seeing this issue somehow in 2023

@nlmazhari
Copy link

@andy-lawler-sbg I'm also experiencing this issue. could you find a work around?

@andy-lawler-sbg
Copy link

@andy-lawler-sbg I'm also experiencing this issue. could you find a work around?

sadly not

@nlmazhari
Copy link

@LeoNatan @d4vidi I see the bug is updated with accepted label a year ago. is there any update I'm missing?

@jailsonpaca
Copy link

up

@IvayloMetodiev
Copy link

up

@MortadhaFadhlaoui
Copy link

MortadhaFadhlaoui commented Jun 28, 2023

We are facing this issue only in the GitHub actions job 🤔

@d4vidi
Copy link
Collaborator

d4vidi commented Jun 29, 2023

+1

I have a horizontal and paging FlatList and list items that are not visible are considered visible on Android. The toBeNotVisible() works great on iOS however.

Quick note: iOS uses matcherForSufficientlyVisible(>=0.750000) where as Android seems to use isVisible() (at least on the toBeNotVisible() matcher). Maybe a simple switch to isDisplayingAtLeast(75) would fix this? I think it would at least fix my case with FlatList:

Returns a matcher which accepts a view so long as a given percentage of that view's area is not obscured by any parent view and is thus visible to the user.

If not, at least the feature would be on parity on both platforms.

HTH

That sounds directly related to facebook/react-native#23870, which differs from the OP.

@d4vidi
Copy link
Collaborator

d4vidi commented Jun 29, 2023

I'd really like to pick this up for fixing in the near future, but I fail to see why globally asserting for effective-visibility using Espresso's withEffectiveVisibility(VISIBLE) is a problem in the case of overlays. From the function comment:

   * <p><b>Note:</b> Contrary to what the name may imply, view visibility does not directly
   * translate into whether the view is displayed on screen (use {@link #isDisplayed()} for that).
   * For example, the view and all of its ancestors can have {@code visibility=VISIBLE}, but the
   * view may need to be scrolled to in order to be actually visible to the user. Unless you're
   * specifically targeting the visibility value with your test, use {@link #isDisplayed()}.

This is definitely an issue for the not.toExist() or not.toBeVisible() cases (and probably other negated cases as well) but it should work for partly visible views. I'd like to better understand that, first, in order to introduce the best fix. @RyanThomas73 would love your take on that.

@ibovegar
Copy link

up

@LahiruRajapaksha
Copy link

Is there any fix for this issue?
I'm also facing a similar issue with the app.

Assertions

    await waitFor(element(by.id('cancelBtn'))).toBeVisible(); // Up to this the tests are passing
    await element(by.id('cancelBtn')).tap(); // Test is failing with this assertion

Error

   Test Failed: No views in hierarchy found matching: (view.getTag() is "cancelBtn" and view has effective visibility <VISIBLE>)

@d4vidi
Copy link
Collaborator

d4vidi commented Oct 26, 2023

@LahiruRajapaksha the waitFor line you've tried running is scarce without a terminating withTimeout() -

-await waitFor(element(by.id('cancelBtn'))).toBeVisible(); // Up to this the tests are passing
+await waitFor(element(by.id('cancelBtn'))).toBeVisible().withTimeout(MY_TIMEOUT); // Up to this the tests are passing

Meaning, a view with cancelBtn as its test ID does not exist in the view hierarchy (you can try running detox test with a --loglevel verbose and inspect it yourself).

In any case, why not use expect instead of waitFor to begin with? Your options are:

// Synchronized => Recommended
await expect(element(by.id('cancelBtn'))).toBeVisible();
// Unsynchronized => Discouraged, but correct:
await waitFor(element(by.id('cancelBtn'))).toBeVisible().withTimeout(MY_TIMEOUT);

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

No branches or pull requests