Close #18774: Migrate mutli-selection to store
Removes the recyclerview-selection library and replaces it with the SelectionHolder/SelectionInteractor with a Store. This is an implementation that's similar to what we have in other UI lists (library).upstream-sync
parent
499aa858b2
commit
9078139e40
@ -0,0 +1,26 @@
|
|||||||
|
/* 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.tabstray
|
||||||
|
|
||||||
|
import android.app.Dialog
|
||||||
|
import android.content.Context
|
||||||
|
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default tabs tray dialog implementation for overriding the default on back pressed.
|
||||||
|
*/
|
||||||
|
class TabsTrayDialog(
|
||||||
|
context: Context,
|
||||||
|
theme: Int,
|
||||||
|
private val interactor: () -> BrowserTrayInteractor
|
||||||
|
) : Dialog(context, theme) {
|
||||||
|
override fun onBackPressed() {
|
||||||
|
if (interactor.invoke().onBackPressed()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
/* 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.tabstray.browser
|
||||||
|
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.drop
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM
|
||||||
|
import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_HIGHLIGHT_SELECTED_ITEM
|
||||||
|
import mozilla.components.lib.state.ext.flowScoped
|
||||||
|
import mozilla.components.support.base.feature.LifecycleAwareFeature
|
||||||
|
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
||||||
|
import org.mozilla.fenix.tabstray.TabsTrayState.Mode
|
||||||
|
import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notifies the adapter when the selection mode changes.
|
||||||
|
*/
|
||||||
|
class SelectedItemAdapterBinding(
|
||||||
|
val store: TabsTrayStore,
|
||||||
|
val adapter: BrowserTabsAdapter
|
||||||
|
) : LifecycleAwareFeature {
|
||||||
|
private var scope: CoroutineScope? = null
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
override fun start() {
|
||||||
|
scope = store.flowScoped { flow ->
|
||||||
|
flow.map { it.mode }
|
||||||
|
// ignore initial mode update; the adapter is already in an updated state.
|
||||||
|
.drop(1)
|
||||||
|
.ifChanged()
|
||||||
|
.collect { mode ->
|
||||||
|
notifyAdapter(mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stop() {
|
||||||
|
scope?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun notifyAdapter(mode: Mode) = with(adapter) {
|
||||||
|
if (mode == Mode.Normal) {
|
||||||
|
notifyItemRangeChanged(0, itemCount, PAYLOAD_HIGHLIGHT_SELECTED_ITEM)
|
||||||
|
} else {
|
||||||
|
notifyItemRangeChanged(0, itemCount, PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,45 +0,0 @@
|
|||||||
/* 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.tabstray.browser
|
|
||||||
|
|
||||||
import android.util.LruCache
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import mozilla.components.concept.tabstray.Tab
|
|
||||||
|
|
||||||
internal const val INITIAL_NUMBER_OF_TABS = 20
|
|
||||||
internal const val CACHE_SIZE_MULTIPLIER = 1.5
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Storage for Browser tabs that need a stable ID for each item in a [RecyclerView.Adapter].
|
|
||||||
* This ID is commonly needed by [RecyclerView.Adapter.getItemId] when
|
|
||||||
* enabling [RecyclerView.Adapter.setHasStableIds].
|
|
||||||
*/
|
|
||||||
internal class TabAdapterIdStorage(initialSize: Int = INITIAL_NUMBER_OF_TABS) {
|
|
||||||
private val uniqueTabIds = LruCache<String, Long>(initialSize)
|
|
||||||
private var lastUsedSuggestionId = 0L
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns a unique tab ID for the given [Tab].
|
|
||||||
*/
|
|
||||||
fun getStableId(tab: Tab): Long {
|
|
||||||
val key = tab.id
|
|
||||||
return uniqueTabIds[key] ?: run {
|
|
||||||
lastUsedSuggestionId += 1
|
|
||||||
uniqueTabIds.put(key, lastUsedSuggestionId)
|
|
||||||
lastUsedSuggestionId
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resizes the internal cache size if the [count] is larger than what is currently available.
|
|
||||||
*/
|
|
||||||
fun resizeCacheIfNeeded(count: Int) {
|
|
||||||
val currentMaxSize = uniqueTabIds.maxSize()
|
|
||||||
if (count > currentMaxSize) {
|
|
||||||
val newMaxSize = (count * CACHE_SIZE_MULTIPLIER).toInt()
|
|
||||||
uniqueTabIds.resize(newMaxSize)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/* 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.tabstray.browser
|
|
||||||
|
|
||||||
import android.view.MotionEvent
|
|
||||||
import androidx.recyclerview.selection.ItemDetailsLookup
|
|
||||||
import androidx.recyclerview.selection.ItemDetailsLookup.ItemDetails
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
import org.mozilla.fenix.tabstray.TabsTrayViewHolder
|
|
||||||
|
|
||||||
/**
|
|
||||||
* An [ItemDetailsLookup] for retrieving the [ItemDetails] of a [TabsTrayViewHolder].
|
|
||||||
*/
|
|
||||||
class TabsDetailsLookup(
|
|
||||||
private val recyclerView: RecyclerView
|
|
||||||
) : ItemDetailsLookup<Long>() {
|
|
||||||
|
|
||||||
override fun getItemDetails(event: MotionEvent): ItemDetails<Long>? {
|
|
||||||
val view = recyclerView.findChildViewUnder(event.x, event.y)
|
|
||||||
if (view != null) {
|
|
||||||
val viewHolder = recyclerView.getChildViewHolder(view) as TabsTrayViewHolder
|
|
||||||
return viewHolder.getItemDetails()
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/* 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.tabstray.browser
|
|
||||||
|
|
||||||
import androidx.recyclerview.selection.ItemKeyProvider
|
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A key provider for the browser tabs.
|
|
||||||
*/
|
|
||||||
class TabsItemKeyProvider(private val recyclerView: RecyclerView) :
|
|
||||||
ItemKeyProvider<Long>(SCOPE_MAPPED) {
|
|
||||||
|
|
||||||
override fun getKey(position: Int): Long? {
|
|
||||||
return recyclerView.adapter?.getItemId(position)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPosition(key: Long): Int {
|
|
||||||
val viewHolder = recyclerView.findViewHolderForItemId(key)
|
|
||||||
return viewHolder?.layoutPosition ?: RecyclerView.NO_POSITION
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,24 @@
|
|||||||
|
/* 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.tabstray
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
|
||||||
|
|
||||||
|
class TabsTrayDialogTest {
|
||||||
|
@Test
|
||||||
|
fun `WHEN onBackPressed THEN invoke interactor`() {
|
||||||
|
val context = mockk<Context>(relaxed = true)
|
||||||
|
val interactor = mockk<BrowserTrayInteractor>(relaxed = true)
|
||||||
|
val dialog = TabsTrayDialog(context, 0) { interactor }
|
||||||
|
|
||||||
|
dialog.onBackPressed()
|
||||||
|
|
||||||
|
verify { interactor.onBackPressed() }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/* 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.tabstray.browser
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM
|
||||||
|
import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_HIGHLIGHT_SELECTED_ITEM
|
||||||
|
import mozilla.components.concept.tabstray.Tab
|
||||||
|
import mozilla.components.concept.tabstray.Tabs
|
||||||
|
import mozilla.components.support.test.robolectric.testContext
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
||||||
|
import org.mozilla.fenix.selection.SelectionHolder
|
||||||
|
import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||||
|
import org.mozilla.fenix.tabstray.TabsTrayViewHolder
|
||||||
|
|
||||||
|
@RunWith(FenixRobolectricTestRunner::class)
|
||||||
|
class BrowserTabsAdapterTest {
|
||||||
|
|
||||||
|
private val context = testContext
|
||||||
|
private val interactor = mockk<BrowserTrayInteractor>(relaxed = true)
|
||||||
|
private val store = TabsTrayStore()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN bind with payloads is called THEN update the holder`() {
|
||||||
|
val adapter = BrowserTabsAdapter(context, interactor, store)
|
||||||
|
val holder = mockk<TabsTrayViewHolder>(relaxed = true)
|
||||||
|
|
||||||
|
adapter.updateTabs(Tabs(
|
||||||
|
list = listOf(
|
||||||
|
createTab("tab1")
|
||||||
|
),
|
||||||
|
selectedIndex = 0
|
||||||
|
))
|
||||||
|
|
||||||
|
adapter.onBindViewHolder(holder, 0, listOf(PAYLOAD_HIGHLIGHT_SELECTED_ITEM))
|
||||||
|
|
||||||
|
verify { holder.updateSelectedTabIndicator(true) }
|
||||||
|
|
||||||
|
adapter.onBindViewHolder(holder, 0, listOf(PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM))
|
||||||
|
|
||||||
|
verify { holder.updateSelectedTabIndicator(false) }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN the selection holder is set THEN update the selected tab`() {
|
||||||
|
val adapter = BrowserTabsAdapter(context, interactor, store)
|
||||||
|
val holder = mockk<TabsTrayViewHolder>(relaxed = true)
|
||||||
|
val tab = createTab("tab1")
|
||||||
|
|
||||||
|
every { holder.tab }.answers { tab }
|
||||||
|
testSelectionHolder.internalState.add(tab)
|
||||||
|
adapter.selectionHolder = testSelectionHolder
|
||||||
|
|
||||||
|
adapter.updateTabs(Tabs(
|
||||||
|
list = listOf(
|
||||||
|
tab
|
||||||
|
),
|
||||||
|
selectedIndex = 0
|
||||||
|
))
|
||||||
|
|
||||||
|
adapter.onBindViewHolder(holder, 0, listOf(PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM))
|
||||||
|
|
||||||
|
verify { holder.showTabIsMultiSelectEnabled(true) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private val testSelectionHolder = object : SelectionHolder<Tab> {
|
||||||
|
override val selectedItems: Set<Tab>
|
||||||
|
get() = internalState
|
||||||
|
|
||||||
|
val internalState = mutableSetOf<Tab>()
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,70 @@
|
|||||||
|
/* 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.tabstray.browser
|
||||||
|
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||||
|
import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM
|
||||||
|
import mozilla.components.browser.tabstray.TabsAdapter.Companion.PAYLOAD_HIGHLIGHT_SELECTED_ITEM
|
||||||
|
import mozilla.components.support.test.libstate.ext.waitUntilIdle
|
||||||
|
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.mozilla.fenix.tabstray.TabsTrayAction
|
||||||
|
import org.mozilla.fenix.tabstray.TabsTrayStore
|
||||||
|
|
||||||
|
class SelectedItemAdapterBindingTest {
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@get:Rule
|
||||||
|
val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher())
|
||||||
|
|
||||||
|
private val adapter = mockk<BrowserTabsAdapter>(relaxed = true)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
every { adapter.itemCount }.answers { 1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN observing on start THEN ignore the initial state update`() {
|
||||||
|
val store = TabsTrayStore()
|
||||||
|
val binding = SelectedItemAdapterBinding(store, adapter)
|
||||||
|
|
||||||
|
binding.start()
|
||||||
|
|
||||||
|
verify(exactly = 0) {
|
||||||
|
adapter.notifyItemRangeChanged(any(), any(), any())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN mode changes THEN notify the adapter`() {
|
||||||
|
val store = TabsTrayStore()
|
||||||
|
val binding = SelectedItemAdapterBinding(store, adapter)
|
||||||
|
|
||||||
|
binding.start()
|
||||||
|
|
||||||
|
store.dispatch(TabsTrayAction.EnterSelectMode)
|
||||||
|
|
||||||
|
store.waitUntilIdle()
|
||||||
|
|
||||||
|
verify {
|
||||||
|
adapter.notifyItemRangeChanged(eq(0), eq(1), eq(PAYLOAD_DONT_HIGHLIGHT_SELECTED_ITEM))
|
||||||
|
}
|
||||||
|
|
||||||
|
store.dispatch(TabsTrayAction.ExitSelectMode)
|
||||||
|
|
||||||
|
store.waitUntilIdle()
|
||||||
|
|
||||||
|
verify {
|
||||||
|
adapter.notifyItemRangeChanged(eq(0), eq(1), eq(PAYLOAD_HIGHLIGHT_SELECTED_ITEM))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,78 +0,0 @@
|
|||||||
/* 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.tabstray.browser
|
|
||||||
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Assert.assertNotEquals
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
||||||
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
||||||
class TabAdapterIdStorageTest {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `the same ID is returned when queried multiple times`() {
|
|
||||||
val storage = TabAdapterIdStorage()
|
|
||||||
val tab = createTab()
|
|
||||||
|
|
||||||
val id1 = storage.getStableId(tab)
|
|
||||||
val id2 = storage.getStableId(tab)
|
|
||||||
|
|
||||||
assertEquals(id1, id2)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `the same ID is returned when the cache is at max`() {
|
|
||||||
val storage = TabAdapterIdStorage(2)
|
|
||||||
val tab1 = createTab()
|
|
||||||
val tab2 = createTab()
|
|
||||||
|
|
||||||
val id1 = storage.getStableId(tab1)
|
|
||||||
val id2 = storage.getStableId(tab2)
|
|
||||||
val id1Again = storage.getStableId(tab1)
|
|
||||||
|
|
||||||
assertEquals(id1, id1Again)
|
|
||||||
assertNotEquals(id1, id2)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `the same ID is NOT returned if the cache is over max`() {
|
|
||||||
val storage = TabAdapterIdStorage(2)
|
|
||||||
val tab1 = createTab()
|
|
||||||
val tab2 = createTab()
|
|
||||||
val tab3 = createTab()
|
|
||||||
|
|
||||||
val id1 = storage.getStableId(tab1)
|
|
||||||
val id2 = storage.getStableId(tab2)
|
|
||||||
val id3 = storage.getStableId(tab3)
|
|
||||||
val id1Again = storage.getStableId(tab1)
|
|
||||||
|
|
||||||
assertNotEquals(id1, id1Again)
|
|
||||||
assertNotEquals(id1, id2)
|
|
||||||
assertNotEquals(id1, id3)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `the same ID is returned if the cache is resized when full`() {
|
|
||||||
val storage = TabAdapterIdStorage(2)
|
|
||||||
val tab1 = createTab()
|
|
||||||
val tab2 = createTab()
|
|
||||||
val tab3 = createTab()
|
|
||||||
|
|
||||||
val id1 = storage.getStableId(tab1)
|
|
||||||
val id2 = storage.getStableId(tab2)
|
|
||||||
|
|
||||||
storage.resizeCacheIfNeeded(3)
|
|
||||||
|
|
||||||
val id3 = storage.getStableId(tab3)
|
|
||||||
val id1Again = storage.getStableId(tab1)
|
|
||||||
|
|
||||||
assertEquals(id1, id1Again)
|
|
||||||
assertNotEquals(id1, id2)
|
|
||||||
assertNotEquals(id1, id3)
|
|
||||||
assertNotEquals(id2, id3)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,76 +0,0 @@
|
|||||||
/* 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.tabstray.browser
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import android.view.ViewGroup
|
|
||||||
import mozilla.components.browser.tabstray.TabViewHolder
|
|
||||||
import mozilla.components.browser.tabstray.TabsTrayStyling
|
|
||||||
import mozilla.components.concept.tabstray.Tab
|
|
||||||
import mozilla.components.concept.tabstray.Tabs
|
|
||||||
import mozilla.components.concept.tabstray.TabsTray
|
|
||||||
import mozilla.components.support.base.observer.Observable
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Assert.assertNotEquals
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
||||||
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
||||||
class TabsAdapterTest {
|
|
||||||
|
|
||||||
lateinit var adapter: TabsAdapter<TestTabsAdapter.ViewHolder>
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() {
|
|
||||||
adapter = TestTabsAdapter()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `getItemId gives a new ID for each position`() {
|
|
||||||
val (tab1, tab2, tab3) = Triple(createTab(), createTab(), createTab())
|
|
||||||
val tabs = Tabs(
|
|
||||||
list = listOf(tab1, tab2, tab3),
|
|
||||||
selectedIndex = 0
|
|
||||||
)
|
|
||||||
|
|
||||||
adapter.updateTabs(tabs)
|
|
||||||
|
|
||||||
val id1 = adapter.getItemId(0)
|
|
||||||
val id2 = adapter.getItemId(1)
|
|
||||||
val id3 = adapter.getItemId(2)
|
|
||||||
val id1Again = adapter.getItemId(0)
|
|
||||||
|
|
||||||
assertEquals(id1, id1Again)
|
|
||||||
assertNotEquals(id1, id2)
|
|
||||||
assertNotEquals(id1, id3)
|
|
||||||
assertNotEquals(id2, id3)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test(expected = IllegalStateException::class)
|
|
||||||
fun `getItemId throws if a tab does not exist for the position`() {
|
|
||||||
adapter.getItemId(4)
|
|
||||||
}
|
|
||||||
|
|
||||||
class TestTabsAdapter : TabsAdapter<TestTabsAdapter.ViewHolder>() {
|
|
||||||
|
|
||||||
inner class ViewHolder(view: View) : TabViewHolder(view) {
|
|
||||||
override var tab: Tab? = null
|
|
||||||
|
|
||||||
override fun bind(
|
|
||||||
tab: Tab,
|
|
||||||
isSelected: Boolean,
|
|
||||||
styling: TabsTrayStyling,
|
|
||||||
observable: Observable<TabsTray.Observer>
|
|
||||||
) = throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreateViewHolder(
|
|
||||||
parent: ViewGroup,
|
|
||||||
viewType: Int
|
|
||||||
): ViewHolder = throw UnsupportedOperationException()
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue