Carlo Matulessy
Carlo Matulessy

Reputation: 995

Check if a resource id from view in array is displayed and click on it (Android-Espresso)

I have the following case. We are using the Gherkin language to drive our native ui automation with Espresso. In our Gherkin lines we've got a line called:

And I tap on button "Next"

Where "Next" is a variable String we pass into our glue code (we are using the Cucumber framework).

As it happens, our app has many "Next" buttons with different resources ids. I ended up by using gherkin lines with variables like:

And I tap on button "Next in screen 1"
And I tap on button "Next in screen 2"

Now I want to use only the Gherkin variable "Next" in my code. I get an Integer array which contains all the resources ids from all the Next buttons, but when I want to check which id is displayed on the screen I got a NoMatchingViewException.

This is my current solution for now:

    final int[] uiElementIds = getArrayWithIdsFromUIElement("Next");

    int testId = 0;
    for(int id : uiElementIds) {
        try {
            onView(withId(id)).check(matches(isDisplayed()));
            testId = id;
            break;
        } catch(NoMatchingViewException ex) {
            // do nothing
        }
    }

    final int uiElementId = testId;
    onView(withId(uiElementId)).withFailureHandler(new FailureHandler() {
            @Override
            public void handle(Throwable error, Matcher<View> viewMatcher) {
                onView(withId(uiElementId)).perform(scrollTo(), click());
            }
        }).perform(click());

As you can see I just catch the thrown NoMatchingViewException and do nothing with it until it finds the right id and break out of the for-loop.


My question is:

Is there a better approach which we can use to loop through it to see which id is displayed and if so click on it?

In my mind I came up with this idea, but it is not integrated in Espresso:

for(int id : uiElementIds) {
 if(onView(withId(id)).exist()) {
    performClick();
 }
}

Upvotes: 4

Views: 1199

Answers (1)

piotrek1543
piotrek1543

Reputation: 19351

According to your question

My question is:

Is there a better approach which we can use to loop through it to see which id is displayed and if so click on it?

In my mind I came up with this idea, but it is not integrated in Espresso:

for(int id : uiElementIds) {
 if(onView(withId(id)).exist()) {
    performClick();
 }
}

Here's my old Espresso framework Activity tests:

@RunWith(AndroidJUnit4.class)
@FixMethodOrder(MethodSorters.NAME_ASCENDING)
public class DetailsActivityTest {

    @Rule
    public ActivityTestRule<MainActivity_> mRule = new ActivityTestRule<>(MainActivity_.class);

    //TODO: Write tests to check if views are visible
    private final int[] allDetailsActivityViewsIdis = {
            R.id.dItem,
            R.id.dDayAndDate,
            R.id.dDay,
            R.id.dDate,
            R.id.dCity,
            R.id.dTempAndIcon,
            R.id.dTemperature,
            R.id.dHighTemp,
            R.id.dLowTemp,
            R.id.dDescriptionLayout,
            R.id.dDescription,
            R.id.dForecast,
            R.id.dHumidityLayout,
            R.id.dHumidityDesc,
            R.id.dHumidityVal,
            R.id.dPressureLayout,
            R.id.dPressureDesc,
            R.id.dPressureVal,
            R.id.dWindLayout,
            R.id.dWindDesc,
            R.id.dWindVal

    };

    private final int[] detailsActivityTextViewsIdis = {
            R.id.dDay,
            R.id.dDate,
            R.id.dHighTemp,
            R.id.dLowTemp,
            R.id.dDescription,
            R.id.dHumidityVal,
            R.id.dPressureVal,
            R.id.dWindVal,

    };

    private final int[] detailsActivityTextViewsDefaultValues = {
            R.string.day,
            R.string.date,
            R.string.high_temp,
            R.string.low_temp,
            R.string.description,
            R.string.humidity_val,
            R.string.pressure_val,
            R.string.wind_val,

    };

    @Before
    //TODO: Rewrite this code using espresso-intents
    public void checkIfAllDetailActivityViewsAreDisplayed() throws InterruptedException {
        for (int viewId : allDetailsActivityViewsIdis)
           onView(withId(viewId)).perform(click());

    }

    @Test
    public void checkIfDetailsActivityViewsHaveNoDefaultValue() throws InterruptedException {
        for (int viewId : detailsActivityTextViewsIdis)
            for (int valueId : detailsActivityTextViewsDefaultValues)
                onView(withId(viewId)).check(matches(not(withText(valueId))));

    }

}

As you see, using foreach with Espresso is possible, but instead of exists() use check(matches(...) with value: isDisplayed, isDisplayedAtLeast or isCompletelyDisplayed

Hope it would help you

Upvotes: 1

Related Questions