Skip to content

Commit

Permalink
Add UI tests for DpadDragHelper
Browse files Browse the repository at this point in the history
  • Loading branch information
rubensousa committed Jun 15, 2024
1 parent c3929d3 commit d7eedf7
Show file tree
Hide file tree
Showing 6 changed files with 621 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ class RecyclerViewFragment : Fragment(R.layout.dpadrecyclerview_test_container)

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<DpadRecyclerView>(R.id.recyclerView).requestFocus()
getRecyclerView().requestFocus()
}

fun getRecyclerView(): DpadRecyclerView {
return requireView().findViewById(R.id.recyclerView)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -76,4 +76,27 @@ class TestAdapter(

}

fun assertContents(predicate: (index: Int) -> Int) {
for (i in 0 until itemCount) {
val item = getItem(i)
val expectedItem = predicate(i)
if (expectedItem != item) {
throw AssertionError("Expected item $expectedItem at position $i but got $item instead. Adapter contents: ${getAdapterContentString()}")
}
}
}

private fun getAdapterContentString(): String {
val builder = StringBuilder()
builder.append("[")
for (i in 0 until itemCount) {
builder.append(getItem(i))
if (i < itemCount - 1) {
builder.append(", ")
}
}
builder.append("]")
return builder.toString()
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,14 @@ import android.os.Looper
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.DiffUtil.DiffResult
import androidx.recyclerview.widget.RecyclerView
import com.rubensousa.dpadrecyclerview.DpadDragHelper
import com.rubensousa.dpadrecyclerview.test.TestAdapterConfiguration
import java.util.Collections
import java.util.concurrent.Executors

abstract class AbstractTestAdapter<VH : RecyclerView.ViewHolder>(
adapterConfiguration: TestAdapterConfiguration
) : RecyclerView.Adapter<VH>() {
) : RecyclerView.Adapter<VH>(), DpadDragHelper.DragAdapter<Int> {

companion object {
private val EMPTY_LIST = ArrayList<Int>(0)
Expand All @@ -43,6 +44,8 @@ abstract class AbstractTestAdapter<VH : RecyclerView.ViewHolder>(
private var currentVersion = 0
private var id = items.size

override fun getMutableItems(): MutableList<Int> = items

fun submitList(newList: MutableList<Int>, commitCallback: Runnable? = null) {
val version = ++currentVersion
if (items === newList) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
/*
* Copyright 2024 Rúben Sousa
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.rubensousa.dpadrecyclerview.test.tests.drag

import androidx.fragment.app.testing.FragmentScenario
import androidx.fragment.app.testing.launchFragmentInContainer
import androidx.recyclerview.widget.RecyclerView
import androidx.test.platform.app.InstrumentationRegistry
import com.rubensousa.dpadrecyclerview.DpadDragHelper
import com.rubensousa.dpadrecyclerview.spacing.DpadGridSpacingDecoration
import com.rubensousa.dpadrecyclerview.test.RecyclerViewFragment
import com.rubensousa.dpadrecyclerview.test.TestAdapter
import com.rubensousa.dpadrecyclerview.test.TestAdapterConfiguration
import com.rubensousa.dpadrecyclerview.test.helpers.assertFocusAndSelection
import com.rubensousa.dpadrecyclerview.test.helpers.onRecyclerView
import com.rubensousa.dpadrecyclerview.test.helpers.waitForCondition
import com.rubensousa.dpadrecyclerview.testing.KeyEvents
import com.rubensousa.dpadrecyclerview.testing.R
import org.junit.Before
import org.junit.Test

class DragHelperGridTest {


private lateinit var fragmentScenario: FragmentScenario<RecyclerViewFragment>
private lateinit var dragHelper: DpadDragHelper<Int>
private lateinit var testAdapter: TestAdapter
private val numberOfItems = 50
private val spanCount = 5
private val dragStarted = mutableListOf<RecyclerView.ViewHolder>()
private var dragStopCount = 0

@Before
fun setup() {
fragmentScenario = launchFragment()
onRecyclerView("Setup RecyclerView ") { recyclerView ->
testAdapter = TestAdapter(
adapterConfiguration = TestAdapterConfiguration(
itemLayoutId = R.layout.dpadrecyclerview_test_item_grid,
numberOfItems = numberOfItems
),
onViewHolderSelected = {

},
onViewHolderDeselected = {

}
)
recyclerView.apply {
adapter = testAdapter
setSpanCount(spanCount)
addItemDecoration(
DpadGridSpacingDecoration.create(
itemSpacing = resources.getDimensionPixelSize(
com.rubensousa.dpadrecyclerview.test.R.dimen.dpadrecyclerview_grid_spacing
)
)
)
}
dragHelper = DpadDragHelper(
adapter = testAdapter,
callback = object : DpadDragHelper.DragCallback {
override fun onDragStarted(viewHolder: RecyclerView.ViewHolder) {
dragStarted.add(viewHolder)
}

override fun onDragStopped() {
dragStopCount++
}
}
)
dragHelper.attachToRecyclerView(recyclerView)
}
}

@Test
fun testDragStartRowToEndRow() {
// given
val endRowPosition = spanCount - 1
startDragging(position = 0)

// when
KeyEvents.pressRight(times = spanCount)

// then
assertFocusAndSelection(position = endRowPosition)
testAdapter.assertContents { index ->
when {
index < spanCount - 1 -> index + 1
index == spanCount - 1 -> 0
else -> index
}
}
}

@Test
fun testDragEndRowToStartRow() {
// given
val endRowPosition = spanCount - 1
startDragging(position = endRowPosition)

// when
KeyEvents.pressLeft(times = spanCount)

// then
assertFocusAndSelection(position = 0)
testAdapter.assertContents { index ->
when {
index == 0 -> endRowPosition
index < spanCount -> index - 1
else -> index
}
}
}

@Test
fun testDragColumnTopToColumnBottom() {
// given
val topColumnPosition = spanCount - 1
val bottomColumnPosition = topColumnPosition + spanCount
startDragging(position = topColumnPosition)

// when
KeyEvents.pressDown(times = 1)

// then
assertFocusAndSelection(position = bottomColumnPosition)
testAdapter.assertContents { index ->
when {
index < topColumnPosition -> index
index < bottomColumnPosition -> index + 1
index == bottomColumnPosition -> topColumnPosition
else -> index
}
}
}

@Test
fun testDragColumnBottomToColumnTop() {
// given
val topColumnPosition = spanCount - 1
val bottomColumnPosition = topColumnPosition + spanCount
startDragging(position = bottomColumnPosition)

// when
KeyEvents.pressUp(times = 1)

// then
assertFocusAndSelection(position = bottomColumnPosition)
testAdapter.assertContents { index ->
when {
index < topColumnPosition -> index
index == topColumnPosition -> bottomColumnPosition
index < bottomColumnPosition -> index - 1
index == bottomColumnPosition -> topColumnPosition
else -> index
}
}
}

@Test
fun testDragStartToEnd() {
// given
startDragging(position = 0)

// when
KeyEvents.pressDown(times = numberOfItems / spanCount)
KeyEvents.pressRight(times = spanCount)

// then
assertFocusAndSelection(position = numberOfItems - 1)
testAdapter.assertContents { index ->
when {
index < numberOfItems - 1 -> index + 1
else -> 0
}
}
}

@Test
fun testDragEndToStart() {
// given
startDragging(position = numberOfItems - 1)

// when
KeyEvents.pressUp(times = numberOfItems / spanCount)
KeyEvents.pressLeft(times = spanCount)

// then
assertFocusAndSelection(position = 0)
testAdapter.assertContents { index ->
when {
index == 0 -> numberOfItems - 1
else -> index - 1
}
}
}

private fun startDragging(position: Int) {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragHelper.startDrag(position)
}
}

private fun stopDragging() {
InstrumentationRegistry.getInstrumentation().runOnMainSync {
dragHelper.stopDrag()
}
}

private fun launchFragment(): FragmentScenario<RecyclerViewFragment> {
return launchFragmentInContainer<RecyclerViewFragment>(
themeResId = R.style.DpadRecyclerViewTestTheme
).also {
fragmentScenario = it
waitForCondition("Waiting for layout pass") { recyclerView ->
!recyclerView.isLayoutRequested
}
}
}

}
Loading

0 comments on commit d7eedf7

Please sign in to comment.