You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
372 lines
18 KiB
Kotlin
372 lines
18 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.settings.quicksettings
|
|
|
|
import android.content.pm.PackageManager
|
|
import io.mockk.MockKAnnotations
|
|
import io.mockk.every
|
|
import io.mockk.impl.annotations.MockK
|
|
import io.mockk.mockk
|
|
import io.mockk.spyk
|
|
import io.mockk.verify
|
|
import kotlinx.coroutines.runBlocking
|
|
import mozilla.components.browser.state.state.BrowserState
|
|
import mozilla.components.browser.state.state.content.PermissionHighlightsState
|
|
import mozilla.components.browser.state.state.createTab
|
|
import mozilla.components.browser.state.store.BrowserStore
|
|
import mozilla.components.concept.engine.permission.SitePermissions
|
|
import mozilla.components.feature.sitepermissions.SitePermissionsRules
|
|
import mozilla.components.feature.sitepermissions.SitePermissionsRules.Action
|
|
import mozilla.components.feature.sitepermissions.SitePermissionsRules.AutoplayAction
|
|
import mozilla.components.support.test.mock
|
|
import mozilla.components.support.test.robolectric.testContext
|
|
import org.junit.Assert.assertEquals
|
|
import org.junit.Assert.assertFalse
|
|
import org.junit.Assert.assertNotNull
|
|
import org.junit.Assert.assertNotSame
|
|
import org.junit.Assert.assertSame
|
|
import org.junit.Assert.assertTrue
|
|
import org.junit.Before
|
|
import org.junit.Test
|
|
import org.junit.runner.RunWith
|
|
import org.mozilla.fenix.R
|
|
import org.mozilla.fenix.ext.components
|
|
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
|
|
import org.mozilla.fenix.settings.PhoneFeature
|
|
import org.mozilla.fenix.settings.quicksettings.QuickSettingsFragmentStore.Companion.toWebsitePermission
|
|
import org.mozilla.fenix.settings.quicksettings.WebsiteInfoState.Companion.createWebsiteInfoState
|
|
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeEnabled
|
|
import org.mozilla.fenix.settings.quicksettings.ext.shouldBeVisible
|
|
import org.mozilla.fenix.settings.sitepermissions.AUTOPLAY_BLOCK_ALL
|
|
import org.mozilla.fenix.trackingprotection.TrackingProtectionState
|
|
import org.mozilla.fenix.utils.Settings
|
|
|
|
@RunWith(FenixRobolectricTestRunner::class)
|
|
class QuickSettingsFragmentStoreTest {
|
|
private val context = spyk(testContext)
|
|
|
|
@MockK(relaxed = true)
|
|
private lateinit var permissions: SitePermissions
|
|
|
|
@MockK(relaxed = true)
|
|
private lateinit var permissionHighlights: PermissionHighlightsState
|
|
|
|
@MockK(relaxed = true)
|
|
private lateinit var appSettings: Settings
|
|
|
|
@Before
|
|
fun setup() {
|
|
MockKAnnotations.init(this)
|
|
|
|
every { appSettings.getSitePermissionsCustomSettingsRules() } returns getRules()
|
|
}
|
|
|
|
@Test
|
|
fun `createStore constructs a QuickSettingsFragmentState`() {
|
|
val tab = createTab(
|
|
url = "https://www.firefox.com",
|
|
title = "Firefox"
|
|
)
|
|
val browserStore = BrowserStore(BrowserState(tabs = listOf(tab)))
|
|
|
|
every { context.components.core.store } returns browserStore
|
|
|
|
val store = QuickSettingsFragmentStore.createStore(
|
|
context = context,
|
|
websiteUrl = tab.content.url,
|
|
websiteTitle = tab.content.title,
|
|
certificateName = "issuer",
|
|
isSecured = true,
|
|
permissions = permissions,
|
|
permissionHighlights = permissionHighlights,
|
|
settings = appSettings,
|
|
sessionId = tab.id,
|
|
isTrackingProtectionEnabled = true
|
|
)
|
|
|
|
assertNotNull(store)
|
|
assertNotNull(store.state)
|
|
assertNotNull(store.state.webInfoState)
|
|
assertNotNull(store.state.websitePermissionsState)
|
|
assertNotNull(store.state.trackingProtectionState)
|
|
}
|
|
|
|
@Test
|
|
fun `createWebsiteInfoState constructs a WebsiteInfoState with the right values for a secure connection`() {
|
|
val websiteUrl = "https://host.com/page1"
|
|
val websiteTitle = "Hello"
|
|
val certificateIssuer = "issuer"
|
|
val securedStatus = true
|
|
|
|
val state = createWebsiteInfoState(websiteUrl, websiteTitle, securedStatus, certificateIssuer)
|
|
|
|
assertNotNull(state)
|
|
assertSame(websiteUrl, state.websiteUrl)
|
|
assertSame(websiteTitle, state.websiteTitle)
|
|
assertEquals(WebsiteSecurityUiValues.SECURE, state.websiteSecurityUiValues)
|
|
}
|
|
|
|
@Test
|
|
fun `createWebsiteInfoState constructs a WebsiteInfoState with the right values for an insecure connection`() {
|
|
val websiteUrl = "https://host.com/page1"
|
|
val websiteTitle = "Hello"
|
|
val certificateIssuer = "issuer"
|
|
val securedStatus = false
|
|
|
|
val state = createWebsiteInfoState(websiteUrl, websiteTitle, securedStatus, certificateIssuer)
|
|
|
|
assertNotNull(state)
|
|
assertSame(websiteUrl, state.websiteUrl)
|
|
assertSame(websiteTitle, state.websiteTitle)
|
|
assertEquals(WebsiteSecurityUiValues.INSECURE, state.websiteSecurityUiValues)
|
|
}
|
|
|
|
@Test
|
|
fun `createWebsitePermissionState helps in constructing an initial WebsitePermissionState for it's Store`() {
|
|
val permissionHighlights = mockk<PermissionHighlightsState>(relaxed = true)
|
|
|
|
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
|
|
every { permissions.localStorage } returns SitePermissions.Status.ALLOWED
|
|
every { permissions.crossOriginStorageAccess } returns SitePermissions.Status.ALLOWED
|
|
every { permissions.mediaKeySystemAccess } returns SitePermissions.Status.NO_DECISION
|
|
every { permissions.autoplayAudible } returns SitePermissions.AutoplayStatus.ALLOWED
|
|
every { permissions.autoplayInaudible } returns SitePermissions.AutoplayStatus.BLOCKED
|
|
every { appSettings.getAutoplayUserSetting() } returns AUTOPLAY_BLOCK_ALL
|
|
|
|
val state = QuickSettingsFragmentStore.createWebsitePermissionState(
|
|
context, permissions, permissionHighlights, appSettings
|
|
)
|
|
|
|
// Just need to know that the WebsitePermissionsState properties are initialized.
|
|
// Making sure they are correctly initialized is tested in the `initWebsitePermission` test.
|
|
assertNotNull(state)
|
|
assertNotNull(state[PhoneFeature.CAMERA])
|
|
assertNotNull(state[PhoneFeature.MICROPHONE])
|
|
assertNotNull(state[PhoneFeature.NOTIFICATION])
|
|
assertNotNull(state[PhoneFeature.LOCATION])
|
|
assertNotNull(state[PhoneFeature.AUTOPLAY_AUDIBLE])
|
|
assertNotNull(state[PhoneFeature.AUTOPLAY_INAUDIBLE])
|
|
assertNotNull(state[PhoneFeature.PERSISTENT_STORAGE])
|
|
assertNotNull(state[PhoneFeature.CROSS_ORIGIN_STORAGE_ACCESS])
|
|
assertNotNull(state[PhoneFeature.MEDIA_KEY_SYSTEM_ACCESS])
|
|
assertNotNull(state[PhoneFeature.AUTOPLAY])
|
|
}
|
|
|
|
@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
|
|
every { permissionHighlights.isAutoPlayBlocking } returns true
|
|
|
|
val websitePermission = cameraFeature.toWebsitePermission(
|
|
context = context,
|
|
permissions = permissions,
|
|
permissionHighlights = permissionHighlights,
|
|
settings = appSettings
|
|
)
|
|
|
|
assertNotNull(websitePermission)
|
|
assertEquals(cameraFeature, websitePermission.phoneFeature)
|
|
assertEquals(allowedStatus, websitePermission.status)
|
|
assertTrue(websitePermission.isVisible)
|
|
assertTrue(websitePermission.isEnabled)
|
|
assertFalse(websitePermission.isBlockedByAndroid)
|
|
|
|
val autoplayPermission = PhoneFeature.AUTOPLAY.toWebsitePermission(
|
|
context = context,
|
|
permissions = permissions,
|
|
permissionHighlights = permissionHighlights,
|
|
settings = appSettings
|
|
) as WebsitePermission.Autoplay
|
|
|
|
assertNotNull(autoplayPermission)
|
|
assertNotNull(autoplayPermission.autoplayValue)
|
|
assertEquals(PhoneFeature.AUTOPLAY, autoplayPermission.phoneFeature)
|
|
assertTrue(websitePermission.isVisible)
|
|
assertTrue(websitePermission.isEnabled)
|
|
}
|
|
|
|
@Test
|
|
fun `PhoneFeature#getPermissionStatus gets the permission properties from delegates`() {
|
|
val permissionHighlights = mockk<PermissionHighlightsState>(relaxed = true)
|
|
val phoneFeature = PhoneFeature.CAMERA
|
|
every { permissions.camera } returns SitePermissions.Status.NO_DECISION
|
|
|
|
val permissionsStatus = phoneFeature.toWebsitePermission(
|
|
context,
|
|
permissions,
|
|
permissionHighlights,
|
|
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)
|
|
}
|
|
|
|
// 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.
|
|
assertNotNull(permissionsStatus)
|
|
}
|
|
|
|
@Test
|
|
fun `TogglePermission should only modify status and visibility of a specific WebsitePermissionsState`() =
|
|
runBlocking {
|
|
val initialCameraStatus = "initialCameraStatus"
|
|
val initialMicStatus = "initialMicStatus"
|
|
val initialNotificationStatus = "initialNotificationStatus"
|
|
val initialLocationStatus = "initialLocationStatus"
|
|
val initialAutoplayAudibleStatus = "initialAutoplayAudibleStatus"
|
|
val initialAutoplayInaudibleStatus = "initialAutoplayInaudibleStatus"
|
|
val updatedMicrophoneStatus = "updatedNotificationStatus"
|
|
val updatedMicrophoneEnabledStatus = false
|
|
val defaultVisibilityStatus = true
|
|
val defaultEnabledStatus = true
|
|
val defaultBlockedByAndroidStatus = true
|
|
val websiteInfoState = mockk<WebsiteInfoState>()
|
|
val baseWebsitePermission = WebsitePermission.Toggleable(
|
|
phoneFeature = PhoneFeature.CAMERA,
|
|
status = "",
|
|
isVisible = true,
|
|
isEnabled = true,
|
|
isBlockedByAndroid = true
|
|
)
|
|
val initialWebsitePermissionsState = mapOf(
|
|
PhoneFeature.CAMERA to baseWebsitePermission.copy(
|
|
phoneFeature = PhoneFeature.CAMERA,
|
|
status = initialCameraStatus
|
|
),
|
|
PhoneFeature.MICROPHONE to baseWebsitePermission.copy(
|
|
phoneFeature = PhoneFeature.MICROPHONE,
|
|
status = initialMicStatus
|
|
),
|
|
PhoneFeature.NOTIFICATION to baseWebsitePermission.copy(
|
|
phoneFeature = PhoneFeature.NOTIFICATION,
|
|
status = initialNotificationStatus
|
|
),
|
|
PhoneFeature.LOCATION to baseWebsitePermission.copy(
|
|
phoneFeature = PhoneFeature.LOCATION,
|
|
status = initialLocationStatus
|
|
),
|
|
PhoneFeature.AUTOPLAY_AUDIBLE to baseWebsitePermission.copy(
|
|
phoneFeature = PhoneFeature.AUTOPLAY_AUDIBLE,
|
|
status = initialAutoplayAudibleStatus
|
|
),
|
|
PhoneFeature.AUTOPLAY_INAUDIBLE to baseWebsitePermission.copy(
|
|
phoneFeature = PhoneFeature.AUTOPLAY_INAUDIBLE,
|
|
status = initialAutoplayInaudibleStatus
|
|
)
|
|
)
|
|
val initialState = QuickSettingsFragmentState(
|
|
webInfoState = websiteInfoState,
|
|
websitePermissionsState = initialWebsitePermissionsState,
|
|
trackingProtectionState = mock()
|
|
)
|
|
val store = QuickSettingsFragmentStore(initialState)
|
|
|
|
store.dispatch(
|
|
WebsitePermissionAction.TogglePermission(
|
|
PhoneFeature.MICROPHONE,
|
|
updatedMicrophoneStatus,
|
|
updatedMicrophoneEnabledStatus
|
|
)
|
|
).join()
|
|
|
|
assertNotNull(store.state)
|
|
assertNotSame(initialState, store.state)
|
|
assertNotSame(initialWebsitePermissionsState, store.state.websitePermissionsState)
|
|
assertSame(websiteInfoState, store.state.webInfoState)
|
|
|
|
assertNotNull(store.state.websitePermissionsState[PhoneFeature.CAMERA])
|
|
assertEquals(PhoneFeature.CAMERA, store.state.websitePermissionsState.getValue(PhoneFeature.CAMERA).phoneFeature)
|
|
assertEquals(initialCameraStatus, store.state.websitePermissionsState.getValue(PhoneFeature.CAMERA).status)
|
|
assertEquals(defaultVisibilityStatus, store.state.websitePermissionsState.getValue(PhoneFeature.CAMERA).isVisible)
|
|
assertEquals(defaultEnabledStatus, store.state.websitePermissionsState.getValue(PhoneFeature.CAMERA).isEnabled)
|
|
assertEquals(defaultBlockedByAndroidStatus, store.state.websitePermissionsState.getValue(PhoneFeature.CAMERA).isBlockedByAndroid)
|
|
|
|
assertNotNull(store.state.websitePermissionsState[PhoneFeature.MICROPHONE])
|
|
assertEquals(PhoneFeature.MICROPHONE, store.state.websitePermissionsState.getValue(PhoneFeature.MICROPHONE).phoneFeature)
|
|
|
|
// Only the following two properties must have been changed!
|
|
assertEquals(updatedMicrophoneStatus, store.state.websitePermissionsState.getValue(PhoneFeature.MICROPHONE).status)
|
|
assertEquals(updatedMicrophoneEnabledStatus, store.state.websitePermissionsState.getValue(PhoneFeature.MICROPHONE).isEnabled)
|
|
|
|
assertEquals(defaultVisibilityStatus, store.state.websitePermissionsState.getValue(PhoneFeature.MICROPHONE).isVisible)
|
|
assertEquals(defaultBlockedByAndroidStatus, store.state.websitePermissionsState.getValue(PhoneFeature.MICROPHONE).isBlockedByAndroid)
|
|
|
|
assertNotNull(store.state.websitePermissionsState[PhoneFeature.NOTIFICATION])
|
|
assertEquals(PhoneFeature.NOTIFICATION, store.state.websitePermissionsState.getValue(PhoneFeature.NOTIFICATION).phoneFeature)
|
|
assertEquals(initialNotificationStatus, store.state.websitePermissionsState.getValue(PhoneFeature.NOTIFICATION).status)
|
|
assertEquals(defaultVisibilityStatus, store.state.websitePermissionsState.getValue(PhoneFeature.NOTIFICATION).isVisible)
|
|
assertEquals(defaultEnabledStatus, store.state.websitePermissionsState.getValue(PhoneFeature.NOTIFICATION).isEnabled)
|
|
assertEquals(defaultBlockedByAndroidStatus, store.state.websitePermissionsState.getValue(PhoneFeature.NOTIFICATION).isBlockedByAndroid)
|
|
|
|
assertNotNull(store.state.websitePermissionsState[PhoneFeature.LOCATION])
|
|
assertEquals(PhoneFeature.LOCATION, store.state.websitePermissionsState.getValue(PhoneFeature.LOCATION).phoneFeature)
|
|
assertEquals(initialLocationStatus, store.state.websitePermissionsState.getValue(PhoneFeature.LOCATION).status)
|
|
assertEquals(defaultVisibilityStatus, store.state.websitePermissionsState.getValue(PhoneFeature.LOCATION).isVisible)
|
|
assertEquals(defaultEnabledStatus, store.state.websitePermissionsState.getValue(PhoneFeature.LOCATION).isEnabled)
|
|
assertEquals(defaultBlockedByAndroidStatus, store.state.websitePermissionsState.getValue(PhoneFeature.LOCATION).isBlockedByAndroid)
|
|
}
|
|
|
|
@Test
|
|
fun `createTrackingProtectionState constructs a TrackingProtectionState with the right values`() {
|
|
val tab = createTab("https://www.firefox.com")
|
|
val browserStore = BrowserStore(BrowserState(tabs = listOf(tab)))
|
|
val isTrackingProtectionEnabled = true
|
|
|
|
every { context.components.core.store } returns browserStore
|
|
|
|
val state = QuickSettingsFragmentStore.createTrackingProtectionState(
|
|
context = context,
|
|
websiteUrl = tab.content.url,
|
|
sessionId = tab.id,
|
|
isTrackingProtectionEnabled = isTrackingProtectionEnabled
|
|
)
|
|
|
|
assertNotNull(state)
|
|
assertEquals(tab, state.tab)
|
|
assertEquals(tab.content.url, state.url)
|
|
assertEquals(isTrackingProtectionEnabled, state.isTrackingProtectionEnabled)
|
|
assertEquals(0, state.listTrackers.size)
|
|
assertEquals(TrackingProtectionState.Mode.Normal, state.mode)
|
|
assertEquals("", state.lastAccessedCategory)
|
|
}
|
|
|
|
private fun getRules() = SitePermissionsRules(
|
|
camera = Action.ASK_TO_ALLOW,
|
|
location = Action.ASK_TO_ALLOW,
|
|
microphone = Action.ASK_TO_ALLOW,
|
|
notification = Action.ASK_TO_ALLOW,
|
|
autoplayAudible = AutoplayAction.BLOCKED,
|
|
autoplayInaudible = AutoplayAction.BLOCKED,
|
|
persistentStorage = Action.ASK_TO_ALLOW,
|
|
mediaKeySystemAccess = Action.ASK_TO_ALLOW,
|
|
crossOriginStorageAccess = Action.ASK_TO_ALLOW,
|
|
)
|
|
}
|