386 lines
15 KiB
Kotlin
386 lines
15 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.browser
|
|
|
|
import android.content.Context
|
|
import android.view.View
|
|
import androidx.lifecycle.Lifecycle
|
|
import androidx.lifecycle.LifecycleOwner
|
|
import androidx.lifecycle.LifecycleRegistry
|
|
import androidx.navigation.NavController
|
|
import io.mockk.every
|
|
import io.mockk.mockk
|
|
import io.mockk.spyk
|
|
import io.mockk.verify
|
|
import kotlinx.coroutines.test.TestCoroutineDispatcher
|
|
import mozilla.components.browser.state.action.ContentAction
|
|
import mozilla.components.browser.state.action.RestoreCompleteAction
|
|
import mozilla.components.browser.state.action.TabListAction
|
|
import mozilla.components.browser.state.state.BrowserState
|
|
import mozilla.components.browser.state.state.LoadRequestState
|
|
import mozilla.components.browser.state.state.SessionState
|
|
import mozilla.components.browser.state.state.TabSessionState
|
|
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.rule.MainCoroutineRule
|
|
import org.junit.After
|
|
import org.junit.Assert.fail
|
|
import org.junit.Before
|
|
import org.junit.Rule
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
import org.mozilla.fenix.FenixApplication
|
|
import org.mozilla.fenix.HomeActivity
|
|
import org.mozilla.fenix.R
|
|
import org.mozilla.fenix.components.toolbar.BrowserToolbarView
|
|
import org.mozilla.fenix.components.toolbar.ToolbarIntegration
|
|
import org.mozilla.fenix.ext.application
|
|
import org.mozilla.fenix.ext.components
|
|
import org.mozilla.fenix.ext.settings
|
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
import org.mozilla.fenix.onboarding.FenixOnboarding
|
|
import org.mozilla.fenix.utils.Settings
|
|
import java.lang.Exception
|
|
|
|
@RunWith(FenixRobolectricTestRunner::class)
|
|
class BrowserFragmentTest {
|
|
|
|
private lateinit var store: BrowserStore
|
|
private lateinit var testTab: TabSessionState
|
|
private lateinit var browserFragment: BrowserFragment
|
|
private lateinit var view: View
|
|
private lateinit var homeActivity: HomeActivity
|
|
private lateinit var fenixApplication: FenixApplication
|
|
private lateinit var context: Context
|
|
private lateinit var lifecycleOwner: MockedLifecycleOwner
|
|
private lateinit var navController: NavController
|
|
private lateinit var onboarding: FenixOnboarding
|
|
|
|
private val testDispatcher = TestCoroutineDispatcher()
|
|
|
|
@get:Rule
|
|
val coroutinesTestRule = MainCoroutineRule(testDispatcher)
|
|
|
|
@Before
|
|
fun setup() {
|
|
context = mockk(relaxed = true)
|
|
fenixApplication = mockk(relaxed = true)
|
|
every { context.application } returns fenixApplication
|
|
|
|
homeActivity = mockk(relaxed = true)
|
|
view = mockk(relaxed = true)
|
|
lifecycleOwner = MockedLifecycleOwner(Lifecycle.State.STARTED)
|
|
navController = mockk(relaxed = true)
|
|
onboarding = mockk(relaxed = true)
|
|
|
|
browserFragment = spyk(BrowserFragment())
|
|
every { browserFragment.view } returns view
|
|
every { browserFragment.isAdded } returns true
|
|
every { browserFragment.browserToolbarView } returns mockk(relaxed = true)
|
|
every { browserFragment.activity } returns homeActivity
|
|
every { browserFragment.lifecycle } returns lifecycleOwner.lifecycle
|
|
every { browserFragment.onboarding } returns onboarding
|
|
|
|
every { browserFragment.requireContext() } returns context
|
|
every { browserFragment.initializeUI(any(), any()) } returns mockk()
|
|
every { browserFragment.fullScreenChanged(any()) } returns Unit
|
|
every { browserFragment.resumeDownloadDialogState(any(), any(), any(), any()) } returns Unit
|
|
|
|
testTab = createTab(url = "https://mozilla.org")
|
|
store = BrowserStore()
|
|
every { context.components.core.store } returns store
|
|
}
|
|
|
|
@After
|
|
fun cleanUp() {
|
|
testDispatcher.cleanupTestCoroutines()
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN fragment is added WHEN selected tab changes THEN theme is updated`() {
|
|
browserFragment.observeTabSelection(store)
|
|
verify(exactly = 0) { browserFragment.updateThemeForSession(testTab) }
|
|
|
|
addAndSelectTab(testTab)
|
|
verify(exactly = 1) { browserFragment.updateThemeForSession(testTab) }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN fragment is removing WHEN selected tab changes THEN theme is not updated`() {
|
|
every { browserFragment.isRemoving } returns true
|
|
browserFragment.observeTabSelection(store)
|
|
|
|
addAndSelectTab(testTab)
|
|
verify(exactly = 0) { browserFragment.updateThemeForSession(testTab) }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN browser UI is not initialized WHEN selected tab changes THEN browser UI is initialized`() {
|
|
browserFragment.observeTabSelection(store)
|
|
verify(exactly = 0) { browserFragment.initializeUI(view, testTab) }
|
|
|
|
addAndSelectTab(testTab)
|
|
verify(exactly = 1) { browserFragment.initializeUI(view, testTab) }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN browser UI is initialized WHEN selected tab changes THEN toolbar is expanded`() {
|
|
browserFragment.browserInitialized = true
|
|
browserFragment.observeTabSelection(store)
|
|
|
|
val toolbar: BrowserToolbarView = mockk(relaxed = true)
|
|
every { browserFragment.browserToolbarView } returns toolbar
|
|
|
|
val newSelectedTab = createTab("https://firefox.com")
|
|
addAndSelectTab(newSelectedTab)
|
|
verify(exactly = 1) { toolbar.expand() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN browser UI is initialized WHEN selected tab changes THEN full screen mode is exited`() {
|
|
browserFragment.browserInitialized = true
|
|
browserFragment.observeTabSelection(store)
|
|
|
|
val newSelectedTab = createTab("https://firefox.com")
|
|
addAndSelectTab(newSelectedTab)
|
|
verify(exactly = 1) { browserFragment.fullScreenChanged(false) }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN browser UI is initialized WHEN selected tab changes THEN download dialog is resumed`() {
|
|
browserFragment.browserInitialized = true
|
|
browserFragment.observeTabSelection(store)
|
|
|
|
val newSelectedTab = createTab("https://firefox.com")
|
|
addAndSelectTab(newSelectedTab)
|
|
verify(exactly = 1) {
|
|
browserFragment.resumeDownloadDialogState(newSelectedTab.id, store, context, any())
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN url changes THEN toolbar is expanded`() {
|
|
addAndSelectTab(testTab)
|
|
browserFragment.expandToolbarOnNavigation(store)
|
|
|
|
val toolbar: BrowserToolbarView = mockk(relaxed = true)
|
|
every { browserFragment.browserToolbarView } returns toolbar
|
|
|
|
store.dispatch(ContentAction.UpdateUrlAction(testTab.id, "https://firefox.com")).joinBlocking()
|
|
verify(exactly = 1) { toolbar.expand() }
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN load request is triggered THEN toolbar is expanded`() {
|
|
addAndSelectTab(testTab)
|
|
browserFragment.expandToolbarOnNavigation(store)
|
|
|
|
val toolbar: BrowserToolbarView = mockk(relaxed = true)
|
|
every { browserFragment.browserToolbarView } returns toolbar
|
|
|
|
store.dispatch(
|
|
ContentAction.UpdateLoadRequestAction(
|
|
testTab.id,
|
|
LoadRequestState("https://firefox.com", false, true)
|
|
)
|
|
).joinBlocking()
|
|
verify(exactly = 1) { toolbar.expand() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN tabs are restored WHEN there are no tabs THEN navigate to home`() {
|
|
browserFragment.observeRestoreComplete(store, navController)
|
|
store.dispatch(RestoreCompleteAction).joinBlocking()
|
|
|
|
verify(exactly = 1) { navController.popBackStack(R.id.homeFragment, false) }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN tabs are restored WHEN there are tabs THEN do not navigate`() {
|
|
addAndSelectTab(testTab)
|
|
browserFragment.observeRestoreComplete(store, navController)
|
|
store.dispatch(RestoreCompleteAction).joinBlocking()
|
|
|
|
verify(exactly = 0) { navController.popBackStack(R.id.homeFragment, false) }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN tabs are restored WHEN there is no selected tab THEN navigate to home`() {
|
|
val store = BrowserStore(initialState = BrowserState(tabs = listOf(testTab)))
|
|
browserFragment.observeRestoreComplete(store, navController)
|
|
store.dispatch(RestoreCompleteAction).joinBlocking()
|
|
|
|
verify(exactly = 1) { navController.popBackStack(R.id.homeFragment, false) }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the onboarding is finished WHEN visiting any link THEN the onboarding is not dismissed `() {
|
|
every { onboarding.userHasBeenOnboarded() } returns true
|
|
|
|
browserFragment.observeTabSource(store)
|
|
|
|
val newSelectedTab = createTab("any-tab.org")
|
|
addAndSelectTab(newSelectedTab)
|
|
|
|
verify(exactly = 0) { onboarding.finish() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the onboarding is not finished WHEN visiting a link THEN the onboarding is dismissed `() {
|
|
every { onboarding.userHasBeenOnboarded() } returns false
|
|
|
|
browserFragment.observeTabSource(store)
|
|
|
|
val newSelectedTab = createTab("any-tab.org")
|
|
addAndSelectTab(newSelectedTab)
|
|
|
|
verify(exactly = 1) { onboarding.finish() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the onboarding is not finished WHEN visiting an onboarding link THEN the onboarding is not dismissed `() {
|
|
every { onboarding.userHasBeenOnboarded() } returns false
|
|
|
|
browserFragment.observeTabSource(store)
|
|
|
|
val newSelectedTab = createTab(BaseBrowserFragment.onboardingLinksList[0])
|
|
addAndSelectTab(newSelectedTab)
|
|
|
|
verify(exactly = 0) { onboarding.finish() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the onboarding is not finished WHEN opening a page from another app THEN the onboarding is not dismissed `() {
|
|
every { onboarding.userHasBeenOnboarded() } returns false
|
|
|
|
browserFragment.observeTabSource(store)
|
|
|
|
val newSelectedTab1 = createTab("any-tab-1.org", source = SessionState.Source.External.ActionSearch(mockk()))
|
|
val newSelectedTab2 = createTab("any-tab-2.org", source = SessionState.Source.External.ActionView(mockk()))
|
|
val newSelectedTab3 = createTab("any-tab-3.org", source = SessionState.Source.External.ActionSend(mockk()))
|
|
val newSelectedTab4 = createTab("any-tab-4.org", source = SessionState.Source.External.CustomTab(mockk()))
|
|
|
|
addAndSelectTab(newSelectedTab1)
|
|
verify(exactly = 0) { onboarding.finish() }
|
|
|
|
addAndSelectTab(newSelectedTab2)
|
|
verify(exactly = 0) { onboarding.finish() }
|
|
|
|
addAndSelectTab(newSelectedTab3)
|
|
verify(exactly = 0) { onboarding.finish() }
|
|
|
|
addAndSelectTab(newSelectedTab4)
|
|
verify(exactly = 0) { onboarding.finish() }
|
|
}
|
|
|
|
@Test
|
|
fun `GIVEN the onboarding is not finished WHEN visiting an link after redirect THEN the onboarding is not dismissed `() {
|
|
every { onboarding.userHasBeenOnboarded() } returns false
|
|
|
|
val newSelectedTab: TabSessionState = mockk(relaxed = true)
|
|
every { newSelectedTab.content.loadRequest?.triggeredByRedirect } returns true
|
|
|
|
browserFragment.observeTabSource(store)
|
|
addAndSelectTab(newSelectedTab)
|
|
|
|
verify(exactly = 0) { onboarding.finish() }
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN isPullToRefreshEnabledInBrowser is disabled THEN pull down refresh is disabled`() {
|
|
every { context.settings().isPullToRefreshEnabledInBrowser } returns true
|
|
assert(browserFragment.shouldPullToRefreshBeEnabled(false))
|
|
|
|
every { context.settings().isPullToRefreshEnabledInBrowser } returns false
|
|
assert(!browserFragment.shouldPullToRefreshBeEnabled(false))
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN in fullscreen THEN pull down refresh is disabled`() {
|
|
every { context.settings().isPullToRefreshEnabledInBrowser } returns true
|
|
assert(browserFragment.shouldPullToRefreshBeEnabled(false))
|
|
assert(!browserFragment.shouldPullToRefreshBeEnabled(true))
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN fragment is not attached THEN toolbar invalidation does nothing`() {
|
|
val browserToolbarView: BrowserToolbarView = mockk(relaxed = true)
|
|
val browserToolbar: BrowserToolbar = mockk(relaxed = true)
|
|
val toolbarIntegration: ToolbarIntegration = mockk(relaxed = true)
|
|
every { browserToolbarView.view } returns browserToolbar
|
|
every { browserToolbarView.toolbarIntegration } returns toolbarIntegration
|
|
every { browserFragment.context } returns null
|
|
browserFragment._browserToolbarView = browserToolbarView
|
|
browserFragment.safeInvalidateBrowserToolbarView()
|
|
|
|
verify(exactly = 0) { browserToolbar.invalidateActions() }
|
|
verify(exactly = 0) { toolbarIntegration.invalidateMenu() }
|
|
}
|
|
|
|
@Test
|
|
@Suppress("TooGenericExceptionCaught")
|
|
fun `WHEN fragment is attached and toolbar view is null THEN toolbar invalidation is safe`() {
|
|
every { browserFragment.context } returns mockk(relaxed = true)
|
|
try {
|
|
browserFragment.safeInvalidateBrowserToolbarView()
|
|
} catch (e: Exception) {
|
|
fail("Exception thrown when invalidating toolbar")
|
|
}
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN fragment and view are attached THEN toolbar invalidation is triggered`() {
|
|
val browserToolbarView: BrowserToolbarView = mockk(relaxed = true)
|
|
val browserToolbar: BrowserToolbar = mockk(relaxed = true)
|
|
val toolbarIntegration: ToolbarIntegration = mockk(relaxed = true)
|
|
every { browserToolbarView.view } returns browserToolbar
|
|
every { browserToolbarView.toolbarIntegration } returns toolbarIntegration
|
|
every { browserFragment.context } returns mockk(relaxed = true)
|
|
browserFragment._browserToolbarView = browserToolbarView
|
|
browserFragment.safeInvalidateBrowserToolbarView()
|
|
|
|
verify(exactly = 1) { browserToolbar.invalidateActions() }
|
|
verify(exactly = 1) { toolbarIntegration.invalidateMenu() }
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN fragment configuration changed THEN menu is dismissed`() {
|
|
val browserToolbarView: BrowserToolbarView = mockk(relaxed = true)
|
|
every { browserFragment.context } returns null
|
|
browserFragment._browserToolbarView = browserToolbarView
|
|
|
|
browserFragment.onConfigurationChanged(mockk(relaxed = true))
|
|
|
|
verify(exactly = 1) { browserToolbarView.dismissMenu() }
|
|
}
|
|
|
|
private fun addAndSelectTab(tab: TabSessionState) {
|
|
store.dispatch(TabListAction.AddTabAction(tab)).joinBlocking()
|
|
store.dispatch(TabListAction.SelectTabAction(tab.id)).joinBlocking()
|
|
}
|
|
|
|
internal class MockedLifecycleOwner(initialState: Lifecycle.State) : LifecycleOwner {
|
|
val lifecycleRegistry = LifecycleRegistry(this).apply {
|
|
currentState = initialState
|
|
}
|
|
|
|
override fun getLifecycle(): Lifecycle = lifecycleRegistry
|
|
}
|
|
|
|
@Test
|
|
fun `WHEN updating the last browse activity THEN update the associated preference`() {
|
|
val settings: Settings = mockk(relaxed = true)
|
|
|
|
every { browserFragment.context } returns context
|
|
every { context.settings() } returns settings
|
|
|
|
browserFragment.updateLastBrowseActivity()
|
|
|
|
verify(exactly = 1) { settings.lastBrowseActivity = any() }
|
|
}
|
|
}
|