Close #13892: Remove Synced Tabs appended to tabs tray
parent
516a6a343f
commit
2f6fcbf196
@ -1,87 +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.tabtray
|
|
||||||
|
|
||||||
import android.view.View
|
|
||||||
import androidx.fragment.app.FragmentManager.findFragment
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.navigation.NavController
|
|
||||||
import androidx.navigation.fragment.findNavController
|
|
||||||
import androidx.recyclerview.widget.ConcatAdapter
|
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.flow.collect
|
|
||||||
import kotlinx.coroutines.flow.drop
|
|
||||||
import kotlinx.coroutines.flow.map
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import mozilla.components.browser.storage.sync.SyncedDeviceTabs
|
|
||||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView
|
|
||||||
import mozilla.components.lib.state.ext.flowScoped
|
|
||||||
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifChanged
|
|
||||||
import org.mozilla.fenix.components.metrics.MetricController
|
|
||||||
import org.mozilla.fenix.sync.ListenerDelegate
|
|
||||||
import org.mozilla.fenix.sync.SyncedTabsAdapter
|
|
||||||
import org.mozilla.fenix.sync.ext.toAdapterList
|
|
||||||
import org.mozilla.fenix.sync.ext.toAdapterItem
|
|
||||||
import org.mozilla.fenix.sync.ext.toStringRes
|
|
||||||
import kotlin.coroutines.CoroutineContext
|
|
||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
|
||||||
class SyncedTabsController(
|
|
||||||
lifecycleOwner: LifecycleOwner,
|
|
||||||
private val view: View,
|
|
||||||
store: TabTrayDialogFragmentStore,
|
|
||||||
private val concatAdapter: ConcatAdapter,
|
|
||||||
coroutineContext: CoroutineContext = Dispatchers.Main,
|
|
||||||
metrics: MetricController
|
|
||||||
) : SyncedTabsView {
|
|
||||||
override var listener: SyncedTabsView.Listener? = null
|
|
||||||
|
|
||||||
val adapter = SyncedTabsAdapter(ListenerDelegate(metrics) { listener })
|
|
||||||
|
|
||||||
private val scope: CoroutineScope = CoroutineScope(coroutineContext)
|
|
||||||
|
|
||||||
init {
|
|
||||||
store.flowScoped(lifecycleOwner) { flow ->
|
|
||||||
flow.map { it.mode }
|
|
||||||
.ifChanged()
|
|
||||||
.drop(1)
|
|
||||||
.collect { mode ->
|
|
||||||
when (mode) {
|
|
||||||
is TabTrayDialogFragmentState.Mode.Normal -> {
|
|
||||||
concatAdapter.addAdapter(adapter)
|
|
||||||
}
|
|
||||||
is TabTrayDialogFragmentState.Mode.MultiSelect -> {
|
|
||||||
concatAdapter.removeAdapter(adapter)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun displaySyncedTabs(syncedTabs: List<SyncedDeviceTabs>) {
|
|
||||||
scope.launch {
|
|
||||||
val tabsList = listOf(SyncedTabsAdapter.AdapterItem.Title) + syncedTabs.toAdapterList()
|
|
||||||
// Reverse layout for TabTrayView which does things backwards.
|
|
||||||
adapter.submitList(tabsList.reversed())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onError(error: SyncedTabsView.ErrorType) {
|
|
||||||
scope.launch {
|
|
||||||
val navController: NavController? = try {
|
|
||||||
findFragment<TabTrayDialogFragment>(view).findNavController()
|
|
||||||
} catch (exception: IllegalStateException) {
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
val descriptionResId = error.toStringRes()
|
|
||||||
val errorItem = error.toAdapterItem(descriptionResId, navController)
|
|
||||||
|
|
||||||
adapter.submitList(listOf(errorItem))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,140 +0,0 @@
|
|||||||
package org.mozilla.fenix.tabtray
|
|
||||||
|
|
||||||
import android.view.LayoutInflater
|
|
||||||
import android.view.View
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.lifecycle.LifecycleOwner
|
|
||||||
import androidx.lifecycle.LifecycleRegistry
|
|
||||||
import androidx.recyclerview.widget.ConcatAdapter
|
|
||||||
import io.mockk.Called
|
|
||||||
import io.mockk.every
|
|
||||||
import io.mockk.mockk
|
|
||||||
import io.mockk.verify
|
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
|
||||||
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
|
||||||
import kotlinx.coroutines.test.runBlockingTest
|
|
||||||
import mozilla.components.browser.storage.sync.SyncedDeviceTabs
|
|
||||||
import mozilla.components.feature.syncedtabs.view.SyncedTabsView.ErrorType
|
|
||||||
import mozilla.components.support.test.ext.joinBlocking
|
|
||||||
import mozilla.components.support.test.robolectric.testContext
|
|
||||||
import mozilla.components.support.test.rule.MainCoroutineRule
|
|
||||||
import org.junit.After
|
|
||||||
import org.junit.Assert.assertEquals
|
|
||||||
import org.junit.Before
|
|
||||||
import org.junit.Rule
|
|
||||||
import org.junit.Test
|
|
||||||
import org.junit.runner.RunWith
|
|
||||||
import org.mozilla.fenix.R
|
|
||||||
import org.mozilla.fenix.components.metrics.MetricController
|
|
||||||
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
||||||
import org.mozilla.fenix.sync.SyncedTabsViewHolder
|
|
||||||
import org.mozilla.fenix.tabtray.TabTrayDialogFragmentAction.EnterMultiSelectMode
|
|
||||||
import org.mozilla.fenix.tabtray.TabTrayDialogFragmentAction.ExitMultiSelectMode
|
|
||||||
import org.mozilla.fenix.tabtray.TabTrayDialogFragmentState.Mode
|
|
||||||
|
|
||||||
@ExperimentalCoroutinesApi
|
|
||||||
@RunWith(FenixRobolectricTestRunner::class)
|
|
||||||
class SyncedTabsControllerTest {
|
|
||||||
|
|
||||||
private val testDispatcher = TestCoroutineDispatcher()
|
|
||||||
@get:Rule
|
|
||||||
val coroutinesTestRule = MainCoroutineRule(testDispatcher)
|
|
||||||
|
|
||||||
private lateinit var view: View
|
|
||||||
private lateinit var controller: SyncedTabsController
|
|
||||||
private lateinit var lifecycleOwner: LifecycleOwner
|
|
||||||
private lateinit var lifecycle: LifecycleRegistry
|
|
||||||
private lateinit var concatAdapter: ConcatAdapter
|
|
||||||
private lateinit var store: TabTrayDialogFragmentStore
|
|
||||||
|
|
||||||
@Before
|
|
||||||
fun setup() = runBlockingTest {
|
|
||||||
lifecycleOwner = mockk()
|
|
||||||
lifecycle = LifecycleRegistry(lifecycleOwner)
|
|
||||||
lifecycle.handleLifecycleEvent(Lifecycle.Event.ON_START)
|
|
||||||
every { lifecycleOwner.lifecycle } returns lifecycle
|
|
||||||
|
|
||||||
concatAdapter = mockk()
|
|
||||||
every { concatAdapter.addAdapter(any()) } returns true
|
|
||||||
every { concatAdapter.removeAdapter(any()) } returns true
|
|
||||||
|
|
||||||
store = TabTrayDialogFragmentStore(
|
|
||||||
initialState = TabTrayDialogFragmentState(
|
|
||||||
mode = Mode.Normal,
|
|
||||||
browserState = mockk(relaxed = true)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
view = LayoutInflater.from(testContext).inflate(R.layout.about_list_item, null)
|
|
||||||
val metrics: MetricController = mockk()
|
|
||||||
controller =
|
|
||||||
SyncedTabsController(lifecycleOwner, view, store, concatAdapter, coroutineContext, metrics)
|
|
||||||
}
|
|
||||||
|
|
||||||
@After
|
|
||||||
fun cleanUp() {
|
|
||||||
testDispatcher.cleanupTestCoroutines()
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `display synced tabs in reverse`() {
|
|
||||||
val tabs = listOf(
|
|
||||||
SyncedDeviceTabs(
|
|
||||||
device = mockk(relaxed = true),
|
|
||||||
tabs = listOf(
|
|
||||||
mockk(relaxed = true),
|
|
||||||
mockk(relaxed = true)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
controller.displaySyncedTabs(tabs)
|
|
||||||
|
|
||||||
val itemCount = controller.adapter.itemCount
|
|
||||||
|
|
||||||
// title + device name + 2 tabs
|
|
||||||
assertEquals(4, itemCount)
|
|
||||||
assertEquals(
|
|
||||||
SyncedTabsViewHolder.TitleViewHolder.LAYOUT_ID,
|
|
||||||
controller.adapter.getItemViewType(itemCount - 1)
|
|
||||||
)
|
|
||||||
assertEquals(
|
|
||||||
SyncedTabsViewHolder.DeviceViewHolder.LAYOUT_ID,
|
|
||||||
controller.adapter.getItemViewType(itemCount - 2)
|
|
||||||
)
|
|
||||||
assertEquals(
|
|
||||||
SyncedTabsViewHolder.TabViewHolder.LAYOUT_ID,
|
|
||||||
controller.adapter.getItemViewType(itemCount - 3)
|
|
||||||
)
|
|
||||||
assertEquals(
|
|
||||||
SyncedTabsViewHolder.TabViewHolder.LAYOUT_ID,
|
|
||||||
controller.adapter.getItemViewType(itemCount - 4)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `show error when we go kaput`() {
|
|
||||||
controller.onError(ErrorType.SYNC_NEEDS_REAUTHENTICATION)
|
|
||||||
|
|
||||||
assertEquals(1, controller.adapter.itemCount)
|
|
||||||
assertEquals(
|
|
||||||
SyncedTabsViewHolder.ErrorViewHolder.LAYOUT_ID,
|
|
||||||
controller.adapter.getItemViewType(0)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `do nothing on init, drop first event`() {
|
|
||||||
verify { concatAdapter wasNot Called }
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `concatAdapter updated on mode changes`() = testDispatcher.runBlockingTest {
|
|
||||||
store.dispatch(EnterMultiSelectMode).joinBlocking()
|
|
||||||
verify { concatAdapter.removeAdapter(any()) }
|
|
||||||
|
|
||||||
store.dispatch(ExitMultiSelectMode).joinBlocking()
|
|
||||||
// When returning from Multiselect the adapter should be added at the end
|
|
||||||
verify { concatAdapter.addAdapter(any()) }
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue