Skip to content
This repository has been archived by the owner on Jun 17, 2024. It is now read-only.

Commit

Permalink
Merge branch 'main' into verifySavedAddressCanBeEditedTest
Browse files Browse the repository at this point in the history
  • Loading branch information
mergify[bot] authored Feb 15, 2024
2 parents 73bea71 + d6718d9 commit fd3c11c
Show file tree
Hide file tree
Showing 31 changed files with 430 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import mozilla.components.concept.engine.Settings
import mozilla.components.concept.engine.webextension.Action
import mozilla.components.concept.engine.webextension.ActionHandler
import mozilla.components.concept.engine.webextension.DisabledFlags
import mozilla.components.concept.engine.webextension.Incognito
import mozilla.components.concept.engine.webextension.MessageHandler
import mozilla.components.concept.engine.webextension.Metadata
import mozilla.components.concept.engine.webextension.Port
Expand Down Expand Up @@ -373,6 +374,7 @@ class GeckoWebExtension(
baseUrl = it.baseUrl,
temporary = it.temporary,
detailUrl = it.amoListingUrl,
incognito = Incognito.fromString(it.incognito),
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import mozilla.components.concept.engine.DefaultSettings
import mozilla.components.concept.engine.webextension.Action
import mozilla.components.concept.engine.webextension.ActionHandler
import mozilla.components.concept.engine.webextension.DisabledFlags
import mozilla.components.concept.engine.webextension.Incognito
import mozilla.components.concept.engine.webextension.MessageHandler
import mozilla.components.concept.engine.webextension.Port
import mozilla.components.concept.engine.webextension.TabHandler
Expand Down Expand Up @@ -420,6 +421,7 @@ class GeckoWebExtensionTest {
updateDate = "updateDate",
reviewCount = 2,
averageRating = 2.2,
incognito = "split",
),
)
val extensionWithMetadata = GeckoWebExtension(nativeWebExtension, runtime)
Expand Down Expand Up @@ -447,6 +449,7 @@ class GeckoWebExtensionTest {
assertTrue(metadata.disabledFlags.contains(DisabledFlags.USER))
assertFalse(metadata.disabledFlags.contains(DisabledFlags.BLOCKLIST))
assertFalse(metadata.disabledFlags.contains(DisabledFlags.APP_SUPPORT))
assertEquals(Incognito.SPLIT, metadata.incognito)
}

@Test
Expand All @@ -463,6 +466,7 @@ class GeckoWebExtensionTest {
baseUrl = "moz-extension://123c5c5b-cd03-4bea-b23f-ac0b9ab40257/",
disabledFlags = DisabledFlags.USER,
permissions = arrayOf("p1", "p2"),
incognito = null,
),
)
val extensionWithMetadata = GeckoWebExtension(nativeWebExtension, runtime)
Expand All @@ -484,6 +488,7 @@ class GeckoWebExtensionTest {
assertNull(metadata.reviewUrl)
assertNull(metadata.updateDate)
assertNull(metadata.downloadUrl)
assertEquals(Incognito.SPANNING, metadata.incognito)
}

@Test
Expand Down Expand Up @@ -583,4 +588,49 @@ class GeckoWebExtensionTest {
webExtensionWithIcon.getIcon(48)
verify(iconMock).getBitmap(48)
}

@Test
fun `incognito set to spanning`() {
val runtime: GeckoRuntime = mock()
val nativeWebExtension = mockNativeWebExtension(
id = "id",
location = "uri",
metaData = mockNativeWebExtensionMetaData(version = "1", incognito = "spanning"),
)
val extensionWithMetadata = GeckoWebExtension(nativeWebExtension, runtime)

val metadata = extensionWithMetadata.getMetadata()
assertNotNull(metadata)
assertEquals(Incognito.SPANNING, metadata.incognito)
}

@Test
fun `incognito set to not_allowed`() {
val runtime: GeckoRuntime = mock()
val nativeWebExtension = mockNativeWebExtension(
id = "id",
location = "uri",
metaData = mockNativeWebExtensionMetaData(version = "1", incognito = "not_allowed"),
)
val extensionWithMetadata = GeckoWebExtension(nativeWebExtension, runtime)

val metadata = extensionWithMetadata.getMetadata()
assertNotNull(metadata)
assertEquals(Incognito.NOT_ALLOWED, metadata.incognito)
}

@Test
fun `incognito set to split`() {
val runtime: GeckoRuntime = mock()
val nativeWebExtension = mockNativeWebExtension(
id = "id",
location = "uri",
metaData = mockNativeWebExtensionMetaData(version = "1", incognito = "split"),
)
val extensionWithMetadata = GeckoWebExtension(nativeWebExtension, runtime)

val metadata = extensionWithMetadata.getMetadata()
assertNotNull(metadata)
assertEquals(Incognito.SPLIT, metadata.incognito)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ fun mockNativeWebExtensionMetaData(
updateDate: String? = null,
reviewCount: Int = 0,
averageRating: Double = 0.0,
incognito: String? = "spanning",
): WebExtension.MetaData {
val metadata: WebExtension.MetaData = mock()
ReflectionUtils.setField(metadata, "icon", icon)
Expand Down Expand Up @@ -82,6 +83,8 @@ fun mockNativeWebExtensionMetaData(
ReflectionUtils.setField(metadata, "updateDate", updateDate)
ReflectionUtils.setField(metadata, "reviewCount", reviewCount)
ReflectionUtils.setField(metadata, "averageRating", averageRating)
ReflectionUtils.setField(metadata, "averageRating", averageRating)
ReflectionUtils.setField(metadata, "incognito", incognito)
return metadata
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -938,6 +938,16 @@ sealed class TranslationsAction : BrowserAction() {
val translationError: TranslationError,
) : TranslationsAction(), ActionWithTab

/**
* Indicates an app level translations error occurred and to set the [TranslationError] on
* [BrowserState.translationEngine].
*
* @property error The [TranslationError] that occurred.
*/
data class EngineExceptionAction(
val error: TranslationError,
) : TranslationsAction()

/**
* Indicates that the given [operation] data should be fetched for the given [tabId].
*
Expand All @@ -949,6 +959,16 @@ sealed class TranslationsAction : BrowserAction() {
val operation: TranslationOperation,
) : TranslationsAction(), ActionWithTab

/**
* Sets whether the device architecture supports translations or not on
* [BrowserState.translationEngine].
*
* @property isEngineSupported If the engine supports translations on this device.
*/
data class SetEngineSupportedAction(
val isEngineSupported: Boolean,
) : TranslationsAction()

/**
* Sets the languages that are supported by the translations engine.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ package mozilla.components.browser.state.engine.middleware
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch
import mozilla.components.browser.state.action.BrowserAction
import mozilla.components.browser.state.action.InitAction
import mozilla.components.browser.state.action.TranslationsAction
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
Expand Down Expand Up @@ -40,6 +41,11 @@ class TranslationsMiddleware(
) {
// Pre process actions
when (action) {
is InitAction ->
scope.launch {
requestEngineSupport(context)
}

is TranslationsAction.OperationRequestedAction -> {
when (action.operation) {
TranslationOperation.FETCH_SUPPORTED_LANGUAGES -> {
Expand Down Expand Up @@ -117,6 +123,35 @@ class TranslationsMiddleware(
next(action)
}

/**
* Checks if the translations engine supports the device architecture and updates the state.
*
* @param context Context to use to dispatch to the store.
*/
private fun requestEngineSupport(
context: MiddlewareContext<BrowserState, BrowserAction>,
) {
engine.isTranslationsEngineSupported(
onSuccess = { isEngineSupported ->
context.store.dispatch(
TranslationsAction.SetEngineSupportedAction(
isEngineSupported = isEngineSupported,
),
)
logger.info("Success requesting engine support.")
},

onError = { error ->
context.store.dispatch(
TranslationsAction.EngineExceptionAction(
error = TranslationError.UnknownEngineSupportError(error),
),
)
logger.error("Error requesting engine support: ", error)
},
)
}

/**
* Retrieves the list of supported languages using [scope] and dispatches the result to the
* store via [TranslationsAction.SetSupportedLanguagesAction] or else dispatches the failure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,17 @@ package mozilla.components.browser.state.reducer
import mozilla.components.browser.state.action.TranslationsAction
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.SessionState
import mozilla.components.browser.state.state.TranslationsState
import mozilla.components.concept.engine.translate.TranslationOperation
import mozilla.components.concept.engine.translate.TranslationPageSettingOperation
import mozilla.components.concept.engine.translate.TranslationPageSettings

internal object TranslationsStateReducer {

/**
* Reducer for [BrowserState.translationEngine] and [SessionState.translationsState]
*/
@Suppress("LongMethod")
fun reduce(state: BrowserState, action: TranslationsAction): BrowserState = when (action) {
is TranslationsAction.TranslateExpectedAction -> {
Expand Down Expand Up @@ -170,6 +174,10 @@ internal object TranslationsStateReducer {
}
}

is TranslationsAction.EngineExceptionAction -> {
state.copy(translationEngine = state.translationEngine.copy(engineError = action.error))
}

is TranslationsAction.SetSupportedLanguagesAction ->
state.copyWithTranslationsState(action.tabId) {
it.copy(
Expand Down Expand Up @@ -278,6 +286,15 @@ internal object TranslationsStateReducer {
}
}
}

is TranslationsAction.SetEngineSupportedAction -> {
state.copy(
translationEngine = state.translationEngine.copy(
isEngineSupported = action.isEngineSupported,
engineError = null,
),
)
}
}

private inline fun BrowserState.copyWithTranslationsState(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import java.util.Locale
* on application startup e.g. as an indicator that tabs have been restored.
* @property locale The current locale of the app. Will be null when following the system default.
* @property awesomeBarState Holds state for interactions with the [AwesomeBar].
* @property translationEngine Holds translation state that applies to the browser.
*/
data class BrowserState(
val tabs: List<TabSessionState> = emptyList(),
Expand All @@ -54,4 +55,5 @@ data class BrowserState(
val showExtensionsProcessDisabledPrompt: Boolean = false,
val extensionsProcessDisabled: Boolean = false,
val awesomeBarState: AwesomeBarState = AwesomeBarState(),
val translationEngine: TranslationsBrowserState = TranslationsBrowserState(),
) : State
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/* 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 mozilla.components.browser.state.state

import mozilla.components.concept.engine.translate.TranslationError

/**
* Value type that represents the state of the translations engine within a [BrowserState].
*
* @property isEngineSupported Whether the translations engine supports the device architecture.
* @property engineError Holds the error state of the translations engine.
* See [TranslationsState.translationError] for session level errors.
*/
data class TranslationsBrowserState(
val isEngineSupported: Boolean? = null,
val engineError: TranslationError? = null,
)
Original file line number Diff line number Diff line change
Expand Up @@ -518,4 +518,36 @@ class TranslationsActionTest {
assertTrue(tabState().translationsState.pageSettings?.neverTranslateLanguage!!)
assertTrue(tabState().translationsState.pageSettings?.neverTranslateSite!!)
}

fun `WHEN a SetEngineSupportAction is dispatched THEN the browser store is updated to match`() {
// Initial state
assertNull(store.state.translationEngine.isEngineSupported)

// Dispatch
store.dispatch(
TranslationsAction.SetEngineSupportedAction(
isEngineSupported = true,
),
).joinBlocking()

// Final state
assertTrue(store.state.translationEngine.isEngineSupported!!)
}

@Test
fun `WHEN an EngineExceptionAction is dispatched THEN the browser store is updated to match`() {
// Initial state
assertNull(store.state.translationEngine.engineError)

// Dispatch
val error = TranslationError.UnknownError(Throwable())
store.dispatch(
TranslationsAction.EngineExceptionAction(
error = error,
),
).joinBlocking()

// Final state
assertEquals(store.state.translationEngine.engineError!!, error)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ package mozilla.components.browser.state.engine.middleware

import kotlinx.coroutines.test.runTest
import mozilla.components.browser.state.action.BrowserAction
import mozilla.components.browser.state.action.InitAction
import mozilla.components.browser.state.action.TranslationsAction
import mozilla.components.browser.state.selector.findTab
import mozilla.components.browser.state.state.BrowserState
Expand Down Expand Up @@ -36,6 +37,7 @@ import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers.anyBoolean
import org.mockito.Mockito.atLeastOnce
import org.mockito.Mockito.spy
import org.mockito.Mockito.verify

Expand Down Expand Up @@ -439,4 +441,51 @@ class TranslationsMiddlewareTest {
),
)
}

@Test
fun `WHEN InitAction is dispatched THEN SetEngineSupportAction is dispatched`() = runTest {
// Send Action
// Note: Will cause a double InitAction
translationsMiddleware.invoke(context, {}, InitAction)
waitForIdle()

// Check expectations
val engineSupportedCallback = argumentCaptor<((Boolean) -> Unit)>()
verify(engine, atLeastOnce()).isTranslationsEngineSupported(
onSuccess = engineSupportedCallback.capture(),
onError = any(),
)
engineSupportedCallback.value.invoke(true)
waitForIdle()

verify(store, atLeastOnce()).dispatch(
TranslationsAction.SetEngineSupportedAction(
isEngineSupported = true,
),
)
waitForIdle()
}

@Test
fun `WHEN InitAction is dispatched AND has an issue THEN TranslateExceptionAction is dispatched`() = runTest() {
// Send Action
// Note: Will cause a double InitAction
translationsMiddleware.invoke(context, {}, InitAction)
waitForIdle()

// Check expectations
val errorCallback = argumentCaptor<((Throwable) -> Unit)>()
verify(engine, atLeastOnce()).isTranslationsEngineSupported(
onSuccess = any(),
onError = errorCallback.capture(),
)
errorCallback.value.invoke(IllegalStateException())
waitForIdle()

verify(store, atLeastOnce()).dispatch(
TranslationsAction.EngineExceptionAction(
error = TranslationError.UnknownEngineSupportError(any()),
),
)
}
}
Loading

0 comments on commit fd3c11c

Please sign in to comment.