Skip to content

Commit

Permalink
Merge pull request #14 from alex-tiurin/scroll-offset
Browse files Browse the repository at this point in the history
Add scroll offset param for UltronRecyclerView item providers
  • Loading branch information
alex-tiurin authored Feb 20, 2022
2 parents 8c45831 + a0993a9 commit 45383c8
Show file tree
Hide file tree
Showing 9 changed files with 378 additions and 59 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import com.atiurin.ultron.page.Page
import com.atiurin.ultron.core.espresso.recyclerview.UltronRecyclerViewItem
import com.atiurin.ultron.core.espresso.recyclerview.withRecyclerView
import com.atiurin.sampleapp.R
import com.atiurin.sampleapp.data.entities.Contact
import com.atiurin.sampleapp.data.repositories.CONTACTS
import com.atiurin.sampleapp.framework.Log
import com.atiurin.sampleapp.framework.step
import com.atiurin.ultron.extensions.click
Expand Down Expand Up @@ -60,4 +62,6 @@ object FriendsListPage : Page<FriendsListPage>() {
fun assertFriendsListSize(size: Int) {
Assert.assertEquals(size, recycler.getSize())
}

fun getItemMatcher(contact: Contact) = hasDescendant(allOf(withId(R.id.tv_name), withText(containsString(contact.name))))
}
Original file line number Diff line number Diff line change
Expand Up @@ -391,4 +391,271 @@ class RecyclerViewTest : BaseTest() {
fun createHandlerFromUiTest(){
page.recycler.getItemAdapterPositionAtIndex(hasDescendant(allOf(withId(R.id.tv_name), withText(containsString(CONTACTS.last().name)))), 0)
}

//item+offset
@Test
fun item_scrollOffsetInItemCountRange_MatcherItem(){
val target = 5
val offset = 10
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.item(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed().click()
}

@Test
fun item_scrollOffsetInItemCountRangeBothAreVisible_MatcherItem(){
val target = 8
val offset = 2
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.item(page.getItemMatcher(targetContact), scrollOffset = offset).isDisplayed()
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed()
}

@Test
fun item_scrollOffsetOutOfItemCountRange_MatcherItem(){
val target = 5
val offset = CONTACTS.size
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS.last()
page.recycler.item(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed().click()
}

@Test
fun item_scrollOffsetLessThenZero_MatcherItem(){
val target = 8
val offset = -18
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS.first()
page.recycler.item(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed().click()
}

@Test
fun item_scrollOffsetInItemCountRange_positionItem(){
val target = 2
val offset = 12
val offsetContact = CONTACTS[target + offset]
page.recycler.item(target, scrollOffset = offset)
page.recycler.item(target + offset, autoScroll = false).isDisplayed()
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target + offset, autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun item_scrollOffsetInItemCountRangeBothAreVisible_positionItem(){
val target = 8
val offset = 2
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.item(target, scrollOffset = offset).isDisplayed()
page.recycler.item(target + offset, autoScroll = false).isDisplayed()
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target, autoScroll = false).name.hasText(targetContact.name).isDisplayed()
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target + offset, autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun item_scrollOffsetOutOfItemCountRange_positionItem(){
val target = 5
val offset = CONTACTS.size
val offsetContact = CONTACTS.last()
page.recycler.item(target, scrollOffset = offset)
page.recycler.lastItem(autoScroll = false).isDisplayed()
page.recycler.getLastItem<FriendsListPage.FriendRecyclerItem>(autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun item_scrollOffsetLessThenZero_positionItem(){
val target = 10
val offset = -18
val offsetContact = CONTACTS.first()
page.recycler.item(target, scrollOffset = offset)
page.recycler.getFirstItem<FriendsListPage.FriendRecyclerItem>(autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

//itemMatched+offset
@Test
fun itemMatched_scrollOffsetInItemCountRange_MatcherItem(){
val target = 5
val offset = 10
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.itemMatched(page.getItemMatcher(targetContact), 0, scrollOffset = offset)
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed().click()
}

@Test
fun itemMatched_scrollOffsetInItemCountRangeBothAreVisible_MatcherItem(){
val target = 8
val offset = 2
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.itemMatched(page.getItemMatcher(targetContact), 0, scrollOffset = offset).isDisplayed()
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed()
}

@Test
fun itemMatched_scrollOffsetOutOfItemCountRange_MatcherItem(){
val target = 5
val offset = CONTACTS.size
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS.last()
page.recycler.itemMatched(page.getItemMatcher(targetContact), 0, scrollOffset = offset)
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed().click()
}

//firstItemMatched+offset
@Test
fun firstItemMatched_scrollOffsetInItemCountRange_MatcherItem(){
val target = 5
val offset = 10
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.firstItemMatched(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed().click()
}

//lastItemMatched+offset
@Test
fun lastItemMatched_scrollOffsetInItemCountRange_MatcherItem(){
val target = 5
val offset = 10
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.lastItemMatched(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.item(page.getItemMatcher(offsetContact), autoScroll = false).isDisplayed().click()
}

//getItem+offset
@Test
fun getItem_scrollOffsetInItemCountRange_MatcherItem(){
val target = 5
val offset = 10
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItem_scrollOffsetInItemCountRangeBothAreVisible_MatcherItem(){
val target = 8
val offset = 2
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItem_scrollOffsetOutOfItemCountRange_MatcherItem(){
val target = 5
val offset = CONTACTS.size
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS.last()
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItem_scrollOffsetLessThenZero_MatcherItem(){
val target = 8
val offset = -18
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS.first()
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItem_scrollOffsetInItemCountRange_positionItem(){
val target = 2
val offset = 12
val offsetContact = CONTACTS[target + offset]
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target, scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target + offset, autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItem_scrollOffsetInItemCountRangeBothAreVisible_positionItem(){
val target = 8
val offset = 2
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target, scrollOffset = offset).name.hasText(targetContact.name).isDisplayed()
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target + offset, autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItem_scrollOffsetOutOfItemCountRange_positionItem(){
val target = 5
val offset = CONTACTS.size
val offsetContact = CONTACTS.last()
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target, scrollOffset = offset)
page.recycler.getLastItem<FriendsListPage.FriendRecyclerItem>(autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItem_scrollOffsetLessThenZero_positionItem(){
val target = 10
val offset = -18
val offsetContact = CONTACTS.first()
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(target, scrollOffset = offset)
page.recycler.getFirstItem<FriendsListPage.FriendRecyclerItem>(autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

//getItemMatched+offset
@Test
fun getItemMatched_scrollOffsetInItemCountRange_MatcherItem(){
val target = 5
val offset = 10
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.getItemMatched<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), 0, scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItemMatched_scrollOffsetInItemCountRangeBothAreVisible_MatcherItem(){
val target = 8
val offset = 2
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.getItemMatched<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), 0, scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

@Test
fun getItemMatched_scrollOffsetOutOfItemCountRange_MatcherItem(){
val target = 5
val offset = CONTACTS.size
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS.last()
page.recycler.getItemMatched<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), 0, scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

//getFirstItemMatched+offset
@Test
fun getFirstItemMatched_scrollOffsetInItemCountRange_MatcherItem(){
val target = 5
val offset = 10
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.getFirstItemMatched<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}

//getLastItemMatched+offset
@Test
fun getLastItemMatched_scrollOffsetInItemCountRange_MatcherItem(){
val target = 5
val offset = 10
val targetContact = CONTACTS[target]
val offsetContact = CONTACTS[target + offset]
page.recycler.getLastItemMatched<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(targetContact), scrollOffset = offset)
page.recycler.getItem<FriendsListPage.FriendRecyclerItem>(page.getItemMatcher(offsetContact), autoScroll = false).name.hasText(offsetContact.name).isDisplayed()
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.recyclerview.widget.RecyclerView
import org.hamcrest.Matcher

interface RecyclerViewItemExecutor {
fun scrollToItem()
fun scrollToItem(offset: Int = 0)
fun getItemMatcher(): Matcher<View>
fun getItemViewHolder(): RecyclerView.ViewHolder?
fun getItemChildMatcher(childMatcher: Matcher<View>): Matcher<View>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ class RecyclerViewItemMatchingExecutor(
private val ultronRecyclerView: UltronRecyclerView,
private val itemViewMatcher: Matcher<View>
) : RecyclerViewItemExecutor {
override fun scrollToItem() {
ultronRecyclerView.scrollToIem(itemViewMatcher)
override fun scrollToItem(offset: Int) {
ultronRecyclerView.scrollToIem(itemViewMatcher, offset = offset)
}

override fun getItemMatcher(): Matcher<View> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,18 @@ class RecyclerViewItemPositionalExecutor(
}
}

override fun scrollToItem() {
override fun scrollToItem(offset: Int) {
ultronRecyclerView.assertHasItemAtPosition(position)
val itemCount = ultronRecyclerView.getSize()
val positionToScroll = position + offset
val finalPositionToScroll = when {
positionToScroll in 1 until itemCount -> positionToScroll
positionToScroll >= itemCount -> itemCount - 1
else -> 0
}
ultronRecyclerView.recyclerViewMatcher.perform(
viewAction = RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(position),
description = "RecyclerViewActions scrollToPosition $position"
viewAction = RecyclerViewActions.scrollToPosition<RecyclerView.ViewHolder>(finalPositionToScroll),
description = "RecyclerViewActions scrollToPosition $position with offset = $offset"
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import com.atiurin.ultron.exceptions.UltronException
import org.hamcrest.Matcher
import org.hamcrest.Matchers.allOf

class RecyclerViewScrollAction(private val itemMatcher: Matcher<View>, private val itemSearchLimit: Int = -1) : ViewAction {
class RecyclerViewScrollAction(private val itemMatcher: Matcher<View>, private val itemSearchLimit: Int = -1, private val offset: Int = 0) : ViewAction {
override fun getConstraints(): Matcher<View> {
return allOf(
ViewMatchers.isAssignableFrom(RecyclerView::class.java),
Expand All @@ -18,14 +18,21 @@ class RecyclerViewScrollAction(private val itemMatcher: Matcher<View>, private v
}

override fun getDescription(): String {
return "Scroll RecyclerView to item $itemMatcher${if (itemSearchLimit >= 0) " with search limit = $itemSearchLimit" else ""}"
return "Scroll RecyclerView to item $itemMatcher${if (itemSearchLimit >= 0) " with offset = $offset and search limit = $itemSearchLimit" else ""}"
}

override fun perform(uiController: UiController, view: View) {
val recyclerView = view as RecyclerView
val viewHolderMatcher: Matcher<RecyclerView.ViewHolder> = viewHolderMatcher(itemMatcher)
val matchedItem = itemsMatching(recyclerView, viewHolderMatcher, 1, itemSearchLimit = itemSearchLimit).firstOrNull()
?: throw UltronException("No RecyclerView item found matches '$itemMatcher'${if (itemSearchLimit >= 0) " with search limit = $itemSearchLimit" else ""} ")
recyclerView.scrollToPosition(matchedItem.position)
val itemCount = recyclerView.adapter?.itemCount ?: 0
val positionToScroll = matchedItem.position + offset
val finalPositionToScroll = when {
positionToScroll in 1 until itemCount -> positionToScroll
positionToScroll >= itemCount -> itemCount - 1
else -> 0
}
recyclerView.scrollToPosition(finalPositionToScroll)
}
}
Loading

0 comments on commit 45383c8

Please sign in to comment.