mirror of
https://github.com/fork-maintainers/iceraven-browser
synced 2024-11-19 09:25:34 +00:00
[fenix] Closes https://github.com/mozilla-mobile/fenix/issues/18816: Disable TabsTray FAB on accessibility enabled (https://github.com/mozilla-mobile/fenix/pull/19170)
This commit is contained in:
parent
d94dcd6bd3
commit
fffa196da9
@ -0,0 +1,104 @@
|
|||||||
|
/* 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.view.View
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.cancel
|
||||||
|
import kotlinx.coroutines.flow.collect
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import mozilla.components.lib.state.ext.flowScoped
|
||||||
|
import mozilla.components.support.base.feature.LifecycleAwareFeature
|
||||||
|
import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged
|
||||||
|
import org.mozilla.fenix.R
|
||||||
|
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
|
||||||
|
import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsInteractor
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not show accessible new tab button when accessibility service is disabled
|
||||||
|
*
|
||||||
|
* This binding is coupled with [FloatingActionButtonBinding].
|
||||||
|
* When [FloatingActionButtonBinding] is visible this should not be visible
|
||||||
|
*/
|
||||||
|
class AccessibleNewTabButtonBinding(
|
||||||
|
private val store: TabsTrayStore,
|
||||||
|
private val settings: Settings,
|
||||||
|
private val newTabButton: ImageButton,
|
||||||
|
private val browserTrayInteractor: BrowserTrayInteractor,
|
||||||
|
private val syncedTabsInteractor: SyncedTabsInteractor
|
||||||
|
) : LifecycleAwareFeature {
|
||||||
|
|
||||||
|
private var scope: CoroutineScope? = null
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
override fun start() {
|
||||||
|
if (!settings.accessibilityServicesEnabled) {
|
||||||
|
newTabButton.visibility = View.GONE
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
scope = store.flowScoped { flow ->
|
||||||
|
flow.map { it }
|
||||||
|
.ifAnyChanged { state ->
|
||||||
|
arrayOf(
|
||||||
|
state.selectedPage,
|
||||||
|
state.syncing
|
||||||
|
)
|
||||||
|
}
|
||||||
|
.collect { state ->
|
||||||
|
setAccessibleNewTabButton(state.selectedPage, state.syncing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun stop() {
|
||||||
|
scope?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setAccessibleNewTabButton(selectedPage: Page, syncing: Boolean) {
|
||||||
|
when (selectedPage) {
|
||||||
|
Page.NormalTabs -> {
|
||||||
|
newTabButton.apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
setImageResource(R.drawable.ic_new)
|
||||||
|
setOnClickListener {
|
||||||
|
browserTrayInteractor.onFabClicked(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Page.PrivateTabs -> {
|
||||||
|
newTabButton.apply {
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
setImageResource(R.drawable.ic_new)
|
||||||
|
setOnClickListener {
|
||||||
|
browserTrayInteractor.onFabClicked(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Page.SyncedTabs -> {
|
||||||
|
newTabButton.apply {
|
||||||
|
visibility =
|
||||||
|
when (syncing) {
|
||||||
|
true -> View.GONE
|
||||||
|
false -> View.VISIBLE
|
||||||
|
}
|
||||||
|
|
||||||
|
setImageResource(R.drawable.ic_fab_sync)
|
||||||
|
setOnClickListener {
|
||||||
|
// Notify the store observers (one of which is the SyncedTabsFeature), that
|
||||||
|
// a sync was requested.
|
||||||
|
if (!syncing) {
|
||||||
|
store.dispatch(TabsTrayAction.SyncNow)
|
||||||
|
syncedTabsInteractor.onRefresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -4,7 +4,6 @@
|
|||||||
|
|
||||||
package org.mozilla.fenix.tabstray
|
package org.mozilla.fenix.tabstray
|
||||||
|
|
||||||
import androidx.appcompat.content.res.AppCompatResources
|
|
||||||
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
@ -17,9 +16,17 @@ import mozilla.components.support.ktx.kotlinx.coroutines.flow.ifAnyChanged
|
|||||||
import org.mozilla.fenix.R
|
import org.mozilla.fenix.R
|
||||||
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
|
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
|
||||||
import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsInteractor
|
import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsInteractor
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Do not show fab when accessibility service is enabled
|
||||||
|
*
|
||||||
|
* This binding is coupled with [AccessibleNewTabButtonBinding].
|
||||||
|
* When [AccessibleNewTabButtonBinding] is visible this should not be visible
|
||||||
|
*/
|
||||||
class FloatingActionButtonBinding(
|
class FloatingActionButtonBinding(
|
||||||
private val store: TabsTrayStore,
|
private val store: TabsTrayStore,
|
||||||
|
private val settings: Settings,
|
||||||
private val actionButton: ExtendedFloatingActionButton,
|
private val actionButton: ExtendedFloatingActionButton,
|
||||||
private val browserTrayInteractor: BrowserTrayInteractor,
|
private val browserTrayInteractor: BrowserTrayInteractor,
|
||||||
private val syncedTabsInteractor: SyncedTabsInteractor
|
private val syncedTabsInteractor: SyncedTabsInteractor
|
||||||
@ -29,7 +36,11 @@ class FloatingActionButtonBinding(
|
|||||||
|
|
||||||
@OptIn(ExperimentalCoroutinesApi::class)
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
override fun start() {
|
override fun start() {
|
||||||
setFab(store.state.selectedPage, store.state.syncing)
|
if (settings.accessibilityServicesEnabled) {
|
||||||
|
actionButton.hide()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
scope = store.flowScoped { flow ->
|
scope = store.flowScoped { flow ->
|
||||||
flow.map { it }
|
flow.map { it }
|
||||||
.ifAnyChanged { state ->
|
.ifAnyChanged { state ->
|
||||||
@ -54,7 +65,7 @@ class FloatingActionButtonBinding(
|
|||||||
actionButton.apply {
|
actionButton.apply {
|
||||||
shrink()
|
shrink()
|
||||||
show()
|
show()
|
||||||
icon = AppCompatResources.getDrawable(context, R.drawable.ic_new)
|
setIconResource(R.drawable.ic_new)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
browserTrayInteractor.onFabClicked(false)
|
browserTrayInteractor.onFabClicked(false)
|
||||||
}
|
}
|
||||||
@ -62,10 +73,10 @@ class FloatingActionButtonBinding(
|
|||||||
}
|
}
|
||||||
Page.PrivateTabs -> {
|
Page.PrivateTabs -> {
|
||||||
actionButton.apply {
|
actionButton.apply {
|
||||||
text = context.getText(R.string.tab_drawer_fab_content)
|
setText(R.string.tab_drawer_fab_content)
|
||||||
extend()
|
extend()
|
||||||
show()
|
show()
|
||||||
icon = AppCompatResources.getDrawable(context, R.drawable.ic_new)
|
setIconResource(R.drawable.ic_new)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
browserTrayInteractor.onFabClicked(true)
|
browserTrayInteractor.onFabClicked(true)
|
||||||
}
|
}
|
||||||
@ -73,11 +84,15 @@ class FloatingActionButtonBinding(
|
|||||||
}
|
}
|
||||||
Page.SyncedTabs -> {
|
Page.SyncedTabs -> {
|
||||||
actionButton.apply {
|
actionButton.apply {
|
||||||
text = if (syncing) context.getText(R.string.sync_syncing_in_progress)
|
setText(
|
||||||
else context.getText(R.string.tab_drawer_fab_sync)
|
when (syncing) {
|
||||||
|
true -> R.string.sync_syncing_in_progress
|
||||||
|
false -> R.string.tab_drawer_fab_sync
|
||||||
|
}
|
||||||
|
)
|
||||||
extend()
|
extend()
|
||||||
show()
|
show()
|
||||||
icon = AppCompatResources.getDrawable(context, R.drawable.ic_fab_sync)
|
setIconResource(R.drawable.ic_fab_sync)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
// Notify the store observers (one of which is the SyncedTabsFeature), that
|
// Notify the store observers (one of which is the SyncedTabsFeature), that
|
||||||
// a sync was requested.
|
// a sync was requested.
|
||||||
|
@ -12,6 +12,7 @@ import android.view.View
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.appcompat.app.AppCompatDialogFragment
|
import androidx.appcompat.app.AppCompatDialogFragment
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
import androidx.core.view.isVisible
|
||||||
import androidx.fragment.app.activityViewModels
|
import androidx.fragment.app.activityViewModels
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.navigation.fragment.findNavController
|
import androidx.navigation.fragment.findNavController
|
||||||
@ -68,6 +69,7 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
|
|||||||
private val tabLayoutMediator = ViewBoundFeatureWrapper<TabLayoutMediator>()
|
private val tabLayoutMediator = ViewBoundFeatureWrapper<TabLayoutMediator>()
|
||||||
private val tabCounterBinding = ViewBoundFeatureWrapper<TabCounterBinding>()
|
private val tabCounterBinding = ViewBoundFeatureWrapper<TabCounterBinding>()
|
||||||
private val floatingActionButtonBinding = ViewBoundFeatureWrapper<FloatingActionButtonBinding>()
|
private val floatingActionButtonBinding = ViewBoundFeatureWrapper<FloatingActionButtonBinding>()
|
||||||
|
private val newTabButtonBinding = ViewBoundFeatureWrapper<AccessibleNewTabButtonBinding>()
|
||||||
private val selectionBannerBinding = ViewBoundFeatureWrapper<SelectionBannerBinding>()
|
private val selectionBannerBinding = ViewBoundFeatureWrapper<SelectionBannerBinding>()
|
||||||
private val selectionHandleBinding = ViewBoundFeatureWrapper<SelectionHandleBinding>()
|
private val selectionHandleBinding = ViewBoundFeatureWrapper<SelectionHandleBinding>()
|
||||||
private val tabsTrayCtaBinding = ViewBoundFeatureWrapper<TabsTrayInfoBannerBinding>()
|
private val tabsTrayCtaBinding = ViewBoundFeatureWrapper<TabsTrayInfoBannerBinding>()
|
||||||
@ -211,6 +213,7 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
|
|||||||
floatingActionButtonBinding.set(
|
floatingActionButtonBinding.set(
|
||||||
feature = FloatingActionButtonBinding(
|
feature = FloatingActionButtonBinding(
|
||||||
store = tabsTrayStore,
|
store = tabsTrayStore,
|
||||||
|
settings = requireComponents.settings,
|
||||||
actionButton = new_tab_button,
|
actionButton = new_tab_button,
|
||||||
browserTrayInteractor = browserTrayInteractor,
|
browserTrayInteractor = browserTrayInteractor,
|
||||||
syncedTabsInteractor = syncedTabsTrayInteractor
|
syncedTabsInteractor = syncedTabsTrayInteractor
|
||||||
@ -219,6 +222,18 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
|
|||||||
view = view
|
view = view
|
||||||
)
|
)
|
||||||
|
|
||||||
|
newTabButtonBinding.set(
|
||||||
|
feature = AccessibleNewTabButtonBinding(
|
||||||
|
store = tabsTrayStore,
|
||||||
|
settings = requireComponents.settings,
|
||||||
|
newTabButton = tab_tray_new_tab,
|
||||||
|
browserTrayInteractor = browserTrayInteractor,
|
||||||
|
syncedTabsInteractor = syncedTabsTrayInteractor
|
||||||
|
),
|
||||||
|
owner = this,
|
||||||
|
view = view
|
||||||
|
)
|
||||||
|
|
||||||
selectionBannerBinding.set(
|
selectionBannerBinding.set(
|
||||||
feature = SelectionBannerBinding(
|
feature = SelectionBannerBinding(
|
||||||
context = requireContext(),
|
context = requireContext(),
|
||||||
@ -312,7 +327,7 @@ class TabsTrayFragment : AppCompatDialogFragment(), TabsTrayInteractor {
|
|||||||
},
|
},
|
||||||
operation = { },
|
operation = { },
|
||||||
elevation = ELEVATION,
|
elevation = ELEVATION,
|
||||||
anchorView = new_tab_button
|
anchorView = if (new_tab_button.isVisible) new_tab_button else null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,6 +17,7 @@
|
|||||||
android:elevation="99dp"
|
android:elevation="99dp"
|
||||||
android:text="@string/tab_drawer_fab_content"
|
android:text="@string/tab_drawer_fab_content"
|
||||||
android:textColor="@color/photonWhite"
|
android:textColor="@color/photonWhite"
|
||||||
|
android:visibility="gone"
|
||||||
app:elevation="99dp"
|
app:elevation="99dp"
|
||||||
app:borderWidth="0dp"
|
app:borderWidth="0dp"
|
||||||
app:icon="@drawable/ic_new"
|
app:icon="@drawable/ic_new"
|
||||||
|
@ -0,0 +1,140 @@
|
|||||||
|
/* 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.view.View
|
||||||
|
import android.widget.ImageButton
|
||||||
|
import androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||||
|
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.R
|
||||||
|
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
|
||||||
|
import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsInteractor
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
class AccessibleNewTabButtonBindingTest {
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@get:Rule
|
||||||
|
val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher())
|
||||||
|
|
||||||
|
private val settings: Settings = mockk(relaxed = true)
|
||||||
|
private val newTabButton: ImageButton = mockk(relaxed = true)
|
||||||
|
private val browserTrayInteractor: BrowserTrayInteractor = mockk(relaxed = true)
|
||||||
|
private val syncedTabsInteractor: SyncedTabsInteractor = mockk(relaxed = true)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
mockkStatic(AppCompatResources::class)
|
||||||
|
every { AppCompatResources.getDrawable(any(), any()) } returns mockk(relaxed = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN tab selected page is normal tab THEN new tab button is visible`() {
|
||||||
|
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.NormalTabs))
|
||||||
|
val newTabButtonBinding = AccessibleNewTabButtonBinding(
|
||||||
|
tabsTrayStore, settings, newTabButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns true
|
||||||
|
|
||||||
|
newTabButtonBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { newTabButton.visibility = View.VISIBLE }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN tab selected page is private tab THEN new tab button is visible`() {
|
||||||
|
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.PrivateTabs))
|
||||||
|
val newTabButtonBinding = AccessibleNewTabButtonBinding(
|
||||||
|
tabsTrayStore, settings, newTabButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns true
|
||||||
|
|
||||||
|
newTabButtonBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { newTabButton.visibility = View.VISIBLE }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN tab selected page is sync tab THEN new tab button is visible`() {
|
||||||
|
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.SyncedTabs))
|
||||||
|
val newTabButtonBinding = AccessibleNewTabButtonBinding(
|
||||||
|
tabsTrayStore, settings, newTabButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns true
|
||||||
|
|
||||||
|
newTabButtonBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { newTabButton.visibility = View.VISIBLE }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN accessibility is disabled THEN new tab button is not visible`() {
|
||||||
|
var tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.NormalTabs))
|
||||||
|
var newTabButtonBinding = AccessibleNewTabButtonBinding(
|
||||||
|
tabsTrayStore, settings, newTabButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns false
|
||||||
|
|
||||||
|
newTabButtonBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { newTabButton.visibility = View.GONE }
|
||||||
|
|
||||||
|
tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.PrivateTabs))
|
||||||
|
newTabButtonBinding = AccessibleNewTabButtonBinding(
|
||||||
|
tabsTrayStore, settings, newTabButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
|
||||||
|
newTabButtonBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 2) { newTabButton.visibility = View.GONE }
|
||||||
|
|
||||||
|
tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.SyncedTabs))
|
||||||
|
newTabButtonBinding = AccessibleNewTabButtonBinding(
|
||||||
|
tabsTrayStore, settings, newTabButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
|
||||||
|
newTabButtonBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 3) { newTabButton.visibility = View.GONE }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN selected page is updated THEN button is updated`() {
|
||||||
|
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.NormalTabs))
|
||||||
|
val newTabButtonBinding = AccessibleNewTabButtonBinding(
|
||||||
|
tabsTrayStore, settings, newTabButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns true
|
||||||
|
|
||||||
|
newTabButtonBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { newTabButton.setImageResource(R.drawable.ic_new) }
|
||||||
|
|
||||||
|
tabsTrayStore.dispatch(TabsTrayAction.PageSelected(Page.positionToPage(Page.PrivateTabs.ordinal)))
|
||||||
|
tabsTrayStore.waitUntilIdle()
|
||||||
|
|
||||||
|
verify(exactly = 2) { newTabButton.setImageResource(R.drawable.ic_new) }
|
||||||
|
|
||||||
|
tabsTrayStore.dispatch(TabsTrayAction.PageSelected(Page.positionToPage(Page.SyncedTabs.ordinal)))
|
||||||
|
tabsTrayStore.waitUntilIdle()
|
||||||
|
|
||||||
|
verify(exactly = 1) { newTabButton.setImageResource(R.drawable.ic_fab_sync) }
|
||||||
|
|
||||||
|
tabsTrayStore.dispatch(TabsTrayAction.SyncNow)
|
||||||
|
tabsTrayStore.waitUntilIdle()
|
||||||
|
|
||||||
|
verify(exactly = 1) { newTabButton.visibility = View.GONE }
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,170 @@
|
|||||||
|
/* 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 androidx.appcompat.content.res.AppCompatResources
|
||||||
|
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton
|
||||||
|
import io.mockk.every
|
||||||
|
import io.mockk.mockk
|
||||||
|
import io.mockk.mockkStatic
|
||||||
|
import io.mockk.verify
|
||||||
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
|
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
||||||
|
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.R
|
||||||
|
import org.mozilla.fenix.tabstray.browser.BrowserTrayInteractor
|
||||||
|
import org.mozilla.fenix.tabstray.syncedtabs.SyncedTabsInteractor
|
||||||
|
import org.mozilla.fenix.utils.Settings
|
||||||
|
|
||||||
|
class FloatingActionButtonBindingTest {
|
||||||
|
|
||||||
|
@OptIn(ExperimentalCoroutinesApi::class)
|
||||||
|
@get:Rule
|
||||||
|
val coroutinesTestRule = MainCoroutineRule(TestCoroutineDispatcher())
|
||||||
|
|
||||||
|
private val settings: Settings = mockk(relaxed = true)
|
||||||
|
private val actionButton: ExtendedFloatingActionButton = mockk(relaxed = true)
|
||||||
|
private val browserTrayInteractor: BrowserTrayInteractor = mockk(relaxed = true)
|
||||||
|
private val syncedTabsInteractor: SyncedTabsInteractor = mockk(relaxed = true)
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
mockkStatic(AppCompatResources::class)
|
||||||
|
every { AppCompatResources.getDrawable(any(), any()) } returns mockk(relaxed = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN tab selected page is normal tab THEN shrink and show is called`() {
|
||||||
|
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.NormalTabs))
|
||||||
|
val fabBinding = FloatingActionButtonBinding(
|
||||||
|
tabsTrayStore, settings, actionButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns false
|
||||||
|
|
||||||
|
fabBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { actionButton.shrink() }
|
||||||
|
verify(exactly = 1) { actionButton.show() }
|
||||||
|
verify(exactly = 0) { actionButton.extend() }
|
||||||
|
verify(exactly = 0) { actionButton.hide() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN tab selected page is private tab THEN extend and show is called`() {
|
||||||
|
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.PrivateTabs))
|
||||||
|
val fabBinding = FloatingActionButtonBinding(
|
||||||
|
tabsTrayStore, settings, actionButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns false
|
||||||
|
|
||||||
|
fabBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { actionButton.extend() }
|
||||||
|
verify(exactly = 1) { actionButton.show() }
|
||||||
|
verify(exactly = 0) { actionButton.shrink() }
|
||||||
|
verify(exactly = 0) { actionButton.hide() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN tab selected page is sync tab THEN extend and show is called`() {
|
||||||
|
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.SyncedTabs))
|
||||||
|
val fabBinding = FloatingActionButtonBinding(
|
||||||
|
tabsTrayStore, settings, actionButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns false
|
||||||
|
|
||||||
|
fabBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { actionButton.extend() }
|
||||||
|
verify(exactly = 1) { actionButton.show() }
|
||||||
|
verify(exactly = 0) { actionButton.shrink() }
|
||||||
|
verify(exactly = 0) { actionButton.hide() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN accessibility is enabled THEN show is not called`() {
|
||||||
|
var tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.NormalTabs))
|
||||||
|
var fabBinding = FloatingActionButtonBinding(
|
||||||
|
tabsTrayStore, settings, actionButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns true
|
||||||
|
|
||||||
|
fabBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 0) { actionButton.show() }
|
||||||
|
verify(exactly = 1) { actionButton.hide() }
|
||||||
|
|
||||||
|
tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.PrivateTabs))
|
||||||
|
fabBinding = FloatingActionButtonBinding(
|
||||||
|
tabsTrayStore, settings, actionButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
|
||||||
|
fabBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 0) { actionButton.show() }
|
||||||
|
verify(exactly = 2) { actionButton.hide() }
|
||||||
|
|
||||||
|
tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.SyncedTabs))
|
||||||
|
fabBinding = FloatingActionButtonBinding(
|
||||||
|
tabsTrayStore, settings, actionButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
|
||||||
|
fabBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 0) { actionButton.show() }
|
||||||
|
verify(exactly = 3) { actionButton.hide() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `WHEN selected page is updated THEN button is updated`() {
|
||||||
|
val tabsTrayStore = TabsTrayStore(TabsTrayState(selectedPage = Page.NormalTabs))
|
||||||
|
val fabBinding = FloatingActionButtonBinding(
|
||||||
|
tabsTrayStore, settings, actionButton, browserTrayInteractor, syncedTabsInteractor
|
||||||
|
)
|
||||||
|
every { settings.accessibilityServicesEnabled } returns false
|
||||||
|
|
||||||
|
fabBinding.start()
|
||||||
|
|
||||||
|
verify(exactly = 1) { actionButton.shrink() }
|
||||||
|
verify(exactly = 1) { actionButton.show() }
|
||||||
|
verify(exactly = 0) { actionButton.extend() }
|
||||||
|
verify(exactly = 0) { actionButton.hide() }
|
||||||
|
verify(exactly = 1) { actionButton.setIconResource(R.drawable.ic_new) }
|
||||||
|
|
||||||
|
tabsTrayStore.dispatch(TabsTrayAction.PageSelected(Page.positionToPage(Page.PrivateTabs.ordinal)))
|
||||||
|
tabsTrayStore.waitUntilIdle()
|
||||||
|
|
||||||
|
verify(exactly = 1) { actionButton.shrink() }
|
||||||
|
verify(exactly = 2) { actionButton.show() }
|
||||||
|
verify(exactly = 1) { actionButton.extend() }
|
||||||
|
verify(exactly = 0) { actionButton.hide() }
|
||||||
|
verify(exactly = 1) { actionButton.setText(R.string.tab_drawer_fab_content) }
|
||||||
|
verify(exactly = 2) { actionButton.setIconResource(R.drawable.ic_new) }
|
||||||
|
|
||||||
|
tabsTrayStore.dispatch(TabsTrayAction.PageSelected(Page.positionToPage(Page.SyncedTabs.ordinal)))
|
||||||
|
tabsTrayStore.waitUntilIdle()
|
||||||
|
|
||||||
|
verify(exactly = 1) { actionButton.shrink() }
|
||||||
|
verify(exactly = 3) { actionButton.show() }
|
||||||
|
verify(exactly = 2) { actionButton.extend() }
|
||||||
|
verify(exactly = 0) { actionButton.hide() }
|
||||||
|
verify(exactly = 1) { actionButton.setText(R.string.tab_drawer_fab_sync) }
|
||||||
|
verify(exactly = 1) { actionButton.setIconResource(R.drawable.ic_fab_sync) }
|
||||||
|
|
||||||
|
tabsTrayStore.dispatch(TabsTrayAction.SyncNow)
|
||||||
|
tabsTrayStore.waitUntilIdle()
|
||||||
|
|
||||||
|
verify(exactly = 1) { actionButton.shrink() }
|
||||||
|
verify(exactly = 4) { actionButton.show() }
|
||||||
|
verify(exactly = 3) { actionButton.extend() }
|
||||||
|
verify(exactly = 0) { actionButton.hide() }
|
||||||
|
verify(exactly = 1) { actionButton.setText(R.string.sync_syncing_in_progress) }
|
||||||
|
verify(exactly = 2) { actionButton.setIconResource(R.drawable.ic_fab_sync) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user