Bug 1820169 - Ensure shortcuts snackbars are shown
[gecko.git] / mobile / android / fenix / app / src / test / java / org / mozilla / fenix / components / toolbar / DefaultBrowserToolbarMenuControllerTest.kt
blobf97e84c3c81a6fdfb55a0e854ee5d54e6fd203e0
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 package org.mozilla.fenix.components.toolbar
7 import android.content.Intent
8 import android.view.ViewGroup
9 import androidx.navigation.NavController
10 import io.mockk.MockKAnnotations
11 import io.mockk.Runs
12 import io.mockk.coEvery
13 import io.mockk.every
14 import io.mockk.impl.annotations.MockK
15 import io.mockk.impl.annotations.RelaxedMockK
16 import io.mockk.just
17 import io.mockk.mockk
18 import io.mockk.mockkObject
19 import io.mockk.mockkStatic
20 import io.mockk.slot
21 import io.mockk.unmockkObject
22 import io.mockk.unmockkStatic
23 import io.mockk.verify
24 import kotlinx.coroutines.CoroutineScope
25 import kotlinx.coroutines.ExperimentalCoroutinesApi
26 import kotlinx.coroutines.test.runTest
27 import mozilla.appservices.places.BookmarkRoot
28 import mozilla.components.browser.state.action.CustomTabListAction
29 import mozilla.components.browser.state.state.BrowserState
30 import mozilla.components.browser.state.state.ReaderState
31 import mozilla.components.browser.state.state.TabSessionState
32 import mozilla.components.browser.state.state.createCustomTab
33 import mozilla.components.browser.state.state.createTab
34 import mozilla.components.browser.state.store.BrowserStore
35 import mozilla.components.concept.engine.EngineSession
36 import mozilla.components.concept.engine.prompt.ShareData
37 import mozilla.components.feature.search.SearchUseCases
38 import mozilla.components.feature.session.SessionFeature
39 import mozilla.components.feature.session.SessionUseCases
40 import mozilla.components.feature.tab.collections.TabCollection
41 import mozilla.components.feature.tabs.CustomTabsUseCases
42 import mozilla.components.feature.top.sites.DefaultTopSitesStorage
43 import mozilla.components.feature.top.sites.PinnedSiteStorage
44 import mozilla.components.feature.top.sites.TopSite
45 import mozilla.components.feature.top.sites.TopSitesUseCases
46 import mozilla.components.service.glean.testing.GleanTestRule
47 import mozilla.components.support.base.feature.ViewBoundFeatureWrapper
48 import mozilla.components.support.test.ext.joinBlocking
49 import mozilla.components.support.test.robolectric.testContext
50 import mozilla.components.support.test.rule.MainCoroutineRule
51 import mozilla.components.support.test.rule.runTestOnMain
52 import org.junit.After
53 import org.junit.Assert.assertEquals
54 import org.junit.Assert.assertNotNull
55 import org.junit.Assert.assertNull
56 import org.junit.Assert.assertTrue
57 import org.junit.Before
58 import org.junit.Rule
59 import org.junit.Test
60 import org.junit.runner.RunWith
61 import org.mozilla.fenix.GleanMetrics.Collections
62 import org.mozilla.fenix.GleanMetrics.Events
63 import org.mozilla.fenix.GleanMetrics.ReaderMode
64 import org.mozilla.fenix.HomeActivity
65 import org.mozilla.fenix.NavGraphDirections
66 import org.mozilla.fenix.R
67 import org.mozilla.fenix.browser.BrowserAnimator
68 import org.mozilla.fenix.browser.BrowserFragmentDirections
69 import org.mozilla.fenix.browser.readermode.ReaderModeController
70 import org.mozilla.fenix.collections.SaveCollectionStep
71 import org.mozilla.fenix.components.FenixSnackbar
72 import org.mozilla.fenix.components.TabCollectionStorage
73 import org.mozilla.fenix.components.accounts.AccountState
74 import org.mozilla.fenix.ext.components
75 import org.mozilla.fenix.ext.directionsEq
76 import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
77 import org.mozilla.fenix.settings.deletebrowsingdata.deleteAndQuit
78 import org.mozilla.fenix.utils.Settings
80 @OptIn(ExperimentalCoroutinesApi::class)
81 @RunWith(FenixRobolectricTestRunner::class)
82 class DefaultBrowserToolbarMenuControllerTest {
84     @get:Rule
85     val coroutinesTestRule = MainCoroutineRule()
87     @get:Rule
88     val gleanTestRule = GleanTestRule(testContext)
90     @MockK private lateinit var snackbarParent: ViewGroup
92     @RelaxedMockK private lateinit var activity: HomeActivity
94     @RelaxedMockK private lateinit var navController: NavController
96     @RelaxedMockK private lateinit var openInFenixIntent: Intent
98     @RelaxedMockK private lateinit var settings: Settings
100     @RelaxedMockK private lateinit var searchUseCases: SearchUseCases
102     @RelaxedMockK private lateinit var sessionUseCases: SessionUseCases
104     @RelaxedMockK private lateinit var customTabUseCases: CustomTabsUseCases
106     @RelaxedMockK private lateinit var browserAnimator: BrowserAnimator
108     @RelaxedMockK private lateinit var snackbar: FenixSnackbar
110     @RelaxedMockK private lateinit var tabCollectionStorage: TabCollectionStorage
112     @RelaxedMockK private lateinit var topSitesUseCase: TopSitesUseCases
114     @RelaxedMockK private lateinit var readerModeController: ReaderModeController
116     @MockK private lateinit var sessionFeatureWrapper: ViewBoundFeatureWrapper<SessionFeature>
118     @RelaxedMockK private lateinit var sessionFeature: SessionFeature
120     @RelaxedMockK private lateinit var topSitesStorage: DefaultTopSitesStorage
122     @RelaxedMockK private lateinit var pinnedSiteStorage: PinnedSiteStorage
124     private lateinit var browserStore: BrowserStore
125     private lateinit var selectedTab: TabSessionState
127     @Before
128     fun setUp() {
129         MockKAnnotations.init(this)
131         mockkStatic(
132             "org.mozilla.fenix.settings.deletebrowsingdata.DeleteAndQuitKt",
133         )
134         every { deleteAndQuit(any(), any(), any()) } just Runs
136         mockkObject(FenixSnackbar.Companion)
137         every { FenixSnackbar.make(any(), any(), any(), any()) } returns snackbar
139         every { activity.components.useCases.sessionUseCases } returns sessionUseCases
140         every { activity.components.useCases.customTabsUseCases } returns customTabUseCases
141         every { activity.components.useCases.searchUseCases } returns searchUseCases
142         every { activity.components.useCases.topSitesUseCase } returns topSitesUseCase
143         every { sessionFeatureWrapper.get() } returns sessionFeature
144         every { navController.currentDestination } returns mockk {
145             every { id } returns R.id.browserFragment
146         }
147         every { settings.topSitesMaxLimit } returns 16
149         val onComplete = slot<(Boolean) -> Unit>()
150         every { browserAnimator.captureEngineViewAndDrawStatically(any(), capture(onComplete)) } answers { onComplete.captured.invoke(true) }
152         selectedTab = createTab("https://www.mozilla.org", id = "1")
153         browserStore = BrowserStore(
154             initialState = BrowserState(
155                 tabs = listOf(selectedTab),
156                 selectedTabId = selectedTab.id,
157             ),
158         )
159     }
161     @After
162     fun tearDown() {
163         unmockkStatic("org.mozilla.fenix.settings.deletebrowsingdata.DeleteAndQuitKt")
164         unmockkObject(FenixSnackbar.Companion)
165     }
167     @Test
168     fun handleToolbarBookmarkPressWithReaderModeInactive() = runTest {
169         val item = ToolbarMenu.Item.Bookmark
171         val expectedTitle = "Mozilla"
172         val expectedUrl = "https://mozilla.org"
173         val regularTab = createTab(
174             url = expectedUrl,
175             readerState = ReaderState(active = false, activeUrl = "https://1234.org"),
176             title = expectedTitle,
177         )
178         val store =
179             BrowserStore(BrowserState(tabs = listOf(regularTab), selectedTabId = regularTab.id))
181         var bookmarkTappedInvoked = false
182         val controller = createController(
183             scope = this,
184             store = store,
185             bookmarkTapped = { url, title ->
186                 assertEquals(expectedTitle, title)
187                 assertEquals(expectedUrl, url)
188                 bookmarkTappedInvoked = true
189             },
190         )
191         assertNull(Events.browserMenuAction.testGetValue())
193         controller.handleToolbarItemInteraction(item)
195         assertNotNull(Events.browserMenuAction.testGetValue())
196         val snapshot = Events.browserMenuAction.testGetValue()!!
197         assertEquals(1, snapshot.size)
198         assertEquals("bookmark", snapshot.single().extra?.getValue("item"))
200         assertTrue(bookmarkTappedInvoked)
201     }
203     @Test
204     fun `IF reader mode is active WHEN bookmark menu item is pressed THEN menu item is handled`() = runTest {
205         val item = ToolbarMenu.Item.Bookmark
206         val expectedTitle = "Mozilla"
207         val readerUrl = "moz-extension://1234"
208         val readerTab = createTab(
209             url = readerUrl,
210             readerState = ReaderState(active = true, activeUrl = "https://mozilla.org"),
211             title = expectedTitle,
212         )
213         browserStore =
214             BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id))
216         var bookmarkTappedInvoked = false
217         val controller = createController(
218             scope = this,
219             store = browserStore,
220             bookmarkTapped = { url, title ->
221                 assertEquals(expectedTitle, title)
222                 assertEquals(readerTab.readerState.activeUrl, url)
223                 bookmarkTappedInvoked = true
224             },
225         )
226         assertNull(Events.browserMenuAction.testGetValue())
228         controller.handleToolbarItemInteraction(item)
230         assertNotNull(Events.browserMenuAction.testGetValue())
231         val snapshot = Events.browserMenuAction.testGetValue()!!
232         assertEquals(1, snapshot.size)
233         assertEquals("bookmark", snapshot.single().extra?.getValue("item"))
235         assertTrue(bookmarkTappedInvoked)
236     }
238     @Test
239     fun `WHEN open in Fenix menu item is pressed THEN menu item is handled correctly`() = runTest {
240         val customTab = createCustomTab("https://mozilla.org")
241         browserStore.dispatch(CustomTabListAction.AddCustomTabAction(customTab)).joinBlocking()
242         val controller = createController(
243             scope = this,
244             store = browserStore,
245             customTabSessionId = customTab.id,
246         )
248         val item = ToolbarMenu.Item.OpenInFenix
250         every { activity.startActivity(any()) } just Runs
251         controller.handleToolbarItemInteraction(item)
253         verify { sessionFeature.release() }
254         verify { customTabUseCases.migrate(customTab.id, true) }
255         verify { activity.startActivity(openInFenixIntent) }
256         verify { activity.finishAndRemoveTask() }
257     }
259     @Test
260     fun `WHEN reader mode menu item is pressed THEN handle appearance change`() = runTest {
261         val item = ToolbarMenu.Item.CustomizeReaderView
262         assertNull(ReaderMode.appearance.testGetValue())
264         val controller = createController(scope = this, store = browserStore)
266         controller.handleToolbarItemInteraction(item)
268         verify { readerModeController.showControls() }
269         assertNotNull(ReaderMode.appearance.testGetValue())
270         assertNull(ReaderMode.appearance.testGetValue()!!.single().extra)
271     }
273     @Test
274     fun `WHEN quit menu item is pressed THEN menu item is handled correctly`() = runTest {
275         val item = ToolbarMenu.Item.Quit
276         val testScope = this
278         val controller = createController(scope = this, store = browserStore)
280         controller.handleToolbarItemInteraction(item)
282         verify { deleteAndQuit(activity, testScope, null) }
283     }
285     @Test
286     fun `WHEN backwards nav menu item is pressed THEN the session navigates back with active session`() = runTest {
287         val item = ToolbarMenu.Item.Back(false)
289         val controller = createController(scope = this, store = browserStore)
290         assertNull(Events.browserMenuAction.testGetValue())
292         controller.handleToolbarItemInteraction(item)
294         assertNotNull(Events.browserMenuAction.testGetValue())
295         val snapshot = Events.browserMenuAction.testGetValue()!!
296         assertEquals(1, snapshot.size)
297         assertEquals("back", snapshot.single().extra?.getValue("item"))
299         verify { sessionUseCases.goBack(browserStore.state.selectedTabId!!) }
300     }
302     @Test
303     fun `WHEN backwards nav menu item is long pressed THEN the session navigates back with no active session`() = runTest {
304         val item = ToolbarMenu.Item.Back(true)
306         val controller = createController(scope = this, store = browserStore)
307         assertNull(Events.browserMenuAction.testGetValue())
309         controller.handleToolbarItemInteraction(item)
311         assertNotNull(Events.browserMenuAction.testGetValue())
312         val snapshot = Events.browserMenuAction.testGetValue()!!
313         assertEquals(1, snapshot.size)
314         assertEquals("back", snapshot.single().extra?.getValue("item"))
315         val directions = BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment(null)
317         verify { navController.navigate(directions) }
318     }
320     @Test
321     fun `WHEN forward nav menu item is pressed THEN the session navigates forward to active session`() = runTest {
322         val item = ToolbarMenu.Item.Forward(false)
324         val controller = createController(scope = this, store = browserStore)
325         assertNull(Events.browserMenuAction.testGetValue())
327         controller.handleToolbarItemInteraction(item)
329         assertNotNull(Events.browserMenuAction.testGetValue())
330         val snapshot = Events.browserMenuAction.testGetValue()!!
331         assertEquals(1, snapshot.size)
332         assertEquals("forward", snapshot.single().extra?.getValue("item"))
334         verify { sessionUseCases.goForward(selectedTab.id) }
335     }
337     @Test
338     fun `WHEN forward nav menu item is long pressed THEN the browser navigates forward with no active session`() = runTest {
339         val item = ToolbarMenu.Item.Forward(true)
341         val controller = createController(scope = this, store = browserStore)
342         assertNull(Events.browserMenuAction.testGetValue())
344         controller.handleToolbarItemInteraction(item)
346         assertNotNull(Events.browserMenuAction.testGetValue())
347         val snapshot = Events.browserMenuAction.testGetValue()!!
348         assertEquals(1, snapshot.size)
349         assertEquals("forward", snapshot.single().extra?.getValue("item"))
351         val directions = BrowserFragmentDirections.actionGlobalTabHistoryDialogFragment(null)
353         verify { navController.navigate(directions) }
354     }
356     @Test
357     fun `WHEN reload nav menu item is pressed THEN the session reloads from cache`() = runTest {
358         val item = ToolbarMenu.Item.Reload(false)
360         val controller = createController(scope = this, store = browserStore)
361         assertNull(Events.browserMenuAction.testGetValue())
363         controller.handleToolbarItemInteraction(item)
365         assertNotNull(Events.browserMenuAction.testGetValue())
366         val snapshot = Events.browserMenuAction.testGetValue()!!
367         assertEquals(1, snapshot.size)
368         assertEquals("reload", snapshot.single().extra?.getValue("item"))
370         verify { sessionUseCases.reload(selectedTab.id) }
371     }
373     @Test
374     fun `WHEN reload nav menu item is long pressed THEN the session reloads with no cache`() = runTest {
375         val item = ToolbarMenu.Item.Reload(true)
377         val controller = createController(scope = this, store = browserStore)
378         assertNull(Events.browserMenuAction.testGetValue())
380         controller.handleToolbarItemInteraction(item)
382         assertNotNull(Events.browserMenuAction.testGetValue())
383         val snapshot = Events.browserMenuAction.testGetValue()!!
384         assertEquals(1, snapshot.size)
385         assertEquals("reload", snapshot.single().extra?.getValue("item"))
387         verify {
388             sessionUseCases.reload(
389                 selectedTab.id,
390                 EngineSession.LoadUrlFlags.select(EngineSession.LoadUrlFlags.BYPASS_CACHE),
391             )
392         }
393     }
395     @Test
396     fun `WHEN stop nav menu item is pressed THEN the session stops loading`() = runTest {
397         val item = ToolbarMenu.Item.Stop
399         val controller = createController(scope = this, store = browserStore)
400         assertNull(Events.browserMenuAction.testGetValue())
402         controller.handleToolbarItemInteraction(item)
404         assertNotNull(Events.browserMenuAction.testGetValue())
405         val snapshot = Events.browserMenuAction.testGetValue()!!
406         assertEquals(1, snapshot.size)
407         assertEquals("stop", snapshot.single().extra?.getValue("item"))
409         verify { sessionUseCases.stopLoading(selectedTab.id) }
410     }
412     @Test
413     fun `WHEN settings menu item is pressed THEN menu item is handled`() = runTest {
414         val item = ToolbarMenu.Item.Settings
416         val controller = createController(scope = this, store = browserStore)
417         assertNull(Events.browserMenuAction.testGetValue())
419         controller.handleToolbarItemInteraction(item)
421         assertNotNull(Events.browserMenuAction.testGetValue())
422         val snapshot = Events.browserMenuAction.testGetValue()!!
423         assertEquals(1, snapshot.size)
424         assertEquals("settings", snapshot.single().extra?.getValue("item"))
425         val directions = BrowserFragmentDirections.actionBrowserFragmentToSettingsFragment()
427         verify { navController.navigate(directions, null) }
428     }
430     @Test
431     fun `WHEN bookmark menu item is pressed THEN navigate to bookmarks page`() = runTest {
432         val item = ToolbarMenu.Item.Bookmarks
434         val controller = createController(scope = this, store = browserStore)
435         assertNull(Events.browserMenuAction.testGetValue())
437         controller.handleToolbarItemInteraction(item)
439         assertNotNull(Events.browserMenuAction.testGetValue())
440         val snapshot = Events.browserMenuAction.testGetValue()!!
441         assertEquals(1, snapshot.size)
442         assertEquals("bookmarks", snapshot.single().extra?.getValue("item"))
443         val directions = BrowserFragmentDirections.actionGlobalBookmarkFragment(BookmarkRoot.Mobile.id)
445         verify { navController.navigate(directions, null) }
446     }
448     @Test
449     fun `WHEN history menu item is pressed THEN navigate to history page`() = runTest {
450         val item = ToolbarMenu.Item.History
452         val controller = createController(scope = this, store = browserStore)
453         assertNull(Events.browserMenuAction.testGetValue())
455         controller.handleToolbarItemInteraction(item)
457         assertNotNull(Events.browserMenuAction.testGetValue())
458         val snapshot = Events.browserMenuAction.testGetValue()!!
459         assertEquals(1, snapshot.size)
460         assertEquals("history", snapshot.single().extra?.getValue("item"))
461         val directions = BrowserFragmentDirections.actionGlobalHistoryFragment()
463         verify { navController.navigate(directions, null) }
464     }
466     @Test
467     fun `WHEN request desktop menu item is toggled On THEN desktop site is requested for the session`() = runTest {
468         val requestDesktopSiteUseCase: SessionUseCases.RequestDesktopSiteUseCase =
469             mockk(relaxed = true)
470         val item = ToolbarMenu.Item.RequestDesktop(true)
472         every { sessionUseCases.requestDesktopSite } returns requestDesktopSiteUseCase
474         val controller = createController(scope = this, store = browserStore)
475         assertNull(Events.browserMenuAction.testGetValue())
477         controller.handleToolbarItemInteraction(item)
479         assertNotNull(Events.browserMenuAction.testGetValue())
480         val snapshot = Events.browserMenuAction.testGetValue()!!
481         assertEquals(1, snapshot.size)
482         assertEquals("desktop_view_on", snapshot.single().extra?.getValue("item"))
484         verify {
485             requestDesktopSiteUseCase.invoke(
486                 true,
487                 selectedTab.id,
488             )
489         }
490     }
492     @Test
493     fun `WHEN request desktop menu item is toggled Off THEN mobile site is requested for the session`() = runTest {
494         val requestDesktopSiteUseCase: SessionUseCases.RequestDesktopSiteUseCase =
495             mockk(relaxed = true)
496         val item = ToolbarMenu.Item.RequestDesktop(false)
498         every { sessionUseCases.requestDesktopSite } returns requestDesktopSiteUseCase
500         val controller = createController(scope = this, store = browserStore)
501         assertNull(Events.browserMenuAction.testGetValue())
503         controller.handleToolbarItemInteraction(item)
505         assertNotNull(Events.browserMenuAction.testGetValue())
506         val snapshot = Events.browserMenuAction.testGetValue()!!
507         assertEquals(1, snapshot.size)
508         assertEquals("desktop_view_off", snapshot.single().extra?.getValue("item"))
510         verify {
511             requestDesktopSiteUseCase.invoke(
512                 false,
513                 selectedTab.id,
514             )
515         }
516     }
518     @Test
519     fun `WHEN add to shortcuts menu item is pressed THEN add site AND show snackbar`() = runTestOnMain {
520         val item = ToolbarMenu.Item.AddToTopSites
521         val addPinnedSiteUseCase: TopSitesUseCases.AddPinnedSiteUseCase = mockk(relaxed = true)
523         every { topSitesUseCase.addPinnedSites } returns addPinnedSiteUseCase
524         every {
525             snackbarParent.context.getString(R.string.snackbar_added_to_shortcuts)
526         } returns "Added to shortcuts!"
528         val controller = createController(scope = this, store = browserStore)
529         assertNull(Events.browserMenuAction.testGetValue())
531         controller.handleToolbarItemInteraction(item)
533         assertNotNull(Events.browserMenuAction.testGetValue())
534         val snapshot = Events.browserMenuAction.testGetValue()!!
535         assertEquals(1, snapshot.size)
536         assertEquals("add_to_top_sites", snapshot.single().extra?.getValue("item"))
538         verify { addPinnedSiteUseCase.invoke(selectedTab.content.title, selectedTab.content.url) }
539         verify { snackbar.setText("Added to shortcuts!") }
540     }
542     @Test
543     fun `GIVEN a shortcut page is open WHEN remove from shortcuts is pressed THEN show snackbar`() = runTestOnMain {
544         val snackbarMessage = "Site removed"
545         val item = ToolbarMenu.Item.RemoveFromTopSites
546         val removePinnedSiteUseCase: TopSitesUseCases.RemoveTopSiteUseCase =
547             mockk(relaxed = true)
548         val topSite: TopSite = mockk()
549         every { topSite.url } returns selectedTab.content.url
550         coEvery { pinnedSiteStorage.getPinnedSites() } returns listOf(topSite)
551         every { topSitesUseCase.removeTopSites } returns removePinnedSiteUseCase
552         every {
553             snackbarParent.context.getString(R.string.snackbar_top_site_removed)
554         } returns snackbarMessage
556         val controller = createController(scope = this, store = browserStore)
557         assertNull(Events.browserMenuAction.testGetValue())
559         controller.handleToolbarItemInteraction(item)
561         assertNotNull(Events.browserMenuAction.testGetValue())
562         val snapshot = Events.browserMenuAction.testGetValue()!!
563         assertEquals(1, snapshot.size)
564         assertEquals("remove_from_top_sites", snapshot.single().extra?.getValue("item"))
566         verify { snackbar.setText(snackbarMessage) }
567         verify { removePinnedSiteUseCase.invoke(topSite) }
568     }
570     @Test
571     fun `WHEN addon extensions menu item is pressed THEN navigate to addons manager`() = runTest {
572         val item = ToolbarMenu.Item.AddonsManager
574         val controller = createController(scope = this, store = browserStore)
575         assertNull(Events.browserMenuAction.testGetValue())
577         controller.handleToolbarItemInteraction(item)
579         assertNotNull(Events.browserMenuAction.testGetValue())
580         val snapshot = Events.browserMenuAction.testGetValue()!!
581         assertEquals(1, snapshot.size)
582         assertEquals("addons_manager", snapshot.single().extra?.getValue("item"))
583     }
585     @Test
586     fun `WHEN Add To Home Screen menu item is pressed THEN add site`() = runTest {
587         val item = ToolbarMenu.Item.AddToHomeScreen
589         val controller = createController(scope = this, store = browserStore)
590         assertNull(Events.browserMenuAction.testGetValue())
592         controller.handleToolbarItemInteraction(item)
594         assertNotNull(Events.browserMenuAction.testGetValue())
595         val snapshot = Events.browserMenuAction.testGetValue()!!
596         assertEquals(1, snapshot.size)
597         assertEquals("add_to_homescreen", snapshot.single().extra?.getValue("item"))
598     }
600     @Test
601     fun `IF reader mode is inactive WHEN share menu item is pressed THEN navigate to share screen`() = runTest {
602         val item = ToolbarMenu.Item.Share
603         val title = "Mozilla"
604         val url = "https://mozilla.org"
605         val regularTab = createTab(
606             url = url,
607             readerState = ReaderState(active = false, activeUrl = "https://1234.org"),
608             title = title,
609         )
610         browserStore = BrowserStore(BrowserState(tabs = listOf(regularTab), selectedTabId = regularTab.id))
611         val controller = createController(scope = this, store = browserStore)
612         assertNull(Events.browserMenuAction.testGetValue())
614         controller.handleToolbarItemInteraction(item)
616         assertNotNull(Events.browserMenuAction.testGetValue())
617         val snapshot = Events.browserMenuAction.testGetValue()!!
618         assertEquals(1, snapshot.size)
619         assertEquals("share", snapshot.single().extra?.getValue("item"))
621         verify {
622             navController.navigate(
623                 directionsEq(
624                     NavGraphDirections.actionGlobalShareFragment(
625                         sessionId = browserStore.state.selectedTabId,
626                         data = arrayOf(ShareData(url = "https://mozilla.org", title = "Mozilla")),
627                         showPage = true,
628                     ),
629                 ),
630             )
631         }
632     }
634     @Test
635     fun `IF reader mode is active WHEN share menu item is pressed THEN navigate to share screen`() = runTest {
636         val item = ToolbarMenu.Item.Share
637         val title = "Mozilla"
638         val readerUrl = "moz-extension://1234"
639         val readerTab = createTab(
640             url = readerUrl,
641             readerState = ReaderState(active = true, activeUrl = "https://mozilla.org"),
642             title = title,
643         )
644         browserStore = BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id))
645         val controller = createController(scope = this, store = browserStore)
646         assertNull(Events.browserMenuAction.testGetValue())
648         controller.handleToolbarItemInteraction(item)
650         assertNotNull(Events.browserMenuAction.testGetValue())
651         val snapshot = Events.browserMenuAction.testGetValue()!!
652         assertEquals(1, snapshot.size)
653         assertEquals("share", snapshot.single().extra?.getValue("item"))
655         verify {
656             navController.navigate(
657                 directionsEq(
658                     NavGraphDirections.actionGlobalShareFragment(
659                         sessionId = browserStore.state.selectedTabId,
660                         data = arrayOf(ShareData(url = "https://mozilla.org", title = "Mozilla")),
661                         showPage = true,
662                     ),
663                 ),
664             )
665         }
666     }
668     @Test
669     fun `WHEN Find In Page menu item is pressed THEN launch finder`() = runTest {
670         val item = ToolbarMenu.Item.FindInPage
672         var launcherInvoked = false
673         val controller = createController(
674             scope = this,
675             store = browserStore,
676             findInPageLauncher = {
677                 launcherInvoked = true
678             },
679         )
680         controller.handleToolbarItemInteraction(item)
682         assertTrue(launcherInvoked)
683     }
685     @Test
686     fun `IF one or more collection exists WHEN Save To Collection menu item is pressed THEN navigate to save collection page`() = runTest {
687         val item = ToolbarMenu.Item.SaveToCollection
688         val cachedTabCollections: List<TabCollection> = mockk(relaxed = true)
689         every { tabCollectionStorage.cachedTabCollections } returns cachedTabCollections
691         val controller = createController(scope = this, store = browserStore)
692         assertNull(Events.browserMenuAction.testGetValue())
694         controller.handleToolbarItemInteraction(item)
696         assertNotNull(Events.browserMenuAction.testGetValue())
697         val snapshot = Events.browserMenuAction.testGetValue()!!
698         assertEquals(1, snapshot.size)
699         assertEquals("save_to_collection", snapshot.single().extra?.getValue("item"))
701         assertNotNull(Collections.saveButton.testGetValue())
702         val recordedEvents = Collections.saveButton.testGetValue()!!
703         assertEquals(1, recordedEvents.size)
704         val eventExtra = recordedEvents.single().extra
705         assertNotNull(eventExtra)
706         assertTrue(eventExtra!!.containsKey("from_screen"))
707         assertEquals(
708             DefaultBrowserToolbarMenuController.TELEMETRY_BROWSER_IDENTIFIER,
709             eventExtra["from_screen"],
710         )
712         val directions = BrowserFragmentDirections.actionGlobalCollectionCreationFragment(
713             saveCollectionStep = SaveCollectionStep.SelectCollection,
714             tabIds = arrayOf(selectedTab.id),
715             selectedTabIds = arrayOf(selectedTab.id),
716         )
717         verify { navController.navigate(directionsEq(directions), null) }
718     }
720     @Test
721     fun `IF no collection exists WHEN Save To Collection menu item is pressed THEN navigate to create collection page`() = runTest {
722         val item = ToolbarMenu.Item.SaveToCollection
723         val cachedTabCollectionsEmpty: List<TabCollection> = emptyList()
724         every { tabCollectionStorage.cachedTabCollections } returns cachedTabCollectionsEmpty
726         val controller = createController(scope = this, store = browserStore)
727         assertNull(Events.browserMenuAction.testGetValue())
729         controller.handleToolbarItemInteraction(item)
731         assertNotNull(Events.browserMenuAction.testGetValue())
732         val snapshot = Events.browserMenuAction.testGetValue()!!
733         assertEquals(1, snapshot.size)
734         assertEquals("save_to_collection", snapshot.single().extra?.getValue("item"))
736         assertNotNull(Collections.saveButton.testGetValue())
737         val recordedEvents = Collections.saveButton.testGetValue()!!
738         assertEquals(1, recordedEvents.size)
739         val eventExtra = recordedEvents.single().extra
740         assertNotNull(eventExtra)
741         assertTrue(eventExtra!!.containsKey("from_screen"))
742         assertEquals(
743             DefaultBrowserToolbarMenuController.TELEMETRY_BROWSER_IDENTIFIER,
744             eventExtra["from_screen"],
745         )
746         val directions = BrowserFragmentDirections.actionGlobalCollectionCreationFragment(
747             saveCollectionStep = SaveCollectionStep.NameCollection,
748             tabIds = arrayOf(selectedTab.id),
749             selectedTabIds = arrayOf(selectedTab.id),
750         )
751         verify { navController.navigate(directionsEq(directions), null) }
752     }
754     @Test
755     fun `WHEN New Tab menu item is pressed THEN navigate to a new tab home`() = runTest {
756         val item = ToolbarMenu.Item.NewTab
758         val controller = createController(scope = this, store = browserStore)
760         controller.handleToolbarItemInteraction(item)
762         verify {
763             navController.navigate(
764                 directionsEq(
765                     NavGraphDirections.actionGlobalHome(
766                         focusOnAddressBar = true,
767                     ),
768                 ),
769             )
770         }
771     }
773     @Test
774     fun `GIVEN account exists and the user is signed in WHEN sign in to sync menu item is pressed THEN navigate to account settings`() = runTest {
775         val item = ToolbarMenu.Item.SyncAccount(AccountState.AUTHENTICATED)
776         val accountSettingsDirections = BrowserFragmentDirections.actionGlobalAccountSettingsFragment()
777         val controller = createController(scope = this, store = browserStore)
779         controller.handleToolbarItemInteraction(item)
781         verify { navController.navigate(accountSettingsDirections, null) }
782     }
784     @Test
785     fun `GIVEN account exists and the user is not signed in WHEN sign in to sync menu item is pressed THEN navigate to account problem fragment`() = runTest {
786         val item = ToolbarMenu.Item.SyncAccount(AccountState.NEEDS_REAUTHENTICATION)
787         val accountProblemDirections = BrowserFragmentDirections.actionGlobalAccountProblemFragment()
788         val controller = createController(scope = this, store = browserStore)
790         controller.handleToolbarItemInteraction(item)
792         verify { navController.navigate(accountProblemDirections, null) }
793     }
795     @Test
796     fun `GIVEN account doesn't exist WHEN sign in to sync menu item is pressed THEN navigate to sign in`() = runTest {
797         val item = ToolbarMenu.Item.SyncAccount(AccountState.NO_ACCOUNT)
798         val turnOnSyncDirections = BrowserFragmentDirections.actionGlobalTurnOnSync()
799         val controller = createController(scope = this, store = browserStore)
801         controller.handleToolbarItemInteraction(item)
803         verify { navController.navigate(turnOnSyncDirections, null) }
804     }
806     @Suppress("LongParameterList")
807     private fun createController(
808         scope: CoroutineScope,
809         store: BrowserStore,
810         activity: HomeActivity = this.activity,
811         customTabSessionId: String? = null,
812         findInPageLauncher: () -> Unit = { },
813         bookmarkTapped: (String, String) -> Unit = { _, _ -> },
814     ) = DefaultBrowserToolbarMenuController(
815         store = store,
816         activity = activity,
817         navController = navController,
818         settings = settings,
819         findInPageLauncher = findInPageLauncher,
820         browserAnimator = browserAnimator,
821         customTabSessionId = customTabSessionId,
822         openInFenixIntent = openInFenixIntent,
823         scope = scope,
824         snackbarParent = snackbarParent,
825         tabCollectionStorage = tabCollectionStorage,
826         bookmarkTapped = bookmarkTapped,
827         readerModeController = readerModeController,
828         sessionFeature = sessionFeatureWrapper,
829         topSitesStorage = topSitesStorage,
830         pinnedSiteStorage = pinnedSiteStorage,
831         browserStore = browserStore,
832     ).apply {
833         ioScope = scope
834     }