Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wait for view pager animations with espresso?

Trying to do some tests with a ViewPager.

I want to swipe between tabs, and I don't want to continue until the swipe is complete. But there doesn't appear to be a way to turn off the animation for the view pager (all animations under the developer options are disabled).

So this always results in a test failure, because the view pager hasn't completed it's animation, and so the view is not completely displayed yet:

// swipe left
onView(withId(R.id.viewpager)).check(matches(isDisplayed())).perform(swipeLeft());

// check to ensure that the next tab is completely visible.
onView(withId(R.id.next_tab)).check(matches(isCompletelyDisplayed()));

Is there an elegant or maybe even recommended way to do this, or am I stuck putting some kind of timed wait in there?

like image 946
Mark Avatar asked Jun 25 '15 17:06

Mark


3 Answers

The IdlingResource @Simas suggests is actually pretty simple to implement:

public class ViewPagerIdlingResource implements IdlingResource {

    private final String mName;

    private boolean mIdle = true; // Default to idle since we can't query the scroll state.

    private ResourceCallback mResourceCallback;

    public ViewPagerIdlingResource(ViewPager viewPager, String name) {
        viewPager.addOnPageChangeListener(new ViewPagerListener());
        mName = name;
    }

    @Override
    public String getName() {
        return mName;
    }

    @Override
    public boolean isIdleNow() {
        return mIdle;
    }

    @Override
    public void registerIdleTransitionCallback(ResourceCallback resourceCallback) {
        mResourceCallback = resourceCallback;
    }

    private class ViewPagerListener extends ViewPager.SimpleOnPageChangeListener {

        @Override
        public void onPageScrollStateChanged(int state) {
            mIdle = (state == ViewPager.SCROLL_STATE_IDLE
                    // Treat dragging as idle, or Espresso will block itself when swiping.
                    || state == ViewPager.SCROLL_STATE_DRAGGING);
            if (mIdle && mResourceCallback != null) {
                mResourceCallback.onTransitionToIdle();
            }
        }
    }
}
like image 188
vaughandroid Avatar answered Oct 07 '22 09:10

vaughandroid


Since I've done this at least twice now, here is the accepted answer in Kotlin and with androidx ViewPager2:

class ViewPager2IdlingResource(viewPager: ViewPager2, name: String) : IdlingResource {

    private val name: String
    private var isIdle = true // Default to idle since we can't query the scroll state.
    private var resourceCallback: IdlingResource.ResourceCallback? = null

    init {
        viewPager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageScrollStateChanged(state: Int) {
                isIdle = (state == ViewPager.SCROLL_STATE_IDLE // Treat dragging as idle, or Espresso will block itself when swiping.
                    || state == ViewPager.SCROLL_STATE_DRAGGING)
                if (isIdle && resourceCallback != null) {
                    resourceCallback!!.onTransitionToIdle()
                }
            }
        })
        this.name = name
    }

    override fun getName(): String {
        return name
    }

    override fun isIdleNow(): Boolean {
        return isIdle
    }

    override fun registerIdleTransitionCallback(resourceCallback: IdlingResource.ResourceCallback) {
        this.resourceCallback = resourceCallback
    }
}

And here is how you use it from a UI test using ActivityScenarioRule:

@get:Rule
val testRule = ActivityScenarioRule(OnboardingActivity::class.java)

private lateinit var viewPager2IdlingResource: ViewPager2IdlingResource

....

@Before
fun setUp() {
    testRule.scenario.onActivity {
        viewPager2IdlingResource =
            ViewPager2IdlingResource(it.findViewById(R.id.onboarding_view_pager), "viewPagerIdlingResource")
        IdlingRegistry.getInstance().register(viewPager2IdlingResource)
    }
}

@After
fun tearDown() {
    IdlingRegistry.getInstance().unregister(viewPager2IdlingResource)
}
like image 25
Anders Ullnæss Avatar answered Oct 07 '22 10:10

Anders Ullnæss


The androidx.test.espresso:espresso-core library offers a ViewPagerActions class which contains a number of methods for scrolling between the pages of a ViewPager. It takes care of waiting until the scroll is complete so you don't need to add any explicit waits or sleeps in your test methods.

If you need to perform similar scrolling on a ViewPager2 instance, you can take the source of the ViewPagerActions class from here and make some minor tweaks to it to get it to work for ViewPager2. Here is an example which you are welcome to take and use.

like image 23
Adil Hussain Avatar answered Oct 07 '22 08:10

Adil Hussain