283 lines
11 KiB
Kotlin
283 lines
11 KiB
Kotlin
/* 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.components.toolbar
|
|
|
|
import android.content.Context
|
|
import android.view.View
|
|
import androidx.compose.ui.unit.dp
|
|
import io.mockk.Runs
|
|
import io.mockk.every
|
|
import io.mockk.just
|
|
import io.mockk.mockk
|
|
import io.mockk.spyk
|
|
import io.mockk.verify
|
|
import kotlinx.coroutines.CoroutineScope
|
|
import kotlinx.coroutines.cancel
|
|
import mozilla.components.browser.state.action.ContentAction
|
|
import mozilla.components.browser.state.state.BrowserState
|
|
import mozilla.components.browser.state.state.CustomTabSessionState
|
|
import mozilla.components.browser.state.state.TabSessionState
|
|
import mozilla.components.browser.state.state.createCustomTab
|
|
import mozilla.components.browser.state.state.createTab
|
|
import mozilla.components.browser.state.store.BrowserStore
|
|
import mozilla.components.browser.toolbar.BrowserToolbar
|
|
import mozilla.components.support.test.ext.joinBlocking
|
|
import mozilla.components.support.test.robolectric.testContext
|
|
import mozilla.components.support.test.rule.MainCoroutineRule
|
|
import mozilla.telemetry.glean.testing.GleanTestRule
|
|
import org.junit.Assert.assertEquals
|
|
import org.junit.Assert.assertFalse
|
|
import org.junit.Assert.assertNotNull
|
|
import org.junit.Assert.assertNull
|
|
import org.junit.Assert.assertTrue
|
|
import org.junit.Rule
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
import org.mozilla.fenix.GleanMetrics.TrackingProtection
|
|
import org.mozilla.fenix.R
|
|
import org.mozilla.fenix.compose.cfr.CFRPopup
|
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
import org.mozilla.fenix.utils.Settings
|
|
|
|
@RunWith(FenixRobolectricTestRunner::class)
|
|
class BrowserToolbarCFRPresenterTest {
|
|
@get:Rule
|
|
val coroutinesTestRule = MainCoroutineRule()
|
|
|
|
@get:Rule
|
|
val gleanTestRule = GleanTestRule(testContext)
|
|
|
|
@Test
|
|
fun `GIVEN the TCP CFR should be shown for a custom tab WHEN the custom tab is fully loaded THEN the TCP CFR is shown`() {
|
|
val customTab = createCustomTab(url = "")
|
|
val browserStore = createBrowserStore(customTab = customTab)
|
|
val presenter = createPresenterThatShowsCFRs(
|
|
browserStore = browserStore,
|
|
sessionId = customTab.id,
|
|
)
|
|
|
|
presenter.start()
|
|
|
|
assertNotNull(presenter.tcpCfrScope)
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(customTab.id, 0)).joinBlocking()
|
|
verify(exactly = 0) { presenter.showTcpCfr() }
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(customTab.id, 33)).joinBlocking()
|
|
verify(exactly = 0) { presenter.showTcpCfr() }
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(customTab.id, 100)).joinBlocking()
|
|
verify { presenter.showTcpCfr() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the TCP CFR should be shown WHEN the current normal tab is fully loaded THEN the TCP CFR is shown`() {
|
|
val normalTab = createTab(url = "", private = false)
|
|
val browserStore = createBrowserStore(
|
|
tab = normalTab,
|
|
selectedTabId = normalTab.id,
|
|
)
|
|
val presenter = createPresenterThatShowsCFRs(browserStore = browserStore)
|
|
|
|
presenter.start()
|
|
|
|
assertNotNull(presenter.tcpCfrScope)
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(normalTab.id, 1)).joinBlocking()
|
|
verify(exactly = 0) { presenter.showTcpCfr() }
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(normalTab.id, 98)).joinBlocking()
|
|
verify(exactly = 0) { presenter.showTcpCfr() }
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(normalTab.id, 100)).joinBlocking()
|
|
verify { presenter.showTcpCfr() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the TCP CFR should be shown WHEN the current private tab is fully loaded THEN the TCP CFR is shown`() {
|
|
val privateTab = createTab(url = "", private = true)
|
|
val browserStore = createBrowserStore(
|
|
tab = privateTab,
|
|
selectedTabId = privateTab.id,
|
|
)
|
|
val presenter = createPresenterThatShowsCFRs(browserStore = browserStore)
|
|
|
|
presenter.start()
|
|
|
|
assertNotNull(presenter.tcpCfrScope)
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(privateTab.id, 14)).joinBlocking()
|
|
verify(exactly = 0) { presenter.showTcpCfr() }
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(privateTab.id, 99)).joinBlocking()
|
|
verify(exactly = 0) { presenter.showTcpCfr() }
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(privateTab.id, 100)).joinBlocking()
|
|
verify { presenter.showTcpCfr() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the TCP CFR should be shown WHEN the current tab is fully loaded THEN the TCP CFR is only shown once`() {
|
|
val tab = createTab(url = "")
|
|
val browserStore = createBrowserStore(
|
|
tab = tab,
|
|
selectedTabId = tab.id,
|
|
)
|
|
val presenter = createPresenterThatShowsCFRs(browserStore = browserStore)
|
|
|
|
presenter.start()
|
|
|
|
assertNotNull(presenter.tcpCfrScope)
|
|
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(tab.id, 99)).joinBlocking()
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(tab.id, 100)).joinBlocking()
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(tab.id, 100)).joinBlocking()
|
|
browserStore.dispatch(ContentAction.UpdateProgressAction(tab.id, 100)).joinBlocking()
|
|
verify(exactly = 1) { presenter.showTcpCfr() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the TCP CFR should not be shown WHEN the feature starts THEN don't observe the store for updates`() {
|
|
val presenter = createPresenter(
|
|
settings = mockk {
|
|
every { shouldShowTotalCookieProtectionCFR } returns false
|
|
},
|
|
)
|
|
|
|
presenter.start()
|
|
|
|
assertNull(presenter.tcpCfrScope)
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the store is observed for updates WHEN the presenter is stopped THEN stop observing the store`() {
|
|
val tcpScope: CoroutineScope = mockk {
|
|
every { cancel() } just Runs
|
|
}
|
|
val presenter = createPresenter()
|
|
presenter.tcpCfrScope = tcpScope
|
|
|
|
presenter.stop()
|
|
|
|
verify { tcpScope.cancel() }
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN the TCP CFR is to be shown THEN instantiate a new one and remember to not show it again`() {
|
|
val settings: Settings = mockk(relaxed = true)
|
|
val presenter = createPresenter(
|
|
anchor = mockk(relaxed = true),
|
|
settings = settings,
|
|
)
|
|
|
|
presenter.showTcpCfr()
|
|
|
|
verify { settings.shouldShowTotalCookieProtectionCFR = false }
|
|
assertNotNull(presenter.tcpCfrPopup)
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN the TCP CFR is instantiated THEN set the intended properties`() {
|
|
val anchor: View = mockk(relaxed = true)
|
|
val settings: Settings = mockk(relaxed = true)
|
|
val presenter = createPresenter(
|
|
anchor = anchor,
|
|
settings = settings,
|
|
)
|
|
|
|
presenter.showTcpCfr()
|
|
|
|
verify { settings.shouldShowTotalCookieProtectionCFR = false }
|
|
assertNotNull(presenter.tcpCfrPopup)
|
|
presenter.tcpCfrPopup?.let {
|
|
assertEquals("Test", it.text)
|
|
assertEquals(anchor, it.anchor)
|
|
assertEquals(CFRPopup.DEFAULT_WIDTH.dp, it.properties.popupWidth)
|
|
assertEquals(CFRPopup.IndicatorDirection.DOWN, it.properties.indicatorDirection)
|
|
assertTrue(it.properties.dismissOnBackPress)
|
|
assertTrue(it.properties.dismissOnClickOutside)
|
|
assertFalse(it.properties.overlapAnchor)
|
|
assertEquals(CFRPopup.DEFAULT_INDICATOR_START_OFFSET.dp, it.properties.indicatorArrowStartOffset)
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN the TCP CFR is shown THEN log telemetry`() {
|
|
val presenter = createPresenter(
|
|
anchor = mockk(relaxed = true),
|
|
)
|
|
|
|
assertNull(TrackingProtection.tcpCfrShown.testGetValue())
|
|
|
|
presenter.showTcpCfr()
|
|
|
|
assertNotNull(TrackingProtection.tcpCfrShown.testGetValue())
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN the TCP CFR is dismissed THEN log telemetry`() {
|
|
val presenter = createPresenter(
|
|
anchor = mockk(relaxed = true),
|
|
)
|
|
|
|
presenter.showTcpCfr()
|
|
|
|
assertNull(TrackingProtection.tcpCfrExplicitDismissal.testGetValue())
|
|
presenter.tcpCfrPopup!!.onDismiss.invoke(true)
|
|
assertNotNull(TrackingProtection.tcpCfrExplicitDismissal.testGetValue())
|
|
|
|
assertNull(TrackingProtection.tcpCfrImplicitDismissal.testGetValue())
|
|
presenter.tcpCfrPopup!!.onDismiss.invoke(false)
|
|
assertNotNull(TrackingProtection.tcpCfrImplicitDismissal.testGetValue())
|
|
}
|
|
|
|
/**
|
|
* Creates and return a [spyk] of a [BrowserToolbarCFRPresenter] that can handle actually showing CFRs.
|
|
*/
|
|
private fun createPresenterThatShowsCFRs(
|
|
context: Context = mockk(),
|
|
anchor: View = mockk(),
|
|
browserStore: BrowserStore = mockk(),
|
|
settings: Settings = mockk { every { shouldShowTotalCookieProtectionCFR } returns true },
|
|
toolbar: BrowserToolbar = mockk(),
|
|
sessionId: String? = null,
|
|
) = spyk(createPresenter(context, anchor, browserStore, settings, toolbar, sessionId)) {
|
|
every { showTcpCfr() } just Runs
|
|
}
|
|
|
|
/**
|
|
* Create and return a [BrowserToolbarCFRPresenter] with all constructor properties mocked by default.
|
|
* Calls to show a CFR will fail. If this behavior is needed to work use [createPresenterThatShowsCFRs].
|
|
*/
|
|
private fun createPresenter(
|
|
context: Context = mockk { every { getString(R.string.tcp_cfr_message) } returns "Test" },
|
|
anchor: View = mockk(),
|
|
browserStore: BrowserStore = mockk(),
|
|
settings: Settings = mockk(relaxed = true) { every { shouldShowTotalCookieProtectionCFR } returns true },
|
|
toolbar: BrowserToolbar = mockk {
|
|
every { findViewById<View>(R.id.mozac_browser_toolbar_security_indicator) } returns anchor
|
|
},
|
|
sessionId: String? = null,
|
|
) = BrowserToolbarCFRPresenter(
|
|
context = context,
|
|
browserStore = browserStore,
|
|
settings = settings,
|
|
toolbar = toolbar,
|
|
sessionId = sessionId,
|
|
)
|
|
|
|
private fun createBrowserStore(
|
|
tab: TabSessionState? = null,
|
|
customTab: CustomTabSessionState? = null,
|
|
selectedTabId: String? = null,
|
|
) = BrowserStore(
|
|
initialState = BrowserState(
|
|
tabs = if (tab != null) listOf(tab) else listOf(),
|
|
customTabs = if (customTab != null) listOf(customTab) else listOf(),
|
|
selectedTabId = selectedTabId,
|
|
),
|
|
)
|
|
}
|