1
0
Fork 0

Add tests for adapters in collections (#12649)

master
Tiger Oakes 2020-07-17 09:04:16 -07:00 committed by GitHub
parent 074b013916
commit ac3df6bc5e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 407 additions and 54 deletions

View File

@ -11,22 +11,24 @@ import androidx.core.view.isGone
import androidx.core.view.isInvisible
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.collection_tab_list_row.view.*
import kotlinx.android.synthetic.main.collection_tab_list_row.*
import org.mozilla.fenix.R
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.ext.loadIntoView
import org.mozilla.fenix.home.Tab
import org.mozilla.fenix.utils.view.ViewHolder
class CollectionCreationTabListAdapter(
private val interactor: CollectionCreationInteractor
) : RecyclerView.Adapter<TabViewHolder>() {
private var tabs: List<Tab> = listOf()
private var selectedTabs: MutableSet<Tab> = mutableSetOf()
private var hideCheckboxes = false
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TabViewHolder {
val view =
LayoutInflater.from(parent.context).inflate(TabViewHolder.LAYOUT_ID, parent, false)
val view = LayoutInflater.from(parent.context)
.inflate(TabViewHolder.LAYOUT_ID, parent, false)
return TabViewHolder(view)
}
@ -39,11 +41,11 @@ class CollectionCreationTabListAdapter(
is CheckChanged -> {
val checkChanged = payloads[0] as CheckChanged
if (checkChanged.shouldBeChecked) {
holder.itemView.tab_selected_checkbox.isChecked = true
holder.tab_selected_checkbox.isChecked = true
} else if (checkChanged.shouldBeUnchecked) {
holder.itemView.tab_selected_checkbox.isChecked = false
holder.tab_selected_checkbox.isChecked = false
}
holder.itemView.tab_selected_checkbox.isGone = checkChanged.shouldHideCheckBox
holder.tab_selected_checkbox.isGone = checkChanged.shouldHideCheckBox
}
}
}
@ -52,7 +54,7 @@ class CollectionCreationTabListAdapter(
override fun onBindViewHolder(holder: TabViewHolder, position: Int) {
val tab = tabs[position]
val isSelected = selectedTabs.contains(tab)
holder.itemView.tab_selected_checkbox.setOnCheckedChangeListener { _, isChecked ->
holder.tab_selected_checkbox.setOnCheckedChangeListener { _, isChecked ->
if (isChecked) {
selectedTabs.add(tab)
interactor.addTabToSelection(tab)
@ -86,57 +88,24 @@ class CollectionCreationTabListAdapter(
}
}
private class TabDiffUtil(
val old: List<Tab>,
val new: List<Tab>,
val oldSelected: Set<Tab>,
val newSelected: Set<Tab>,
val oldHideCheckboxes: Boolean,
val newHideCheckboxes: Boolean
) : DiffUtil.Callback() {
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
old[oldItemPosition].sessionId == new[newItemPosition].sessionId
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val isSameTab = old[oldItemPosition].sessionId == new[newItemPosition].sessionId
val sameSelectedState = oldSelected.contains(old[oldItemPosition]) == newSelected.contains(new[newItemPosition])
val isSameHideCheckboxes = oldHideCheckboxes == newHideCheckboxes
return isSameTab && sameSelectedState && isSameHideCheckboxes
}
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val shouldBeChecked = newSelected.contains(new[newItemPosition]) && !oldSelected.contains(old[oldItemPosition])
val shouldBeUnchecked =
!newSelected.contains(new[newItemPosition]) && oldSelected.contains(old[oldItemPosition])
return CheckChanged(shouldBeChecked, shouldBeUnchecked, newHideCheckboxes)
}
override fun getOldListSize(): Int = old.size
override fun getNewListSize(): Int = new.size
}
data class CheckChanged(val shouldBeChecked: Boolean, val shouldBeUnchecked: Boolean, val shouldHideCheckBox: Boolean)
class TabViewHolder(view: View) : RecyclerView.ViewHolder(view) {
private val checkbox = view.tab_selected_checkbox!!
class TabViewHolder(view: View) : ViewHolder(view) {
init {
view.collection_item_tab.setOnClickListener {
checkbox.isChecked = !checkbox.isChecked
collection_item_tab.setOnClickListener {
tab_selected_checkbox.isChecked = !tab_selected_checkbox.isChecked
}
}
fun bind(tab: Tab, isSelected: Boolean, shouldHideCheckBox: Boolean) {
itemView.hostname.text = tab.hostname
itemView.tab_title.text = tab.title
checkbox.isInvisible = shouldHideCheckBox
hostname.text = tab.hostname
tab_title.text = tab.title
tab_selected_checkbox.isInvisible = shouldHideCheckBox
itemView.isClickable = !shouldHideCheckBox
if (checkbox.isChecked != isSelected) {
checkbox.isChecked = isSelected
if (tab_selected_checkbox.isChecked != isSelected) {
tab_selected_checkbox.isChecked = isSelected
}
itemView.context.components.core.icons.loadIntoView(itemView.favicon_image, tab.url)
itemView.context.components.core.icons.loadIntoView(favicon_image, tab.url)
}
companion object {

View File

@ -10,12 +10,13 @@ import android.view.ViewGroup
import androidx.core.graphics.BlendModeColorFilterCompat.createBlendModeColorFilterCompat
import androidx.core.graphics.BlendModeCompat.SRC_IN
import androidx.recyclerview.widget.RecyclerView
import kotlinx.android.synthetic.main.collections_list_item.view.*
import kotlinx.android.synthetic.main.collections_list_item.*
import mozilla.components.feature.tab.collections.TabCollection
import org.mozilla.fenix.R
import org.mozilla.fenix.components.description
import org.mozilla.fenix.ext.getIconColor
import org.mozilla.fenix.home.Tab
import org.mozilla.fenix.utils.view.ViewHolder
class SaveCollectionListAdapter(
private val interactor: CollectionCreationInteractor
@ -48,12 +49,12 @@ class SaveCollectionListAdapter(
}
}
class CollectionViewHolder(view: View) : RecyclerView.ViewHolder(view) {
class CollectionViewHolder(view: View) : ViewHolder(view) {
fun bind(collection: TabCollection) {
itemView.collection_item.text = collection.title
itemView.collection_description.text = collection.description(itemView.context)
itemView.collection_icon.colorFilter =
collection_item.text = collection.title
collection_description.text = collection.description(itemView.context)
collection_icon.colorFilter =
createBlendModeColorFilterCompat(collection.getIconColor(itemView.context), SRC_IN)
}

View File

@ -0,0 +1,63 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.collections
import androidx.recyclerview.widget.DiffUtil
import org.mozilla.fenix.home.Tab
/**
* Diff callback for comparing tab lists with selected state.
*/
internal class TabDiffUtil(
private val old: List<Tab>,
private val new: List<Tab>,
private val oldSelected: Set<Tab>,
private val newSelected: Set<Tab>,
private val oldHideCheckboxes: Boolean,
private val newHideCheckboxes: Boolean
) : DiffUtil.Callback() {
/**
* Checks if the tabs in the given positions refer to the same tab (based on ID).
*/
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean =
old[oldItemPosition].sessionId == new[newItemPosition].sessionId
/**
* Checks if the combination of tab ID, selection, and checkbox visibility is the same.
*/
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
val isSameTab = old[oldItemPosition].sessionId == new[newItemPosition].sessionId
val sameSelectedState = oldItemSelected(oldItemPosition) == newItemSelected(newItemPosition)
val isSameHideCheckboxes = oldHideCheckboxes == newHideCheckboxes
return isSameTab && sameSelectedState && isSameHideCheckboxes
}
/**
* Returns a change payload indication if the item is now/no longer selected.
*/
override fun getChangePayload(oldItemPosition: Int, newItemPosition: Int): Any? {
val shouldBeChecked = newItemSelected(newItemPosition) && !oldItemSelected(oldItemPosition)
val shouldBeUnchecked = !newItemSelected(newItemPosition) && oldItemSelected(oldItemPosition)
return CheckChanged(shouldBeChecked, shouldBeUnchecked, newHideCheckboxes)
}
override fun getOldListSize(): Int = old.size
override fun getNewListSize(): Int = new.size
private fun oldItemSelected(oldItemPosition: Int) = oldSelected.contains(old[oldItemPosition])
private fun newItemSelected(newItemPosition: Int) = newSelected.contains(new[newItemPosition])
}
/**
* @property shouldBeChecked Item was previously unchecked and should be checked.
* @property shouldBeUnchecked Item was previously checked and should be unchecked.
* @property shouldHideCheckBox Checkbox should be visible.
*/
data class CheckChanged(
val shouldBeChecked: Boolean,
val shouldBeUnchecked: Boolean,
val shouldHideCheckBox: Boolean
)

View File

@ -0,0 +1,89 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.collections
import android.widget.FrameLayout
import androidx.core.view.isInvisible
import androidx.recyclerview.widget.RecyclerView
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.collection_tab_list_row.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.Tab
@RunWith(FenixRobolectricTestRunner::class)
class CollectionCreationTabListAdapterTest {
private lateinit var interactor: CollectionCreationInteractor
private lateinit var adapter: CollectionCreationTabListAdapter
@Before
fun setup() {
interactor = mockk()
adapter = CollectionCreationTabListAdapter(interactor)
every { interactor.selectCollection(any(), any()) } just Runs
}
@Test
fun `getItemCount should return the number of tab collections`() {
val tab = mockk<Tab>()
assertEquals(0, adapter.itemCount)
adapter.updateData(
tabs = listOf(tab),
selectedTabs = emptySet()
)
assertEquals(1, adapter.itemCount)
}
@Test
fun `creates and binds viewholder`() {
val tab = mockk<Tab> {
every { sessionId } returns "abc"
every { title } returns "Mozilla"
every { hostname } returns "mozilla.org"
every { url } returns "https://mozilla.org"
}
adapter.updateData(
tabs = listOf(tab),
selectedTabs = emptySet()
)
val holder = adapter.createViewHolder(FrameLayout(testContext), 0)
adapter.bindViewHolder(holder, 0)
assertEquals("Mozilla", holder.tab_title.text)
assertEquals("mozilla.org", holder.hostname.text)
assertFalse(holder.tab_selected_checkbox.isInvisible)
assertTrue(holder.itemView.isClickable)
}
@Test
fun `updateData inserts item`() {
val tab = mockk<Tab> {
every { sessionId } returns "abc"
}
val observer = mockk<RecyclerView.AdapterDataObserver>(relaxed = true)
adapter.registerAdapterDataObserver(observer)
adapter.updateData(
tabs = listOf(tab),
selectedTabs = emptySet()
)
verify { observer.onItemRangeInserted(0, 1) }
}
}

View File

@ -0,0 +1,80 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.collections
import android.view.ViewGroup
import android.widget.FrameLayout
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.collections_list_item.view.*
import mozilla.components.feature.tab.collections.TabCollection
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class SaveCollectionListAdapterTest {
private lateinit var parent: ViewGroup
private lateinit var interactor: CollectionCreationInteractor
private lateinit var adapter: SaveCollectionListAdapter
@Before
fun setup() {
parent = FrameLayout(testContext)
interactor = mockk()
adapter = SaveCollectionListAdapter(interactor)
every { interactor.selectCollection(any(), any()) } just Runs
}
@Test
fun `getItemCount should return the number of tab collections`() {
val collection = mockk<TabCollection>()
assertEquals(0, adapter.itemCount)
adapter.updateData(
tabCollections = listOf(collection),
selectedTabs = emptySet()
)
assertEquals(1, adapter.itemCount)
}
@Test
fun `creates and binds viewholder`() {
val collection = mockk<TabCollection> {
every { id } returns 0L
every { title } returns "Collection"
every { tabs } returns listOf(
mockk {
every { url } returns "https://mozilla.org"
},
mockk {
every { url } returns "https://firefox.com"
}
)
}
adapter.updateData(
tabCollections = listOf(collection),
selectedTabs = emptySet()
)
val holder = adapter.createViewHolder(parent, 0)
adapter.bindViewHolder(holder, 0)
assertEquals("Collection", holder.itemView.collection_item.text)
assertEquals("mozilla.org, firefox.com", holder.itemView.collection_description.text)
holder.itemView.performClick()
verify { interactor.selectCollection(collection, emptyList()) }
}
}

View File

@ -0,0 +1,151 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.fenix.collections
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Test
import org.mozilla.fenix.home.Tab
class TabDiffUtilTest {
@Test
fun `list size is returned`() {
val diffUtil = TabDiffUtil(
old = listOf(mockk(), mockk()),
new = listOf(mockk()),
oldSelected = emptySet(),
newSelected = emptySet(),
oldHideCheckboxes = false,
newHideCheckboxes = false
)
assertEquals(2, diffUtil.oldListSize)
assertEquals(1, diffUtil.newListSize)
}
@Test
fun `single lists are the same`() {
val tab = mockk<Tab> {
every { sessionId } returns "abc"
}
val diffUtil = TabDiffUtil(
old = listOf(tab),
new = listOf(tab),
oldSelected = emptySet(),
newSelected = emptySet(),
oldHideCheckboxes = false,
newHideCheckboxes = false
)
assertTrue(diffUtil.areItemsTheSame(0, 0))
assertTrue(diffUtil.areContentsTheSame(0, 0))
}
@Test
fun `selection affects contents`() {
val tab = mockk<Tab> {
every { sessionId } returns "abc"
}
val diffUtil = TabDiffUtil(
old = listOf(tab),
new = listOf(tab),
oldSelected = emptySet(),
newSelected = setOf(tab),
oldHideCheckboxes = false,
newHideCheckboxes = false
)
assertTrue(diffUtil.areItemsTheSame(0, 0))
assertFalse(diffUtil.areContentsTheSame(0, 0))
}
@Test
fun `hide checkboxes affects contents`() {
val tab = mockk<Tab> {
every { sessionId } returns "abc"
}
val diffUtil = TabDiffUtil(
old = listOf(tab),
new = listOf(tab),
oldSelected = setOf(tab),
newSelected = setOf(tab),
oldHideCheckboxes = false,
newHideCheckboxes = true
)
assertTrue(diffUtil.areItemsTheSame(0, 0))
assertFalse(diffUtil.areContentsTheSame(0, 0))
}
@Test
fun `change payload covers no change case`() {
val tab = mockk<Tab>()
val payload = TabDiffUtil(
old = listOf(tab),
new = listOf(tab),
oldSelected = setOf(tab),
newSelected = setOf(tab),
oldHideCheckboxes = false,
newHideCheckboxes = false
).getChangePayload(0, 0)
assertEquals(
CheckChanged(
shouldBeChecked = false,
shouldBeUnchecked = false,
shouldHideCheckBox = false
),
payload
)
}
@Test
fun `include shouldBeChecked in change payload`() {
val tab = mockk<Tab>()
val payload = TabDiffUtil(
old = listOf(tab),
new = listOf(tab),
oldSelected = emptySet(),
newSelected = setOf(tab),
oldHideCheckboxes = false,
newHideCheckboxes = false
).getChangePayload(0, 0)
assertEquals(
CheckChanged(
shouldBeChecked = true,
shouldBeUnchecked = false,
shouldHideCheckBox = false
),
payload
)
}
@Test
fun `include shouldBeUnchecked in change payload`() {
val tab = mockk<Tab>()
val payload = TabDiffUtil(
old = listOf(tab),
new = listOf(tab),
oldSelected = setOf(tab),
newSelected = emptySet(),
oldHideCheckboxes = false,
newHideCheckboxes = true
).getChangePayload(0, 0)
assertEquals(
CheckChanged(
shouldBeChecked = false,
shouldBeUnchecked = true,
shouldHideCheckBox = true
),
payload
)
}
}