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 @file:Suppress("TooManyFunctions")
7 package org.mozilla.fenix.ui.robots
9 import android.util.Log
10 import android.view.View
11 import androidx.compose.ui.semantics.SemanticsActions
12 import androidx.compose.ui.test.ExperimentalTestApi
13 import androidx.compose.ui.test.assert
14 import androidx.compose.ui.test.assertIsNotSelected
15 import androidx.compose.ui.test.assertIsSelected
16 import androidx.compose.ui.test.filter
17 import androidx.compose.ui.test.hasAnyChild
18 import androidx.compose.ui.test.hasContentDescription
19 import androidx.compose.ui.test.hasParent
20 import androidx.compose.ui.test.hasTestTag
21 import androidx.compose.ui.test.hasText
22 import androidx.compose.ui.test.junit4.ComposeTestRule
23 import androidx.compose.ui.test.longClick
24 import androidx.compose.ui.test.onAllNodesWithTag
25 import androidx.compose.ui.test.onChildAt
26 import androidx.compose.ui.test.onChildren
27 import androidx.compose.ui.test.onFirst
28 import androidx.compose.ui.test.onNodeWithContentDescription
29 import androidx.compose.ui.test.onNodeWithTag
30 import androidx.compose.ui.test.performClick
31 import androidx.compose.ui.test.performScrollTo
32 import androidx.compose.ui.test.performSemanticsAction
33 import androidx.compose.ui.test.performTouchInput
34 import androidx.compose.ui.test.swipeLeft
35 import androidx.compose.ui.test.swipeRight
36 import androidx.test.espresso.Espresso
37 import androidx.test.espresso.UiController
38 import androidx.test.espresso.ViewAction
39 import androidx.test.espresso.action.GeneralLocation
40 import androidx.test.espresso.assertion.ViewAssertions
41 import androidx.test.espresso.matcher.ViewMatchers
42 import com.google.android.material.bottomsheet.BottomSheetBehavior
43 import org.hamcrest.Matcher
44 import org.mozilla.fenix.R
45 import org.mozilla.fenix.helpers.Constants
46 import org.mozilla.fenix.helpers.Constants.TAG
47 import org.mozilla.fenix.helpers.DataGenerationHelper.getStringResource
48 import org.mozilla.fenix.helpers.HomeActivityComposeTestRule
49 import org.mozilla.fenix.helpers.MatcherHelper.assertUIObjectExists
50 import org.mozilla.fenix.helpers.MatcherHelper.itemContainingText
51 import org.mozilla.fenix.helpers.TestAssetHelper.waitingTime
52 import org.mozilla.fenix.helpers.TestAssetHelper.waitingTimeShort
53 import org.mozilla.fenix.helpers.TestHelper.mDevice
54 import org.mozilla.fenix.helpers.clickAtLocationInView
55 import org.mozilla.fenix.helpers.idlingresource.BottomSheetBehaviorStateIdlingResource
56 import org.mozilla.fenix.helpers.matchers.BottomSheetBehaviorHalfExpandedMaxRatioMatcher
57 import org.mozilla.fenix.helpers.matchers.BottomSheetBehaviorStateMatcher
58 import org.mozilla.fenix.tabstray.TabsTrayTestTag
61 * Implementation of Robot Pattern for the Tabs Tray.
63 class ComposeTabDrawerRobot(private val composeTestRule: HomeActivityComposeTestRule) {
65 fun verifyNormalBrowsingButtonIsSelected(isSelected: Boolean = true) {
67 Log.i(TAG, "verifyNormalBrowsingButtonIsSelected: Trying to verify that the normal browsing button is selected")
68 composeTestRule.normalBrowsingButton().assertIsSelected()
69 Log.i(TAG, "verifyNormalBrowsingButtonIsSelected: Verified that the normal browsing button is selected")
71 Log.i(TAG, "verifyNormalBrowsingButtonIsSelected: Trying to verify that the normal browsing button is not selected")
72 composeTestRule.normalBrowsingButton().assertIsNotSelected()
73 Log.i(TAG, "verifyNormalBrowsingButtonIsSelected: Verified that the normal browsing button is not selected")
77 fun verifyPrivateBrowsingButtonIsSelected(isSelected: Boolean = true) {
79 Log.i(TAG, "verifyPrivateBrowsingButtonIsSelected: Trying to verify that the private browsing button is selected")
80 composeTestRule.privateBrowsingButton().assertIsSelected()
81 Log.i(TAG, "verifyPrivateBrowsingButtonIsSelected: Verified that the private browsing button is selected")
83 Log.i(TAG, "verifyPrivateBrowsingButtonIsSelected: Trying to verify that the private browsing button is not selected")
84 composeTestRule.privateBrowsingButton().assertIsNotSelected()
85 Log.i(TAG, "verifyPrivateBrowsingButtonIsSelected: Verified that the private browsing button is not selected")
89 fun verifySyncedTabsButtonIsSelected(isSelected: Boolean = true) {
91 Log.i(TAG, "verifySyncedTabsButtonIsSelected: Trying to verify that the synced tabs button is selected")
92 composeTestRule.syncedTabsButton().assertIsSelected()
93 Log.i(TAG, "verifySyncedTabsButtonIsSelected: Verified that the synced tabs button is selected")
95 Log.i(TAG, "verifySyncedTabsButtonIsSelected: Trying to verify that the synced tabs button is not selected")
96 composeTestRule.syncedTabsButton().assertIsNotSelected()
97 Log.i(TAG, "verifySyncedTabsButtonIsSelected: Verified that the synced tabs button is not selected")
101 fun verifySyncedTabsListWhenUserIsNotSignedIn() {
102 verifySyncedTabsList()
103 assertUIObjectExists(
104 itemContainingText(getStringResource(R.string.synced_tabs_sign_in_message)),
105 itemContainingText(getStringResource(R.string.sync_sign_in)),
106 itemContainingText(getStringResource(R.string.tab_drawer_fab_sync)),
110 fun verifyExistingOpenTabs(vararg titles: String) {
111 titles.forEach { title ->
112 Log.i(TAG, "verifyExistingOpenTabs: Waiting for $waitingTime ms for tab with title: $title to exist")
113 itemContainingText(title).waitForExists(waitingTime)
114 Log.i(TAG, "verifyExistingOpenTabs: Waited for $waitingTime ms for tab with title: $title to exist")
115 Log.i(TAG, "verifyExistingOpenTabs: Trying to verify that the open tab with title: $title exists")
116 composeTestRule.tabItem(title).assertExists()
117 Log.i(TAG, "verifyExistingOpenTabs: Verified that the open tab with title: $title exists")
121 fun verifyOpenTabsOrder(title: String, position: Int) {
122 Log.i(TAG, "verifyOpenTabsOrder: Trying to verify that the open tab at position: $position has title: $title")
123 composeTestRule.normalTabsList()
124 .onChildAt(position - 1)
125 .assert(hasTestTag(TabsTrayTestTag.tabItemRoot))
126 .assert(hasAnyChild(hasText(title)))
127 Log.i(TAG, "verifyOpenTabsOrder: Verified that the open tab at position: $position has title: $title")
130 fun verifyNoExistingOpenTabs(vararg titles: String) {
131 titles.forEach { title ->
132 assertUIObjectExists(
133 itemContainingText(title),
139 @OptIn(ExperimentalTestApi::class)
140 fun verifyNormalTabsList() {
141 composeTestRule.waitUntilDoesNotExist(hasTestTag("tabstray.tabList.normal.empty"), waitingTime)
142 Log.i(TAG, "verifyNormalTabsList: Trying to verify that the normal tabs list exists")
143 composeTestRule.normalTabsList().assertExists()
144 Log.i(TAG, "verifyNormalTabsList: Verified that the normal tabs list exists")
147 fun verifyPrivateTabsList() {
148 Log.i(TAG, "verifyPrivateTabsList: Trying to verify that the private tabs list exists")
149 composeTestRule.privateTabsList().assertExists()
150 Log.i(TAG, "verifyPrivateTabsList: Verified that the private tabs list exists")
153 fun verifySyncedTabsList() {
154 Log.i(TAG, "verifySyncedTabsList: Trying to verify that the synced tabs list exists")
155 composeTestRule.syncedTabsList().assertExists()
156 Log.i(TAG, "verifySyncedTabsList: Verified that the synced tabs list exists")
159 fun verifyNoOpenTabsInNormalBrowsing() {
160 Log.i(TAG, "verifyNoOpenTabsInNormalBrowsing: Trying to verify that the empty normal tabs list exists")
161 composeTestRule.emptyNormalTabsList().assertExists()
162 Log.i(TAG, "verifyNoOpenTabsInNormalBrowsing: Verified that the empty normal tabs list exists")
165 fun verifyNoOpenTabsInPrivateBrowsing() {
166 Log.i(TAG, "verifyNoOpenTabsInPrivateBrowsing: Trying to verify that the empty private tabs list exists")
167 composeTestRule.emptyPrivateTabsList().assertExists()
168 Log.i(TAG, "verifyNoOpenTabsInPrivateBrowsing: Verified that the empty private tabs list exists")
171 fun verifyAccountSettingsButton() {
172 Log.i(TAG, "verifyAccountSettingsButton: Trying to verify that the \"Account settings\" menu button exists")
173 composeTestRule.dropdownMenuItemAccountSettings().assertExists()
174 Log.i(TAG, "verifyAccountSettingsButton: Verified that the \"Account settings\" menu button exists")
177 fun verifyCloseAllTabsButton() {
178 Log.i(TAG, "verifyCloseAllTabsButton: Trying to verify that the \"Close all tabs\" menu button exists")
179 composeTestRule.dropdownMenuItemCloseAllTabs().assertExists()
180 Log.i(TAG, "verifyCloseAllTabsButton: Verified that the \"Close all tabs\" menu button exists")
183 fun verifySelectTabsButton() {
184 Log.i(TAG, "verifySelectTabsButton: Trying to verify that the \"Select tabs\" menu button exists")
185 composeTestRule.dropdownMenuItemSelectTabs().assertExists()
186 Log.i(TAG, "verifySelectTabsButton: Verified that the \"Select tabs\" menu button exists")
189 fun verifyShareAllTabsButton() {
190 Log.i(TAG, "verifyShareAllTabsButton: Trying to verify that the \"Share all tabs\" menu button exists")
191 composeTestRule.dropdownMenuItemShareAllTabs().assertExists()
192 Log.i(TAG, "verifyShareAllTabsButton: Verified that the \"Share all tabs\" menu button exists")
195 fun verifyRecentlyClosedTabsButton() {
196 Log.i(TAG, "verifyRecentlyClosedTabsButton: Trying to verify that the \"Recently closed tabs\" menu button exists")
197 composeTestRule.dropdownMenuItemRecentlyClosedTabs().assertExists()
198 Log.i(TAG, "verifyRecentlyClosedTabsButton: Verified that the \"Recently closed tabs\" menu button exists")
201 fun verifyTabSettingsButton() {
202 Log.i(TAG, "verifyTabSettingsButton: Trying to verify that the \"Tab settings\" menu button exists")
203 composeTestRule.dropdownMenuItemTabSettings().assertExists()
204 Log.i(TAG, "verifyTabSettingsButton: Verified that the \"Tab settings\" menu button exists")
207 fun verifyThreeDotButton() {
208 Log.i(TAG, "verifyThreeDotButton: Trying to verify that the three dot button exists")
209 composeTestRule.threeDotButton().assertExists()
210 Log.i(TAG, "verifyThreeDotButton: Verified that the three dot button exists")
214 Log.i(TAG, "verifyFab: Trying to verify that the new tab FAB button exists")
215 composeTestRule.tabsTrayFab().assertExists()
216 Log.i(TAG, "verifyFab: Verified that the new tab FAB button exists")
219 fun verifyNormalTabCounter() {
220 Log.i(TAG, "verifyNormalTabCounter: Trying to verify that the normal tabs list counter exists")
221 composeTestRule.normalTabsCounter().assertExists()
222 Log.i(TAG, "verifyNormalTabCounter: Verified that the normal tabs list counter exists")
226 * Verifies a tab's thumbnail when there is only one tab open.
228 fun verifyTabThumbnail() {
229 Log.i(TAG, "verifyTabThumbnail: Trying to verify that the tab thumbnail exists")
230 composeTestRule.tabThumbnail().assertExists()
231 Log.i(TAG, "verifyTabThumbnail: Verified that the tab thumbnail exists")
235 * Verifies a tab's close button when there is only one tab open.
237 fun verifyTabCloseButton() {
238 Log.i(TAG, "verifyTabCloseButton: Trying to verify that the close tab button exists")
239 composeTestRule.closeTabButton().assertExists()
240 Log.i(TAG, "verifyTabCloseButton: Verified that the close tab button exists")
243 fun verifyTabsTrayBehaviorState(expectedState: Int) {
244 Log.i(TAG, "verifyTabsTrayBehaviorState: Trying to verify that the tabs tray state matches: $expectedState")
245 tabsTrayView().check(ViewAssertions.matches(BottomSheetBehaviorStateMatcher(expectedState)))
246 Log.i(TAG, "verifyTabsTrayBehaviorState: Verified that the tabs tray state matches: $expectedState")
249 fun verifyMinusculeHalfExpandedRatio() {
250 Log.i(TAG, "verifyMinusculeHalfExpandedRatio: Trying to verify the tabs tray half expanded ratio")
251 tabsTrayView().check(ViewAssertions.matches(BottomSheetBehaviorHalfExpandedMaxRatioMatcher(0.001f)))
252 Log.i(TAG, "verifyMinusculeHalfExpandedRatio: Verified the tabs tray half expanded ratio")
255 fun verifyTabTrayIsOpen() {
256 Log.i(TAG, "verifyTabTrayIsOpen: Trying to verify that the tabs tray exists")
257 composeTestRule.tabsTray().assertExists()
258 Log.i(TAG, "verifyTabTrayIsOpen: Verified that the tabs tray exists")
261 fun verifyTabTrayIsClosed() {
262 Log.i(TAG, "verifyTabTrayIsClosed: Trying to verify that the tabs tray does not exist")
263 composeTestRule.tabsTray().assertDoesNotExist()
264 Log.i(TAG, "verifyTabTrayIsClosed: Verified that the tabs tray does not exist")
268 * Closes a tab when there is only one tab open.
270 @OptIn(ExperimentalTestApi::class)
272 Log.i(TAG, "closeTab: Waiting until the close tab button exists")
273 composeTestRule.waitUntilAtLeastOneExists(hasTestTag(TabsTrayTestTag.tabItemClose))
274 Log.i(TAG, "closeTab: Waited until the close tab button exists")
275 Log.i(TAG, "closeTab: Trying to verify that the close tab button exists")
276 composeTestRule.closeTabButton().assertExists()
277 Log.i(TAG, "closeTab: Verified that the close tab button exists")
278 Log.i(TAG, "closeTab: Trying to click the close tab button")
279 composeTestRule.closeTabButton().performClick()
280 Log.i(TAG, "closeTab: Clicked the close tab button")
284 * Swipes a tab with [title] left.
286 fun swipeTabLeft(title: String) {
287 Log.i(TAG, "swipeTabLeft: Trying to perform swipe left action on tab: $title")
288 composeTestRule.tabItem(title).performTouchInput { swipeLeft() }
289 Log.i(TAG, "swipeTabLeft: Performed swipe left action on tab: $title")
293 * Swipes a tab with [title] right.
295 fun swipeTabRight(title: String) {
296 Log.i(TAG, "swipeTabRight: Trying to perform swipe right action on tab: $title")
297 composeTestRule.tabItem(title).performTouchInput { swipeRight() }
298 Log.i(TAG, "swipeTabRight: Performed swipe right action on tab: $title")
302 * Creates a collection from the provided [tabTitles].
304 fun createCollection(
305 vararg tabTitles: String,
306 collectionName: String,
307 firstCollection: Boolean = true,
309 Log.i(TAG, "createCollection: Trying to click the three dot button")
310 composeTestRule.threeDotButton().performClick()
311 Log.i(TAG, "createCollection: Clicked the three dot button")
312 Log.i(TAG, "createCollection: Trying to click the \"Select tabs\" menu button")
313 composeTestRule.dropdownMenuItemSelectTabs().performClick()
314 Log.i(TAG, "createCollection: Clicked the \"Select tabs\" menu button")
316 for (tab in tabTitles) {
320 clickCollectionsButton(composeTestRule) {
321 if (!firstCollection) {
322 clickAddNewCollection()
324 typeCollectionNameAndSave(collectionName)
329 * Selects a tab with [title].
331 @OptIn(ExperimentalTestApi::class)
332 fun selectTab(title: String) {
333 Log.i(TAG, "selectTab: Waiting for $waitingTime ms until the tab with title: $title exists")
334 composeTestRule.waitUntilExactlyOneExists(hasText(title), waitingTime)
335 Log.i(TAG, "selectTab: Waited for $waitingTime ms until the tab with title: $title exists")
336 Log.i(TAG, "selectTab: Trying to click tab with title: $title")
337 composeTestRule.tabItem(title).performClick()
338 Log.i(TAG, "selectTab: Clicked tab with title: $title")
342 * Performs a long click on a tab with [title].
344 fun longClickTab(title: String) {
345 Log.i(TAG, "longClickTab: Trying to long click tab with title: $title")
346 composeTestRule.tabItem(title)
347 .performTouchInput { longClick(durationMillis = Constants.LONG_CLICK_DURATION) }
348 Log.i(TAG, "longClickTab: Long clicked tab with title: $title")
352 * Verifies the multi selection counter displays [numOfTabs].
354 fun verifyTabsMultiSelectionCounter(numOfTabs: Int) {
355 Log.i(TAG, "verifyTabsMultiSelectionCounter: Trying to verify that $numOfTabs tabs are selected")
356 composeTestRule.multiSelectionCounter()
357 .assert(hasText("$numOfTabs selected"))
358 Log.i(TAG, "verifyTabsMultiSelectionCounter: Verified that $numOfTabs tabs are selected")
362 * Verifies a tab's media button matches [action] when there is only one tab with media.
364 @OptIn(ExperimentalTestApi::class)
365 fun verifyTabMediaControlButtonState(action: String) {
366 Log.i(TAG, "verifyTabMediaControlButtonStateTab: Waiting for $waitingTime ms until the media tab control button: $action exists")
367 composeTestRule.waitUntilAtLeastOneExists(hasContentDescription(action), waitingTime)
368 Log.i(TAG, "verifyTabMediaControlButtonStateTab: Waited for $waitingTime ms until the media tab control button: $action exists")
369 Log.i(TAG, "verifyTabMediaControlButtonStateTab: Trying to verify that the tab media control button: $action exists")
370 composeTestRule.tabMediaControlButton(action)
372 Log.i(TAG, "verifyTabMediaControlButtonStateTab: Verified tab media control button: $action exists")
376 * Clicks a tab's media button when there is only one tab with media.
378 @OptIn(ExperimentalTestApi::class)
379 fun clickTabMediaControlButton(action: String) {
380 Log.i(TAG, "clickTabMediaControlButton: Waiting for $waitingTime ms until the media tab control button: $action exists")
381 composeTestRule.waitUntilAtLeastOneExists(hasContentDescription(action), waitingTime)
382 Log.i(TAG, "clickTabMediaControlButton: Waited for $waitingTime ms until the media tab control button: $action exists")
383 Log.i(TAG, "clickTabMediaControlButton: Trying to click the tab media control button: $action")
384 composeTestRule.tabMediaControlButton(action)
386 Log.i(TAG, "clickTabMediaControlButton: Clicked the tab media control button: $action")
390 * Closes a tab with a given [title].
392 fun closeTabWithTitle(title: String) {
393 Log.i(TAG, "closeTabWithTitle: Trying to click the close button for tab with title: $title")
394 composeTestRule.onAllNodesWithTag(TabsTrayTestTag.tabItemClose)
395 .filter(hasParent(hasText(title)))
398 Log.i(TAG, "closeTabWithTitle: Clicked the close button for tab with title: $title")
401 class Transition(private val composeTestRule: HomeActivityComposeTestRule) {
403 fun openNewTab(interact: SearchRobot.() -> Unit): SearchRobot.Transition {
404 Log.i(TAG, "openNewTab: Waiting for device to be idle")
405 mDevice.waitForIdle()
406 Log.i(TAG, "openNewTab: Waited for device to be idle")
407 Log.i(TAG, "openNewTab: Trying to click the new tab FAB button")
408 composeTestRule.tabsTrayFab().performClick()
409 Log.i(TAG, "openNewTab: Clicked the new tab FAB button")
410 SearchRobot().interact()
411 return SearchRobot.Transition()
414 fun toggleToNormalTabs(interact: ComposeTabDrawerRobot.() -> Unit): Transition {
415 Log.i(TAG, "toggleToNormalTabs: Trying to click the normal browsing button")
416 composeTestRule.normalBrowsingButton().performClick()
417 Log.i(TAG, "toggleToNormalTabs: Clicked the normal browsing button")
418 ComposeTabDrawerRobot(composeTestRule).interact()
419 return Transition(composeTestRule)
422 fun toggleToPrivateTabs(interact: ComposeTabDrawerRobot.() -> Unit): Transition {
423 Log.i(TAG, "toggleToPrivateTabs: Trying to click the private browsing button")
424 composeTestRule.privateBrowsingButton().performClick()
425 Log.i(TAG, "toggleToPrivateTabs: Clicked the private browsing button")
426 ComposeTabDrawerRobot(composeTestRule).interact()
427 return Transition(composeTestRule)
430 fun toggleToSyncedTabs(interact: ComposeTabDrawerRobot.() -> Unit): Transition {
431 Log.i(TAG, "toggleToSyncedTabs: Trying to click the synced tabs button")
432 composeTestRule.syncedTabsButton().performClick()
433 Log.i(TAG, "toggleToSyncedTabs: Clicked the synced tabs button")
434 ComposeTabDrawerRobot(composeTestRule).interact()
435 return Transition(composeTestRule)
438 fun clickSignInToSyncButton(interact: SyncSignInRobot.() -> Unit): SyncSignInRobot.Transition {
439 Log.i(TAG, "clickSignInToSyncButton: Trying to click the sign in to sync button and wait for $waitingTimeShort ms for a new window")
440 itemContainingText(getStringResource(R.string.sync_sign_in))
441 .clickAndWaitForNewWindow(waitingTimeShort)
442 Log.i(TAG, "clickSignInToSyncButton: Clicked the sign in to sync button and waited for $waitingTimeShort ms for a new window")
443 SyncSignInRobot().interact()
444 return SyncSignInRobot.Transition()
447 fun openThreeDotMenu(interact: ComposeTabDrawerRobot.() -> Unit): Transition {
448 Log.i(TAG, "openThreeDotMenu: Trying to click the three dot button")
449 composeTestRule.threeDotButton().performClick()
450 Log.i(TAG, "openThreeDotMenu: Clicked three dot button")
451 ComposeTabDrawerRobot(composeTestRule).interact()
452 return Transition(composeTestRule)
455 fun closeAllTabs(interact: HomeScreenRobot.() -> Unit): HomeScreenRobot.Transition {
456 Log.i(TAG, "closeAllTabs: Trying to click the \"Close all tabs\" menu button")
457 composeTestRule.dropdownMenuItemCloseAllTabs().performClick()
458 Log.i(TAG, "closeAllTabs: Clicked the \"Close all tabs\" menu button")
459 HomeScreenRobot().interact()
460 return HomeScreenRobot.Transition()
463 fun openTab(title: String, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
464 Log.i(TAG, "openTab: Trying to scroll to tab with title: $title")
465 composeTestRule.tabItem(title).performScrollTo()
466 Log.i(TAG, "openTab: Scrolled to tab with title: $title")
467 Log.i(TAG, "openTab: Trying to click tab with title: $title")
468 composeTestRule.tabItem(title).performClick()
469 Log.i(TAG, "openTab: Clicked tab with title: $title")
471 BrowserRobot().interact()
472 return BrowserRobot.Transition()
475 fun openPrivateTab(position: Int, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
476 Log.i(TAG, "openPrivateTab: Trying to click private tab at position: ${position + 1}")
477 composeTestRule.privateTabsList()
478 .onChildren()[position]
480 Log.i(TAG, "openPrivateTab: Clicked private tab at position: ${position + 1}")
482 BrowserRobot().interact()
483 return BrowserRobot.Transition()
486 fun openNormalTab(position: Int, interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
487 Log.i(TAG, "openNormalTab: Trying to click tab at position: ${position + 1}")
488 composeTestRule.normalTabsList()
489 .onChildren()[position]
491 Log.i(TAG, "openNormalTab: Clicked tab at position: ${position + 1}")
493 BrowserRobot().interact()
494 return BrowserRobot.Transition()
497 fun clickTopBar(interact: ComposeTabDrawerRobot.() -> Unit): Transition {
498 // The topBar contains other views.
499 // Don't do the default click in the middle, rather click in some free space - top right.
500 Log.i(TAG, "clickTopBar: Trying to click the tabs tray top bar")
501 Espresso.onView(ViewMatchers.withId(R.id.topBar)).clickAtLocationInView(GeneralLocation.TOP_RIGHT)
502 Log.i(TAG, "clickTopBar: Clicked the tabs tray top bar")
503 ComposeTabDrawerRobot(composeTestRule).interact()
504 return Transition(composeTestRule)
507 fun waitForTabTrayBehaviorToIdle(interact: ComposeTabDrawerRobot.() -> Unit): Transition {
508 // Need to get the behavior of tab_wrapper and wait for that to idle.
509 var behavior: BottomSheetBehavior<*>? = null
511 // Null check here since it's possible that the view is already animated away from the screen.
512 tabsTrayView()?.perform(
513 object : ViewAction {
514 override fun getDescription(): String {
515 return "Postpone actions to after the BottomSheetBehavior has settled"
518 override fun getConstraints(): Matcher<View> {
519 return ViewMatchers.isAssignableFrom(View::class.java)
522 override fun perform(uiController: UiController?, view: View?) {
523 behavior = BottomSheetBehavior.from(view!!)
529 runWithIdleRes(BottomSheetBehaviorStateIdlingResource(it)) {
530 ComposeTabDrawerRobot(composeTestRule).interact()
534 return Transition(composeTestRule)
537 fun advanceToHalfExpandedState(interact: ComposeTabDrawerRobot.() -> Unit): Transition {
538 tabsTrayView().perform(
539 object : ViewAction {
540 override fun getDescription(): String {
541 return "Advance a BottomSheetBehavior to STATE_HALF_EXPANDED"
544 override fun getConstraints(): Matcher<View> {
545 return ViewMatchers.isAssignableFrom(View::class.java)
548 override fun perform(uiController: UiController?, view: View?) {
549 val behavior = BottomSheetBehavior.from(view!!)
550 behavior.state = BottomSheetBehavior.STATE_HALF_EXPANDED
554 ComposeTabDrawerRobot(composeTestRule).interact()
555 return Transition(composeTestRule)
558 fun closeTabDrawer(interact: BrowserRobot.() -> Unit): BrowserRobot.Transition {
559 Log.i(TAG, "closeTabDrawer: Trying to close the tabs tray by clicking the handle")
560 composeTestRule.bannerHandle().performSemanticsAction(SemanticsActions.OnClick)
561 Log.i(TAG, "closeTabDrawer: Closed the tabs tray by clicking the handle")
563 BrowserRobot().interact()
564 return BrowserRobot.Transition()
567 fun clickSaveCollection(interact: CollectionRobot.() -> Unit): CollectionRobot.Transition {
568 Log.i(TAG, "clickSaveCollection: Trying to click the collections button")
569 composeTestRule.collectionsButton().performClick()
570 Log.i(TAG, "clickSaveCollection: Clicked collections button")
572 CollectionRobot().interact()
573 return CollectionRobot.Transition()
576 fun clickShareAllTabsButton(interact: ShareOverlayRobot.() -> Unit): ShareOverlayRobot.Transition {
577 Log.i(TAG, "clickShareAllTabsButton: Trying to click the \"Share all tabs\" menu button button")
578 composeTestRule.dropdownMenuItemShareAllTabs().performClick()
579 Log.i(TAG, "clickShareAllTabsButton: Clicked the \"Share all tabs\" menu button button")
581 ShareOverlayRobot().interact()
582 return ShareOverlayRobot.Transition()
588 * Opens a transition in the [ComposeTabDrawerRobot].
590 fun composeTabDrawer(composeTestRule: HomeActivityComposeTestRule, interact: ComposeTabDrawerRobot.() -> Unit): ComposeTabDrawerRobot.Transition {
591 ComposeTabDrawerRobot(composeTestRule).interact()
592 return ComposeTabDrawerRobot.Transition(composeTestRule)
596 * Clicks on the Collections button in the Tabs Tray banner and opens a transition in the [CollectionRobot].
598 private fun clickCollectionsButton(composeTestRule: HomeActivityComposeTestRule, interact: CollectionRobot.() -> Unit): CollectionRobot.Transition {
599 Log.i(TAG, "clickCollectionsButton: Trying to click the collections button")
600 composeTestRule.collectionsButton().performClick()
601 Log.i(TAG, "clickCollectionsButton: Clicked the collections button")
603 CollectionRobot().interact()
604 return CollectionRobot.Transition()
608 * Obtains the root [View] that wraps the Tabs Tray.
610 private fun tabsTrayView() = Espresso.onView(ViewMatchers.withId(R.id.tabs_tray_root))
613 * Obtains the root Tabs Tray.
615 private fun ComposeTestRule.tabsTray() = onNodeWithTag(TabsTrayTestTag.tabsTray)
618 * Obtains the Tabs Tray FAB.
620 private fun ComposeTestRule.tabsTrayFab() = onNodeWithTag(TabsTrayTestTag.fab)
623 * Obtains the normal browsing page button of the Tabs Tray banner.
625 private fun ComposeTestRule.normalBrowsingButton() = onNodeWithTag(TabsTrayTestTag.normalTabsPageButton)
628 * Obtains the private browsing page button of the Tabs Tray banner.
630 private fun ComposeTestRule.privateBrowsingButton() = onNodeWithTag(TabsTrayTestTag.privateTabsPageButton)
633 * Obtains the synced tabs page button of the Tabs Tray banner.
635 private fun ComposeTestRule.syncedTabsButton() = onNodeWithTag(TabsTrayTestTag.syncedTabsPageButton)
638 * Obtains the normal tabs list.
640 private fun ComposeTestRule.normalTabsList() = onNodeWithTag(TabsTrayTestTag.normalTabsList)
643 * Obtains the private tabs list.
645 private fun ComposeTestRule.privateTabsList() = onNodeWithTag(TabsTrayTestTag.privateTabsList)
648 * Obtains the synced tabs list.
650 private fun ComposeTestRule.syncedTabsList() = onNodeWithTag(TabsTrayTestTag.syncedTabsList)
653 * Obtains the empty normal tabs list.
655 private fun ComposeTestRule.emptyNormalTabsList() = onNodeWithTag(TabsTrayTestTag.emptyNormalTabsList)
658 * Obtains the empty private tabs list.
660 private fun ComposeTestRule.emptyPrivateTabsList() = onNodeWithTag(TabsTrayTestTag.emptyPrivateTabsList)
663 * Obtains the tab with the provided [title]
665 private fun ComposeTestRule.tabItem(title: String) = onAllNodesWithTag(TabsTrayTestTag.tabItemRoot)
666 .filter(hasAnyChild(hasText(title)))
670 * Obtains an open tab's close button when there's only one tab open.
672 private fun ComposeTestRule.closeTabButton() = onNodeWithTag(TabsTrayTestTag.tabItemClose)
675 * Obtains an open tab's thumbnail when there's only one tab open.
677 private fun ComposeTestRule.tabThumbnail() = onNodeWithTag(TabsTrayTestTag.tabItemThumbnail)
680 * Obtains the three dot button in the Tabs Tray banner.
682 private fun ComposeTestRule.threeDotButton() = onNodeWithTag(TabsTrayTestTag.threeDotButton)
685 * Obtains the dropdown menu item to access account settings.
687 private fun ComposeTestRule.dropdownMenuItemAccountSettings() = onNodeWithTag(TabsTrayTestTag.accountSettings)
690 * Obtains the dropdown menu item to close all tabs.
692 private fun ComposeTestRule.dropdownMenuItemCloseAllTabs() = onNodeWithTag(TabsTrayTestTag.closeAllTabs)
695 * Obtains the dropdown menu item to access recently closed tabs.
697 private fun ComposeTestRule.dropdownMenuItemRecentlyClosedTabs() = onNodeWithTag(TabsTrayTestTag.recentlyClosedTabs)
700 * Obtains the dropdown menu item to select tabs.
702 private fun ComposeTestRule.dropdownMenuItemSelectTabs() = onNodeWithTag(TabsTrayTestTag.selectTabs)
705 * Obtains the dropdown menu item to share all tabs.
707 private fun ComposeTestRule.dropdownMenuItemShareAllTabs() = onNodeWithTag(TabsTrayTestTag.shareAllTabs)
710 * Obtains the dropdown menu item to access tab settings.
712 private fun ComposeTestRule.dropdownMenuItemTabSettings() = onNodeWithTag(TabsTrayTestTag.tabSettings)
715 * Obtains the normal tabs counter.
717 private fun ComposeTestRule.normalTabsCounter() = onNodeWithTag(TabsTrayTestTag.normalTabsCounter)
720 * Obtains the Tabs Tray banner collections button.
722 private fun ComposeTestRule.collectionsButton() = onNodeWithTag(TabsTrayTestTag.collectionsButton)
725 * Obtains the Tabs Tray banner multi selection counter.
727 private fun ComposeTestRule.multiSelectionCounter() = onNodeWithTag(TabsTrayTestTag.selectionCounter)
730 * Obtains the Tabs Tray banner handle.
732 private fun ComposeTestRule.bannerHandle() = onNodeWithTag(TabsTrayTestTag.bannerHandle)
735 * Obtains the media control button with the given [action] as its content description.
737 private fun ComposeTestRule.tabMediaControlButton(action: String) = onNodeWithContentDescription(action)