[fenix] For https://github.com/mozilla-mobile/fenix/issues/4126 - Add tests for the new classes resulting from refactoring
Used runBlocking to ensure we wait for the code using coroutines to execute instead of runBlockingTest and join() since this last option led to failed tests in CI because of "java.lang.IllegalStateException: This job has not completed yet".pull/600/head
parent
35de878eae
commit
c3936f8fc8
@ -0,0 +1,328 @@
|
||||
/* 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.settings.quicksettings
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import assertk.assertAll
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFailure
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.isSameAs
|
||||
import assertk.assertions.isTrue
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkStatic
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifyOrder
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.ObsoleteCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.components.browser.session.Session
|
||||
import mozilla.components.feature.session.SessionUseCases
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions.Status.NO_DECISION
|
||||
import mozilla.components.feature.tabs.TabsUseCases
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.TestApplication
|
||||
import org.mozilla.fenix.browser.BrowserFragment
|
||||
import org.mozilla.fenix.components.PermissionStorage
|
||||
import org.mozilla.fenix.exceptions.ExceptionDomains
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled
|
||||
import org.mozilla.fenix.settings.toggle
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@UseExperimental(ObsoleteCoroutinesApi::class)
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(application = TestApplication::class)
|
||||
class DefaultQuickSettingsControllerTest {
|
||||
private val context = testContext
|
||||
private val store = mockk<QuickSettingsFragmentStore>()
|
||||
private val coroutinesScope = GlobalScope
|
||||
private val navController = mockk<NavController>(relaxed = true)
|
||||
private val browserSession = mockk<Session>()
|
||||
private val sitePermissions = SitePermissions(origin = "", savedAt = 123)
|
||||
private val appSettings = mockk<Settings>(relaxed = true)
|
||||
private val permissionStorage = mockk<PermissionStorage>(relaxed = true)
|
||||
private val trackingExceptions = mockk<ExceptionDomains>(relaxed = true)
|
||||
private val reload = mockk<SessionUseCases.ReloadUrlUseCase>(relaxed = true)
|
||||
private val addNewTab = mockk<TabsUseCases.AddNewTabUseCase>(relaxed = true)
|
||||
private val requestPermissions = mockk<(Array<String>) -> Unit>(relaxed = true)
|
||||
private val reportIssue = mockk<() -> Unit>(relaxed = true)
|
||||
private val displayTrackingProtection = mockk<() -> Unit>(relaxed = true)
|
||||
private val displayPermissions = mockk<() -> Unit>(relaxed = true)
|
||||
private val dismiss = mockk<() -> Unit>(relaxed = true)
|
||||
private val controller = DefaultQuickSettingsController(
|
||||
context = context,
|
||||
quickSettingsStore = store,
|
||||
coroutineScope = coroutinesScope,
|
||||
navController = navController,
|
||||
session = browserSession,
|
||||
sitePermissions = sitePermissions,
|
||||
settings = appSettings,
|
||||
permissionStorage = permissionStorage,
|
||||
trackingExceptions = trackingExceptions,
|
||||
reload = reload,
|
||||
addNewTab = addNewTab,
|
||||
requestRuntimePermissions = requestPermissions,
|
||||
reportSiteIssue = reportIssue,
|
||||
displayTrackingProtection = displayTrackingProtection,
|
||||
displayPermissions = displayPermissions,
|
||||
dismiss = dismiss
|
||||
)
|
||||
|
||||
@Test
|
||||
fun `handleTrackingProtectionToggled should toggle tracking and reload website`() {
|
||||
val testWebsiteHost = "host.com"
|
||||
val websiteHost = slot<String>()
|
||||
val session = slot<Session>()
|
||||
every { store.dispatch(any()) } returns mockk()
|
||||
|
||||
controller.handleTrackingProtectionToggled("https://$testWebsiteHost/page1", false)
|
||||
|
||||
verifyOrder {
|
||||
trackingExceptions.toggle(capture(websiteHost))
|
||||
reload(capture(session))
|
||||
}
|
||||
assertAll {
|
||||
assertThat(websiteHost.isCaptured).isTrue()
|
||||
assertThat(websiteHost.captured).isEqualTo(testWebsiteHost)
|
||||
assertThat(session.isCaptured).isTrue()
|
||||
assertThat(session.captured).isEqualTo(browserSession)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleTrackingProtectionSettingsSelected should navigate to TrackingProtectionFragment`() {
|
||||
controller.handleTrackingProtectionSettingsSelected()
|
||||
|
||||
verify {
|
||||
navController.navigate(
|
||||
QuickSettingsSheetDialogFragmentDirections
|
||||
.actionQuickSettingsSheetDialogFragmentToTrackingProtectionFragment()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
fun `handleReportTrackingProblem should open a report issue webpage and dismiss when in normal mode`() {
|
||||
val websiteWithIssuesUrl = "https://host.com/page1"
|
||||
val testReportUrl = String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, websiteWithIssuesUrl)
|
||||
val reportUrl = slot<String>()
|
||||
// `handleReportTrackingProblem` will behave differently depending on `isCustomTabSession`
|
||||
every { browserSession.isCustomTabSession() } returns false
|
||||
|
||||
controller.handleReportTrackingProblem(websiteWithIssuesUrl)
|
||||
|
||||
verify {
|
||||
addNewTab(capture(reportUrl))
|
||||
dismiss()
|
||||
}
|
||||
assertAll {
|
||||
assertThat(reportUrl.isCaptured).isTrue()
|
||||
assertThat(reportUrl.captured).isEqualTo(testReportUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ObsoleteCoroutinesApi
|
||||
@ExperimentalCoroutinesApi
|
||||
fun `handleReportTrackingProblem should open a report issue in browser from custom tab and dismiss`() {
|
||||
val websiteWithIssuesUrl = "https://host.com/page1"
|
||||
val testReportUrl = String.format(BrowserFragment.REPORT_SITE_ISSUE_URL, websiteWithIssuesUrl)
|
||||
val reportUrl = slot<String>()
|
||||
// `handleReportTrackingProblem` will behave differently depending on `isCustomTabSession`
|
||||
every { browserSession.isCustomTabSession() } returns true
|
||||
|
||||
controller.handleReportTrackingProblem(websiteWithIssuesUrl)
|
||||
|
||||
verify {
|
||||
addNewTab(capture(reportUrl))
|
||||
reportIssue()
|
||||
dismiss()
|
||||
}
|
||||
assertAll {
|
||||
assertThat(reportUrl.isCaptured).isTrue()
|
||||
assertThat(reportUrl.captured).isEqualTo(testReportUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleTrackingProtectionShown should delegate to an injected parameter`() {
|
||||
controller.handleTrackingProtectionShown()
|
||||
|
||||
verify {
|
||||
displayTrackingProtection()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handlePermissionsShown should delegate to an injected parameter`() {
|
||||
controller.handlePermissionsShown()
|
||||
|
||||
verify {
|
||||
displayPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handlePermissionToggled blocked by Android should handleAndroidPermissionRequest`() {
|
||||
val cameraFeature = PhoneFeature.CAMERA
|
||||
val websitePermission = mockk<WebsitePermission.Camera>()
|
||||
val androidPermissions = slot<Array<String>>()
|
||||
every { websitePermission.isBlockedByAndroid } returns true
|
||||
|
||||
controller.handlePermissionToggled(websitePermission)
|
||||
|
||||
verify {
|
||||
controller.handleAndroidPermissionRequest(capture(androidPermissions))
|
||||
}
|
||||
assertAll {
|
||||
assertThat(androidPermissions.isCaptured).isTrue()
|
||||
assertThat(androidPermissions.captured).isEqualTo(cameraFeature.androidPermissionsList)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handlePermissionToggled allowed by Android should toggle the permissions and modify View's state`() {
|
||||
val permissionName = "CAMERA"
|
||||
val websitePermission = mockk<WebsitePermission.Camera>()
|
||||
val toggledFeature = slot<PhoneFeature>()
|
||||
val action = slot<WebsitePermissionAction>()
|
||||
every { websitePermission.isBlockedByAndroid } returns false
|
||||
every { websitePermission.name } returns permissionName
|
||||
every { store.dispatch(any()) } returns mockk()
|
||||
// For using the SitePermissions.toggle(..) extension method we need a static mock of SitePermissions.
|
||||
mockkStatic("org.mozilla.fenix.settings.ExtensionsKt")
|
||||
|
||||
controller.handlePermissionToggled(websitePermission)
|
||||
|
||||
// We want to verify that the Status is toggled and this event is passed to Controller also.
|
||||
assertThat(sitePermissions.camera).isSameAs(NO_DECISION)
|
||||
verifyOrder {
|
||||
sitePermissions.toggle(capture(toggledFeature)).also {
|
||||
controller.handlePermissionsChange(it)
|
||||
}
|
||||
}
|
||||
// We should also modify View's state. Not necessarily as the last operation.
|
||||
verify {
|
||||
store.dispatch(capture(action))
|
||||
}
|
||||
assertAll {
|
||||
assertThat(toggledFeature.isCaptured).isTrue()
|
||||
assertThat(toggledFeature.captured).isSameAs(PhoneFeature.CAMERA)
|
||||
|
||||
assertThat(action.isCaptured).isTrue()
|
||||
assertThat(action.captured).isInstanceOf(WebsitePermissionAction.TogglePermission::class)
|
||||
assertThat((action.captured as WebsitePermissionAction.TogglePermission).websitePermission)
|
||||
.isInstanceOf(websitePermission::class)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleAndroidPermissionGranted should update the View's state`() {
|
||||
val featureGranted = PhoneFeature.CAMERA
|
||||
val permission = with(controller) {
|
||||
featureGranted.getCorrespondingPermission()
|
||||
}
|
||||
val permissionStatus = featureGranted.getActionLabel(context, sitePermissions, appSettings)
|
||||
val permissionEnabled = featureGranted.shouldBeEnabled(context, sitePermissions, appSettings)
|
||||
val action = slot<QuickSettingsFragmentAction>()
|
||||
every { store.dispatch(any()) } returns mockk()
|
||||
|
||||
controller.handleAndroidPermissionGranted(featureGranted)
|
||||
|
||||
verify {
|
||||
store.dispatch(capture(action))
|
||||
}
|
||||
assertAll {
|
||||
assertThat(action.isCaptured).isTrue()
|
||||
assertThat(action.captured).isInstanceOf(WebsitePermissionAction.TogglePermission::class)
|
||||
assertThat((action.captured as WebsitePermissionAction.TogglePermission).websitePermission).isEqualTo(permission)
|
||||
assertThat((action.captured as WebsitePermissionAction.TogglePermission).updatedStatus).isEqualTo(permissionStatus)
|
||||
assertThat((action.captured as WebsitePermissionAction.TogglePermission).updatedEnabledStatus).isEqualTo(permissionEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `handleAndroidPermissionRequest should request from the injected callback`() {
|
||||
val testPermissions = arrayOf("TestPermission")
|
||||
val requiredPermissions = slot<Array<String>>()
|
||||
// every { requestPermissions(capture(requiredPermissions)) } just Runs
|
||||
|
||||
controller.handleAndroidPermissionRequest(testPermissions)
|
||||
|
||||
verify { requestPermissions(capture(requiredPermissions)) }
|
||||
assertAll {
|
||||
assertThat(requiredPermissions.isCaptured).isTrue()
|
||||
assertThat(requiredPermissions.captured).isEqualTo(testPermissions)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ExperimentalCoroutinesApi
|
||||
fun `handlePermissionsChange should store the updated permission and reload webpage`() = runBlocking {
|
||||
val testPermissions = mockk<SitePermissions>()
|
||||
val permissions = slot<SitePermissions>()
|
||||
val session = slot<Session>()
|
||||
|
||||
controller.handlePermissionsChange(testPermissions)
|
||||
|
||||
verifyOrder {
|
||||
permissionStorage.updateSitePermissions(capture(permissions))
|
||||
reload(capture(session))
|
||||
}
|
||||
assertAll {
|
||||
assertThat(permissions.isCaptured).isTrue()
|
||||
assertThat(permissions.captured).isEqualTo(testPermissions)
|
||||
assertThat(session.isCaptured).isTrue()
|
||||
assertThat(session.captured).isEqualTo(browserSession)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `WebsitePermission#getBackingFeature should return the PhoneFeature this permission is mapped from`() {
|
||||
val cameraPermission = mockk<WebsitePermission.Camera>()
|
||||
val microphonePermission = mockk<WebsitePermission.Microphone>()
|
||||
val notificationPermission = mockk<WebsitePermission.Notification>()
|
||||
val locationPermission = mockk<WebsitePermission.Location>()
|
||||
|
||||
with(controller) {
|
||||
assertAll {
|
||||
assertThat(cameraPermission.getBackingFeature()).isSameAs(PhoneFeature.CAMERA)
|
||||
assertThat(microphonePermission.getBackingFeature()).isSameAs(PhoneFeature.MICROPHONE)
|
||||
assertThat(notificationPermission.getBackingFeature()).isSameAs(PhoneFeature.NOTIFICATION)
|
||||
assertThat(locationPermission.getBackingFeature()).isSameAs(PhoneFeature.LOCATION)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PhoneFeature#getCorrespondingPermission should return the WebsitePermission which it maps to`() {
|
||||
with(controller) {
|
||||
assertAll {
|
||||
assertThat(PhoneFeature.CAMERA.getCorrespondingPermission())
|
||||
.isInstanceOf(WebsitePermission.Camera::class)
|
||||
assertThat(PhoneFeature.MICROPHONE.getCorrespondingPermission())
|
||||
.isInstanceOf(WebsitePermission.Microphone::class)
|
||||
assertThat(PhoneFeature.NOTIFICATION.getCorrespondingPermission())
|
||||
.isInstanceOf(WebsitePermission.Notification::class)
|
||||
assertThat(PhoneFeature.LOCATION.getCorrespondingPermission())
|
||||
.isInstanceOf(WebsitePermission.Location::class)
|
||||
assertThat { PhoneFeature.AUTOPLAY.getCorrespondingPermission() }
|
||||
.isFailure().isInstanceOf(KotlinNullPointerException::class)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,323 @@
|
||||
/* 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.settings.quicksettings
|
||||
|
||||
import android.content.pm.PackageManager
|
||||
import assertk.assertAll
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isInstanceOf
|
||||
import assertk.assertions.isNotEqualTo
|
||||
import assertk.assertions.isNotNull
|
||||
import assertk.assertions.isNotSameAs
|
||||
import assertk.assertions.isSameAs
|
||||
import assertk.assertions.isTrue
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.spyk
|
||||
import io.mockk.verify
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import mozilla.components.support.test.robolectric.testContext
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.TestApplication
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.getInsecureWebsiteUiValues
|
||||
import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.getPermissionStatus
|
||||
import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.getSecuredWebsiteUiValues
|
||||
import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.toWebsitePermission
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled
|
||||
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible
|
||||
import org.mozilla.fenix.utils.Settings
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@UseExperimental(kotlinx.coroutines.ObsoleteCoroutinesApi::class)
|
||||
@Config(application = TestApplication::class)
|
||||
class QuickSettingsFragmentStoreTest {
|
||||
private val context = spyk(testContext)
|
||||
private val permissions = mockk<SitePermissions>()
|
||||
private val appSettings = mockk<Settings>()
|
||||
private val secureStringRes = R.string.quick_settings_sheet_secure_connection
|
||||
private val secureDrawableRes = R.drawable.mozac_ic_lock
|
||||
private val secureColorRes = R.color.photonGreen50
|
||||
private val insecureStringRes = R.string.quick_settings_sheet_insecure_connection
|
||||
private val insecureDrawableRes = R.drawable.mozac_ic_globe
|
||||
private val insecureColorRes = R.color.photonRed50
|
||||
|
||||
@Test
|
||||
fun `createStore constructs a QuickSettingsFragmentState`() {
|
||||
val settings = mockk<Settings>(relaxed = true)
|
||||
val permissions = mockk<SitePermissions>(relaxed = true)
|
||||
every { settings.shouldUseTrackingProtection } returns true
|
||||
|
||||
val store = QuickSettingsFragmentStore.createStore(
|
||||
context, "url", true, true, permissions, settings
|
||||
)
|
||||
|
||||
assertAll {
|
||||
assertThat(store).isNotNull()
|
||||
assertThat(store.state).isNotNull()
|
||||
assertThat(store.state.webInfoState).isNotNull()
|
||||
assertThat(store.state.trackingProtectionState).isNotNull()
|
||||
assertThat(store.state.websitePermissionsState).isNotNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createWebsiteInfoState constructs a WebsiteInfoState with the right values for a secure connection`() {
|
||||
val websiteUrl = "https://host.com/page1"
|
||||
val securedStatus = true
|
||||
|
||||
val state = QuickSettingsFragmentStore.createWebsiteInfoState(websiteUrl, securedStatus)
|
||||
|
||||
assertAll {
|
||||
assertThat(state).isNotNull()
|
||||
assertThat(state.websiteUrl).isSameAs(websiteUrl)
|
||||
assertThat(state.securityInfoRes).isEqualTo(secureStringRes)
|
||||
assertThat(state.iconRes).isEqualTo(secureDrawableRes)
|
||||
assertThat(state.iconTintRes).isEqualTo(secureColorRes)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createWebsiteInfoState constructs a WebsiteInfoState with the right values for an insecure connection`() {
|
||||
val websiteUrl = "https://host.com/page1"
|
||||
val securedStatus = false
|
||||
|
||||
val state = QuickSettingsFragmentStore.createWebsiteInfoState(websiteUrl, securedStatus)
|
||||
|
||||
assertAll {
|
||||
assertThat(state).isNotNull()
|
||||
assertThat(state.websiteUrl).isSameAs(websiteUrl)
|
||||
assertThat(state.securityInfoRes).isEqualTo(insecureStringRes)
|
||||
assertThat(state.iconRes).isEqualTo(insecureDrawableRes)
|
||||
assertThat(state.iconTintRes).isEqualTo(insecureColorRes)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createTrackingProtectionState helps in constructing an initial TrackingProtectionState for it's Store`() {
|
||||
val websiteUrl = "https://host.com/pageThatShouldUseTrackingProtection"
|
||||
val trackingPerWebsiteStatus = true
|
||||
val trackingPerAppStatus = true
|
||||
every { appSettings.shouldUseTrackingProtection } returns trackingPerAppStatus
|
||||
|
||||
val state = QuickSettingsFragmentStore.createTrackingProtectionState(
|
||||
websiteUrl, trackingPerWebsiteStatus, appSettings
|
||||
)
|
||||
|
||||
assertAll {
|
||||
assertThat(state).isNotNull()
|
||||
assertThat(state).isNotNull()
|
||||
assertThat(state.websiteUrl).isSameAs(websiteUrl)
|
||||
assertThat(state.isTrackingProtectionEnabledPerWebsite).isSameAs(trackingPerWebsiteStatus)
|
||||
assertThat(state.isTrackingProtectionEnabledPerApp).isEqualTo(trackingPerAppStatus)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `createWebsitePermissionState helps in constructing an initial WebsitePermissionState for it's Store`() {
|
||||
every { context.checkPermission(any(), any(), any()) }.returns(PackageManager.PERMISSION_GRANTED)
|
||||
every { permissions.camera } returns SitePermissions.Status.ALLOWED
|
||||
every { permissions.microphone } returns SitePermissions.Status.NO_DECISION
|
||||
every { permissions.notification } returns SitePermissions.Status.BLOCKED
|
||||
every { permissions.location } returns SitePermissions.Status.ALLOWED
|
||||
|
||||
val state = QuickSettingsFragmentStore.createWebsitePermissionState(
|
||||
context, permissions, appSettings
|
||||
)
|
||||
|
||||
// Just need to know that the WebsitePermissionsState properties are initialized.
|
||||
// Making sure they are correctly initialized is tested in the `initWebsitePermission` test.
|
||||
assertAll {
|
||||
assertThat(state).isNotNull()
|
||||
assertThat(state.camera).isNotNull()
|
||||
assertThat(state.microphone).isNotNull()
|
||||
assertThat(state.notification).isNotNull()
|
||||
assertThat(state.location).isNotNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PhoneFeature#toWebsitePermission helps in constructing the right WebsitePermission`() {
|
||||
val cameraFeature = PhoneFeature.CAMERA
|
||||
val allowedStatus = testContext.getString(R.string.preference_option_phone_feature_allowed)
|
||||
every { context.checkPermission(any(), any(), any()) }.returns(PackageManager.PERMISSION_GRANTED)
|
||||
every { permissions.camera } returns SitePermissions.Status.ALLOWED
|
||||
|
||||
val websitePermission = cameraFeature.toWebsitePermission(context, permissions, appSettings)
|
||||
|
||||
assertAll {
|
||||
assertThat(websitePermission).isNotNull()
|
||||
assertThat(websitePermission).isInstanceOf(WebsitePermission.Camera::class)
|
||||
assertThat(websitePermission.status).isEqualTo(allowedStatus)
|
||||
assertThat(websitePermission.isVisible).isTrue()
|
||||
assertThat(websitePermission.isEnabled).isTrue()
|
||||
assertThat(websitePermission.isBlockedByAndroid).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `PhoneFeature#getPermissionStatus gets the permission properties from delegates`() {
|
||||
val phoneFeature = PhoneFeature.CAMERA
|
||||
every { permissions.camera } returns SitePermissions.Status.NO_DECISION
|
||||
|
||||
val permissionsStatus = phoneFeature.getPermissionStatus(context, permissions, appSettings)
|
||||
|
||||
verify {
|
||||
// Verifying phoneFeature.getActionLabel gets "Status(child of #2#4).ordinal()) was not called"
|
||||
// phoneFeature.getActionLabel(context, permissions, appSettings)
|
||||
phoneFeature.shouldBeVisible(permissions, appSettings)
|
||||
phoneFeature.shouldBeEnabled(context, permissions, appSettings)
|
||||
phoneFeature.isAndroidPermissionGranted(context)
|
||||
}
|
||||
assertAll {
|
||||
// Check that we only have a non-null permission status.
|
||||
// Having each property calculated in a separate delegate means their correctness is
|
||||
// to be tested in that delegated method.
|
||||
assertThat(permissionsStatus).isNotNull()
|
||||
}
|
||||
}
|
||||
|
||||
@ExperimentalCoroutinesApi
|
||||
@Test
|
||||
fun `TrackingProtectionToggled should update only the tracking enabled status`() = runBlocking {
|
||||
val initialUrl = "https://host.com/page1"
|
||||
val initialTrackingPerApp = true
|
||||
val initialTrackingPerWebsite = true
|
||||
val updatedTrackingPerWebsite = false
|
||||
val appSettings = mockk<Settings>()
|
||||
every { appSettings.shouldUseTrackingProtection } returns initialTrackingPerApp
|
||||
val initialTrackingProtectionState = QuickSettingsFragmentStore.createTrackingProtectionState(
|
||||
initialUrl, initialTrackingPerWebsite, appSettings
|
||||
)
|
||||
val initialWebsiteInfoState = mockk<WebsiteInfoState>()
|
||||
val initialWebsitePermissionsState = mockk<WebsitePermissionsState>()
|
||||
val store = QuickSettingsFragmentStore(QuickSettingsFragmentState(
|
||||
initialTrackingProtectionState, initialWebsiteInfoState, initialWebsitePermissionsState
|
||||
))
|
||||
|
||||
store.dispatch(TrackingProtectionAction.TrackingProtectionToggled(updatedTrackingPerWebsite)).join()
|
||||
|
||||
assertAll {
|
||||
assertThat(store.state.webInfoState).isSameAs(initialWebsiteInfoState)
|
||||
assertThat(store.state.websitePermissionsState).isSameAs(initialWebsitePermissionsState)
|
||||
assertThat(store.state.trackingProtectionState).isNotSameAs(initialTrackingProtectionState)
|
||||
|
||||
assertThat(store.state.trackingProtectionState.isTrackingProtectionEnabledPerWebsite)
|
||||
.isNotEqualTo(initialTrackingPerWebsite)
|
||||
assertThat(store.state.trackingProtectionState.isTrackingProtectionEnabledPerWebsite)
|
||||
.isEqualTo(updatedTrackingPerWebsite)
|
||||
assertThat(store.state.trackingProtectionState.isTrackingProtectionEnabledPerApp)
|
||||
.isSameAs(initialTrackingPerApp)
|
||||
assertThat(store.state.trackingProtectionState.websiteUrl).isSameAs(initialUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
@ExperimentalCoroutinesApi
|
||||
fun `TogglePermission should only modify status and visibility of a specific WebsitePermissionsState`() = runBlocking {
|
||||
val cameraPermissionName = "Camera"
|
||||
val microphonePermissionName = "Microphone"
|
||||
val notificationPermissionName = "Notification"
|
||||
val locationPermissionName = "Location"
|
||||
val initialCameraStatus = "initialCameraStatus"
|
||||
val initialMicStatus = "initialMicStatus"
|
||||
val initialNotificationStatus = "initialNotificationStatus"
|
||||
val initialLocationStatus = "initialLocationStatus"
|
||||
val updatedMicrophoneStatus = "updatedNotificationStatus"
|
||||
val updatedMicrophoneEnabledStatus = false
|
||||
val defaultVisibilityStatus = true
|
||||
val defaultEnabledStatus = true
|
||||
val defaultBlockedByAndroidStatus = true
|
||||
val websiteInfoState = mockk<WebsiteInfoState>()
|
||||
val trackingProtectionState = mockk<TrackingProtectionState>()
|
||||
val initialWebsitePermissionsState = WebsitePermissionsState(
|
||||
isVisible = true,
|
||||
camera = WebsitePermission.Camera(initialCameraStatus, defaultVisibilityStatus,
|
||||
defaultEnabledStatus, defaultBlockedByAndroidStatus, cameraPermissionName),
|
||||
microphone = WebsitePermission.Microphone(initialMicStatus, defaultVisibilityStatus,
|
||||
defaultEnabledStatus, defaultBlockedByAndroidStatus, microphonePermissionName),
|
||||
notification = WebsitePermission.Notification(initialNotificationStatus, defaultVisibilityStatus,
|
||||
defaultEnabledStatus, defaultBlockedByAndroidStatus, notificationPermissionName),
|
||||
location = WebsitePermission.Location(initialLocationStatus, defaultVisibilityStatus,
|
||||
defaultEnabledStatus, defaultBlockedByAndroidStatus, locationPermissionName)
|
||||
)
|
||||
val initialState = QuickSettingsFragmentState(
|
||||
trackingProtectionState, websiteInfoState, initialWebsitePermissionsState
|
||||
)
|
||||
val store = QuickSettingsFragmentStore(initialState)
|
||||
|
||||
store.dispatch(WebsitePermissionAction.TogglePermission(
|
||||
mockk<WebsitePermission.Microphone>(), updatedMicrophoneStatus, updatedMicrophoneEnabledStatus)
|
||||
).join()
|
||||
|
||||
assertAll {
|
||||
assertThat(store.state).isNotNull()
|
||||
assertThat(store.state).isNotSameAs(initialState)
|
||||
assertThat(store.state.websitePermissionsState).isNotSameAs(initialWebsitePermissionsState)
|
||||
assertThat(store.state.webInfoState).isSameAs(websiteInfoState)
|
||||
assertThat(store.state.trackingProtectionState).isSameAs(trackingProtectionState)
|
||||
|
||||
assertThat(store.state.websitePermissionsState.camera).isNotNull()
|
||||
assertThat((store.state.websitePermissionsState.camera as WebsitePermission.Camera).name).isEqualTo(cameraPermissionName)
|
||||
assertThat(store.state.websitePermissionsState.camera.status).isEqualTo(initialCameraStatus)
|
||||
assertThat(store.state.websitePermissionsState.camera.isVisible).isEqualTo(defaultVisibilityStatus)
|
||||
assertThat(store.state.websitePermissionsState.camera.isEnabled).isEqualTo(defaultEnabledStatus)
|
||||
assertThat(store.state.websitePermissionsState.camera.isBlockedByAndroid).isEqualTo(defaultBlockedByAndroidStatus)
|
||||
|
||||
assertThat(store.state.websitePermissionsState.microphone).isNotNull()
|
||||
assertThat((store.state.websitePermissionsState.microphone as WebsitePermission.Microphone).name).isEqualTo(microphonePermissionName)
|
||||
// Only the following two properties must have been changed!
|
||||
assertThat(store.state.websitePermissionsState.microphone.status).isEqualTo(updatedMicrophoneStatus)
|
||||
assertThat(store.state.websitePermissionsState.microphone.isEnabled).isEqualTo(updatedMicrophoneEnabledStatus)
|
||||
|
||||
assertThat(store.state.websitePermissionsState.microphone.isVisible).isEqualTo(defaultVisibilityStatus)
|
||||
assertThat(store.state.websitePermissionsState.microphone.isBlockedByAndroid).isEqualTo(defaultBlockedByAndroidStatus)
|
||||
|
||||
assertThat(store.state.websitePermissionsState.notification).isNotNull()
|
||||
assertThat((store.state.websitePermissionsState.notification as WebsitePermission.Notification).name).isEqualTo(notificationPermissionName)
|
||||
assertThat(store.state.websitePermissionsState.notification.status).isEqualTo(initialNotificationStatus)
|
||||
assertThat(store.state.websitePermissionsState.notification.isVisible).isEqualTo(defaultVisibilityStatus)
|
||||
assertThat(store.state.websitePermissionsState.notification.isEnabled).isEqualTo(defaultEnabledStatus)
|
||||
assertThat(store.state.websitePermissionsState.notification.isBlockedByAndroid).isEqualTo(defaultBlockedByAndroidStatus)
|
||||
|
||||
assertThat(store.state.websitePermissionsState.location).isNotNull()
|
||||
assertThat((store.state.websitePermissionsState.location as WebsitePermission.Location).name).isEqualTo(locationPermissionName)
|
||||
assertThat(store.state.websitePermissionsState.location.status).isEqualTo(initialLocationStatus)
|
||||
assertThat(store.state.websitePermissionsState.location.isVisible).isEqualTo(defaultVisibilityStatus)
|
||||
assertThat(store.state.websitePermissionsState.location.isEnabled).isEqualTo(defaultEnabledStatus)
|
||||
assertThat(store.state.websitePermissionsState.location.isBlockedByAndroid).isEqualTo(defaultBlockedByAndroidStatus)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getSecuredWebsiteUiValues() should return the right values`() {
|
||||
val uiValues = getSecuredWebsiteUiValues
|
||||
|
||||
assertAll {
|
||||
assertThat(uiValues.first).isEqualTo(secureStringRes)
|
||||
assertThat(uiValues.second).isEqualTo(secureDrawableRes)
|
||||
assertThat(uiValues.third).isEqualTo(secureColorRes)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `getInsecureWebsiteUiValues() should return the right values`() {
|
||||
val uiValues = getInsecureWebsiteUiValues
|
||||
|
||||
assertAll {
|
||||
assertThat(uiValues.first).isEqualTo(insecureStringRes)
|
||||
assertThat(uiValues.second).isEqualTo(insecureDrawableRes)
|
||||
assertThat(uiValues.third).isEqualTo(insecureColorRes)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,100 @@
|
||||
/* 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.settings.quicksettings
|
||||
|
||||
import assertk.assertAll
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isEqualTo
|
||||
import assertk.assertions.isTrue
|
||||
import io.mockk.mockk
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
import io.mockk.verifyAll
|
||||
import org.junit.Test
|
||||
|
||||
class QuickSettingsInteractorTest {
|
||||
private val controller = mockk<QuickSettingsController>(relaxed = true)
|
||||
private val interactor = QuickSettingsInteractor(controller)
|
||||
|
||||
@Test
|
||||
fun `onReportProblemSelected should delegate the controller`() {
|
||||
val websiteUrl = "https://host.com/page1"
|
||||
val url = slot<String>()
|
||||
|
||||
interactor.onReportProblemSelected(websiteUrl)
|
||||
|
||||
verify {
|
||||
controller.handleReportTrackingProblem(capture(url))
|
||||
}
|
||||
assertAll {
|
||||
assertThat(url.isCaptured).isTrue()
|
||||
assertThat(url.captured).isEqualTo(websiteUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onProtectionToggled should delegate the controller`() {
|
||||
val websiteUrl = "https://host.com/page1"
|
||||
val trackingEnabled = true
|
||||
val url = slot<String>()
|
||||
val trackingStatus = slot<Boolean>()
|
||||
|
||||
interactor.onProtectionToggled(websiteUrl, trackingEnabled)
|
||||
|
||||
verifyAll {
|
||||
controller.handleTrackingProtectionToggled(capture(url), capture(trackingStatus))
|
||||
}
|
||||
assertAll {
|
||||
assertThat(url.isCaptured).isTrue()
|
||||
assertThat(url.captured).isEqualTo(websiteUrl)
|
||||
|
||||
assertThat(trackingStatus.isCaptured).isTrue()
|
||||
assertThat(trackingStatus.captured).isEqualTo(trackingEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onProtectionSettingsSelected should delegate the controller`() {
|
||||
interactor.onProtectionSettingsSelected()
|
||||
|
||||
verify {
|
||||
controller.handleTrackingProtectionSettingsSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onTrackingProtectionShown should delegate the controller`() {
|
||||
interactor.onTrackingProtectionShown()
|
||||
|
||||
verify {
|
||||
controller.handleTrackingProtectionShown()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onPermissionsShown should delegate the controller`() {
|
||||
interactor.onPermissionsShown()
|
||||
|
||||
verify {
|
||||
controller.handlePermissionsShown()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onPermissionToggled should delegate the controller`() {
|
||||
val websitePermission = mockk<WebsitePermission>()
|
||||
val permission = slot<WebsitePermission>()
|
||||
|
||||
interactor.onPermissionToggled(websitePermission)
|
||||
|
||||
verify {
|
||||
controller.handlePermissionToggled(capture(permission))
|
||||
}
|
||||
assertAll {
|
||||
assertThat(permission.isCaptured).isTrue()
|
||||
assertThat(permission.captured).isEqualTo(websitePermission)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,85 @@
|
||||
/* 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.settings.quicksettings.ext
|
||||
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import assertk.assertAll
|
||||
import assertk.assertThat
|
||||
import assertk.assertions.isFalse
|
||||
import assertk.assertions.isTrue
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import mozilla.components.feature.sitepermissions.SitePermissions
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.settings.PhoneFeature
|
||||
|
||||
class PhoneFeatureExtKtTest {
|
||||
@Test
|
||||
fun `shouldBeVisible returns if the user made a decision about the permission`() {
|
||||
val noDecisionForPermission = mockk<SitePermissions>()
|
||||
val userAllowedPermission = mockk<SitePermissions>()
|
||||
val userBlockedPermission = mockk<SitePermissions>()
|
||||
every { noDecisionForPermission.camera } returns SitePermissions.Status.NO_DECISION
|
||||
every { userAllowedPermission.camera } returns SitePermissions.Status.ALLOWED
|
||||
every { userBlockedPermission.camera } returns SitePermissions.Status.BLOCKED
|
||||
|
||||
assertAll {
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeVisible(noDecisionForPermission, mockk())).isFalse()
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeVisible(userAllowedPermission, mockk())).isTrue()
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeVisible(userBlockedPermission, mockk())).isTrue()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `isUserPermissionGranted returns if user allowed or denied a permission`() {
|
||||
val noDecisionForPermission = mockk<SitePermissions>()
|
||||
val userAllowedPermission = mockk<SitePermissions>()
|
||||
val userBlockedPermission = mockk<SitePermissions>()
|
||||
every { noDecisionForPermission.camera } returns SitePermissions.Status.NO_DECISION
|
||||
every { userAllowedPermission.camera } returns SitePermissions.Status.ALLOWED
|
||||
every { userBlockedPermission.camera } returns SitePermissions.Status.BLOCKED
|
||||
|
||||
assertAll {
|
||||
assertThat(PhoneFeature.CAMERA.isUserPermissionGranted(userAllowedPermission, mockk())).isTrue()
|
||||
assertThat(PhoneFeature.CAMERA.isUserPermissionGranted(noDecisionForPermission, mockk())).isFalse()
|
||||
assertThat(PhoneFeature.CAMERA.isUserPermissionGranted(userBlockedPermission, mockk())).isFalse()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `shouldBeEnabled returns if permission is granted by user and Android`() {
|
||||
val androidPermissionGrantedContext = mockk<Context>()
|
||||
val androidPermissionDeniedContext = mockk<Context>()
|
||||
val userAllowedPermission = mockk<SitePermissions>()
|
||||
val noDecisionForPermission = mockk<SitePermissions>()
|
||||
val userBlockedPermission = mockk<SitePermissions>()
|
||||
every { androidPermissionGrantedContext.checkPermission(any(), any(), any()) }
|
||||
.returns(PackageManager.PERMISSION_GRANTED)
|
||||
every { androidPermissionDeniedContext.checkPermission(any(), any(), any()) }
|
||||
.returns(PackageManager.PERMISSION_DENIED)
|
||||
every { userAllowedPermission.camera } returns SitePermissions.Status.ALLOWED
|
||||
every { noDecisionForPermission.camera } returns SitePermissions.Status.NO_DECISION
|
||||
every { userBlockedPermission.camera } returns SitePermissions.Status.BLOCKED
|
||||
|
||||
assertAll {
|
||||
// Check result for when the Android permission is granted to the app
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeEnabled(
|
||||
androidPermissionGrantedContext, userAllowedPermission, mockk())).isTrue()
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeEnabled(
|
||||
androidPermissionGrantedContext, noDecisionForPermission, mockk())).isFalse()
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeEnabled(
|
||||
androidPermissionGrantedContext, userBlockedPermission, mockk())).isFalse()
|
||||
|
||||
// Check result for when the Android permission is denied to the app
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeEnabled(
|
||||
androidPermissionDeniedContext, userAllowedPermission, mockk())).isFalse()
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeEnabled(
|
||||
androidPermissionDeniedContext, noDecisionForPermission, mockk())).isFalse()
|
||||
assertThat(PhoneFeature.CAMERA.shouldBeEnabled(
|
||||
androidPermissionDeniedContext, userBlockedPermission, mockk())).isFalse()
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue