1
0
Fork 0

For #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".
master
Mugurell 2019-10-16 20:16:41 +03:00 committed by Emily Kager
parent 9f56624788
commit c5739df969
6 changed files with 862 additions and 12 deletions

View File

@ -5,6 +5,7 @@
package org.mozilla.fenix.settings.quicksettings
import android.content.Context
import androidx.annotation.VisibleForTesting
import androidx.navigation.NavController
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
@ -197,7 +198,8 @@ class DefaultQuickSettingsController(
*
* @param requestedPermissions [Array]<[String]> runtime permissions needed to be requested.
*/
private fun handleAndroidPermissionRequest(requestedPermissions: Array<String>) {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun handleAndroidPermissionRequest(requestedPermissions: Array<String>) {
requestRuntimePermissions(requestedPermissions)
}
@ -207,7 +209,8 @@ class DefaultQuickSettingsController(
*
* @param updatedPermissions [SitePermissions] updated website permissions.
*/
private fun handlePermissionsChange(updatedPermissions: SitePermissions) {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun handlePermissionsChange(updatedPermissions: SitePermissions) {
coroutineScope.launch(Dispatchers.IO) {
permissionStorage.updateSitePermissions(updatedPermissions)
reload(session)
@ -219,7 +222,8 @@ class DefaultQuickSettingsController(
*
* Get this [WebsitePermission]'s [PhoneFeature].
*/
private fun WebsitePermission.getBackingFeature(): PhoneFeature = when (this) {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun WebsitePermission.getBackingFeature(): PhoneFeature = when (this) {
is WebsitePermission.Camera -> PhoneFeature.CAMERA
is WebsitePermission.Microphone -> PhoneFeature.MICROPHONE
is WebsitePermission.Notification -> PhoneFeature.NOTIFICATION
@ -232,7 +236,8 @@ class DefaultQuickSettingsController(
* **The result only informs about the type of [WebsitePermission].
* The resulting object's properties are just stubs and not dependable.**
*/
private fun PhoneFeature.getCorrespondingPermission(): WebsitePermission {
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun PhoneFeature.getCorrespondingPermission(): WebsitePermission {
val defaultStatus = ""
val defaultEnabled = false
val defaultVisible = false

View File

@ -8,6 +8,7 @@ import android.content.Context
import androidx.annotation.ColorRes
import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import androidx.annotation.VisibleForTesting
import mozilla.components.feature.sitepermissions.SitePermissions
import mozilla.components.lib.state.Action
import mozilla.components.lib.state.Reducer
@ -42,7 +43,8 @@ class QuickSettingsFragmentStore(
/**
* String, Drawable & Drawable Tint color used to display that the current website connection is secured.
*/
private val getSecuredWebsiteUiValues = Triple(
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
val getSecuredWebsiteUiValues = Triple(
R.string.quick_settings_sheet_secure_connection,
R.drawable.mozac_ic_lock,
R.color.photonGreen50
@ -52,7 +54,8 @@ class QuickSettingsFragmentStore(
* String, Drawable & Drawable Tint color used to display that the current website connection is
* **not** secured.
*/
private val getInsecureWebsiteUiValues = Triple(
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
val getInsecureWebsiteUiValues = Triple(
R.string.quick_settings_sheet_insecure_connection,
R.drawable.mozac_ic_globe,
R.color.photonRed50
@ -97,7 +100,8 @@ class QuickSettingsFragmentStore(
* tracking protection is enabled for the current website or not.
* @param settings [Settings] application settings.
*/
private fun createTrackingProtectionState(
@VisibleForTesting
fun createTrackingProtectionState(
websiteUrl: String,
isTrackingProtectionOn: Boolean,
settings: Settings
@ -117,7 +121,8 @@ class QuickSettingsFragmentStore(
* @param websiteUrl [String] the URL of the current web page.
* @param isSecured [Boolean] whether the connection is secured (TLS) or not.
*/
private fun createWebsiteInfoState(
@VisibleForTesting
fun createWebsiteInfoState(
websiteUrl: String,
isSecured: Boolean
): WebsiteInfoState {
@ -138,7 +143,8 @@ class QuickSettingsFragmentStore(
* @param permissions [SitePermissions]? list of website permissions and their status.
* @param settings [Settings] application settings.
*/
private fun createWebsitePermissionState(
@VisibleForTesting
fun createWebsitePermissionState(
context: Context,
permissions: SitePermissions?,
settings: Settings
@ -158,7 +164,8 @@ class QuickSettingsFragmentStore(
/**
* [PhoneFeature] to a [WebsitePermission] mapper.
*/
private fun PhoneFeature.toWebsitePermission(
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun PhoneFeature.toWebsitePermission(
context: Context,
permissions: SitePermissions?,
settings: Settings
@ -185,7 +192,8 @@ class QuickSettingsFragmentStore(
/**
* Helper method for getting the [WebsitePermission] properties based on a specific [PhoneFeature].
*/
private fun PhoneFeature.getPermissionStatus(
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
fun PhoneFeature.getPermissionStatus(
context: Context,
permissions: SitePermissions?,
settings: Settings
@ -199,7 +207,8 @@ class QuickSettingsFragmentStore(
/**
* Helper class acting as a temporary container of [WebsitePermission] properties.
*/
private data class PermissionStatus(
@VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
data class PermissionStatus(
val status: String,
val isVisible: Boolean,
val isEnabled: Boolean,

View File

@ -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)
}
}
}
}

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}

View File

@ -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()
}
}
}