content: package atownsend.swipeopenhelper;
  
  import android.view.View;
  import androidx.test.core.app.ActivityScenario;
  import androidx.test.espresso.ViewAction;
  import androidx.test.espresso.action.GeneralLocation;
  import androidx.test.espresso.action.GeneralSwipeAction;
  import androidx.test.espresso.action.Press;
  import androidx.test.espresso.action.Swipe;
  import androidx.test.espresso.matcher.BoundedMatcher;
  import androidx.test.ext.junit.runners.AndroidJUnit4;
  import atownsend.swipeopenhelper.test.R;
  import org.hamcrest.Description;
  import org.hamcrest.Matcher;
  import org.junit.Test;
  import org.junit.runner.RunWith;
  
  import static androidx.test.espresso.Espresso.onView;
  import static androidx.test.espresso.action.ViewActions.swipeLeft;
  import static androidx.test.espresso.action.ViewActions.swipeRight;
  import static androidx.test.espresso.assertion.ViewAssertions.matches;
  import static androidx.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
  import static androidx.test.espresso.matcher.ViewMatchers.withId;
  import static androidx.test.espresso.matcher.ViewMatchers.withText;
  
  /**
   * Instrumentation tests for SwipeOpenItemTouchHelper
   */
  @RunWith(AndroidJUnit4.class) public class SwipeOpenItemTouchHelperTest {
  
    @Test public void swipeCloseOnActionTest() {
  
      try (ActivityScenario<SwipeOpenItemTouchHelperTestActivity> ignored =
               ActivityScenario.launch(SwipeOpenItemTouchHelperTestActivity.class)) {
  
        // swipe open position 1
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(1, swipeRight()));
        onView(withText("Test 1")).check(matches(checkTranslationX(true)));
  
        // swipe open position 3
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(3, swipeLeft()));
  
        // position 1 should have closed, and position 3 should be open
        onView(withText("Test 3")).check(matches(checkTranslationX(false)));
        onView(withText("Test 1")).check(matches(checkZeroTranslation()));
      }
    }
  
    @Test public void scrollCloseOnActionTest() {
      try (ActivityScenario<SwipeOpenItemTouchHelperTestActivity> ignored = ActivityScenario.launch(
          SwipeOpenItemTouchHelperTestActivity.class)) {
  
        // swipe open position 2
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(2, swipeRight()));
        onView(withText("Test 2")).check(matches(checkTranslationX(true)));
  
        // scroll on position 3
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(3, scroll()));
  
        // position 2 should now be closed
        onView(withText("Test 2")).check(matches(checkZeroTranslation()));
      }
    }
  
    @Test
    public void stateSavingTest() {
      try (ActivityScenario<SwipeOpenItemTouchHelperTestActivity> scenario = ActivityScenario.launch(
          SwipeOpenItemTouchHelperTestActivity.class)) {
  
        scenario.onActivity(activity -> activity.helper.setCloseOnAction(false));
  
        // open positions 1 and 2
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(1, swipeRight()));
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(2, swipeLeft()));
  
        // recreate the activity
        scenario.recreate();
  
        // both positions should still be open
        onView(withText("Test 1")).check(matches(checkTranslationX(true)));
        onView(withText("Test 2")).check(matches(checkTranslationX(false)));
      }
    }
  
    @Test public void closePositionsTest() {
      try (ActivityScenario<SwipeOpenItemTouchHelperTestActivity> scenario = ActivityScenario.launch(
          SwipeOpenItemTouchHelperTestActivity.class)) {
  
        scenario.onActivity(activity -> activity.helper.setCloseOnAction(false));
  
        // open positions 1 and 2
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(1, swipeRight()));
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(2, swipeLeft()));
  
        scenario.onActivity(activity -> {
          activity.helper.closeOpenPosition(1);
          activity.helper.closeOpenPosition(2);
        });
  
        // both positions should be closed
        onView(withText("Test 1")).check(matches(checkZeroTranslation()));
        onView(withText("Test 2")).check(matches(checkZeroTranslation()));
      }
    }
  
    @Test public void closeAllPositionsTest() {
      try (ActivityScenario<SwipeOpenItemTouchHelperTestActivity> scenario =
               ActivityScenario.launch(SwipeOpenItemTouchHelperTestActivity.class)) {
        scenario.onActivity(activity -> activity.helper.setCloseOnAction(false));
        // open positions 1 and 2
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(1, swipeRight()));
        onView(withId(R.id.test_recycler)).perform(actionOnItemAtPosition(2, swipeLeft()));
  
        scenario.onActivity(activity -> activity.helper.closeAllOpenPositions());
  
        // both positions should be closed
        onView(withText("Test 1")).check(matches(checkZeroTranslation()));
        onView(withText("Test 2")).check(matches(checkZeroTranslation()));
      }
    }
  
    @Test public void openPositionTest() {
      try (ActivityScenario<SwipeOpenItemTouchHelperTestActivity> scenario =
               ActivityScenario.launch(SwipeOpenItemTouchHelperTestActivity.class)) {
        scenario.onActivity(activity -> {
          activity.helper.setCloseOnAction(true);
          activity.helper.openPositionStart(1);
        });
  
        onView(withText("Test 1")).check(matches(checkTranslationX(true)));
        scenario.onActivity(activity -> activity.helper.openPositionEnd(2));
  
        onView(withText("Test 2")).check(matches(checkTranslationX(false)));
        onView(withText("Test 1")).check(matches(checkZeroTranslation()));
      }
    }
  
    /**
     * Uses a slow swipe to simulate a scroll
     *
     * @return the view action
     */
    private ViewAction scroll() {
      return new GeneralSwipeAction(Swipe.SLOW, GeneralLocation.BOTTOM_CENTER,
          GeneralLocation.TOP_CENTER, Press.FINGER);
    }
  
    /**
     * Checks for a positive or negative translationX in a View
     *
     * @param positive true if positive translation, false if negative
     * @return matcher for checking positive/negative translationX
     */
    public static Matcher<View> checkTranslationX(final boolean positive) {
      return new BoundedMatcher<View, View>(View.class) {
  
        @Override public void describeTo(Description description) {
          description.appendText("translationX should be non-zero");
        }
  
        @Override protected boolean matchesSafely(View item) {
          if (positive) {
            return item.getTranslationX() > 0;
          } else {
            return item.getTranslationX() < 0;
          }
        }
      };
    }
  
    /**
     * Checks for zero translation of a view
     *
     * @return a matcher that checks that all translation on a view is zero
     */
    public static Matcher<View> checkZeroTranslation() {
      return new BoundedMatcher<View, View>(View.class) {
  
        @Override public void describeTo(Description description) {
          description.appendText("translation should be zero");
        }
  
        @Override protected boolean matchesSafely(View item) {
          return item.getTranslationX() == 0;
        }
      };
    }
  }
  