diff --git a/library/src/main/java/com/adevinta/android/barista/assertion/BaristaListAssertions.kt b/library/src/main/java/com/adevinta/android/barista/assertion/BaristaListAssertions.kt index 0b63b06b..bcee354a 100644 --- a/library/src/main/java/com/adevinta/android/barista/assertion/BaristaListAssertions.kt +++ b/library/src/main/java/com/adevinta/android/barista/assertion/BaristaListAssertions.kt @@ -1,6 +1,9 @@ package com.adevinta.android.barista.assertion +import android.content.res.Resources +import android.graphics.Rect import android.view.View +import android.view.ViewGroup import android.widget.ListView import androidx.annotation.DrawableRes import androidx.annotation.IdRes @@ -74,18 +77,16 @@ object BaristaListAssertions { @JvmStatic fun assertDisplayedAtPosition(@IdRes listId: Int, position: Int, @IdRes targetViewId: Int = NO_VIEW_ID, text: String) { - scrollListToPosition(listId, position) - assertCustomAssertionAtPosition( - listId = listId, - position = position, - targetViewId = targetViewId, - viewAssertion = ViewAssertions.matches( - CoreMatchers.anyOf( - ViewMatchers.withChild(withCompatText(text)), - withCompatText(text) - ) + listId = listId, + position = position, + targetViewId = targetViewId, + viewAssertion = ViewAssertions.matches( + CoreMatchers.anyOf( + ViewMatchers.withChild(withCompatText(text)), + withCompatText(text) ) + ) ) } @@ -96,46 +97,58 @@ object BaristaListAssertions { @JvmStatic fun assertDisplayedAtPosition(@IdRes listId: Int, position: Int, @IdRes targetViewId: Int = NO_VIEW_ID, @StringRes textId: Int) { - scrollListToPosition(listId, position) - assertCustomAssertionAtPosition( - listId = listId, - position = position, - targetViewId = targetViewId, - viewAssertion = ViewAssertions.matches( - CoreMatchers.anyOf( - ViewMatchers.withChild(ViewMatchers.withText(textId)), - ViewMatchers.withText(textId) - ) + listId = listId, + position = position, + targetViewId = targetViewId, + viewAssertion = ViewAssertions.matches( + CoreMatchers.anyOf( + ViewMatchers.withChild(ViewMatchers.withText(textId)), + ViewMatchers.withText(textId) ) + ) ) } @JvmStatic - fun assertDrawableDisplayedAtPosition(@IdRes listId: Int, position: Int, @IdRes targetViewId: Int = NO_VIEW_ID, @DrawableRes drawableRes: Int) { + fun assertDrawableDisplayedAtPosition( + @IdRes listId: Int, + position: Int, + @IdRes targetViewId: Int = NO_VIEW_ID, + @DrawableRes drawableRes: Int + ) { scrollListToPosition(listId, position) assertCustomAssertionAtPosition( - listId = listId, - position = position, - targetViewId = targetViewId, - viewAssertion = ViewAssertions.matches( - CoreMatchers.anyOf( - ViewMatchers.hasDescendant(DrawableMatcher.withDrawable(drawableRes)), - DrawableMatcher.withDrawable(drawableRes) - ) + listId = listId, + position = position, + targetViewId = targetViewId, + viewAssertion = ViewAssertions.matches( + CoreMatchers.anyOf( + ViewMatchers.hasDescendant(DrawableMatcher.withDrawable(drawableRes)), + DrawableMatcher.withDrawable(drawableRes) ) + ) ) } @JvmStatic - fun assertCustomAssertionAtPosition(@IdRes listId: Int, position: Int, @IdRes targetViewId: Int = NO_VIEW_ID, viewAssertion: ViewAssertion) { + fun assertCustomAssertionAtPosition( + @IdRes listId: Int, + position: Int, + @IdRes targetViewId: Int = NO_VIEW_ID, + viewAssertion: ViewAssertion + ) { scrollListToPosition(listId, position) - Espresso.onView(atPositionOnList(listId = listId, + Espresso.onView( + atPositionOnList( + listId = listId, position = position, - targetViewId = targetViewId)) - .check(viewAssertion) + targetViewId = targetViewId + ) + ) + .check(viewAssertion) } private fun atPositionOnList(@IdRes listId: Int, position: Int, @IdRes targetViewId: Int): Matcher { @@ -162,13 +175,18 @@ object BaristaListAssertions { var childView: View? = null if (childView == null) { - val listView: ListView? = view.rootView.findViewById(listViewId) as ListView - if (listView != null && listView.id == listViewId) { - val positionOnScreen = position - listView.firstVisiblePosition - val viewAtPosition = listView.getChildAt(positionOnScreen) + val views = getShownViewsById(view.rootView as ViewGroup, listViewId) + if (views != null && views.isNotEmpty()) { + val listView: ListView = views[0] as ListView + if (listView.id == listViewId) { + val positionOnScreen = position - listView.firstVisiblePosition + val viewAtPosition = listView.getChildAt(positionOnScreen) - viewAtPosition?.let { - childView = it + viewAtPosition?.let { + childView = it + } + } else { + return false } } else { return false @@ -187,11 +205,16 @@ object BaristaListAssertions { var childView: View? = null if (childView == null) { - val recyclerView: RecyclerView? = view.rootView.findViewById(recyclerViewId) as RecyclerView - if (recyclerView != null && recyclerView.id == recyclerViewId) { - val viewHolder = recyclerView.findViewHolderForAdapterPosition(position) - viewHolder?.let { checkedViewHolder -> - childView = checkedViewHolder.itemView + val views = getShownViewsById(view.rootView as ViewGroup, recyclerViewId) + if (views != null && views.isNotEmpty()) { + val recyclerView: RecyclerView = views[0] as RecyclerView + if (recyclerView.id == recyclerViewId) { + val viewHolder = recyclerView.findViewHolderForAdapterPosition(position) + viewHolder?.let { checkedViewHolder -> + childView = checkedViewHolder.itemView + } + } else { + return false } } else { return false @@ -205,4 +228,31 @@ object BaristaListAssertions { view == targetView } } + + private fun getShownViewsById(root: ViewGroup, viewId: Int): ArrayList? { + val views = ArrayList() + val childCount = root.childCount + for (i in 0 until childCount) { + val child = root.getChildAt(i) + if (child is ViewGroup) { + views.addAll(getShownViewsById(child, viewId)!!) + } + val childId = child.id + if (childId == viewId && isShowOnScreen(child)) { + views.add(child) + } + } + return views + } + + private fun isShowOnScreen(view: View): Boolean { + if (!view.isShown) { + return false + } + val actualPosition = Rect().also { view.getGlobalVisibleRect(it) } + val screen = Resources.getSystem().displayMetrics.run { + Rect(0, 0, widthPixels, heightPixels) + } + return actualPosition.intersect(screen) + } } \ No newline at end of file diff --git a/sample/src/androidTest/java/com/adevinta/android/barista/sample/ListViewWithDifferentDataInsideViewPagerTest.java b/sample/src/androidTest/java/com/adevinta/android/barista/sample/ListViewWithDifferentDataInsideViewPagerTest.java new file mode 100644 index 00000000..3eb02284 --- /dev/null +++ b/sample/src/androidTest/java/com/adevinta/android/barista/sample/ListViewWithDifferentDataInsideViewPagerTest.java @@ -0,0 +1,33 @@ +package com.adevinta.android.barista.sample; + +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static com.adevinta.android.barista.assertion.BaristaListAssertions.assertDisplayedAtPosition; +import static com.adevinta.android.barista.interaction.BaristaSleepInteractions.sleep; +import static com.adevinta.android.barista.interaction.BaristaViewPagerInteractions.swipeViewPagerBack; +import static com.adevinta.android.barista.interaction.BaristaViewPagerInteractions.swipeViewPagerForward; + +@RunWith(AndroidJUnit4.class) +public class ListViewWithDifferentDataInsideViewPagerTest { + + @Rule + public ActivityTestRule activityRule = + new ActivityTestRule<>(ListViewsWithDifferentDataInsideViewPagerActivity.class); + + @Test + public void checkClickRecyclerViewItem() { + swipeViewPagerForward(R.id.pager); + sleep(500); + + assertDisplayedAtPosition(R.id.listview, 1, "Marionberry"); + + swipeViewPagerBack(R.id.pager); + sleep(500); + + assertDisplayedAtPosition(R.id.listview, 0, android.R.id.text1, "Apple"); + } +} \ No newline at end of file diff --git a/sample/src/androidTest/java/com/adevinta/android/barista/sample/RecyclerViewWithDifferentDataInsideViewPagerTest.java b/sample/src/androidTest/java/com/adevinta/android/barista/sample/RecyclerViewWithDifferentDataInsideViewPagerTest.java new file mode 100644 index 00000000..39efadb4 --- /dev/null +++ b/sample/src/androidTest/java/com/adevinta/android/barista/sample/RecyclerViewWithDifferentDataInsideViewPagerTest.java @@ -0,0 +1,34 @@ +package com.adevinta.android.barista.sample; + +import androidx.test.rule.ActivityTestRule; +import androidx.test.runner.AndroidJUnit4; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static com.adevinta.android.barista.assertion.BaristaListAssertions.assertDisplayedAtPosition; +import static com.adevinta.android.barista.interaction.BaristaSleepInteractions.sleep; +import static com.adevinta.android.barista.interaction.BaristaViewPagerInteractions.swipeViewPagerBack; +import static com.adevinta.android.barista.interaction.BaristaViewPagerInteractions.swipeViewPagerForward; + +@RunWith(AndroidJUnit4.class) +public class RecyclerViewWithDifferentDataInsideViewPagerTest { + + @Rule + public ActivityTestRule activityRule = + new ActivityTestRule<>(RecyclerViewsWithDifferentDataInsideViewPagerActivity.class); + + @Test + public void checkClickRecyclerViewItem() { + swipeViewPagerForward(R.id.pager); + sleep(500); + + assertDisplayedAtPosition(R.id.recycler, 1, "Marionberry"); + + swipeViewPagerBack(R.id.pager); + sleep(500); + + assertDisplayedAtPosition(R.id.recycler, 0, R.id.textview, "Apple"); + } +} \ No newline at end of file diff --git a/sample/src/main/AndroidManifest.xml b/sample/src/main/AndroidManifest.xml index 6e97b064..54c04898 100644 --- a/sample/src/main/AndroidManifest.xml +++ b/sample/src/main/AndroidManifest.xml @@ -48,6 +48,8 @@ android:theme="@style/AppTheme.NoActionBar" /> + + diff --git a/sample/src/main/java/com/adevinta/android/barista/sample/ListViewsWithDifferentDataInsideViewPagerActivity.java b/sample/src/main/java/com/adevinta/android/barista/sample/ListViewsWithDifferentDataInsideViewPagerActivity.java new file mode 100644 index 00000000..c62c6577 --- /dev/null +++ b/sample/src/main/java/com/adevinta/android/barista/sample/ListViewsWithDifferentDataInsideViewPagerActivity.java @@ -0,0 +1,95 @@ +package com.adevinta.android.barista.sample; + +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.ListView; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +public class ListViewsWithDifferentDataInsideViewPagerActivity extends AppCompatActivity { + + private static final int NUM_PAGES = 2; + + private static final String[] FRUIT_LIST_ONE = { + "Apple", "Apricot", "Avocado", "Banana", "Bilberry", "Blackberry", "Blackcurrant", + "Blueberry", "Boysenberry", "Currant", "Cherry", "Cherimoya", "Cloudberry", "Coconut", + "Cranberry", "Cucumber", "Custardapple", "Damson", "Date", "Dragonfruit", "Durian", + "Elderberry", "Feijoa", "Fig", "Gojiberry", "Gooseberry", "Grape", "Raisin", "Grapefruit", + "Guava", "Honeyberry", "Huckleberry", "Jabuticaba", "Jackfruit", "Jambul", "Jujube", + "Juniperberry", "Kiwifruit", "Kumquat", "Lemon", "Lime", "Loquat", "Longan", "Lychee"}; + + private static final String[] FRUIT_LIST_TWO = {"Mango", "Marionberry", "Melon", "Cantaloupe", "Honeydew", "Watermelon", "Miraclefruit", + "Mulberry", "Nectarine", "Nance", "Olive", "Orange", "Bloodorange", "Clementine", "Mandarine", + "Tangerine", "Papaya", "Passionfruit", "Peach", "Pear", "Persimmon", "Physalis", "Plantain", + "Plum", "Prune(driedplum)", "Pineapple", "Plumcot(orPluot)", "Pomegranate", "Pomelo", + "Purplemangosteen", "Quince", "Raspberry", "Salmonberry", "Rambutan", "Redcurrant", + "Salalberry", "Salak", "Satsuma", "Starfruit", "Solanumquitoense", "Strawberry", "Tamarillo", + "Tamarind", "Uglifruit", "Yuzu"}; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_viewpager); + + ViewPager mPager = findViewById(R.id.pager); + PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); + mPager.setAdapter(mPagerAdapter); + } + + private static class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { + ScreenSlidePagerAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public Fragment getItem(int position) { + return new ScreenSlidePageFragment(position); + } + + @Override + public int getCount() { + return NUM_PAGES; + } + } + + public static class ScreenSlidePageFragment extends Fragment { + + private int position; + + public ScreenSlidePageFragment(int position) { + this.position = position; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.activity_lists, container, false); + + LinearLayout listsContainer = rootView.findViewById(R.id.multi_list_container); + + ListView listView = new ListView(requireContext()); + if (position == 0) { + listView.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, FRUIT_LIST_ONE)); + } else { + listView.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, FRUIT_LIST_TWO)); + } + listView.setId(R.id.listview); + listsContainer.addView(listView, + new LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.MATCH_PARENT, + 1 + )); + + return rootView; + } + } +} \ No newline at end of file diff --git a/sample/src/main/java/com/adevinta/android/barista/sample/RecyclerViewsWithDifferentDataInsideViewPagerActivity.java b/sample/src/main/java/com/adevinta/android/barista/sample/RecyclerViewsWithDifferentDataInsideViewPagerActivity.java new file mode 100644 index 00000000..9911a140 --- /dev/null +++ b/sample/src/main/java/com/adevinta/android/barista/sample/RecyclerViewsWithDifferentDataInsideViewPagerActivity.java @@ -0,0 +1,150 @@ +package com.adevinta.android.barista.sample; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.TextView; + +import androidx.appcompat.app.AppCompatActivity; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; + +public class RecyclerViewsWithDifferentDataInsideViewPagerActivity extends AppCompatActivity { + + private static final int NUM_PAGES = 2; + + private static final String[] FRUIT_LIST_ONE = { + "Apple", "Apricot", "Avocado", "Banana", "Bilberry", "Blackberry", "Blackcurrant", + "Blueberry", "Boysenberry", "Currant", "Cherry", "Cherimoya", "Cloudberry", "Coconut", + "Cranberry", "Cucumber", "Custardapple", "Damson", "Date", "Dragonfruit", "Durian", + "Elderberry", "Feijoa", "Fig", "Gojiberry", "Gooseberry", "Grape", "Raisin", "Grapefruit", + "Guava", "Honeyberry", "Huckleberry", "Jabuticaba", "Jackfruit", "Jambul", "Jujube", + "Juniperberry", "Kiwifruit", "Kumquat", "Lemon", "Lime", "Loquat", "Longan", "Lychee"}; + + private static final String[] FRUIT_LIST_TWO = {"Mango", "Marionberry", "Melon", "Cantaloupe", "Honeydew", "Watermelon", "Miraclefruit", + "Mulberry", "Nectarine", "Nance", "Olive", "Orange", "Bloodorange", "Clementine", "Mandarine", + "Tangerine", "Papaya", "Passionfruit", "Peach", "Pear", "Persimmon", "Physalis", "Plantain", + "Plum", "Prune(driedplum)", "Pineapple", "Plumcot(orPluot)", "Pomegranate", "Pomelo", + "Purplemangosteen", "Quince", "Raspberry", "Salmonberry", "Rambutan", "Redcurrant", + "Salalberry", "Salak", "Satsuma", "Starfruit", "Solanumquitoense", "Strawberry", "Tamarillo", + "Tamarind", "Uglifruit", "Yuzu"}; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_viewpager); + + ViewPager mPager = findViewById(R.id.pager); + PagerAdapter mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager()); + mPager.setAdapter(mPagerAdapter); + } + + private static class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter { + ScreenSlidePagerAdapter(FragmentManager fm) { + super(fm); + } + + @Override + public Fragment getItem(int position) { + return new ScreenSlidePageFragment(position); + } + + @Override + public int getCount() { + return NUM_PAGES; + } + } + + public static class ScreenSlidePageFragment extends Fragment { + + private int position; + + public ScreenSlidePageFragment(int position) { + this.position = position; + } + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + ViewGroup rootView = + (ViewGroup) inflater.inflate(R.layout.activity_recyclerview, container, false); + + RecyclerView recyclerView = rootView.findViewById(R.id.recycler); + recyclerView.setHasFixedSize(true); + + LinearLayoutManager mLayoutManager = new LinearLayoutManager(getActivity()); + recyclerView.setLayoutManager(mLayoutManager); + if (position == 0) { + recyclerView.setAdapter(new TextAdapter(getActivity(), FRUIT_LIST_ONE)); + } else { + recyclerView.setAdapter(new TextAdapter(getActivity(), FRUIT_LIST_TWO)); + } + + return rootView; + } + } + + static class TextAdapter extends RecyclerView.Adapter { + private final Activity activity; + private final String[] items; + + TextAdapter(Activity activity, String[] myDataset) { + this.activity = activity; + items = myDataset.clone(); + } + + public TextAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + View root = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.row_with_image_and_buttons, parent, false); + TextView textView = root.findViewById(R.id.textview); + View yesButton = root.findViewById(R.id.yes); + View noButton = root.findViewById(R.id.no); + return new ViewHolder(root, textView, yesButton, noButton); + } + + public void onBindViewHolder(final ViewHolder holder, int position) { + holder.textView.setText(items[position]); + holder.itemView.setOnClickListener(view -> { + Intent i = new Intent(activity, LabelActivity.class); + i.putExtra(LabelActivity.EXTRA_TEXT, + holder.textView.getText().toString() + " has been clicked"); + activity.startActivity(i); + }); + holder.yesButton.setOnClickListener(view -> { + Intent i = new Intent(activity, LabelActivity.class); + i.putExtra(LabelActivity.EXTRA_TEXT, "'yes' has been clicked"); + activity.startActivity(i); + }); + holder.noButton.setOnClickListener(view -> { + Intent i = new Intent(activity, LabelActivity.class); + i.putExtra(LabelActivity.EXTRA_TEXT, "'no' has been clicked"); + activity.startActivity(i); + }); + } + + public int getItemCount() { + return items.length; + } + + static class ViewHolder extends RecyclerView.ViewHolder { + TextView textView; + View yesButton; + View noButton; + + ViewHolder(View root, TextView textView, View yesButton, View noButton) { + super(root); + this.textView = textView; + this.yesButton = yesButton; + this.noButton = noButton; + } + } + } +} \ No newline at end of file