Bug 1875817 - Translations Integration Toolbar Showing
parent
936bb85d6c
commit
ddb15214df
@ -0,0 +1,72 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.browser
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.distinctUntilChangedBy
|
||||||
|
import kotlinx.coroutines.flow.mapNotNull
|
||||||
|
import mozilla.components.browser.state.selector.findTab
|
||||||
|
import mozilla.components.browser.state.state.BrowserState
|
||||||
|
import mozilla.components.browser.state.state.TranslationsState
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.concept.engine.translate.Language
|
||||||
|
import mozilla.components.concept.engine.translate.initialFromLanguage
|
||||||
|
import mozilla.components.concept.engine.translate.initialToLanguage
|
||||||
|
import mozilla.components.lib.state.helpers.AbstractBinding
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A binding for observing [TranslationsState] changes
|
||||||
|
* from the [BrowserStore] and updating the translations action button.
|
||||||
|
*
|
||||||
|
* @param browserStore [BrowserStore] observed for any changes related to [TranslationsState].
|
||||||
|
* @param sessionId Current open tab session id.
|
||||||
|
* @param onStateUpdated Invoked when the translations action button should be updated with the new translations state.
|
||||||
|
*/
|
||||||
|
class TranslationsBinding(
|
||||||
|
private val browserStore: BrowserStore,
|
||||||
|
private val sessionId: String,
|
||||||
|
private val onStateUpdated: (
|
||||||
|
isVisible: Boolean,
|
||||||
|
isTranslated: Boolean,
|
||||||
|
fromSelectedLanguage: Language?,
|
||||||
|
toSelectedLanguage: Language?,
|
||||||
|
) -> Unit,
|
||||||
|
) : AbstractBinding<BrowserState>(browserStore) {
|
||||||
|
|
||||||
|
override suspend fun onState(flow: Flow<BrowserState>) {
|
||||||
|
flow.mapNotNull { state -> state.findTab(sessionId) }.distinctUntilChangedBy {
|
||||||
|
it.translationsState
|
||||||
|
}.collect { sessionState ->
|
||||||
|
val translationsState = sessionState.translationsState
|
||||||
|
|
||||||
|
if (translationsState.isTranslated) {
|
||||||
|
val fromSelected = translationsState.translationEngineState?.initialFromLanguage(
|
||||||
|
translationsState.supportedLanguages?.fromLanguages,
|
||||||
|
)
|
||||||
|
val toSelected = translationsState.translationEngineState?.initialToLanguage(
|
||||||
|
translationsState.supportedLanguages?.toLanguages,
|
||||||
|
)
|
||||||
|
|
||||||
|
if (fromSelected != null && toSelected != null) {
|
||||||
|
onStateUpdated(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
fromSelected,
|
||||||
|
toSelected,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} else if (translationsState.isExpectedTranslate) {
|
||||||
|
onStateUpdated(
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
onStateUpdated(false, false, null, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,168 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.fenix.browser
|
||||||
|
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import mozilla.components.browser.state.action.TranslationsAction
|
||||||
|
import mozilla.components.browser.state.state.BrowserState
|
||||||
|
import mozilla.components.browser.state.state.createTab
|
||||||
|
import mozilla.components.browser.state.store.BrowserStore
|
||||||
|
import mozilla.components.concept.engine.translate.DetectedLanguages
|
||||||
|
import mozilla.components.concept.engine.translate.Language
|
||||||
|
import mozilla.components.concept.engine.translate.TranslationEngineState
|
||||||
|
import mozilla.components.concept.engine.translate.TranslationOperation
|
||||||
|
import mozilla.components.concept.engine.translate.TranslationPair
|
||||||
|
import mozilla.components.concept.engine.translate.TranslationSupport
|
||||||
|
import mozilla.components.support.test.ext.joinBlocking
|
||||||
|
import mozilla.components.support.test.rule.MainCoroutineRule
|
||||||
|
import mozilla.components.support.test.rule.runTestOnMain
|
||||||
|
import org.junit.Rule
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.mockito.Mockito.spy
|
||||||
|
import org.mockito.Mockito.verify
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class TranslationsBindingTest {
|
||||||
|
@get:Rule
|
||||||
|
val coroutineRule = MainCoroutineRule()
|
||||||
|
|
||||||
|
lateinit var browserStore: BrowserStore
|
||||||
|
|
||||||
|
private val tabId = "1"
|
||||||
|
private val tab = createTab(url = tabId, id = tabId)
|
||||||
|
private val onIconChanged: (
|
||||||
|
isVisible: Boolean,
|
||||||
|
isTranslated: Boolean,
|
||||||
|
fromSelectedLanguage: Language?,
|
||||||
|
toSelectedLanguage: Language?,
|
||||||
|
) -> Unit = spy()
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN translationState WHEN translation status isTranslated THEN invoke onIconChanged callback`() =
|
||||||
|
runTestOnMain {
|
||||||
|
val englishLanguage = Language("en", "English")
|
||||||
|
val spanishLanguage = Language("es", "Spanish")
|
||||||
|
|
||||||
|
browserStore = BrowserStore(
|
||||||
|
BrowserState(
|
||||||
|
tabs = listOf(tab),
|
||||||
|
selectedTabId = tabId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val binding = TranslationsBinding(
|
||||||
|
browserStore = browserStore,
|
||||||
|
sessionId = tabId,
|
||||||
|
onStateUpdated = onIconChanged,
|
||||||
|
)
|
||||||
|
binding.start()
|
||||||
|
|
||||||
|
val detectedLanguages = DetectedLanguages(
|
||||||
|
documentLangTag = englishLanguage.code,
|
||||||
|
supportedDocumentLang = true,
|
||||||
|
userPreferredLangTag = spanishLanguage.code,
|
||||||
|
)
|
||||||
|
|
||||||
|
val translationEngineState = TranslationEngineState(
|
||||||
|
detectedLanguages = detectedLanguages,
|
||||||
|
error = null,
|
||||||
|
isEngineReady = true,
|
||||||
|
requestedTranslationPair = TranslationPair(
|
||||||
|
fromLanguage = englishLanguage.code,
|
||||||
|
toLanguage = spanishLanguage.code,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val supportLanguages = TranslationSupport(
|
||||||
|
fromLanguages = listOf(englishLanguage),
|
||||||
|
toLanguages = listOf(spanishLanguage),
|
||||||
|
)
|
||||||
|
|
||||||
|
browserStore.dispatch(
|
||||||
|
TranslationsAction.SetSupportedLanguagesAction(
|
||||||
|
tabId = tab.id,
|
||||||
|
supportedLanguages = supportLanguages,
|
||||||
|
),
|
||||||
|
).joinBlocking()
|
||||||
|
|
||||||
|
browserStore.dispatch(
|
||||||
|
TranslationsAction.TranslateStateChangeAction(
|
||||||
|
tabId = tabId,
|
||||||
|
translationEngineState = translationEngineState,
|
||||||
|
),
|
||||||
|
).joinBlocking()
|
||||||
|
|
||||||
|
browserStore.dispatch(
|
||||||
|
TranslationsAction.TranslateSuccessAction(
|
||||||
|
tabId = tab.id,
|
||||||
|
operation = TranslationOperation.TRANSLATE,
|
||||||
|
),
|
||||||
|
).joinBlocking()
|
||||||
|
|
||||||
|
verify(onIconChanged).invoke(
|
||||||
|
true,
|
||||||
|
true,
|
||||||
|
englishLanguage,
|
||||||
|
spanishLanguage,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN translationState WHEN translation status isExpectedTranslate THEN invoke onIconChanged callback`() =
|
||||||
|
runTestOnMain {
|
||||||
|
browserStore = BrowserStore(
|
||||||
|
BrowserState(
|
||||||
|
tabs = listOf(tab),
|
||||||
|
selectedTabId = tabId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val binding = TranslationsBinding(
|
||||||
|
browserStore = browserStore,
|
||||||
|
sessionId = tabId,
|
||||||
|
onStateUpdated = onIconChanged,
|
||||||
|
)
|
||||||
|
binding.start()
|
||||||
|
|
||||||
|
browserStore.dispatch(
|
||||||
|
TranslationsAction.TranslateExpectedAction(
|
||||||
|
tabId = tabId,
|
||||||
|
),
|
||||||
|
).joinBlocking()
|
||||||
|
|
||||||
|
verify(onIconChanged).invoke(
|
||||||
|
true,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `GIVEN translationState WHEN translation status is not isExpectedTranslate or isTranslated THEN invoke onIconChanged callback`() =
|
||||||
|
runTestOnMain {
|
||||||
|
browserStore = BrowserStore(
|
||||||
|
BrowserState(
|
||||||
|
tabs = listOf(tab),
|
||||||
|
selectedTabId = tabId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
val binding = TranslationsBinding(
|
||||||
|
browserStore = browserStore,
|
||||||
|
sessionId = tabId,
|
||||||
|
onStateUpdated = onIconChanged,
|
||||||
|
)
|
||||||
|
binding.start()
|
||||||
|
|
||||||
|
verify(onIconChanged).invoke(
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
null,
|
||||||
|
null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue