/* 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.action import kotlin.test.assertNotNull import mozilla.components.browser.state.reducer.BrowserStateReducer import mozilla.components.browser.state.selector.findTab import mozilla.components.browser.state.state.BrowserState import mozilla.components.browser.state.state.TabSessionState import mozilla.components.browser.state.state.createTab import mozilla.components.concept.engine.translate.DetectedLanguages import mozilla.components.concept.engine.translate.Language import mozilla.components.concept.engine.translate.LanguageModel import mozilla.components.concept.engine.translate.LanguageSetting import mozilla.components.concept.engine.translate.ModelManagementOptions import mozilla.components.concept.engine.translate.ModelOperation import mozilla.components.concept.engine.translate.ModelState import mozilla.components.concept.engine.translate.OperationLevel import mozilla.components.concept.engine.translate.TranslationDownloadSize import mozilla.components.concept.engine.translate.TranslationEngineState import mozilla.components.concept.engine.translate.TranslationError import mozilla.components.concept.engine.translate.TranslationOperation import mozilla.components.concept.engine.translate.TranslationPageSettingOperation import mozilla.components.concept.engine.translate.TranslationPageSettings import mozilla.components.concept.engine.translate.TranslationPair import mozilla.components.concept.engine.translate.TranslationSupport import mozilla.components.support.test.mock import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertNull import org.junit.Assert.assertTrue import org.junit.Before import org.junit.Test class TranslationsActionTest { private lateinit var tab: TabSessionState private lateinit var state: BrowserState @Before fun setUp() { tab = createTab("https://www.mozilla.org") state = BrowserState(tabs = listOf(tab)) } private fun tabState(): TabSessionState = state.findTab(tab.id)!! @Test fun `WHEN a TranslateExpectedAction is dispatched THEN update translation expected status`() { assertEquals(false, tabState().translationsState.isExpectedTranslate) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExpectedAction(tabId = tab.id), ) assertEquals(true, tabState().translationsState.isExpectedTranslate) } @Test fun `WHEN a TranslateOfferAction is dispatched THEN update translation expected status`() { assertEquals(false, tabState().translationsState.isOfferTranslate) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateOfferAction(tabId = tab.id, isOfferTranslate = true), ) assertEquals(true, tabState().translationsState.isOfferTranslate) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateOfferAction(tabId = tab.id, isOfferTranslate = false), ) assertFalse(tabState().translationsState.isOfferTranslate) } @Test fun `WHEN a TranslateStateChangeAction is dispatched THEN update translation expected status`() { assertEquals(null, tabState().translationsState.translationEngineState) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction(tabId = tab.id, mock()), ) assertEquals(true, tabState().translationsState.translationEngineState != null) } @Test fun `WHEN a TranslateStateChangeAction is dispatched THEN the translation status updates accordingly`() { assertNull(tabState().translationsState.translationEngineState) assertFalse(tabState().translationsState.isTranslated) assertFalse(tabState().translationsState.isExpectedTranslate) assertFalse(tabState().translationsState.isTranslateProcessing) // Set an initial state for is translate processing via a translation request: state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateAction(tabId = tab.id, "en", "es", null), ) assertTrue(tabState().translationsState.isTranslateProcessing) val translatedEngineState = TranslationEngineState( detectedLanguages = DetectedLanguages( documentLangTag = "es", supportedDocumentLang = true, userPreferredLangTag = "en", ), error = null, isEngineReady = true, requestedTranslationPair = TranslationPair(fromLanguage = "es", toLanguage = "en"), hasVisibleChange = true, ) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = translatedEngineState, ), ) // Translated state assertEquals(translatedEngineState, tabState().translationsState.translationEngineState) assertTrue(tabState().translationsState.isTranslated) assertFalse(tabState().translationsState.isExpectedTranslate) assertFalse(tabState().translationsState.isTranslateProcessing) val nonTranslatedEngineState = TranslationEngineState( detectedLanguages = DetectedLanguages( documentLangTag = "es", supportedDocumentLang = true, userPreferredLangTag = "en", ), error = null, isEngineReady = true, requestedTranslationPair = TranslationPair(fromLanguage = null, toLanguage = null), hasVisibleChange = false, ) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction(tabId = tab.id, nonTranslatedEngineState), ) // Non-translated state assertEquals(nonTranslatedEngineState, tabState().translationsState.translationEngineState) assertFalse(tabState().translationsState.isTranslated) assertFalse(tabState().translationsState.isExpectedTranslate) } @Test fun `GIVEN isOfferTranslate is true WHEN a TranslateAction is dispatched THEN isOfferTranslate should be set to false`() { // Initial State assertFalse(tabState().translationsState.isOfferTranslate) // Initial Offer State state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateOfferAction(tabId = tab.id, true), ) assertTrue(tabState().translationsState.isOfferTranslate) // Action state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateAction( tabId = tab.id, fromLanguage = "en", toLanguage = "en", options = null, ), ) // Should revert to false assertFalse(tabState().translationsState.isOfferTranslate) } @Test fun `WHEN a TranslateStateChangeAction is dispatched THEN the isExpectedTranslate status updates accordingly`() { // Initial State assertNull(tabState().translationsState.translationEngineState) // Sending an initial request to set state; however, the engine hasn't decided if it is an // expected state var translatedEngineState = TranslationEngineState( detectedLanguages = DetectedLanguages( documentLangTag = "es", supportedDocumentLang = true, userPreferredLangTag = "en", ), error = null, isEngineReady = true, requestedTranslationPair = TranslationPair(fromLanguage = "es", toLanguage = "en"), ) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = translatedEngineState, ), ) assertFalse(tabState().translationsState.isExpectedTranslate) // Engine is sending a translation expected action state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExpectedAction(tabId = tab.id), ) // Initial expected translation state state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = translatedEngineState, ), ) assertTrue(tabState().translationsState.isExpectedTranslate) // Not expected translation state, because it is no longer supported translatedEngineState = TranslationEngineState( detectedLanguages = DetectedLanguages( documentLangTag = "es", supportedDocumentLang = false, userPreferredLangTag = "en", ), error = null, isEngineReady = true, requestedTranslationPair = TranslationPair(fromLanguage = "es", toLanguage = "en"), ) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = translatedEngineState, ), ) assertFalse(tabState().translationsState.isExpectedTranslate) } @Test fun `WHEN a TranslateStateChangeAction is dispatched THEN the isOfferTranslate status updates accordingly`() { // Initial State assertNull(tabState().translationsState.translationEngineState) // Sending an initial request to set state; however, the engine hasn't decided if it is an // offered state var translatedEngineState = TranslationEngineState( detectedLanguages = DetectedLanguages( documentLangTag = "es", supportedDocumentLang = true, userPreferredLangTag = "en", ), error = null, isEngineReady = true, requestedTranslationPair = TranslationPair(fromLanguage = "es", toLanguage = "en"), ) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = translatedEngineState, ), ) assertFalse(tabState().translationsState.isOfferTranslate) // Engine is sending a translation offer action state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateOfferAction(tabId = tab.id, isOfferTranslate = true), ) // Initial expected translation state state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = translatedEngineState, ), ) assertTrue(tabState().translationsState.isOfferTranslate) // Not in an offer translation state, because it is no longer supported translatedEngineState = TranslationEngineState( detectedLanguages = DetectedLanguages( documentLangTag = "es", supportedDocumentLang = false, userPreferredLangTag = "en", ), error = null, isEngineReady = true, requestedTranslationPair = TranslationPair(fromLanguage = "es", toLanguage = "en"), ) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = translatedEngineState, ), ) assertFalse(tabState().translationsState.isOfferTranslate) } @Test fun `WHEN a TranslateStateChangeAction is dispatched THEN the translationError status updates accordingly`() { // Initial State assertNull(tabState().translationsState.translationEngineState) assertNull(tabState().translationsState.translationError) // Sending an initial request to set state, notice the supportedDocumentLang isn't supported val noSupportedState = TranslationEngineState( detectedLanguages = DetectedLanguages( documentLangTag = "unknown", supportedDocumentLang = false, userPreferredLangTag = "en", ), error = null, isEngineReady = true, requestedTranslationPair = null, ) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = noSupportedState, ), ) // Response state assertEquals(noSupportedState, tabState().translationsState.translationEngineState) assertNotNull(tabState().translationsState.translationError) // Sending a request to show state change, notice the supportedDocumentLang is now supported val supportedState = TranslationEngineState( detectedLanguages = DetectedLanguages( documentLangTag = "es", supportedDocumentLang = true, userPreferredLangTag = "en", ), error = null, isEngineReady = true, requestedTranslationPair = null, ) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateStateChangeAction( tabId = tab.id, translationEngineState = supportedState, ), ) // Response state assertEquals(supportedState, tabState().translationsState.translationEngineState) assertNull(tabState().translationsState.translationError) } @Test fun `WHEN a TranslateAction is dispatched AND successful THEN update translation processing status`() { // Initial assertEquals(false, tabState().translationsState.isTranslateProcessing) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateAction(tabId = tab.id, "en", "es", null), ) assertEquals(true, tabState().translationsState.isTranslateProcessing) // Action success state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateSuccessAction( tabId = tab.id, operation = TranslationOperation.TRANSLATE, ), ) assertEquals(null, tabState().translationsState.translationError) } @Test fun `WHEN a TranslateAction is dispatched AND fails THEN update translation processing status`() { // Initial assertEquals(false, tabState().translationsState.isTranslateProcessing) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateAction(tabId = tab.id, "en", "es", null), ) assertEquals(true, tabState().translationsState.isTranslateProcessing) // Action failure val error = TranslationError.UnknownError(Exception()) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExceptionAction( tabId = tab.id, operation = TranslationOperation.TRANSLATE, error, ), ) assertEquals(false, tabState().translationsState.isTranslateProcessing) assertEquals(false, tabState().translationsState.isTranslated) assertEquals(error, tabState().translationsState.translationError) } @Test fun `WHEN a TranslateRestoreAction is dispatched AND successful THEN update translation processing status`() { // Initial assertEquals(false, tabState().translationsState.isRestoreProcessing) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateRestoreAction(tabId = tab.id), ) assertEquals(true, tabState().translationsState.isRestoreProcessing) // Action success state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateSuccessAction( tabId = tab.id, operation = TranslationOperation.RESTORE, ), ) assertEquals(false, tabState().translationsState.isRestoreProcessing) assertEquals(false, tabState().translationsState.isTranslated) assertEquals(null, tabState().translationsState.translationError) } @Test fun `WHEN a TranslateRestoreAction is dispatched AND fails THEN update translation processing status`() { // Initial assertEquals(false, tabState().translationsState.isRestoreProcessing) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateRestoreAction(tabId = tab.id), ) assertEquals(true, tabState().translationsState.isRestoreProcessing) // Action failure val error = TranslationError.UnknownError(Exception()) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExceptionAction( tabId = tab.id, operation = TranslationOperation.RESTORE, error, ), ) assertEquals(false, tabState().translationsState.isRestoreProcessing) assertEquals(false, tabState().translationsState.isTranslated) assertEquals(error, tabState().translationsState.translationError) } @Test fun `WHEN a SetSupportedLanguagesAction is dispatched AND successful THEN update supportedLanguages`() { // Initial assertNull(state.translationEngine.supportedLanguages) // Action started val toLanguage = Language("de", "German") val fromLanguage = Language("es", "Spanish") val supportedLanguages = TranslationSupport(listOf(fromLanguage), listOf(toLanguage)) state = BrowserStateReducer.reduce( state, TranslationsAction.SetSupportedLanguagesAction(supportedLanguages = supportedLanguages), ) // Action success assertEquals(supportedLanguages, state.translationEngine.supportedLanguages) } @Test fun `WHEN a SetNeverTranslateSitesAction is dispatched AND successful THEN update neverTranslateSites`() { // Initial assertNull(state.translationEngine.neverTranslateSites) // Action started val neverTranslateSites = listOf("google.com") state = BrowserStateReducer.reduce( state, TranslationsAction.SetNeverTranslateSitesAction(neverTranslateSites = neverTranslateSites), ) // Action success assertEquals(neverTranslateSites, state.translationEngine.neverTranslateSites) } @Test fun `WHEN a RemoveNeverTranslateSiteAction is dispatched AND successful THEN update neverTranslateSites`() { // Initial add to neverTranslateSites assertNull(state.translationEngine.neverTranslateSites) val neverTranslateSites = listOf("google.com") state = BrowserStateReducer.reduce( state, TranslationsAction.SetNeverTranslateSitesAction(neverTranslateSites = neverTranslateSites), ) assertEquals(neverTranslateSites, state.translationEngine.neverTranslateSites) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.RemoveNeverTranslateSiteAction(origin = "google.com"), ) // Action success assertEquals(listOf(), state.translationEngine.neverTranslateSites) } @Test fun `WHEN a TranslateExceptionAction is dispatched due to an error THEN update the error condition according to the operation`() { // Initial state assertEquals(null, tabState().translationsState.translationError) assertFalse(tabState().translationsState.isTranslateProcessing) // Set an initial state for is translate processing via a translation request: state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateAction(tabId = tab.id, "en", "es", null), ) assertTrue(tabState().translationsState.isTranslateProcessing) // TRANSLATE usage val translateError = TranslationError.CouldNotLoadLanguagesError(null) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExceptionAction( tabId = tab.id, operation = TranslationOperation.TRANSLATE, translationError = translateError, ), ) assertEquals(translateError, tabState().translationsState.translationError) // A translate error should clear this state assertFalse(tabState().translationsState.isTranslateProcessing) // RESTORE usage val restoreError = TranslationError.CouldNotRestoreError(null) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExceptionAction( tabId = tab.id, operation = TranslationOperation.RESTORE, translationError = restoreError, ), ) assertEquals(restoreError, tabState().translationsState.translationError) // FETCH_LANGUAGES usage val fetchLanguagesError = TranslationError.CouldNotLoadLanguagesError(null) // Testing setting tab level error state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExceptionAction( tabId = tab.id, operation = TranslationOperation.FETCH_SUPPORTED_LANGUAGES, translationError = fetchLanguagesError, ), ) assertEquals(fetchLanguagesError, tabState().translationsState.translationError) // Testing setting browser level error state = BrowserStateReducer.reduce( state, TranslationsAction.EngineExceptionAction(error = fetchLanguagesError), ) assertEquals(fetchLanguagesError, state.translationEngine.engineError) } @Test fun `WHEN a TranslateSuccessAction is dispatched THEN update the condition according to the operation`() { // Initial state assertEquals(null, tabState().translationsState.translationError) assertEquals(false, tabState().translationsState.isTranslated) assertEquals(false, tabState().translationsState.isTranslateProcessing) // TRANSLATE usage state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateSuccessAction( tabId = tab.id, operation = TranslationOperation.TRANSLATE, ), ) assertEquals(null, tabState().translationsState.translationError) assertEquals(false, tabState().translationsState.isTranslateProcessing) // RESTORE usage state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateSuccessAction( tabId = tab.id, operation = TranslationOperation.RESTORE, ), ) assertEquals(null, tabState().translationsState.translationError) assertEquals(false, tabState().translationsState.isTranslated) assertEquals(false, tabState().translationsState.isRestoreProcessing) // FETCH_LANGUAGES usage state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateSuccessAction( tabId = tab.id, operation = TranslationOperation.FETCH_SUPPORTED_LANGUAGES, ), ) assertEquals(null, tabState().translationsState.translationError) assertEquals(false, tabState().translationsState.isTranslated) } @Test fun `WHEN a SetPageSettingsAction is dispatched THEN set pageSettings`() { // Initial assertNull(tabState().translationsState.pageSettings) // Action started val pageSettings = TranslationPageSettings( alwaysOfferPopup = true, alwaysTranslateLanguage = true, neverTranslateLanguage = true, neverTranslateSite = true, ) state = BrowserStateReducer.reduce( state, TranslationsAction.SetPageSettingsAction( tabId = tab.id, pageSettings = pageSettings, ), ) // Action success assertEquals(pageSettings, tabState().translationsState.pageSettings) } @Test fun `WHEN a SetTranslateProcessingAction is dispatched THEN set isTranslateProcessing`() { // Initial assertFalse(tabState().translationsState.isTranslateProcessing) // Action started val isProcessing = true state = BrowserStateReducer.reduce( state, TranslationsAction.SetTranslateProcessingAction( tabId = tab.id, isProcessing = isProcessing, ), ) // Action success assertEquals(isProcessing, tabState().translationsState.isTranslateProcessing) } @Test fun `WHEN a SetTranslationDownloadSize is dispatched THEN set translationSize is set`() { // Initial assertNull(tabState().translationsState.translationDownloadSize) // Action started val translationSize = TranslationDownloadSize( fromLanguage = Language("en", "English"), toLanguage = Language("fr", "French"), size = 10000L, error = null, ) state = BrowserStateReducer.reduce( state, TranslationsAction.SetTranslationDownloadSizeAction( tabId = tab.id, translationSize = translationSize, ), ) // Action success assertEquals(translationSize, tabState().translationsState.translationDownloadSize) } @Test fun `WHEN a FetchTranslationDownloadSize is dispatched THEN translationSize is cleared`() { // Initial setting size for a more robust test val translationSize = TranslationDownloadSize( fromLanguage = Language("en", "English"), toLanguage = Language("fr", "French"), size = 10000L, error = null, ) state = BrowserStateReducer.reduce( state, TranslationsAction.SetTranslationDownloadSizeAction( tabId = tab.id, translationSize = translationSize, ), ) assertEquals(translationSize, tabState().translationsState.translationDownloadSize) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.FetchTranslationDownloadSizeAction( tabId = tab.id, fromLanguage = Language("en", "English"), toLanguage = Language("fr", "French"), ), ) // Action success assertNull(tabState().translationsState.translationDownloadSize) } @Test fun `WHEN a OperationRequestedAction is dispatched for FETCH_PAGE_SETTINGS THEN clear pageSettings`() { // Setting first to have a more robust initial state assertNull(tabState().translationsState.pageSettings) val pageSettings = TranslationPageSettings( alwaysOfferPopup = true, alwaysTranslateLanguage = true, neverTranslateLanguage = true, neverTranslateSite = true, ) state = BrowserStateReducer.reduce( state, TranslationsAction.SetPageSettingsAction( tabId = tab.id, pageSettings = pageSettings, ), ) assertEquals(pageSettings, tabState().translationsState.pageSettings) assertNull(tabState().translationsState.settingsError) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.OperationRequestedAction( tabId = tab.id, operation = TranslationOperation.FETCH_PAGE_SETTINGS, ), ) // Action success assertNull(tabState().translationsState.pageSettings) } @Test fun `WHEN a OperationRequestedAction is dispatched for FETCH_SUPPORTED_LANGUAGES THEN clear supportLanguages`() { // Setting first to have a more robust initial state assertNull(state.translationEngine.supportedLanguages) val supportLanguages = TranslationSupport( fromLanguages = listOf(Language("en", "English")), toLanguages = listOf(Language("en", "English")), ) state = BrowserStateReducer.reduce( state, TranslationsAction.SetSupportedLanguagesAction(supportedLanguages = supportLanguages), ) assertEquals(supportLanguages, state.translationEngine.supportedLanguages) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.OperationRequestedAction( tabId = tab.id, operation = TranslationOperation.FETCH_SUPPORTED_LANGUAGES, ), ) // Action success assertNull(state.translationEngine.supportedLanguages) } @Test fun `WHEN a UpdatePageSettingAction is dispatched for UPDATE_ALWAYS_OFFER_POPUP THEN set page settings for alwaysOfferPopup `() { // Initial State assertNull(tabState().translationsState.pageSettings?.alwaysOfferPopup) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_ALWAYS_OFFER_POPUP, setting = true, ), ) // Action success assertTrue(tabState().translationsState.pageSettings?.alwaysOfferPopup!!) } @Test fun `WHEN a UpdatePageSettingAction is dispatched for UPDATE_ALWAYS_TRANSLATE_LANGUAGE THEN set page settings for alwaysTranslateLanguage `() { // Initial State assertNull(tabState().translationsState.pageSettings?.alwaysTranslateLanguage) assertNull(tabState().translationsState.pageSettings?.neverTranslateLanguage) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_ALWAYS_TRANSLATE_LANGUAGE, setting = true, ), ) // Action success assertTrue(tabState().translationsState.pageSettings?.alwaysTranslateLanguage!!) assertFalse(tabState().translationsState.pageSettings?.neverTranslateLanguage!!) } @Test fun `WHEN a UpdatePageSettingAction is dispatched for UPDATE_NEVER_TRANSLATE_LANGUAGE THEN set page settings for alwaysTranslateLanguage `() { // Initial State assertNull(tabState().translationsState.pageSettings?.neverTranslateLanguage) assertNull(tabState().translationsState.pageSettings?.alwaysTranslateLanguage) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_NEVER_TRANSLATE_LANGUAGE, setting = true, ), ) // Action success assertTrue(tabState().translationsState.pageSettings?.neverTranslateLanguage!!) assertFalse(tabState().translationsState.pageSettings?.alwaysTranslateLanguage!!) } @Test fun `WHEN a UpdatePageSettingAction is dispatched for UPDATE_NEVER_TRANSLATE_SITE THEN set page settings for neverTranslateSite`() { // Initial State assertNull(tabState().translationsState.pageSettings?.neverTranslateLanguage) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_NEVER_TRANSLATE_SITE, setting = true, ), ) // Action success assertTrue(tabState().translationsState.pageSettings?.neverTranslateSite!!) } @Test fun `WHEN an UpdatePageSettingAction is dispatched for UPDATE_ALWAYS_TRANSLATE_LANGUAGE AND UPDATE_ALWAYS_TRANSLATE_LANGUAGE THEN must be opposites of each other or both must be false `() { // Initial state assertNull(tabState().translationsState.pageSettings?.alwaysTranslateLanguage) assertNull(tabState().translationsState.pageSettings?.neverTranslateLanguage) // Action started to update the always offer setting to true state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_ALWAYS_TRANSLATE_LANGUAGE, setting = true, ), ) // When always is true, never should be false assertTrue(tabState().translationsState.pageSettings?.alwaysTranslateLanguage!!) assertFalse(tabState().translationsState.pageSettings?.neverTranslateLanguage!!) // Action started to update the never offer setting to true state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_NEVER_TRANSLATE_LANGUAGE, setting = true, ), ) // When never is true, always should be false assertFalse(tabState().translationsState.pageSettings?.alwaysTranslateLanguage!!) assertTrue(tabState().translationsState.pageSettings?.neverTranslateLanguage!!) // Action started to update the never language setting to false state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_NEVER_TRANSLATE_LANGUAGE, setting = false, ), ) // When never is false, always may also be false assertFalse(tabState().translationsState.pageSettings?.alwaysTranslateLanguage!!) assertFalse(tabState().translationsState.pageSettings?.neverTranslateLanguage!!) } @Test fun `WHEN a UpdatePageSettingAction is dispatched for each option THEN the page setting is consistent`() { // Initial State assertNull(tabState().translationsState.pageSettings?.alwaysOfferPopup) assertNull(tabState().translationsState.pageSettings?.alwaysTranslateLanguage) assertNull(tabState().translationsState.pageSettings?.neverTranslateLanguage) assertNull(tabState().translationsState.pageSettings?.neverTranslateSite) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_ALWAYS_OFFER_POPUP, setting = true, ), ) state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_ALWAYS_TRANSLATE_LANGUAGE, setting = true, ), ) state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_NEVER_TRANSLATE_LANGUAGE, setting = true, ), ) state = BrowserStateReducer.reduce( state, TranslationsAction.UpdatePageSettingAction( tabId = tab.id, operation = TranslationPageSettingOperation.UPDATE_NEVER_TRANSLATE_SITE, setting = true, ), ) // Action success assertTrue(tabState().translationsState.pageSettings?.alwaysOfferPopup!!) // neverTranslateLanguage was posted last and will prevent a contradictory state on the alwaysTranslateLanguage // state. assertFalse(tabState().translationsState.pageSettings?.alwaysTranslateLanguage!!) assertTrue(tabState().translationsState.pageSettings?.neverTranslateLanguage!!) assertTrue(tabState().translationsState.pageSettings?.neverTranslateSite!!) } @Test fun `WHEN a SetLanguageSettingsAction is dispatched THEN the browser store is updated to match`() { // Initial state assertNull(state.translationEngine.languageSettings) // Dispatch val languageSetting = mapOf("es" to LanguageSetting.OFFER) state = BrowserStateReducer.reduce( state, TranslationsAction.SetLanguageSettingsAction(languageSettings = languageSetting), ) // Final state assertEquals(state.translationEngine.languageSettings!!, languageSetting) } @Test fun `WHEN a OperationRequestedAction is dispatched for FETCH_AUTOMATIC_LANGUAGE_SETTINGS THEN clear languageSettings`() { // Setting first to have a more robust initial state val languageSetting = mapOf("es" to LanguageSetting.OFFER) state = BrowserStateReducer.reduce( state, TranslationsAction.SetLanguageSettingsAction(languageSettings = languageSetting), ) assertEquals(state.translationEngine.languageSettings, languageSetting) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.OperationRequestedAction( tabId = tab.id, operation = TranslationOperation.FETCH_AUTOMATIC_LANGUAGE_SETTINGS, ), ) // Action success assertNull(state.translationEngine.languageSettings) } @Test fun `WHEN a TranslateExceptionAction is dispatched for FETCH_AUTOMATIC_LANGUAGE_SETTINGS THEN set the error`() { // Action started val error = TranslationError.UnknownError(IllegalStateException()) state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExceptionAction( tabId = tab.id, operation = TranslationOperation.FETCH_AUTOMATIC_LANGUAGE_SETTINGS, translationError = error, ), ) // Action success assertEquals(error, tabState().translationsState.translationError) } @Test fun `WHEN a SetTranslationsEnabledAction is dispatched THEN the browser store is updated to match`() { // Initial state assertTrue(state.translationEngine.isTranslationsEnabled) // Dispatch state = BrowserStateReducer.reduce( state, TranslationsAction.SetTranslationsEnabledAction(isTranslationsEnabled = false), ) // Final state assertFalse(state.translationEngine.isTranslationsEnabled) } @Test fun `WHEN a SetTranslationsEnabledAction is dispatched with false THEN both the browser and session translation stores are cleared`() { val fromLanguage = Language("en", "English") val toLanguage = Language("es", "Spanish") val supportedLanguages = TranslationSupport(listOf(fromLanguage), listOf(toLanguage)) // Set up populated browser store state state = BrowserStateReducer.reduce( state, TranslationsAction.SetSupportedLanguagesAction(supportedLanguages = supportedLanguages), ) state = BrowserStateReducer.reduce( state, TranslationsAction.SetEngineSupportedAction(isEngineSupported = true), ) // Set up populated session store state state = BrowserStateReducer.reduce( state, TranslationsAction.TranslateExpectedAction(tabId = tab.id), ) // Verify initial populated state assertEquals(supportedLanguages, state.translationEngine.supportedLanguages) assertTrue(tabState().translationsState.isExpectedTranslate) // Turn off translations state = BrowserStateReducer.reduce( state, TranslationsAction.SetTranslationsEnabledAction(isTranslationsEnabled = false), ) // Browser store is cleared and disabled assertFalse(state.translationEngine.isTranslationsEnabled) assertNull(state.translationEngine.supportedLanguages) assertNull(state.translationEngine.languageModels) assertNull(state.translationEngine.languageSettings) assertNull(state.translationEngine.neverTranslateSites) // isEngineSupported is kept assertTrue(state.translationEngine.isEngineSupported!!) // Session store is cleared assertFalse(tabState().translationsState.isExpectedTranslate) assertFalse(tabState().translationsState.isTranslated) assertNull(tabState().translationsState.translationEngineState) assertNull(tabState().translationsState.pageSettings) } @Test fun `WHEN a SetEngineSupportAction is dispatched THEN the browser store is updated to match`() { // Initial state assertNull(state.translationEngine.isEngineSupported) // Dispatch state = BrowserStateReducer.reduce( state, TranslationsAction.SetEngineSupportedAction(isEngineSupported = true), ) // Final state assertTrue(state.translationEngine.isEngineSupported!!) } @Test fun `WHEN an EngineExceptionAction is dispatched THEN the browser store is updated to match`() { // Initial state assertNull(state.translationEngine.engineError) // Dispatch val error = TranslationError.UnknownError(Throwable()) state = BrowserStateReducer.reduce( state, TranslationsAction.EngineExceptionAction(error = error), ) // Final state assertEquals(state.translationEngine.engineError!!, error) } @Test fun `WHEN a SetLanguageModelsAction is dispatched and successful THEN the browser store is updated to match`() { // Initial state assertNull(state.translationEngine.languageModels) val code = "es" val localizedDisplayName = "Spanish" val downloaded = ModelState.DOWNLOADED val size: Long = 1234 val language = Language(code, localizedDisplayName) val languageModel = LanguageModel(language, downloaded, size) val languageModels = mutableListOf(languageModel) // Dispatch state = BrowserStateReducer.reduce( state, TranslationsAction.SetLanguageModelsAction(languageModels = languageModels), ) // Final state assertEquals(languageModels, state.translationEngine.languageModels) } @Test fun `WHEN a ManageLanguageModelsAction is dispatched and successful THEN the browser store is updated to match`() { // Initial state assertNull(state.translationEngine.languageModels) // Test Operation val options = ModelManagementOptions( languageToManage = "es", operation = ModelOperation.DOWNLOAD, operationLevel = OperationLevel.LANGUAGE, ) // Dispatch a request when state is not setup state = BrowserStateReducer.reduce( state, TranslationsAction.ManageLanguageModelsAction(options = options), ) // We don't have an initial state, so nothing should change. assertNull(state.translationEngine.languageModels) // Setting up an initial test state. val code = "es" val localizedDisplayName = "Spanish" val processState = ModelState.NOT_DOWNLOADED val size: Long = 1234 val language = Language(code, localizedDisplayName) val languageModel = LanguageModel(language, processState, size) val languageModels = mutableListOf(languageModel) state = BrowserStateReducer.reduce( state, TranslationsAction.SetLanguageModelsAction(languageModels = languageModels), ) // Dispatch a valid request state = BrowserStateReducer.reduce( state, TranslationsAction.ManageLanguageModelsAction(options = options), ) // Expectations based on operation val expectedLanguageModel = LanguageModel(language, ModelState.DOWNLOAD_IN_PROGRESS, size) val expectedLanguageModels = mutableListOf(expectedLanguageModel) assertEquals(expectedLanguageModels, state.translationEngine.languageModels) // Dispatch a language not listed state = BrowserStateReducer.reduce( state, TranslationsAction.ManageLanguageModelsAction( options = ModelManagementOptions( languageToManage = "de", operation = ModelOperation.DOWNLOAD, operationLevel = OperationLevel.LANGUAGE, ) ), ) // Nothing should change, since it isn't a known option assertEquals(expectedLanguageModels, state.translationEngine.languageModels) } @Test fun `WHEN SetOfferTranslateSettingAction is called then set offerToTranslate`() { // Initial State assertNull(state.translationEngine.offerTranslation) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.SetGlobalOfferTranslateSettingAction(offerTranslation = false), ) // Action success assertFalse(state.translationEngine.offerTranslation!!) } @Test fun `WHEN UpdateOfferTranslateSettingAction is called then set offerToTranslate`() { // Initial State assertNull(state.translationEngine.offerTranslation) // Action started state = BrowserStateReducer.reduce( state, TranslationsAction.UpdateGlobalOfferTranslateSettingAction(offerTranslation = false), ) // Action success assertFalse(state.translationEngine.offerTranslation!!) } @Test fun `WHEN UpdateGlobalLanguageSettingAction is called then update languageSettings`() { // Initial State assertNull(state.translationEngine.languageSettings) // No-op null test state = BrowserStateReducer.reduce( state, TranslationsAction.UpdateLanguageSettingsAction( languageCode = "fr", setting = LanguageSetting.ALWAYS, ), ) assertNull(state.translationEngine.languageSettings) // Setting Initial State val languageSettings = mapOf( "en" to LanguageSetting.OFFER, "es" to LanguageSetting.NEVER, "de" to LanguageSetting.ALWAYS, ) state = BrowserStateReducer.reduce( state, TranslationsAction.SetLanguageSettingsAction(languageSettings = languageSettings), ) assertEquals(languageSettings, state.translationEngine.languageSettings) // No-op update test state = BrowserStateReducer.reduce( state, TranslationsAction.UpdateLanguageSettingsAction( languageCode = "fr", setting = LanguageSetting.ALWAYS, ), ) assertEquals(languageSettings, state.translationEngine.languageSettings) // Main action started state = BrowserStateReducer.reduce( state, TranslationsAction.UpdateLanguageSettingsAction( languageCode = "es", setting = LanguageSetting.ALWAYS, ), ) // Action success assertEquals(LanguageSetting.ALWAYS, state.translationEngine.languageSettings!!["es"]) } }