commit 2cf6abd54c1cbb56ad123ceebac555f38752b09b
parent fa582eed17d875e161c0654cb4fd1a69aebf7a44
Author: pollymce <pmceldowney@mozilla.com>
Date: Wed, 3 Dec 2025 15:10:05 +0000
Bug 2000584 - async all the engine observer action dispatches r=android-reviewers,jonalmeida
This seems to speed up the browsertime performance metrics.
Differential Revision: https://phabricator.services.mozilla.com/D274783
Diffstat:
4 files changed, 463 insertions(+), 211 deletions(-)
diff --git a/mobile/android/android-components/components/browser/state/src/main/java/mozilla/components/browser/state/engine/EngineObserver.kt b/mobile/android/android-components/components/browser/state/src/main/java/mozilla/components/browser/state/engine/EngineObserver.kt
@@ -6,6 +6,8 @@ package mozilla.components.browser.state.engine
import android.content.Intent
import android.os.Environment
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.launch
import mozilla.components.browser.state.action.BrowserAction
import mozilla.components.browser.state.action.ContentAction
import mozilla.components.browser.state.action.CookieBannerAction
@@ -52,46 +54,47 @@ private const val PAGE_LOAD_COMPLETION_PROGRESS = 100
internal class EngineObserver(
private val tabId: String,
private val store: Store<BrowserState, BrowserAction>,
+ private val scope: CoroutineScope,
) : EngineSession.Observer {
override fun onScrollChange(scrollX: Int, scrollY: Int) {
- store.dispatch(ReaderAction.UpdateReaderScrollYAction(tabId, scrollY))
+ dispatchAsync(ReaderAction.UpdateReaderScrollYAction(tabId, scrollY))
}
override fun onNavigateBack() {
- store.dispatch(ContentAction.UpdateSearchTermsAction(tabId, ""))
+ dispatchAsync(ContentAction.UpdateSearchTermsAction(tabId, ""))
}
override fun onNavigateForward() {
- store.dispatch(ContentAction.UpdateSearchTermsAction(tabId, ""))
+ dispatchAsync(ContentAction.UpdateSearchTermsAction(tabId, ""))
}
override fun onGotoHistoryIndex() {
- store.dispatch(ContentAction.UpdateSearchTermsAction(tabId, ""))
+ dispatchAsync(ContentAction.UpdateSearchTermsAction(tabId, ""))
}
override fun onLoadData() {
- store.dispatch(ContentAction.UpdateSearchTermsAction(tabId, ""))
+ dispatchAsync(ContentAction.UpdateSearchTermsAction(tabId, ""))
}
override fun onLoadUrl() {
if (store.state.findTabOrCustomTab(tabId)?.content?.isSearch == true) {
- store.dispatch(ContentAction.UpdateIsSearchAction(tabId, false))
+ dispatchAsync(ContentAction.UpdateIsSearchAction(tabId, false))
} else {
- store.dispatch(ContentAction.UpdateSearchTermsAction(tabId, ""))
+ dispatchAsync(ContentAction.UpdateSearchTermsAction(tabId, ""))
}
}
override fun onFirstContentfulPaint() {
- store.dispatch(ContentAction.UpdateFirstContentfulPaintStateAction(tabId, true))
+ dispatchAsync(ContentAction.UpdateFirstContentfulPaintStateAction(tabId, true))
}
override fun onPaintStatusReset() {
- store.dispatch(ContentAction.UpdateFirstContentfulPaintStateAction(tabId, false))
+ dispatchAsync(ContentAction.UpdateFirstContentfulPaintStateAction(tabId, false))
}
override fun onLocationChange(url: String, hasUserGesture: Boolean) {
- store.dispatch(ContentAction.UpdateUrlAction(tabId, url, hasUserGesture))
+ dispatchAsync(ContentAction.UpdateUrlAction(tabId, url, hasUserGesture))
}
@Suppress("DEPRECATION") // Session observable is deprecated
@@ -101,11 +104,11 @@ internal class EngineObserver(
triggeredByWebContent: Boolean,
) {
if (triggeredByWebContent) {
- store.dispatch(ContentAction.UpdateSearchTermsAction(tabId, ""))
+ dispatchAsync(ContentAction.UpdateSearchTermsAction(tabId, ""))
}
val loadRequest = LoadRequestState(url, triggeredByRedirect, triggeredByWebContent)
- store.dispatch(ContentAction.UpdateLoadRequestAction(tabId, loadRequest))
+ dispatchAsync(ContentAction.UpdateLoadRequestAction(tabId, loadRequest))
}
override fun onLaunchIntentRequest(
@@ -114,7 +117,7 @@ internal class EngineObserver(
fallbackUrl: String?,
appName: String?,
) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateAppIntentAction(
tabId,
AppIntentState(url, appIntent, fallbackUrl, appName),
@@ -123,11 +126,11 @@ internal class EngineObserver(
}
override fun onTitleChange(title: String) {
- store.dispatch(ContentAction.UpdateTitleAction(tabId, title))
+ dispatchAsync(ContentAction.UpdateTitleAction(tabId, title))
}
override fun onPreviewImageChange(previewImageUrl: String) {
- store.dispatch(ContentAction.UpdatePreviewImageAction(tabId, previewImageUrl))
+ dispatchAsync(ContentAction.UpdatePreviewImageAction(tabId, previewImageUrl))
}
override fun onProgress(progress: Int) {
@@ -135,32 +138,32 @@ internal class EngineObserver(
// referencing to a field in the state is not recommended, this flow should be reconsidered
// while the visual completeness logic is revisited in Bug 1966977.
if (progress == PAGE_LOAD_COMPLETION_PROGRESS && !store.state.translationsInitialized) {
- store.dispatch(TranslationsAction.InitTranslationsBrowserState)
+ dispatchAsync(TranslationsAction.InitTranslationsBrowserState)
}
- store.dispatch(ContentAction.UpdateProgressAction(tabId, progress))
+ dispatchAsync(ContentAction.UpdateProgressAction(tabId, progress))
}
override fun onLoadingStateChange(loading: Boolean) {
- store.dispatch(ContentAction.UpdateLoadingStateAction(tabId, loading))
+ dispatchAsync(ContentAction.UpdateLoadingStateAction(tabId, loading))
if (loading) {
- store.dispatch(ContentAction.ClearFindResultsAction(tabId))
- store.dispatch(ContentAction.UpdateRefreshCanceledStateAction(tabId, false))
- store.dispatch(TrackingProtectionAction.ClearTrackersAction(tabId))
+ dispatchAsync(ContentAction.ClearFindResultsAction(tabId))
+ dispatchAsync(ContentAction.UpdateRefreshCanceledStateAction(tabId, false))
+ dispatchAsync(TrackingProtectionAction.ClearTrackersAction(tabId))
}
}
override fun onNavigationStateChange(canGoBack: Boolean?, canGoForward: Boolean?) {
canGoBack?.let {
- store.dispatch(ContentAction.UpdateBackNavigationStateAction(tabId, canGoBack))
+ dispatchAsync(ContentAction.UpdateBackNavigationStateAction(tabId, canGoBack))
}
canGoForward?.let {
- store.dispatch(ContentAction.UpdateForwardNavigationStateAction(tabId, canGoForward))
+ dispatchAsync(ContentAction.UpdateForwardNavigationStateAction(tabId, canGoForward))
}
}
override fun onSecurityChange(secure: Boolean, host: String?, issuer: String?, certificate: X509Certificate?) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateSecurityInfoAction(
tabId,
SecurityInfoState(secure, host ?: "", issuer ?: "", certificate),
@@ -169,41 +172,41 @@ internal class EngineObserver(
}
override fun onTrackerBlocked(tracker: Tracker) {
- store.dispatch(TrackingProtectionAction.TrackerBlockedAction(tabId, tracker))
+ dispatchAsync(TrackingProtectionAction.TrackerBlockedAction(tabId, tracker))
}
override fun onTrackerLoaded(tracker: Tracker) {
- store.dispatch(TrackingProtectionAction.TrackerLoadedAction(tabId, tracker))
+ dispatchAsync(TrackingProtectionAction.TrackerLoadedAction(tabId, tracker))
}
override fun onExcludedOnTrackingProtectionChange(excluded: Boolean) {
- store.dispatch(TrackingProtectionAction.ToggleExclusionListAction(tabId, excluded))
+ dispatchAsync(TrackingProtectionAction.ToggleExclusionListAction(tabId, excluded))
}
override fun onTrackerBlockingEnabledChange(enabled: Boolean) {
- store.dispatch(TrackingProtectionAction.ToggleAction(tabId, enabled))
+ dispatchAsync(TrackingProtectionAction.ToggleAction(tabId, enabled))
}
override fun onCookieBannerChange(status: EngineSession.CookieBannerHandlingStatus) {
- store.dispatch(CookieBannerAction.UpdateStatusAction(tabId, status))
+ dispatchAsync(CookieBannerAction.UpdateStatusAction(tabId, status))
}
override fun onTranslatePageChange() {
- store.dispatch(TranslationsAction.SetTranslateProcessingAction(tabId, isProcessing = false))
+ dispatchAsync(TranslationsAction.SetTranslateProcessingAction(tabId, isProcessing = false))
}
override fun onLongPress(hitResult: HitResult) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateHitResultAction(tabId, hitResult),
)
}
override fun onFind(text: String) {
- store.dispatch(ContentAction.ClearFindResultsAction(tabId))
+ dispatchAsync(ContentAction.ClearFindResultsAction(tabId))
}
override fun onFindResult(activeMatchOrdinal: Int, numberOfMatches: Int, isDoneCounting: Boolean) {
- store.dispatch(
+ dispatchAsync(
ContentAction.AddFindResultAction(
tabId,
FindResultState(
@@ -246,7 +249,7 @@ internal class EngineObserver(
etag = response?.headers?.get(E_TAG),
)
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateDownloadAction(
tabId,
download,
@@ -255,7 +258,7 @@ internal class EngineObserver(
}
override fun onDesktopModeChange(enabled: Boolean) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateTabDesktopMode(
tabId,
enabled,
@@ -264,7 +267,7 @@ internal class EngineObserver(
}
override fun onFullScreenChange(enabled: Boolean) {
- store.dispatch(
+ dispatchAsync(
ContentAction.FullScreenChangedAction(
tabId,
enabled,
@@ -273,7 +276,7 @@ internal class EngineObserver(
}
override fun onMetaViewportFitChanged(layoutInDisplayCutoutMode: Int) {
- store.dispatch(
+ dispatchAsync(
ContentAction.ViewportFitChangedAction(
tabId,
layoutInDisplayCutoutMode,
@@ -282,7 +285,7 @@ internal class EngineObserver(
}
override fun onContentPermissionRequest(permissionRequest: PermissionRequest) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdatePermissionsRequest(
tabId,
permissionRequest,
@@ -291,7 +294,7 @@ internal class EngineObserver(
}
override fun onCancelContentPermissionRequest(permissionRequest: PermissionRequest) {
- store.dispatch(
+ dispatchAsync(
ContentAction.ConsumePermissionsRequest(
tabId,
permissionRequest,
@@ -300,7 +303,7 @@ internal class EngineObserver(
}
override fun onAppPermissionRequest(permissionRequest: PermissionRequest) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateAppPermissionsRequest(
tabId,
permissionRequest,
@@ -309,7 +312,7 @@ internal class EngineObserver(
}
override fun onPromptRequest(promptRequest: PromptRequest) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdatePromptRequestAction(
tabId,
promptRequest,
@@ -318,27 +321,27 @@ internal class EngineObserver(
}
override fun onPromptDismissed(promptRequest: PromptRequest) {
- store.dispatch(
+ dispatchAsync(
ContentAction.ConsumePromptRequestAction(tabId, promptRequest),
)
}
override fun onPromptUpdate(previousPromptRequestUid: String, promptRequest: PromptRequest) {
- store.dispatch(
+ dispatchAsync(
ContentAction.ReplacePromptRequestAction(tabId, previousPromptRequestUid, promptRequest),
)
}
override fun onRepostPromptCancelled() {
- store.dispatch(ContentAction.UpdateRefreshCanceledStateAction(tabId, true))
+ dispatchAsync(ContentAction.UpdateRefreshCanceledStateAction(tabId, true))
}
override fun onBeforeUnloadPromptDenied() {
- store.dispatch(ContentAction.UpdateRefreshCanceledStateAction(tabId, true))
+ dispatchAsync(ContentAction.UpdateRefreshCanceledStateAction(tabId, true))
}
override fun onWindowRequest(windowRequest: WindowRequest) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateWindowRequestAction(
tabId,
windowRequest,
@@ -347,13 +350,13 @@ internal class EngineObserver(
}
override fun onShowDynamicToolbar() {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateExpandedToolbarStateAction(tabId, true),
)
}
override fun onMediaActivated(mediaSessionController: MediaSession.Controller) {
- store.dispatch(
+ dispatchAsync(
MediaSessionAction.ActivatedMediaSessionAction(
tabId,
mediaSessionController,
@@ -362,15 +365,15 @@ internal class EngineObserver(
}
override fun onMediaDeactivated() {
- store.dispatch(MediaSessionAction.DeactivatedMediaSessionAction(tabId))
+ dispatchAsync(MediaSessionAction.DeactivatedMediaSessionAction(tabId))
}
override fun onMediaMetadataChanged(metadata: MediaSession.Metadata) {
- store.dispatch(MediaSessionAction.UpdateMediaMetadataAction(tabId, metadata))
+ dispatchAsync(MediaSessionAction.UpdateMediaMetadataAction(tabId, metadata))
}
override fun onMediaPlaybackStateChanged(playbackState: MediaSession.PlaybackState) {
- store.dispatch(
+ dispatchAsync(
MediaSessionAction.UpdateMediaPlaybackStateAction(
tabId,
playbackState,
@@ -379,7 +382,7 @@ internal class EngineObserver(
}
override fun onMediaFeatureChanged(features: MediaSession.Feature) {
- store.dispatch(
+ dispatchAsync(
MediaSessionAction.UpdateMediaFeatureAction(
tabId,
features,
@@ -388,7 +391,7 @@ internal class EngineObserver(
}
override fun onMediaPositionStateChanged(positionState: MediaSession.PositionState) {
- store.dispatch(
+ dispatchAsync(
MediaSessionAction.UpdateMediaPositionStateAction(
tabId,
positionState,
@@ -397,7 +400,7 @@ internal class EngineObserver(
}
override fun onMediaMuteChanged(muted: Boolean) {
- store.dispatch(
+ dispatchAsync(
MediaSessionAction.UpdateMediaMutedAction(
tabId,
muted,
@@ -409,7 +412,7 @@ internal class EngineObserver(
fullscreen: Boolean,
elementMetadata: MediaSession.ElementMetadata?,
) {
- store.dispatch(
+ dispatchAsync(
MediaSessionAction.UpdateMediaFullscreenAction(
tabId,
fullscreen,
@@ -419,11 +422,11 @@ internal class EngineObserver(
}
override fun onWebAppManifestLoaded(manifest: WebAppManifest) {
- store.dispatch(ContentAction.UpdateWebAppManifestAction(tabId, manifest))
+ dispatchAsync(ContentAction.UpdateWebAppManifestAction(tabId, manifest))
}
override fun onCrash() {
- store.dispatch(
+ dispatchAsync(
CrashAction.SessionCrashedAction(
tabId,
),
@@ -431,7 +434,7 @@ internal class EngineObserver(
}
override fun onProcessKilled() {
- store.dispatch(
+ dispatchAsync(
EngineAction.KillEngineSessionAction(
tabId,
),
@@ -439,7 +442,7 @@ internal class EngineObserver(
}
override fun onStateUpdated(state: EngineSessionState) {
- store.dispatch(
+ dispatchAsync(
EngineAction.UpdateEngineSessionStateAction(
tabId,
state,
@@ -448,7 +451,7 @@ internal class EngineObserver(
}
override fun onRecordingStateChanged(devices: List<RecordingDevice>) {
- store.dispatch(
+ dispatchAsync(
ContentAction.SetRecordingDevices(
tabId,
devices,
@@ -457,7 +460,7 @@ internal class EngineObserver(
}
override fun onHistoryStateChanged(historyList: List<HistoryItem>, currentIndex: Int) {
- store.dispatch(
+ dispatchAsync(
ContentAction.UpdateHistoryStateAction(
tabId,
historyList,
@@ -467,46 +470,50 @@ internal class EngineObserver(
}
override fun onSaveToPdfException(throwable: Throwable) {
- store.dispatch(EngineAction.SaveToPdfExceptionAction(tabId, throwable))
+ dispatchAsync(EngineAction.SaveToPdfExceptionAction(tabId, throwable))
}
override fun onPrintFinish() {
- store.dispatch(EngineAction.PrintContentCompletedAction(tabId))
+ dispatchAsync(EngineAction.PrintContentCompletedAction(tabId))
}
override fun onPrintException(isPrint: Boolean, throwable: Throwable) {
- store.dispatch(EngineAction.PrintContentExceptionAction(tabId, isPrint, throwable))
+ dispatchAsync(EngineAction.PrintContentExceptionAction(tabId, isPrint, throwable))
}
override fun onSaveToPdfComplete() {
- store.dispatch(EngineAction.SaveToPdfCompleteAction(tabId))
+ dispatchAsync(EngineAction.SaveToPdfCompleteAction(tabId))
}
override fun onCheckForFormData(containsFormData: Boolean, adjustPriority: Boolean) {
- store.dispatch(ContentAction.UpdateHasFormDataAction(tabId, containsFormData, adjustPriority))
+ dispatchAsync(ContentAction.UpdateHasFormDataAction(tabId, containsFormData, adjustPriority))
}
override fun onCheckForFormDataException(throwable: Throwable) {
- store.dispatch(ContentAction.CheckForFormDataExceptionAction(tabId, throwable))
+ dispatchAsync(ContentAction.CheckForFormDataExceptionAction(tabId, throwable))
}
override fun onTranslateExpected() {
- store.dispatch(TranslationsAction.TranslateExpectedAction(tabId))
+ dispatchAsync(TranslationsAction.TranslateExpectedAction(tabId))
}
override fun onTranslateOffer() {
- store.dispatch(TranslationsAction.TranslateOfferAction(tabId = tabId, isOfferTranslate = true))
+ dispatchAsync(TranslationsAction.TranslateOfferAction(tabId = tabId, isOfferTranslate = true))
}
override fun onTranslateStateChange(state: TranslationEngineState) {
- store.dispatch(TranslationsAction.TranslateStateChangeAction(tabId, state))
+ dispatchAsync(TranslationsAction.TranslateStateChangeAction(tabId, state))
}
override fun onTranslateComplete(operation: TranslationOperation) {
- store.dispatch(TranslationsAction.TranslateSuccessAction(tabId, operation))
+ dispatchAsync(TranslationsAction.TranslateSuccessAction(tabId, operation))
}
override fun onTranslateException(operation: TranslationOperation, translationError: TranslationError) {
- store.dispatch(TranslationsAction.TranslateExceptionAction(tabId, operation, translationError))
+ dispatchAsync(TranslationsAction.TranslateExceptionAction(tabId, operation, translationError))
+ }
+
+ private fun dispatchAsync(action: BrowserAction) = scope.launch {
+ store.dispatch(action)
}
}
diff --git a/mobile/android/android-components/components/browser/state/src/main/java/mozilla/components/browser/state/engine/middleware/LinkingMiddleware.kt b/mobile/android/android-components/components/browser/state/src/main/java/mozilla/components/browser/state/engine/middleware/LinkingMiddleware.kt
@@ -83,7 +83,7 @@ internal class LinkingMiddleware(
skipLoading: Boolean = true,
includeParent: Boolean,
): Pair<String, EngineObserver> {
- val observer = EngineObserver(tab.id, context.store)
+ val observer = EngineObserver(tab.id, context.store, scope)
engineSession.register(observer)
if (skipLoading) {
diff --git a/mobile/android/android-components/components/browser/state/src/test/java/mozilla/components/browser/state/engine/EngineObserverTest.kt b/mobile/android/android-components/components/browser/state/src/test/java/mozilla/components/browser/state/engine/EngineObserverTest.kt
@@ -7,6 +7,9 @@ package mozilla.components.browser.state.engine
import android.content.Intent
import android.view.WindowManager
import androidx.test.ext.junit.runners.AndroidJUnit4
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import mozilla.components.browser.state.action.BrowserAction
import mozilla.components.browser.state.action.ContentAction
@@ -60,11 +63,12 @@ import org.mockito.Mockito.never
import org.mockito.Mockito.verify
import org.mockito.Mockito.`when`
+@OptIn(ExperimentalCoroutinesApi::class)
@RunWith(AndroidJUnit4::class)
class EngineObserverTest {
// TO DO: add tests for product URL after a test endpoint is implemented in desktop (Bug 1846341)
@Test
- fun engineSessionObserver() {
+ fun engineSessionObserver() = runTest {
val engineSession = object : EngineSession() {
override val settings: Settings = mock()
override fun goBack(userInteraction: Boolean) {}
@@ -141,9 +145,10 @@ class EngineObserverTest {
val store = BrowserStore()
store.dispatch(TabListAction.AddTabAction(createTab("https://www.mozilla.org", id = "mozilla")))
- engineSession.register(EngineObserver("mozilla", store))
+ engineSession.register(createEngineObserver(store = store, scope = this))
engineSession.loadUrl("http://mozilla.org")
engineSession.toggleDesktopMode(true)
+ advanceUntilIdle()
assertEquals("http://mozilla.org", store.state.selectedTab?.content?.url)
assertEquals(100, store.state.selectedTab?.content?.progress)
@@ -156,7 +161,7 @@ class EngineObserverTest {
}
@Test
- fun engineSessionObserverWithSecurityChanges() {
+ fun engineSessionObserverWithSecurityChanges() = runTest {
val engineSession = object : EngineSession() {
override val settings: Settings = mock()
override fun goBack(userInteraction: Boolean) {}
@@ -232,17 +237,21 @@ class EngineObserverTest {
),
)
- engineSession.register(EngineObserver("mozilla", store))
+ engineSession.register(createEngineObserver(store = store, scope = this))
engineSession.loadUrl("http://mozilla.org")
+ advanceUntilIdle()
+
assertEquals(SecurityInfoState(secure = false), store.state.tabs[0].content.securityInfo)
engineSession.loadUrl("https://mozilla.org")
+ advanceUntilIdle()
+
assertEquals(SecurityInfoState(secure = true, "host", "issuer"), store.state.tabs[0].content.securityInfo)
}
@Test
- fun engineSessionObserverWithTrackingProtection() {
+ fun engineSessionObserverWithTrackingProtection() = runTest {
val engineSession = object : EngineSession() {
override val settings: Settings = mock()
override fun goBack(userInteraction: Boolean) {}
@@ -310,23 +319,25 @@ class EngineObserverTest {
),
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
engineSession.register(observer)
val tracker1 = Tracker("tracker1", emptyList())
val tracker2 = Tracker("tracker2", emptyList())
observer.onTrackerBlocked(tracker1)
+ advanceUntilIdle()
assertEquals(listOf(tracker1), store.state.tabs[0].trackingProtection.blockedTrackers)
observer.onTrackerBlocked(tracker2)
+ advanceUntilIdle()
assertEquals(listOf(tracker1, tracker2), store.state.tabs[0].trackingProtection.blockedTrackers)
}
@Test
- fun `WHEN the first page load is complete, set the translations initialized`() {
+ fun `WHEN the first page load is complete, set the translations initialized`() = runTest {
val engineSession = object : EngineSession() {
override val settings: Settings = mock()
override fun goBack(userInteraction: Boolean) {}
@@ -400,15 +411,17 @@ class EngineObserverTest {
assertEquals(false, store.state.translationsInitialized)
- engineSession.register(EngineObserver("mozilla", store))
+ engineSession.register(createEngineObserver(store = store, scope = this))
engineSession.loadUrl("https://mozilla.org")
+ advanceUntilIdle()
+
assertEquals(true, store.state.translationsInitialized)
}
@Test
- fun `WHEN the first page load is not complete, do not set the translations initialized`() {
+ fun `WHEN the first page load is not complete, do not set the translations initialized`() = runTest {
val engineSession = object : EngineSession() {
override val settings: Settings = mock()
override fun goBack(userInteraction: Boolean) {}
@@ -482,7 +495,7 @@ class EngineObserverTest {
assertEquals(false, store.state.translationsInitialized)
- engineSession.register(EngineObserver("mozilla", store))
+ engineSession.register(createEngineObserver(store = store, scope = this))
engineSession.loadUrl("https://mozilla.org")
@@ -490,13 +503,13 @@ class EngineObserverTest {
}
@Test
- fun `Do not initialize the translations flow if the page load is not complete`() {
+ fun `Do not initialize the translations flow if the page load is not complete`() = runTest {
val store: BrowserStore = mock()
val state: BrowserState = mock()
`when`(store.state).thenReturn(state)
`when`(store.state.translationsInitialized).thenReturn(false)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onProgress(80)
@@ -506,15 +519,16 @@ class EngineObserverTest {
}
@Test
- fun `Initialize the translations flow if page load is complete and it is not yet initialized`() {
+ fun `Initialize the translations flow if page load is complete and it is not yet initialized`() = runTest {
val store: BrowserStore = mock()
val state: BrowserState = mock()
`when`(store.state).thenReturn(state)
`when`(store.state.translationsInitialized).thenReturn(false)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onProgress(100)
+ advanceUntilIdle()
verify(store).dispatch(
TranslationsAction.InitTranslationsBrowserState,
@@ -522,13 +536,13 @@ class EngineObserverTest {
}
@Test
- fun `Do not initialize the translations flow if it is already initialized`() {
+ fun `Do not initialize the translations flow if it is already initialized`() = runTest {
val store: BrowserStore = mock()
val state: BrowserState = mock()
`when`(store.state).thenReturn(state)
`when`(store.state.translationsInitialized).thenReturn(true)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onProgress(100)
@@ -538,11 +552,12 @@ class EngineObserverTest {
}
@Test
- fun engineSessionObserverExcludedOnTrackingProtection() {
+ fun engineSessionObserverExcludedOnTrackingProtection() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onExcludedOnTrackingProtectionChange(true)
+ advanceUntilIdle()
verify(store).dispatch(
TrackingProtectionAction.ToggleExclusionListAction(
@@ -553,11 +568,12 @@ class EngineObserverTest {
}
@Test
- fun `WHEN onCookieBannerChange is called THEN dispatch an CookieBannerAction UpdateStatusAction`() {
+ fun `WHEN onCookieBannerChange is called THEN dispatch an CookieBannerAction UpdateStatusAction`() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onCookieBannerChange(HANDLED)
+ advanceUntilIdle()
verify(store).dispatch(
CookieBannerAction.UpdateStatusAction(
@@ -568,11 +584,12 @@ class EngineObserverTest {
}
@Test
- fun `WHEN onTranslatePageChange is called THEN dispatch a TranslationsAction SetTranslateProcessingAction`() {
+ fun `WHEN onTranslatePageChange is called THEN dispatch a TranslationsAction SetTranslateProcessingAction`() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onTranslatePageChange()
+ advanceUntilIdle()
verify(store).dispatch(
TranslationsAction.SetTranslateProcessingAction(
@@ -583,11 +600,12 @@ class EngineObserverTest {
}
@Test
- fun `WHEN onTranslateComplete is called THEN dispatch a TranslationsAction TranslateSuccessAction`() {
+ fun `WHEN onTranslateComplete is called THEN dispatch a TranslationsAction TranslateSuccessAction`() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onTranslateComplete(operation = TranslationOperation.TRANSLATE)
+ advanceUntilIdle()
verify(store).dispatch(
TranslationsAction.TranslateSuccessAction("mozilla", operation = TranslationOperation.TRANSLATE),
@@ -595,12 +613,13 @@ class EngineObserverTest {
}
@Test
- fun `WHEN onTranslateException is called THEN dispatch a TranslationsAction TranslateExceptionAction`() {
+ fun `WHEN onTranslateException is called THEN dispatch a TranslationsAction TranslateExceptionAction`() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val exception = TranslationError.UnknownError(Exception())
observer.onTranslateException(operation = TranslationOperation.TRANSLATE, exception)
+ advanceUntilIdle()
verify(store).dispatch(
TranslationsAction.TranslateExceptionAction("mozilla", operation = TranslationOperation.TRANSLATE, exception),
@@ -608,7 +627,7 @@ class EngineObserverTest {
}
@Test
- fun engineObserverClearsWebsiteTitleIfNewPageStartsLoading() {
+ fun engineObserverClearsWebsiteTitleIfNewPageStartsLoading() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -621,18 +640,20 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onTitleChange("Mozilla")
+ advanceUntilIdle()
assertEquals("Mozilla", store.state.tabs[0].content.title)
observer.onLocationChange("https://getpocket.com", false)
+ advanceUntilIdle()
assertEquals("", store.state.tabs[0].content.title)
}
@Test
- fun `EngineObserver does not clear title if the URL did not change`() {
+ fun `EngineObserver does not clear title if the URL did not change`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -645,19 +666,21 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onTitleChange("Mozilla")
+ advanceUntilIdle()
assertEquals("Mozilla", store.state.tabs[0].content.title)
observer.onLocationChange("https://www.mozilla.org", false)
+ advanceUntilIdle()
assertEquals("Mozilla", store.state.tabs[0].content.title)
}
@Test
- fun `EngineObserver does not clear title if the URL changes hash`() {
+ fun `EngineObserver does not clear title if the URL changes hash`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -670,19 +693,21 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onTitleChange("Mozilla")
+ advanceUntilIdle()
assertEquals("Mozilla", store.state.tabs[0].content.title)
observer.onLocationChange("https://www.mozilla.org/#something", false)
+ advanceUntilIdle()
assertEquals("Mozilla", store.state.tabs[0].content.title)
}
@Test
- fun `EngineObserver clears previewImageUrl if new page starts loading`() {
+ fun `EngineObserver clears previewImageUrl if new page starts loading`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -696,18 +721,20 @@ class EngineObserverTest {
)
val previewImageUrl = "https://test.com/og-image-url"
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onPreviewImageChange(previewImageUrl)
+ advanceUntilIdle()
assertEquals(previewImageUrl, store.state.tabs[0].content.previewImageUrl)
observer.onLocationChange("https://getpocket.com", false)
+ advanceUntilIdle()
assertNull(store.state.tabs[0].content.previewImageUrl)
}
@Test
- fun `EngineObserver does not clear previewImageUrl if the URL did not change`() {
+ fun `EngineObserver does not clear previewImageUrl if the URL did not change`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -721,23 +748,26 @@ class EngineObserverTest {
)
val previewImageUrl = "https://test.com/og-image-url"
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onPreviewImageChange(previewImageUrl)
+ advanceUntilIdle()
assertEquals(previewImageUrl, store.state.tabs[0].content.previewImageUrl)
observer.onLocationChange("https://www.mozilla.org", false)
+ advanceUntilIdle()
assertEquals(previewImageUrl, store.state.tabs[0].content.previewImageUrl)
observer.onLocationChange("https://www.mozilla.org/#something", false)
+ advanceUntilIdle()
assertEquals(previewImageUrl, store.state.tabs[0].content.previewImageUrl)
}
@Test
- fun engineObserverClearsBlockedTrackersIfNewPageStartsLoading() {
+ fun engineObserverClearsBlockedTrackersIfNewPageStartsLoading() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -749,23 +779,25 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val tracker1 = Tracker("tracker1")
val tracker2 = Tracker("tracker2")
observer.onTrackerBlocked(tracker1)
observer.onTrackerBlocked(tracker2)
+ advanceUntilIdle()
assertEquals(listOf(tracker1, tracker2), store.state.tabs[0].trackingProtection.blockedTrackers)
observer.onLoadingStateChange(true)
+ advanceUntilIdle()
assertEquals(emptyList<String>(), store.state.tabs[0].trackingProtection.blockedTrackers)
}
@Test
- fun engineObserverClearsLoadedTrackersIfNewPageStartsLoading() {
+ fun engineObserverClearsLoadedTrackersIfNewPageStartsLoading() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -777,23 +809,25 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val tracker1 = Tracker("tracker1")
val tracker2 = Tracker("tracker2")
observer.onTrackerLoaded(tracker1)
observer.onTrackerLoaded(tracker2)
+ advanceUntilIdle()
assertEquals(listOf(tracker1, tracker2), store.state.tabs[0].trackingProtection.loadedTrackers)
observer.onLoadingStateChange(true)
+ advanceUntilIdle()
assertEquals(emptyList<String>(), store.state.tabs[0].trackingProtection.loadedTrackers)
}
@Test
- fun engineObserverClearsWebAppManifestIfNewPageStartsLoading() {
+ fun engineObserverClearsWebAppManifestIfNewPageStartsLoading() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -807,19 +841,21 @@ class EngineObserverTest {
val manifest = WebAppManifest(name = "Mozilla", startUrl = "https://mozilla.org")
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onWebAppManifestLoaded(manifest)
+ advanceUntilIdle()
assertEquals(manifest, store.state.tabs[0].content.webAppManifest)
observer.onLocationChange("https://getpocket.com", false)
+ advanceUntilIdle()
assertNull(store.state.tabs[0].content.webAppManifest)
}
@Test
- fun engineObserverClearsContentPermissionRequestIfNewPageStartsLoading() {
+ fun engineObserverClearsContentPermissionRequestIfNewPageStartsLoading() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -831,21 +867,23 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val request: PermissionRequest = mock()
store.dispatch(ContentAction.UpdatePermissionsRequest("mozilla", request))
+ advanceUntilIdle()
assertEquals(listOf(request), store.state.tabs[0].content.permissionRequestsList)
observer.onLocationChange("https://getpocket.com", false)
+ advanceUntilIdle()
assertEquals(emptyList<PermissionRequest>(), store.state.tabs[0].content.permissionRequestsList)
}
@Test
- fun engineObserverDoesNotClearContentPermissionRequestIfSamePageStartsLoading() {
+ fun engineObserverDoesNotClearContentPermissionRequestIfSamePageStartsLoading() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -857,7 +895,7 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val request: PermissionRequest = mock()
@@ -871,7 +909,7 @@ class EngineObserverTest {
}
@Test
- fun engineObserverDoesNotClearWebAppManifestIfNewPageInStartUrlScope() {
+ fun engineObserverDoesNotClearWebAppManifestIfNewPageInStartUrlScope() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -885,9 +923,10 @@ class EngineObserverTest {
val manifest = WebAppManifest(name = "Mozilla", startUrl = "https://www.mozilla.org")
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onWebAppManifestLoaded(manifest)
+ advanceUntilIdle()
assertEquals(manifest, store.state.tabs[0].content.webAppManifest)
@@ -897,7 +936,7 @@ class EngineObserverTest {
}
@Test
- fun engineObserverDoesNotClearWebAppManifestIfNewPageInScope() {
+ fun engineObserverDoesNotClearWebAppManifestIfNewPageInScope() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -915,22 +954,26 @@ class EngineObserverTest {
scope = "https://www.mozilla.org/hello/",
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onWebAppManifestLoaded(manifest)
+ advanceUntilIdle()
assertEquals(manifest, store.state.tabs[0].content.webAppManifest)
observer.onLocationChange("https://www.mozilla.org/hello/page2.html", false)
+ advanceUntilIdle()
assertEquals(manifest, store.state.tabs[0].content.webAppManifest)
observer.onLocationChange("https://www.mozilla.org/hello.html", false)
+ advanceUntilIdle()
+
assertNull(store.state.tabs[0].content.webAppManifest)
}
@Test
- fun engineObserverPassingHitResult() {
+ fun engineObserverPassingHitResult() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -942,76 +985,88 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val hitResult = HitResult.UNKNOWN("data://foobar")
observer.onLongPress(hitResult)
+ advanceUntilIdle()
assertEquals(hitResult, store.state.tabs[0].content.hitResult)
}
@Test
- fun engineObserverClearsFindResults() {
+ fun engineObserverClearsFindResults() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("tab", store)
+ val observer = createEngineObserver(tabId = "tab", store = store, scope = this)
observer.onFindResult(0, 1, false)
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.AddFindResultAction::class) { action ->
assertEquals("tab", action.sessionId)
assertEquals(FindResultState(0, 1, false), action.findResult)
}
observer.onFind("mozilla")
+ advanceUntilIdle()
middleware.assertLastAction(ContentAction.ClearFindResultsAction::class) { action ->
assertEquals("tab", action.sessionId)
}
}
@Test
- fun engineObserverClearsFindResultIfNewPageStartsLoading() {
+ fun engineObserverClearsFindResultIfNewPageStartsLoading() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("tab-id", store)
+ val observer = createEngineObserver(tabId = "tab-id", store = store, scope = this)
observer.onFindResult(0, 1, false)
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.AddFindResultAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertEquals(FindResultState(0, 1, false), action.findResult)
}
observer.onFindResult(1, 2, true)
+ advanceUntilIdle()
middleware.assertLastAction(ContentAction.AddFindResultAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertEquals(FindResultState(1, 2, true), action.findResult)
}
observer.onLoadingStateChange(true)
+ advanceUntilIdle()
middleware.assertLastAction(ContentAction.ClearFindResultsAction::class) { action ->
assertEquals("tab-id", action.sessionId)
}
}
@Test
- fun engineObserverClearsRefreshCanceledIfNewPageStartsLoading() {
+ fun engineObserverClearsRefreshCanceledIfNewPageStartsLoading() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onRepostPromptCancelled()
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.UpdateRefreshCanceledStateAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertTrue(action.refreshCanceled)
}
observer.onLoadingStateChange(true)
+ advanceUntilIdle()
middleware.assertLastAction(ContentAction.UpdateRefreshCanceledStateAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertFalse(action.refreshCanceled)
@@ -1019,38 +1074,55 @@ class EngineObserverTest {
}
@Test
- fun engineObserverHandlesOnRepostPromptCancelled() {
+ fun engineObserverHandlesOnRepostPromptCancelled() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onRepostPromptCancelled()
+ advanceUntilIdle()
+
verify(store).dispatch(ContentAction.UpdateRefreshCanceledStateAction("tab-id", true))
}
@Test
- fun engineObserverHandlesOnBeforeUnloadDenied() {
+ fun engineObserverHandlesOnBeforeUnloadDenied() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onBeforeUnloadPromptDenied()
+ advanceUntilIdle()
verify(store).dispatch(ContentAction.UpdateRefreshCanceledStateAction("tab-id", true))
}
@Test
- fun engineObserverNotifiesFullscreenMode() {
+ fun engineObserverNotifiesFullscreenMode() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onFullScreenChange(true)
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.FullScreenChangedAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertTrue(action.fullScreenEnabled)
}
observer.onFullScreenChange(false)
+ advanceUntilIdle()
middleware.assertLastAction(ContentAction.FullScreenChangedAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertFalse(action.fullScreenEnabled)
@@ -1058,20 +1130,26 @@ class EngineObserverTest {
}
@Test
- fun engineObserverNotifiesDesktopMode() {
+ fun engineObserverNotifiesDesktopMode() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onDesktopModeChange(true)
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.UpdateTabDesktopMode::class) { action ->
assertEquals("tab-id", action.sessionId)
assertTrue(action.enabled)
}
observer.onDesktopModeChange(false)
+ advanceUntilIdle()
middleware.assertLastAction(ContentAction.UpdateTabDesktopMode::class) { action ->
assertEquals("tab-id", action.sessionId)
assertFalse(action.enabled)
@@ -1079,14 +1157,20 @@ class EngineObserverTest {
}
@Test
- fun engineObserverNotifiesMetaViewportFitChange() {
+ fun engineObserverNotifiesMetaViewportFitChange() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onMetaViewportFitChanged(WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT)
+ advanceUntilIdle()
+
middleware.assertFirstAction(ContentAction.ViewportFitChangedAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertEquals(
@@ -1096,6 +1180,8 @@ class EngineObserverTest {
}
observer.onMetaViewportFitChanged(WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES)
+ advanceUntilIdle()
+
middleware.assertLastAction(ContentAction.ViewportFitChangedAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertEquals(
@@ -1105,6 +1191,8 @@ class EngineObserverTest {
}
observer.onMetaViewportFitChanged(WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER)
+ advanceUntilIdle()
+
middleware.assertLastAction(ContentAction.ViewportFitChangedAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertEquals(
@@ -1114,6 +1202,8 @@ class EngineObserverTest {
}
observer.onMetaViewportFitChanged(123)
+ advanceUntilIdle()
+
middleware.assertLastAction(ContentAction.ViewportFitChangedAction::class) { action ->
assertEquals("tab-id", action.sessionId)
assertEquals(123, action.layoutInDisplayCutoutMode)
@@ -1121,7 +1211,7 @@ class EngineObserverTest {
}
@Test
- fun engineObserverNotifiesWebAppManifest() {
+ fun engineObserverNotifiesWebAppManifest() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1133,13 +1223,14 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val manifest = WebAppManifest(
name = "Minimal",
startUrl = "/",
)
observer.onWebAppManifestLoaded(manifest)
+ advanceUntilIdle()
assertEquals(manifest, store.state.tabs[0].content.webAppManifest)
}
@@ -1148,12 +1239,18 @@ class EngineObserverTest {
fun engineSessionObserverWithContentPermissionRequests() = runTest {
val permissionRequest: PermissionRequest = mock()
val store: BrowserStore = mock()
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
val action = ContentAction.UpdatePermissionsRequest(
"tab-id",
permissionRequest,
)
observer.onContentPermissionRequest(permissionRequest)
+ advanceUntilIdle()
+
verify(store).dispatch(action)
}
@@ -1161,23 +1258,35 @@ class EngineObserverTest {
fun engineSessionObserverWithAppPermissionRequests() = runTest {
val permissionRequest: PermissionRequest = mock()
val store: BrowserStore = mock()
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
val action = ContentAction.UpdateAppPermissionsRequest(
"tab-id",
permissionRequest,
)
observer.onAppPermissionRequest(permissionRequest)
+ advanceUntilIdle()
+
verify(store).dispatch(action)
}
@Test
- fun engineObserverHandlesPromptRequest() {
+ fun engineObserverHandlesPromptRequest() = runTest {
val promptRequest: PromptRequest = mock<PromptRequest.SingleChoice>()
val store: BrowserStore = mock()
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onPromptRequest(promptRequest)
+ advanceUntilIdle()
+
verify(store).dispatch(
ContentAction.UpdatePromptRequestAction(
"tab-id",
@@ -1187,13 +1296,19 @@ class EngineObserverTest {
}
@Test
- fun engineObserverHandlesOnPromptUpdate() {
+ fun engineObserverHandlesOnPromptUpdate() = runTest {
val promptRequest: PromptRequest = mock<PromptRequest.SingleChoice>()
val store: BrowserStore = mock()
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
val previousPromptUID = "prompt-uid"
observer.onPromptUpdate(previousPromptUID, promptRequest)
+ advanceUntilIdle()
+
verify(store).dispatch(
ContentAction.ReplacePromptRequestAction(
"tab-id",
@@ -1204,13 +1319,19 @@ class EngineObserverTest {
}
@Test
- fun engineObserverHandlesWindowRequest() {
+ fun engineObserverHandlesWindowRequest() = runTest {
val windowRequest: WindowRequest = mock()
val store: BrowserStore = mock()
whenever(store.state).thenReturn(mock())
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onWindowRequest(windowRequest)
+ advanceUntilIdle()
+
verify(store).dispatch(
ContentAction.UpdateWindowRequestAction(
"tab-id",
@@ -1220,12 +1341,18 @@ class EngineObserverTest {
}
@Test
- fun engineObserverHandlesFirstContentfulPaint() {
+ fun engineObserverHandlesFirstContentfulPaint() = runTest {
val store: BrowserStore = mock()
whenever(store.state).thenReturn(mock())
- val observer = EngineObserver("tab-id", store)
+ val observer = createEngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onFirstContentfulPaint()
+ advanceUntilIdle()
+
verify(store).dispatch(
ContentAction.UpdateFirstContentfulPaintStateAction(
"tab-id",
@@ -1235,12 +1362,18 @@ class EngineObserverTest {
}
@Test
- fun engineObserverHandlesPaintStatusReset() {
+ fun engineObserverHandlesPaintStatusReset() = runTest {
val store: BrowserStore = mock()
whenever(store.state).thenReturn(mock())
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onPaintStatusReset()
+ advanceUntilIdle()
+
verify(store).dispatch(
ContentAction.UpdateFirstContentfulPaintStateAction(
"tab-id",
@@ -1250,16 +1383,22 @@ class EngineObserverTest {
}
@Test
- fun engineObserverHandlesOnShowDynamicToolbar() {
+ fun engineObserverHandlesOnShowDynamicToolbar() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("tab-id", store)
+ val observer = EngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onShowDynamicToolbar()
+ advanceUntilIdle()
+
verify(store).dispatch(ContentAction.UpdateExpandedToolbarStateAction("tab-id", true))
}
@Test
- fun `onMediaActivated will update the store`() {
+ fun `onMediaActivated will update the store`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1271,12 +1410,17 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(
+ tabId = "mozilla",
+ store = store,
+ scope = this,
+ )
val mediaSessionController: MediaSession.Controller = mock()
assertNull(store.state.tabs[0].mediaSessionState)
observer.onMediaActivated(mediaSessionController)
+ advanceUntilIdle()
val observedMediaSessionState = store.state.tabs[0].mediaSessionState
assertNotNull(observedMediaSessionState)
@@ -1284,7 +1428,7 @@ class EngineObserverTest {
}
@Test
- fun `onMediaDeactivated will update the store`() {
+ fun `onMediaDeactivated will update the store`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1299,18 +1443,19 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
assertNotNull(store.state.findTab("mozilla")?.mediaSessionState)
observer.onMediaDeactivated()
+ advanceUntilIdle()
val observedMediaSessionState = store.state.findTab("mozilla")?.mediaSessionState
assertNull(observedMediaSessionState)
}
@Test
- fun `onMediaMetadataChanged will update the store`() {
+ fun `onMediaMetadataChanged will update the store`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1325,12 +1470,13 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val mediaSessionController: MediaSession.Controller = mock()
val metaData: MediaSession.Metadata = mock()
observer.onMediaActivated(mediaSessionController)
observer.onMediaMetadataChanged(metaData)
+ advanceUntilIdle()
val observedMediaSessionState = store.state.findTab("mozilla")?.mediaSessionState
assertNotNull(observedMediaSessionState)
@@ -1339,7 +1485,7 @@ class EngineObserverTest {
}
@Test
- fun `onMediaPlaybackStateChanged will update the store`() {
+ fun `onMediaPlaybackStateChanged will update the store`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1354,12 +1500,13 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val mediaSessionController: MediaSession.Controller = mock()
val playbackState: MediaSession.PlaybackState = mock()
observer.onMediaActivated(mediaSessionController)
observer.onMediaPlaybackStateChanged(playbackState)
+ advanceUntilIdle()
val observedMediaSessionState = store.state.findTab("mozilla")?.mediaSessionState
assertNotNull(observedMediaSessionState)
@@ -1368,7 +1515,7 @@ class EngineObserverTest {
}
@Test
- fun `onMediaFeatureChanged will update the store`() {
+ fun `onMediaFeatureChanged will update the store`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1383,12 +1530,13 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val mediaSessionController: MediaSession.Controller = mock()
val features: MediaSession.Feature = mock()
observer.onMediaActivated(mediaSessionController)
observer.onMediaFeatureChanged(features)
+ advanceUntilIdle()
val observedMediaSessionState = store.state.findTab("mozilla")?.mediaSessionState
assertNotNull(observedMediaSessionState)
@@ -1397,7 +1545,7 @@ class EngineObserverTest {
}
@Test
- fun `onMediaPositionStateChanged will update the store`() {
+ fun `onMediaPositionStateChanged will update the store`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1412,12 +1560,13 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val mediaSessionController: MediaSession.Controller = mock()
val positionState: MediaSession.PositionState = mock()
observer.onMediaActivated(mediaSessionController)
observer.onMediaPositionStateChanged(positionState)
+ advanceUntilIdle()
val observedMediaSessionState = store.state.findTab("mozilla")?.mediaSessionState
assertNotNull(observedMediaSessionState)
@@ -1426,7 +1575,7 @@ class EngineObserverTest {
}
@Test
- fun `onMediaMuteChanged will update the store`() {
+ fun `onMediaMuteChanged will update the store`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1441,11 +1590,12 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val mediaSessionController: MediaSession.Controller = mock()
observer.onMediaActivated(mediaSessionController)
observer.onMediaMuteChanged(true)
+ advanceUntilIdle()
val observedMediaSessionState = store.state.findTab("mozilla")?.mediaSessionState
assertNotNull(observedMediaSessionState)
@@ -1454,7 +1604,7 @@ class EngineObserverTest {
}
@Test
- fun `onMediaFullscreenChanged will update the store`() {
+ fun `onMediaFullscreenChanged will update the store`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1469,12 +1619,13 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val mediaSessionController: MediaSession.Controller = mock()
val elementMetadata: MediaSession.ElementMetadata = mock()
observer.onMediaActivated(mediaSessionController)
observer.onMediaFullscreenChanged(true, elementMetadata)
+ advanceUntilIdle()
val observedMediaSessionState = store.state.findTab("mozilla")?.mediaSessionState
assertNotNull(observedMediaSessionState)
@@ -1484,7 +1635,7 @@ class EngineObserverTest {
}
@Test
- fun `updates are ignored when media session is deactivated`() {
+ fun `updates are ignored when media session is deactivated`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1496,7 +1647,7 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
val elementMetadata: MediaSession.ElementMetadata = mock()
observer.onMediaFullscreenChanged(true, elementMetadata)
@@ -1508,7 +1659,7 @@ class EngineObserverTest {
}
@Test
- fun `onExternalResource will update the store`() {
+ fun `onExternalResource will update the store`() = runTest {
val response = mock<Response> {
`when`(headers).thenReturn(MutableHeaders(listOf(Header(E_TAG, "12345"))))
}
@@ -1527,7 +1678,7 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("mozilla", store)
+ val observer = createEngineObserver(store = store, scope = this)
observer.onExternalResource(
url = "mozilla.org/file.txt",
@@ -1538,6 +1689,7 @@ class EngineObserverTest {
contentLength = 100L,
response = response,
)
+ advanceUntilIdle()
val tab = store.state.findTab("mozilla")!!
@@ -1552,7 +1704,7 @@ class EngineObserverTest {
}
@Test
- fun `onExternalResource with negative contentLength`() {
+ fun `onExternalResource with negative contentLength`() = runTest {
val store = BrowserStore(
initialState = BrowserState(
tabs = listOf(
@@ -1567,7 +1719,11 @@ class EngineObserverTest {
),
)
- val observer = EngineObserver("test-tab", store)
+ val observer = createEngineObserver(
+ tabId = "test-tab",
+ store = store,
+ scope = this,
+ )
observer.onExternalResource(url = "mozilla.org/file.txt", contentLength = -1)
@@ -1577,11 +1733,16 @@ class EngineObserverTest {
}
@Test
- fun `onCrashStateChanged will update session and notify observer`() {
+ fun `onCrashStateChanged will update session and notify observer`() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onCrash()
+ advanceUntilIdle()
verify(store).dispatch(
CrashAction.SessionCrashedAction(
@@ -1591,20 +1752,25 @@ class EngineObserverTest {
}
@Test
- fun `onLocationChange does not clear search terms`() {
+ fun `onLocationChange does not clear search terms`() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onLocationChange("https://www.mozilla.org/en-US/", false)
+ advanceUntilIdle()
middleware.assertNotDispatched(ContentAction.UpdateSearchTermsAction::class)
}
@Test
- fun `onLoadRequest clears search terms for requests triggered by web content`() {
+ fun `onLoadRequest clears search terms for requests triggered by web content`() = runTest {
val url = "https://www.mozilla.org"
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
@@ -1612,8 +1778,13 @@ class EngineObserverTest {
middleware = listOf(middleware),
)
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onLoadRequest(url = url, triggeredByRedirect = false, triggeredByWebContent = true)
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.UpdateSearchTermsAction::class) { action ->
assertEquals("", action.searchTerms)
@@ -1623,12 +1794,17 @@ class EngineObserverTest {
@Test
@Suppress("DEPRECATION") // Session observable is deprecated
- fun `onLoadRequest notifies session observers`() {
+ fun `onLoadRequest notifies session observers`() = runTest {
val url = "https://www.mozilla.org"
val store: BrowserStore = mock()
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onLoadRequest(url = url, triggeredByRedirect = true, triggeredByWebContent = false)
+ advanceUntilIdle()
verify(store)
.dispatch(
@@ -1640,7 +1816,7 @@ class EngineObserverTest {
}
@Test
- fun `onLoadRequest does not clear search terms for requests not triggered by user interacting with web content`() {
+ fun `onLoadRequest does not clear search terms for requests not triggered by user interacting with web content`() = runTest {
val url = "https://www.mozilla.org"
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
@@ -1648,33 +1824,48 @@ class EngineObserverTest {
middleware = listOf(middleware),
)
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onLoadRequest(url = url, triggeredByRedirect = false, triggeredByWebContent = false)
+ advanceUntilIdle()
middleware.assertNotDispatched(ContentAction.UpdateSearchTermsAction::class)
}
@Test
- fun `onLaunchIntentRequest dispatches UpdateAppIntentAction`() {
+ fun `onLaunchIntentRequest dispatches UpdateAppIntentAction`() = runTest {
val url = "https://www.mozilla.org"
val store: BrowserStore = mock()
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
val intent: Intent = mock()
observer.onLaunchIntentRequest(url = url, appIntent = intent, fallbackUrl = null, appName = null)
+ advanceUntilIdle()
verify(store).dispatch(ContentAction.UpdateAppIntentAction("test-id", AppIntentState(url, intent, null, null)))
}
@Test
- fun `onNavigateBack clears search terms when navigating back`() {
+ fun `onNavigateBack clears search terms when navigating back`() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onNavigateBack()
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.UpdateSearchTermsAction::class) { action ->
assertEquals("", action.searchTerms)
@@ -1683,14 +1874,19 @@ class EngineObserverTest {
}
@Test
- fun `WHEN navigating forward THEN search terms are cleared`() {
+ fun `WHEN navigating forward THEN search terms are cleared`() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onNavigateForward()
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.UpdateSearchTermsAction::class) { action ->
assertEquals("", action.searchTerms)
@@ -1699,14 +1895,19 @@ class EngineObserverTest {
}
@Test
- fun `WHEN navigating to history index THEN search terms are cleared`() {
+ fun `WHEN navigating to history index THEN search terms are cleared`() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onGotoHistoryIndex()
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.UpdateSearchTermsAction::class) { action ->
assertEquals("", action.searchTerms)
@@ -1715,14 +1916,19 @@ class EngineObserverTest {
}
@Test
- fun `WHEN loading data THEN the search terms are cleared`() {
+ fun `WHEN loading data THEN the search terms are cleared`() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
middleware = listOf(middleware),
)
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onLoadData()
+ advanceUntilIdle()
middleware.assertFirstAction(ContentAction.UpdateSearchTermsAction::class) { action ->
assertEquals("", action.searchTerms)
@@ -1731,7 +1937,7 @@ class EngineObserverTest {
}
@Test
- fun `GIVEN a search is not performed WHEN loading the URL THEN the search terms are cleared`() {
+ fun `GIVEN a search is not performed WHEN loading the URL THEN the search terms are cleared`() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
initialState = BrowserState(
@@ -1744,8 +1950,13 @@ class EngineObserverTest {
store.dispatch(ContentAction.UpdateIsSearchAction("mozilla", false))
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onLoadUrl()
+ advanceUntilIdle()
middleware.assertLastAction(ContentAction.UpdateSearchTermsAction::class) { action ->
assertEquals("", action.searchTerms)
@@ -1754,7 +1965,7 @@ class EngineObserverTest {
}
@Test
- fun `GIVEN a search is performed WHEN loading the URL THEN the search terms are cleared`() {
+ fun `GIVEN a search is performed WHEN loading the URL THEN the search terms are cleared`() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
initialState = BrowserState(
@@ -1767,8 +1978,13 @@ class EngineObserverTest {
store.dispatch(ContentAction.UpdateIsSearchAction("test-id", true))
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onLoadUrl()
+ advanceUntilIdle()
middleware.assertLastAction(ContentAction.UpdateIsSearchAction::class) { action ->
assertEquals(false, action.isSearch)
@@ -1777,7 +1993,7 @@ class EngineObserverTest {
}
@Test
- fun `GIVEN a search is performed WHEN the location is changed without user interaction THEN the search terms are not cleared`() {
+ fun `GIVEN a search is performed WHEN the location is changed without user interaction THEN the search terms are not cleared`() = runTest {
val middleware = CaptureActionsMiddleware<BrowserState, BrowserAction>()
val store = BrowserStore(
initialState = BrowserState(
@@ -1790,18 +2006,29 @@ class EngineObserverTest {
store.dispatch(ContentAction.UpdateIsSearchAction("test-id", true))
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onLocationChange("testUrl", false)
+ advanceUntilIdle()
middleware.assertNotDispatched(ContentAction.UpdateSearchTermsAction::class)
}
@Test
- fun `onHistoryStateChanged dispatches UpdateHistoryStateAction`() {
+ fun `onHistoryStateChanged dispatches UpdateHistoryStateAction`() = runTest {
val store: BrowserStore = mock()
- val observer = EngineObserver("test-id", store)
+ val observer = createEngineObserver(
+ tabId = "test-id",
+ store = store,
+ scope = this,
+ )
observer.onHistoryStateChanged(emptyList(), 0)
+ advanceUntilIdle()
+
verify(store).dispatch(
ContentAction.UpdateHistoryStateAction(
"test-id",
@@ -1817,6 +2044,7 @@ class EngineObserverTest {
),
1,
)
+ advanceUntilIdle()
verify(store).dispatch(
ContentAction.UpdateHistoryStateAction(
@@ -1831,12 +2059,18 @@ class EngineObserverTest {
}
@Test
- fun `onScrollChange dispatches UpdateReaderScrollYAction`() {
+ fun `onScrollChange dispatches UpdateReaderScrollYAction`() = runTest {
val store: BrowserStore = mock()
whenever(store.state).thenReturn(mock())
- val observer = EngineObserver("tab-id", store)
+ val observer = createEngineObserver(
+ tabId = "tab-id",
+ store = store,
+ scope = this,
+ )
observer.onScrollChange(4321, 1234)
+ advanceUntilIdle()
+
verify(store).dispatch(
ReaderAction.UpdateReaderScrollYAction(
"tab-id",
@@ -1885,4 +2119,10 @@ class EngineObserverTest {
assertFalse(none == customNone)
}
+
+ private fun createEngineObserver(
+ tabId: String = "mozilla",
+ store: BrowserStore,
+ scope: CoroutineScope,
+ ): EngineObserver = EngineObserver(tabId, store, scope)
}
diff --git a/mobile/android/android-components/components/browser/state/src/test/java/mozilla/components/browser/state/engine/middleware/LinkingMiddlewareTest.kt b/mobile/android/android-components/components/browser/state/src/test/java/mozilla/components/browser/state/engine/middleware/LinkingMiddlewareTest.kt
@@ -5,7 +5,9 @@
package mozilla.components.browser.state.engine.middleware
import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.StandardTestDispatcher
+import kotlinx.coroutines.test.advanceUntilIdle
import kotlinx.coroutines.test.runTest
import mozilla.components.browser.state.action.EngineAction
import mozilla.components.browser.state.action.TabListAction
@@ -25,6 +27,7 @@ import org.mockito.Mockito.anyString
import org.mockito.Mockito.never
import org.mockito.Mockito.verify
+@OptIn(ExperimentalCoroutinesApi::class)
class LinkingMiddlewareTest {
private val testDispatcher = StandardTestDispatcher()
private val scope = CoroutineScope(testDispatcher)
@@ -168,7 +171,7 @@ class LinkingMiddlewareTest {
val tab1 = createTab("https://www.mozilla.org", id = "1")
val tab2 = createTab("https://www.mozilla.org", id = "2")
- val middleware = LinkingMiddleware(scope)
+ val middleware = LinkingMiddleware(this)
val store = BrowserStore(
initialState = BrowserState(tabs = listOf(tab1, tab2)),
@@ -186,6 +189,7 @@ class LinkingMiddlewareTest {
verify(engineSession2).register(engineObserver!!)
engineObserver.onTitleChange("test")
+ advanceUntilIdle()
assertEquals("test", store.state.tabs[1].content.title)
}
@@ -219,7 +223,7 @@ class LinkingMiddlewareTest {
val tab1 = createTab("https://www.mozilla.org", id = "1")
val tab2 = createTab("https://www.mozilla.org", id = "2", engineSession = engineSession)
- val middleware = LinkingMiddleware(scope)
+ val middleware = LinkingMiddleware(this)
val store = BrowserStore(
initialState = BrowserState(),
@@ -234,6 +238,7 @@ class LinkingMiddlewareTest {
assertNotNull(engineObserver)
verify(engineSession).register(engineObserver!!)
engineObserver.onTitleChange("test")
+ advanceUntilIdle()
assertEquals("test", store.state.tabs[1].content.title)
}