Merge remote-tracking branch 'origin/fenix/117.0' into iceraven
commit
8c13773efd
@ -1 +1 @@
|
||||
Subproject commit 10bdc0f0a180ea2e892068ebd25e2e9183eb1c95
|
||||
Subproject commit c8b0ad246e4eeaf545298fbc8d0370edb0f3ee9c
|
@ -0,0 +1,120 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.helpers
|
||||
|
||||
import android.content.Context
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import mozilla.components.browser.icons.IconRequest
|
||||
import mozilla.components.browser.icons.generator.DefaultIconGenerator
|
||||
import mozilla.components.browser.state.search.SearchEngine
|
||||
import mozilla.components.browser.storage.sync.PlacesBookmarksStorage
|
||||
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
|
||||
import mozilla.components.concept.storage.PageVisit
|
||||
import mozilla.components.concept.storage.VisitType
|
||||
import mozilla.components.feature.search.ext.createSearchEngine
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.TestHelper.appContext
|
||||
import org.mozilla.fenix.search.SearchEngineSource.None.searchEngine
|
||||
|
||||
object MockBrowserDataHelper {
|
||||
val context: Context = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
|
||||
/**
|
||||
* Adds a new bookmark item, visible in the Bookmarks folder.
|
||||
*
|
||||
* @param url The URL of the bookmark item to add. URLs should use the "https://example.com" format.
|
||||
* @param title The title of the bookmark item to add.
|
||||
* @param position Example for the position param: 1u, 2u, etc.
|
||||
*/
|
||||
fun createBookmarkItem(url: String, title: String, position: UInt?) {
|
||||
runBlocking {
|
||||
PlacesBookmarksStorage(context)
|
||||
.addItem(
|
||||
BookmarkRoot.Mobile.id,
|
||||
url,
|
||||
title,
|
||||
position,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new history item, visible in the History folder.
|
||||
*
|
||||
* @param url The URL of the history item to add. URLs should use the "https://example.com" format.
|
||||
*/
|
||||
fun createHistoryItem(url: String) {
|
||||
runBlocking {
|
||||
PlacesHistoryStorage(appContext)
|
||||
.recordVisit(
|
||||
url,
|
||||
PageVisit(VisitType.LINK),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new tab with a webpage, also visible in the History folder.
|
||||
*
|
||||
* URLs should use the "https://example.com" format.
|
||||
*/
|
||||
fun createTabItem(url: String) {
|
||||
runBlocking {
|
||||
appContext.components.useCases.tabsUseCases.addTab(url)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Triggers a search for the provided search term in a new tab.
|
||||
*
|
||||
*/
|
||||
fun createSearchHistory(searchTerm: String) {
|
||||
appContext.components.useCases.searchUseCases.newTabSearch.invoke(searchTerm)
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new custom search engine object.
|
||||
*
|
||||
* @param mockWebServer The mockWebServer instance.
|
||||
* @param searchEngineName The name of the new search engine.
|
||||
*/
|
||||
private fun createCustomSearchEngine(mockWebServer: MockWebServer, searchEngineName: String): SearchEngine {
|
||||
val searchString =
|
||||
"http://localhost:${mockWebServer.port}/pages/searchResults.html?search={searchTerms}"
|
||||
return createSearchEngine(
|
||||
name = searchEngineName,
|
||||
url = searchString,
|
||||
icon = DefaultIconGenerator().generate(appContext, IconRequest(searchString)).bitmap,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new custom search engine to the apps Search Engines list.
|
||||
*
|
||||
* @param searchEngine Use createCustomSearchEngine method to create one.
|
||||
*/
|
||||
fun addCustomSearchEngine(mockWebServer: MockWebServer, searchEngineName: String) {
|
||||
val searchEngine = createCustomSearchEngine(mockWebServer, searchEngineName)
|
||||
|
||||
appContext.components.useCases.searchUseCases.addSearchEngine(searchEngine)
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds and selects as default a new custom search engine to the apps Search Engines list.
|
||||
*
|
||||
* @param searchEngine Use createCustomSearchEngine method to create one.
|
||||
*/
|
||||
fun setCustomSearchEngine(mockWebServer: MockWebServer, searchEngineName: String) {
|
||||
val searchEngine = createCustomSearchEngine(mockWebServer, searchEngineName)
|
||||
|
||||
with(appContext.components.useCases.searchUseCases) {
|
||||
addSearchEngine(searchEngine)
|
||||
selectSearchEngine(searchEngine)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,189 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package org.mozilla.fenix.screenshots
|
||||
|
||||
import android.os.SystemClock
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.ui.robots.bookmarksMenu
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import org.mozilla.fenix.ui.robots.swipeToBottom
|
||||
import tools.fastlane.screengrab.Screengrab
|
||||
import tools.fastlane.screengrab.locale.LocaleTestRule
|
||||
|
||||
class ComposeMenuScreenShotTest : ScreenshotTest() {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private lateinit var mDevice: UiDevice
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
val localeTestRule = LocaleTestRule()
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityTestRule.withDefaultSettingsOverrides(
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
composeTestRule.activity.finishAndRemoveTask()
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun threeDotMenuTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
Screengrab.screenshot("ThreeDotMenuMainRobot_three-dot-menu")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun settingsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
Screengrab.screenshot("SettingsRobot_settings-menu")
|
||||
}.openTurnOnSyncMenu {
|
||||
Screengrab.screenshot("AccountSettingsRobot_settings-account")
|
||||
}.goBack {
|
||||
}.openSearchSubMenu {
|
||||
Screengrab.screenshot("SettingsSubMenuSearchRobot_settings-search")
|
||||
}.goBack {
|
||||
}.openCustomizeSubMenu {
|
||||
Screengrab.screenshot("SettingsSubMenuThemeRobot_settings-theme")
|
||||
}.goBack {
|
||||
}.openAccessibilitySubMenu {
|
||||
Screengrab.screenshot("SettingsSubMenuAccessibilityRobot_settings-accessibility")
|
||||
}.goBack {
|
||||
}.openLanguageSubMenu {
|
||||
Screengrab.screenshot("SettingsSubMenuAccessibilityRobot_settings-language")
|
||||
}.goBack {
|
||||
// From about here we need to scroll up to ensure all settings options are visible.
|
||||
}.openSetDefaultBrowserSubMenu {
|
||||
Screengrab.screenshot("SettingsSubMenuDefaultBrowserRobot_settings-default-browser")
|
||||
}.goBack {
|
||||
// Disabled for Pixel 2
|
||||
// }.openEnhancedTrackingProtectionSubMenu {
|
||||
// Screengrab.screenshot("settings-enhanced-tp")
|
||||
// }.goBack {
|
||||
}.openLoginsAndPasswordSubMenu {
|
||||
Screengrab.screenshot("SettingsSubMenuLoginsAndPasswords-settings-logins-passwords")
|
||||
}.goBack {
|
||||
swipeToBottom()
|
||||
Screengrab.screenshot("SettingsRobot_settings-scroll-to-bottom")
|
||||
}.openSettingsSubMenuDataCollection {
|
||||
Screengrab.screenshot("settings-telemetry")
|
||||
}.goBack {
|
||||
}.openAddonsManagerMenu {
|
||||
Screengrab.screenshot("settings-addons")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun historyTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}
|
||||
openHistoryThreeDotMenu()
|
||||
Screengrab.screenshot("HistoryRobot_history-menu")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bookmarksManagementTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}
|
||||
openBookmarksThreeDotMenu()
|
||||
Screengrab.screenshot("BookmarksRobot_bookmarks-menu")
|
||||
bookmarksMenu {
|
||||
clickAddFolderButtonUsingId()
|
||||
Screengrab.screenshot("BookmarksRobot_add-folder-view")
|
||||
saveNewFolder()
|
||||
Screengrab.screenshot("BookmarksRobot_error-empty-folder-name")
|
||||
addNewFolderName("test")
|
||||
saveNewFolder()
|
||||
}.openThreeDotMenu("test") {
|
||||
Screengrab.screenshot("ThreeDotMenuBookmarksRobot_folder-menu")
|
||||
}
|
||||
editBookmarkFolder()
|
||||
Screengrab.screenshot("ThreeDotMenuBookmarksRobot_edit-bookmark-folder-menu")
|
||||
// It may be needed to wait here to have the screenshot
|
||||
bookmarksMenu {
|
||||
navigateUp()
|
||||
}.openThreeDotMenu("test") {
|
||||
deleteBookmarkFolder()
|
||||
Screengrab.screenshot("ThreeDotMenuBookmarksRobot_delete-bookmark-folder-menu")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun collectionMenuTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
navigationToolbar {
|
||||
Screengrab.screenshot("NavigationToolbarRobot_navigation-toolbar")
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
Screengrab.screenshot("BrowserRobot_enter-url")
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
TestAssetHelper.waitingTime
|
||||
Screengrab.screenshot("TabDrawerRobot_one-tab-open")
|
||||
}.openThreeDotMenu {
|
||||
TestAssetHelper.waitingTime
|
||||
Screengrab.screenshot("TabDrawerRobot_three-dot-menu")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tabMenuTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
Screengrab.screenshot("TabDrawerRobot_browser-tab-menu")
|
||||
}.closeBrowserMenuToBrowser {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
Screengrab.screenshot("TabDrawerRobot_tab-drawer-with-tabs")
|
||||
closeTab()
|
||||
TestAssetHelper.waitingTime
|
||||
Screengrab.screenshot("TabDrawerRobot_remove-tab")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun saveLoginPromptTest() {
|
||||
val saveLoginTest =
|
||||
TestAssetHelper.getSaveLoginAsset(mockWebServer)
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(saveLoginTest.url) {
|
||||
verifySaveLoginPromptIsShownNotSave()
|
||||
SystemClock.sleep(TestAssetHelper.waitingTimeShort)
|
||||
Screengrab.screenshot("save-login-prompt")
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,872 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import androidx.test.espresso.Espresso.pressBack
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.appservices.places.BookmarkRoot
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.ext.bookmarkStorage
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.RecyclerViewIdlingResource
|
||||
import org.mozilla.fenix.helpers.RetryTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.clickSnackbarButton
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.helpers.TestHelper.longTapSelectItem
|
||||
import org.mozilla.fenix.helpers.TestHelper.registerAndCleanupIdlingResources
|
||||
import org.mozilla.fenix.ui.robots.bookmarksMenu
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.multipleSelectionToolbar
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of bookmarks
|
||||
*/
|
||||
class ComposeBookmarksTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private lateinit var mDevice: UiDevice
|
||||
private val bookmarksFolderName = "New Folder"
|
||||
private val testBookmark = object {
|
||||
var title: String = "Bookmark title"
|
||||
var url: String = "https://www.example.com"
|
||||
}
|
||||
|
||||
@get:Rule(order = 0)
|
||||
val activityTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityIntentTestRule.withDefaultSettingsOverrides(
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Rule(order = 1)
|
||||
@JvmField
|
||||
val retryTestRule = RetryTestRule(3)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mDevice = UiDevice.getInstance(getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
// Clearing all bookmarks data after each test to avoid overlapping data
|
||||
val bookmarksStorage = activityTestRule.activity?.bookmarkStorage
|
||||
runBlocking {
|
||||
val bookmarks = bookmarksStorage?.getTree(BookmarkRoot.Mobile.id)?.children
|
||||
bookmarks?.forEach { bookmarksStorage.deleteNode(it.guid) }
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyEmptyBookmarksMenuTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1),
|
||||
) {
|
||||
verifyBookmarksMenuView()
|
||||
verifyAddFolderButton()
|
||||
verifyCloseButton()
|
||||
verifyBookmarkTitle("Desktop Bookmarks")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun defaultDesktopBookmarksFoldersTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1),
|
||||
) {
|
||||
selectFolder("Desktop Bookmarks")
|
||||
verifyFolderTitle("Bookmarks Menu")
|
||||
verifyFolderTitle("Bookmarks Toolbar")
|
||||
verifyFolderTitle("Other Bookmarks")
|
||||
verifySyncSignInButton()
|
||||
}
|
||||
}.clickSingInToSyncButton {
|
||||
verifyTurnOnSyncToolbarTitle()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyBookmarkButtonTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
}.bookmarkPage {
|
||||
}.openThreeDotMenu {
|
||||
verifyEditBookmarkButton()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun addBookmarkTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {
|
||||
verifyBookmarkedURL(defaultWebPage.url.toString())
|
||||
verifyBookmarkFavicon(defaultWebPage.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createBookmarkFolderTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1),
|
||||
) {
|
||||
clickAddFolderButton()
|
||||
verifyKeyboardVisible()
|
||||
addNewFolderName(bookmarksFolderName)
|
||||
saveNewFolder()
|
||||
verifyFolderTitle(bookmarksFolderName)
|
||||
verifyKeyboardHidden()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cancelCreateBookmarkFolderTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
clickAddFolderButton()
|
||||
addNewFolderName(bookmarksFolderName)
|
||||
navigateUp()
|
||||
verifyKeyboardHidden()
|
||||
verifyBookmarkFolderIsNotCreated(bookmarksFolderName)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun cancelEditBookmarkTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
}.bookmarkPage {
|
||||
clickSnackbarButton("EDIT")
|
||||
}
|
||||
bookmarksMenu {
|
||||
verifyEditBookmarksView()
|
||||
changeBookmarkTitle(testBookmark.title)
|
||||
changeBookmarkUrl(testBookmark.url)
|
||||
}.closeEditBookmarkSection {
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
verifyBookmarkTitle(defaultWebPage.title)
|
||||
verifyBookmarkedURL(defaultWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun editBookmarkTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.editBookmarkPage {
|
||||
verifyEditBookmarksView()
|
||||
changeBookmarkTitle(testBookmark.title)
|
||||
changeBookmarkUrl(testBookmark.url)
|
||||
saveEditBookmark()
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {}
|
||||
verifyBookmarkTitle(testBookmark.title)
|
||||
verifyBookmarkedURL(testBookmark.url)
|
||||
}.openBookmarkWithTitle(testBookmark.title) {
|
||||
verifyUrl("example.com")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun copyBookmarkURLTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {}
|
||||
}.openThreeDotMenu(defaultWebPage.title) {
|
||||
}.clickCopy {
|
||||
verifyCopySnackBarText()
|
||||
navigateUp()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.clickUrlbar {
|
||||
clickClearButton()
|
||||
longClickToolbar()
|
||||
clickPasteText()
|
||||
verifyTypedToolbarText(defaultWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun threeDotMenuShareBookmarkTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {}
|
||||
}.openThreeDotMenu(defaultWebPage.title) {
|
||||
}.clickShare {
|
||||
verifyShareOverlay()
|
||||
verifyShareBookmarkFavicon()
|
||||
verifyShareBookmarkTitle()
|
||||
verifyShareBookmarkUrl()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openBookmarkInNewTabTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {}
|
||||
}.openThreeDotMenu(defaultWebPage.title) {
|
||||
}.clickOpenInNewTab(activityTestRule) {
|
||||
verifyTabTrayIsOpen()
|
||||
verifyNormalBrowsingButtonIsSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openAllInTabsTest() {
|
||||
val webPages = listOf(
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 1),
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 2),
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 3),
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4),
|
||||
)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
createFolder("root")
|
||||
createFolder("sub", "root")
|
||||
createFolder("empty", "root")
|
||||
}.closeMenu {
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
createBookmark(webPages[0].url)
|
||||
createBookmark(webPages[1].url, "root")
|
||||
createBookmark(webPages[2].url, "root")
|
||||
createBookmark(webPages[3].url, "sub")
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
}.openThreeDotMenu("root") {
|
||||
}.clickOpenAllInTabs(activityTestRule) {
|
||||
verifyTabTrayIsOpen()
|
||||
verifyNormalBrowsingButtonIsSelected()
|
||||
|
||||
verifyExistingOpenTabs("Test_Page_2", "Test_Page_3", "Test_Page_4")
|
||||
|
||||
// Bookmark that is not under the root folder should not be opened
|
||||
verifyNoExistingOpenTabs("Test_Page_1")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openAllInPrivateTabsTest() {
|
||||
val webPages = listOf(
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 1),
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 2),
|
||||
)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
createFolder("root")
|
||||
createFolder("sub", "root")
|
||||
createFolder("empty", "root")
|
||||
}.closeMenu {
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
createBookmark(webPages[0].url, "root")
|
||||
createBookmark(webPages[1].url, "sub")
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
}.openThreeDotMenu("root") {
|
||||
}.clickOpenAllInPrivateTabs(activityTestRule) {
|
||||
verifyTabTrayIsOpen()
|
||||
verifyPrivateBrowsingButtonIsSelected()
|
||||
|
||||
verifyExistingOpenTabs("Test_Page_1", "Test_Page_2")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openBookmarkInPrivateTabTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {}
|
||||
}.openThreeDotMenu(defaultWebPage.title) {
|
||||
}.clickOpenInPrivateTab(activityTestRule) {
|
||||
verifyTabTrayIsOpen()
|
||||
verifyPrivateBrowsingButtonIsSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteBookmarkTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {}
|
||||
}.openThreeDotMenu(defaultWebPage.title) {
|
||||
}.clickDelete {
|
||||
verifyDeleteSnackBarText()
|
||||
verifyUndoDeleteSnackBarButton()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun undoDeleteBookmarkTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {}
|
||||
}.openThreeDotMenu(defaultWebPage.title) {
|
||||
}.clickDelete {
|
||||
verifyUndoDeleteSnackBarButton()
|
||||
clickUndoDeleteButton()
|
||||
verifySnackBarHidden()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {
|
||||
verifyBookmarkedURL(defaultWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun bookmarksMultiSelectionToolbarItemsTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {
|
||||
longTapSelectItem(defaultWebPage.url)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
verifyMultiSelectionCheckmark(defaultWebPage.url)
|
||||
verifyMultiSelectionCounter()
|
||||
verifyShareBookmarksButton()
|
||||
verifyCloseToolbarButton()
|
||||
}.closeToolbarReturnToBookmarks {
|
||||
verifyBookmarksMenuView()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun openSelectionInNewTabTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {
|
||||
longTapSelectItem(defaultWebPage.url)
|
||||
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
}.clickOpenNewTab(activityTestRule) {
|
||||
verifyTabTrayIsOpen()
|
||||
verifyNormalBrowsingButtonIsSelected()
|
||||
verifyNormalTabsList()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun openSelectionInPrivateTabTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {
|
||||
longTapSelectItem(defaultWebPage.url)
|
||||
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
}.clickOpenPrivateTab(activityTestRule) {
|
||||
verifyPrivateBrowsingButtonIsSelected()
|
||||
verifyPrivateTabsList()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteMultipleSelectionTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(firstWebPage.url)
|
||||
createBookmark(secondWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 3),
|
||||
) {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
longTapSelectItem(secondWebPage.url)
|
||||
}
|
||||
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
clickMultiSelectionDelete()
|
||||
}
|
||||
|
||||
bookmarksMenu {
|
||||
verifyDeleteMultipleBookmarksSnackBar()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun undoDeleteMultipleSelectionTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(firstWebPage.url)
|
||||
createBookmark(secondWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 3),
|
||||
) {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
longTapSelectItem(secondWebPage.url)
|
||||
}
|
||||
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
clickMultiSelectionDelete()
|
||||
}
|
||||
|
||||
bookmarksMenu {
|
||||
verifyDeleteMultipleBookmarksSnackBar()
|
||||
clickUndoDeleteButton()
|
||||
verifyBookmarkedURL(firstWebPage.url.toString())
|
||||
verifyBookmarkedURL(secondWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multipleSelectionShareButtonTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {
|
||||
longTapSelectItem(defaultWebPage.url)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
clickShareBookmarksButton()
|
||||
verifyShareOverlay()
|
||||
verifyShareTabFavicon()
|
||||
verifyShareTabTitle()
|
||||
verifyShareTabUrl()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun multipleBookmarkDeletionsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
createFolder("1")
|
||||
getInstrumentation().waitForIdleSync()
|
||||
createFolder("2")
|
||||
getInstrumentation().waitForIdleSync()
|
||||
createFolder("3")
|
||||
getInstrumentation().waitForIdleSync()
|
||||
}.openThreeDotMenu("1") {
|
||||
}.clickDelete {
|
||||
verifyDeleteFolderConfirmationMessage()
|
||||
confirmDeletion()
|
||||
verifyDeleteSnackBarText()
|
||||
}.openThreeDotMenu("2") {
|
||||
}.clickDelete {
|
||||
verifyDeleteFolderConfirmationMessage()
|
||||
confirmDeletion()
|
||||
verifyDeleteSnackBarText()
|
||||
verifyFolderTitle("3")
|
||||
// On some devices we need to wait for the Snackbar to be gone before continuing
|
||||
TestHelper.waitUntilSnackbarGone()
|
||||
}.closeMenu {
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
verifyFolderTitle("3")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun changeBookmarkParentFolderTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {
|
||||
createFolder(bookmarksFolderName)
|
||||
}
|
||||
}.openThreeDotMenu(defaultWebPage.title) {
|
||||
}.clickEdit {
|
||||
clickParentFolderSelector()
|
||||
selectFolder(bookmarksFolderName)
|
||||
navigateUp()
|
||||
saveEditBookmark()
|
||||
selectFolder(bookmarksFolderName)
|
||||
verifyBookmarkedURL(defaultWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun navigateBookmarksFoldersTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
createFolder("1")
|
||||
getInstrumentation().waitForIdleSync()
|
||||
waitForBookmarksFolderContentToExist("Bookmarks", "1")
|
||||
selectFolder("1")
|
||||
verifyCurrentFolderTitle("1")
|
||||
createFolder("2")
|
||||
getInstrumentation().waitForIdleSync()
|
||||
waitForBookmarksFolderContentToExist("1", "2")
|
||||
selectFolder("2")
|
||||
verifyCurrentFolderTitle("2")
|
||||
navigateUp()
|
||||
waitForBookmarksFolderContentToExist("1", "2")
|
||||
verifyCurrentFolderTitle("1")
|
||||
mDevice.pressBack()
|
||||
verifyBookmarksMenuView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun cantSelectDesktopFoldersTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list)),
|
||||
) {
|
||||
longTapDesktopFolder("Desktop Bookmarks")
|
||||
verifySelectDefaultFolderSnackBarText()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyCloseMenuTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
}.closeMenu {
|
||||
verifyHomeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteBookmarkInEditModeTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 2),
|
||||
) {}
|
||||
}.openThreeDotMenu(defaultWebPage.title) {
|
||||
}.clickEdit {
|
||||
clickDeleteInEditModeButton()
|
||||
cancelDeletion()
|
||||
clickDeleteInEditModeButton()
|
||||
confirmDeletion()
|
||||
verifyDeleteSnackBarText()
|
||||
verifyBookmarkIsDeleted("Test_Page_1")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun undoDeleteBookmarkFolderTest() {
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.bookmark_list), 1),
|
||||
) {
|
||||
createFolder("My Folder")
|
||||
verifyFolderTitle("My Folder")
|
||||
}
|
||||
}.openThreeDotMenu("My Folder") {
|
||||
}.clickDelete {
|
||||
cancelFolderDeletion()
|
||||
verifyFolderTitle("My Folder")
|
||||
}.openThreeDotMenu("My Folder") {
|
||||
}.clickDelete {
|
||||
confirmDeletion()
|
||||
verifyDeleteSnackBarText()
|
||||
clickUndoDeleteButton()
|
||||
verifyFolderTitle("My Folder")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySearchBookmarksViewTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
}.clickSearchButton {
|
||||
verifySearchView()
|
||||
verifySearchToolbar(true)
|
||||
verifySearchSelectorButton()
|
||||
verifySearchEngineIcon("Bookmarks")
|
||||
verifySearchBarPlaceholder("Search bookmarks")
|
||||
verifySearchBarPosition(true)
|
||||
tapOutsideToDismissSearchBar()
|
||||
verifySearchToolbar(false)
|
||||
}
|
||||
bookmarksMenu {
|
||||
}.goBackToBrowserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openCustomizeSubMenu {
|
||||
clickTopToolbarToggle()
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
}.clickSearchButton {
|
||||
verifySearchToolbar(true)
|
||||
verifySearchEngineIcon("Bookmarks")
|
||||
verifySearchBarPosition(false)
|
||||
pressBack()
|
||||
verifySearchToolbar(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySearchForBookmarkedItemsTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getHTMLControlsFormAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
createFolder(bookmarksFolderName)
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
browserScreen {
|
||||
createBookmark(firstWebPage.url, bookmarksFolderName)
|
||||
createBookmark(secondWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
}.clickSearchButton {
|
||||
// Search for a valid term
|
||||
typeSearch(firstWebPage.title)
|
||||
verifySearchEngineSuggestionResults(activityTestRule, firstWebPage.url.toString())
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, secondWebPage.url.toString())
|
||||
// Search for invalid term
|
||||
typeSearch("Android")
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, firstWebPage.url.toString())
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, secondWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyVoiceSearchInBookmarksTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(defaultWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
}.clickSearchButton {
|
||||
verifySearchToolbar(true)
|
||||
verifySearchEngineIcon("Bookmarks")
|
||||
startVoiceSearch()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyDeletedBookmarksCanNotBeSearchedTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
val thirdWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 3)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(firstWebPage.url)
|
||||
createBookmark(secondWebPage.url)
|
||||
createBookmark(thirdWebPage.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
}.openThreeDotMenu(firstWebPage.title) {
|
||||
}.clickDelete {
|
||||
verifyBookmarkIsDeleted(firstWebPage.title)
|
||||
}.openThreeDotMenu(secondWebPage.title) {
|
||||
}.clickDelete {
|
||||
verifyBookmarkIsDeleted(secondWebPage.title)
|
||||
}.clickSearchButton {
|
||||
// Search for a valid term
|
||||
typeSearch("generic")
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, firstWebPage.url.toString())
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, secondWebPage.url.toString())
|
||||
verifySearchEngineSuggestionResults(activityTestRule, thirdWebPage.url.toString())
|
||||
pressBack()
|
||||
}
|
||||
bookmarksMenu {
|
||||
}.openThreeDotMenu(thirdWebPage.title) {
|
||||
}.clickDelete {
|
||||
verifyBookmarkIsDeleted(thirdWebPage.title)
|
||||
}.clickSearchButton {
|
||||
// Search for a valid term
|
||||
typeSearch("generic")
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, thirdWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,530 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.getGenericAsset
|
||||
import org.mozilla.fenix.helpers.TestHelper.clickSnackbarButton
|
||||
import org.mozilla.fenix.helpers.TestHelper.verifySnackBarText
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.collectionRobot
|
||||
import org.mozilla.fenix.ui.robots.composeTabDrawer
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of tab collections
|
||||
*
|
||||
*/
|
||||
|
||||
class ComposeCollectionTest {
|
||||
private lateinit var mDevice: UiDevice
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private val firstCollectionName = "testcollection_1"
|
||||
private val secondCollectionName = "testcollection_2"
|
||||
private val collectionName = "First Collection"
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityIntentTestRule(
|
||||
isHomeOnboardingDialogEnabled = false,
|
||||
isJumpBackInCFREnabled = false,
|
||||
isRecentTabsFeatureEnabled = false,
|
||||
isRecentlyVisitedFeatureEnabled = false,
|
||||
isPocketEnabled = false,
|
||||
isWallpaperOnboardingEnabled = false,
|
||||
isTCPCFREnabled = false,
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun createFirstCollectionTest() {
|
||||
val firstWebPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.openNewTab {
|
||||
}.submitQuery(secondWebPage.url.toString()) {
|
||||
mDevice.waitForIdle()
|
||||
}.goToHomescreen {
|
||||
}.clickSaveTabsToCollectionButton(composeTestRule) {
|
||||
longClickTab(firstWebPage.title)
|
||||
selectTab(secondWebPage.title)
|
||||
verifyTabsMultiSelectionCounter(2)
|
||||
}.clickSaveCollection {
|
||||
typeCollectionNameAndSave(collectionName)
|
||||
}
|
||||
|
||||
composeTabDrawer(composeTestRule) {
|
||||
verifySnackBarText("Collection saved!")
|
||||
clickSnackbarButton("VIEW")
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun verifyExpandedCollectionItemsTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
val webPageUrl = webPage.url.host.toString()
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(webPage.title, collectionName = collectionName)
|
||||
clickSnackbarButton("VIEW")
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
verifyTabSavedInCollection(webPage.title)
|
||||
verifyCollectionTabUrl(true, webPageUrl)
|
||||
verifyShareCollectionButtonIsVisible(true)
|
||||
verifyCollectionMenuIsVisible(true, composeTestRule)
|
||||
verifyCollectionItemRemoveButtonIsVisible(webPage.title, true)
|
||||
}.collapseCollection(collectionName) {}
|
||||
|
||||
collectionRobot {
|
||||
verifyTabSavedInCollection(webPage.title, false)
|
||||
verifyShareCollectionButtonIsVisible(false)
|
||||
verifyCollectionMenuIsVisible(false, composeTestRule)
|
||||
verifyCollectionTabUrl(false, webPageUrl)
|
||||
verifyCollectionItemRemoveButtonIsVisible(webPage.title, false)
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
verifyTabSavedInCollection(webPage.title)
|
||||
verifyCollectionTabUrl(true, webPageUrl)
|
||||
verifyShareCollectionButtonIsVisible(true)
|
||||
verifyCollectionMenuIsVisible(true, composeTestRule)
|
||||
verifyCollectionItemRemoveButtonIsVisible(webPage.title, true)
|
||||
}.collapseCollection(collectionName) {}
|
||||
|
||||
collectionRobot {
|
||||
verifyTabSavedInCollection(webPage.title, false)
|
||||
verifyShareCollectionButtonIsVisible(false)
|
||||
verifyCollectionMenuIsVisible(false, composeTestRule)
|
||||
verifyCollectionTabUrl(false, webPageUrl)
|
||||
verifyCollectionItemRemoveButtonIsVisible(webPage.title, false)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun openAllTabsInCollectionTest() {
|
||||
val firstTestPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondTestPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstTestPage.url) {
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.openNewTab {
|
||||
}.submitQuery(secondTestPage.url.toString()) {
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(
|
||||
firstTestPage.title,
|
||||
secondTestPage.title,
|
||||
collectionName = collectionName,
|
||||
)
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
clickCollectionThreeDotButton(composeTestRule)
|
||||
selectOpenTabs(composeTestRule)
|
||||
}
|
||||
composeTabDrawer(composeTestRule) {
|
||||
verifyExistingOpenTabs(firstTestPage.title, secondTestPage.title)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun shareCollectionTest() {
|
||||
val firstWebsite = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebsite = getGenericAsset(mockWebServer, 2)
|
||||
val sharingApp = "Gmail"
|
||||
val urlString = "${secondWebsite.url}\n\n${firstWebsite.url}"
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebsite.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.openNewTab {
|
||||
}.submitQuery(secondWebsite.url.toString()) {
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(firstWebsite.title, secondWebsite.title, collectionName = collectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
}.clickShareCollectionButton {
|
||||
verifyShareTabsOverlay(firstWebsite.title, secondWebsite.title)
|
||||
verifySharingWithSelectedApp(sharingApp, urlString, collectionName)
|
||||
}
|
||||
}
|
||||
|
||||
// Test running on beta/release builds in CI:
|
||||
// caution when making changes to it, so they don't block the builds
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(webPage.title, collectionName = collectionName)
|
||||
clickSnackbarButton("VIEW")
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
clickCollectionThreeDotButton(composeTestRule)
|
||||
selectDeleteCollection(composeTestRule)
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifySnackBarText("Collection deleted")
|
||||
verifyNoCollectionsText()
|
||||
}
|
||||
}
|
||||
|
||||
// open a webpage, and add currently opened tab to existing collection
|
||||
@Test
|
||||
fun mainMenuSaveToExistingCollection() {
|
||||
val firstWebPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(firstWebPage.title, collectionName = collectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
verifyPageContent(secondWebPage.content)
|
||||
}.openThreeDotMenu {
|
||||
}.openSaveToCollection {
|
||||
}.selectExistingCollection(collectionName) {
|
||||
verifySnackBarText("Tab saved!")
|
||||
}.goToHomescreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
verifyTabSavedInCollection(firstWebPage.title)
|
||||
verifyTabSavedInCollection(secondWebPage.title)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyAddTabButtonOfCollectionMenu() {
|
||||
val firstWebPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(firstWebPage.title, collectionName = collectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
closeTab()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
}.goToHomescreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
clickCollectionThreeDotButton(composeTestRule)
|
||||
selectAddTabToCollection(composeTestRule)
|
||||
verifyTabsSelectedCounterText(1)
|
||||
saveTabsSelectedForCollection()
|
||||
verifySnackBarText("Tab saved!")
|
||||
verifyTabSavedInCollection(secondWebPage.title)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun renameCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(webPage.title, collectionName = firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen {
|
||||
verifyCollectionIsDisplayed(firstCollectionName)
|
||||
}.expandCollection(firstCollectionName) {
|
||||
clickCollectionThreeDotButton(composeTestRule)
|
||||
selectRenameCollection(composeTestRule)
|
||||
}.typeCollectionNameAndSave(secondCollectionName) {}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(secondCollectionName)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun createSecondCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(webPage.title, collectionName = firstCollectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
createCollection(
|
||||
webPage.title,
|
||||
collectionName = secondCollectionName,
|
||||
firstCollection = false,
|
||||
)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen {
|
||||
verifyCollectionIsDisplayed(firstCollectionName)
|
||||
verifyCollectionIsDisplayed(secondCollectionName)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun removeTabFromCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(webPage.title, collectionName = collectionName)
|
||||
closeTab()
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
verifyTabSavedInCollection(webPage.title, true)
|
||||
removeTabFromCollection(webPage.title)
|
||||
verifyTabSavedInCollection(webPage.title, false)
|
||||
}
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName, false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun undoTabRemovalFromCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(webPage.title, collectionName = collectionName)
|
||||
closeTab()
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
verifyTabSavedInCollection(webPage.title, true)
|
||||
removeTabFromCollection(webPage.title)
|
||||
}
|
||||
homeScreen {
|
||||
verifySnackBarText("Collection deleted")
|
||||
clickSnackbarButton("UNDO")
|
||||
verifyCollectionIsDisplayed(collectionName, true)
|
||||
verifyCollectionIsDisplayed(collectionName, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun swipeLeftToRemoveTabFromCollectionTest() {
|
||||
val testPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(testPage.url) {
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(
|
||||
testPage.title,
|
||||
collectionName = collectionName,
|
||||
)
|
||||
closeTab()
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
swipeTabLeft(testPage.title, composeTestRule)
|
||||
verifyTabSavedInCollection(testPage.title, false)
|
||||
}
|
||||
homeScreen {
|
||||
verifySnackBarText("Collection deleted")
|
||||
verifySnackBarText("UNDO")
|
||||
verifyCollectionIsDisplayed(collectionName, false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun swipeRightToRemoveTabFromCollectionTest() {
|
||||
val testPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(testPage.url) {
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(
|
||||
testPage.title,
|
||||
collectionName = collectionName,
|
||||
)
|
||||
closeTab()
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
swipeTabRight(testPage.title, composeTestRule)
|
||||
verifyTabSavedInCollection(testPage.title, false)
|
||||
}
|
||||
homeScreen {
|
||||
verifySnackBarText("Collection deleted")
|
||||
verifySnackBarText("UNDO")
|
||||
verifyCollectionIsDisplayed(collectionName, false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun selectTabOnLongTapTest() {
|
||||
val firstWebPage = getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.openNewTab {
|
||||
}.submitQuery(secondWebPage.url.toString()) {
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
verifyExistingOpenTabs(firstWebPage.title, secondWebPage.title)
|
||||
longClickTab(firstWebPage.title)
|
||||
verifyTabsMultiSelectionCounter(1)
|
||||
selectTab(secondWebPage.title)
|
||||
verifyTabsMultiSelectionCounter(2)
|
||||
}.clickSaveCollection {
|
||||
typeCollectionNameAndSave(collectionName)
|
||||
verifySnackBarText("Tabs saved!")
|
||||
}
|
||||
|
||||
composeTabDrawer(composeTestRule) {
|
||||
}.closeTabDrawer {
|
||||
}.goToHomescreen {
|
||||
}.expandCollection(collectionName) {
|
||||
verifyTabSavedInCollection(firstWebPage.title)
|
||||
verifyTabSavedInCollection(secondWebPage.title)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun navigateBackInCollectionFlowTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(webPage.title, collectionName = collectionName)
|
||||
verifySnackBarText("Collection saved!")
|
||||
}.closeTabDrawer {
|
||||
}.openThreeDotMenu {
|
||||
}.openSaveToCollection {
|
||||
verifySelectCollectionScreen()
|
||||
goBackInCollectionFlow()
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSaveToCollection {
|
||||
verifySelectCollectionScreen()
|
||||
clickAddNewCollection()
|
||||
verifyCollectionNameTextField()
|
||||
goBackInCollectionFlow()
|
||||
verifySelectCollectionScreen()
|
||||
goBackInCollectionFlow()
|
||||
}
|
||||
// verify the browser layout is visible
|
||||
browserScreen {
|
||||
verifyMenuButton()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun undoDeleteCollectionTest() {
|
||||
val webPage = getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(webPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
createCollection(webPage.title, collectionName = collectionName)
|
||||
clickSnackbarButton("VIEW")
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyCollectionIsDisplayed(collectionName)
|
||||
}.expandCollection(collectionName) {
|
||||
clickCollectionThreeDotButton(composeTestRule)
|
||||
selectDeleteCollection(composeTestRule)
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifySnackBarText("Collection deleted")
|
||||
clickSnackbarButton("UNDO")
|
||||
verifyCollectionIsDisplayed(collectionName, true)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,302 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.itemContainingText
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.itemWithText
|
||||
import org.mozilla.fenix.helpers.RetryTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.assertYoutubeAppOpens
|
||||
import org.mozilla.fenix.helpers.TestHelper.clickSnackbarButton
|
||||
import org.mozilla.fenix.ui.robots.clickContextMenuItem
|
||||
import org.mozilla.fenix.ui.robots.clickPageObject
|
||||
import org.mozilla.fenix.ui.robots.downloadRobot
|
||||
import org.mozilla.fenix.ui.robots.longClickPageObject
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import org.mozilla.fenix.ui.robots.shareOverlay
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of content context menus
|
||||
*
|
||||
* - Verifies long click "Open link in new tab" UI and functionality
|
||||
* - Verifies long click "Open link in new Private tab" UI and functionality
|
||||
* - Verifies long click "Copy Link" UI and functionality
|
||||
* - Verifies long click "Share Link" UI and functionality
|
||||
* - Verifies long click "Open image in new tab" UI and functionality
|
||||
* - Verifies long click "Save Image" UI and functionality
|
||||
* - Verifies long click "Copy image location" UI and functionality
|
||||
* - Verifies long click items of mixed hypertext items
|
||||
*
|
||||
*/
|
||||
|
||||
class ComposeContextMenusTest {
|
||||
private lateinit var mDevice: UiDevice
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
@get:Rule(order = 0)
|
||||
val composeTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityIntentTestRule.withDefaultSettingsOverrides(
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Rule(order = 1)
|
||||
@JvmField
|
||||
val retryTestRule = RetryTestRule(3)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
composeTestRule.activity.applicationContext.settings().shouldShowJumpBackInCFR = false
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun verifyContextOpenLinkNewTab() {
|
||||
val pageLinks =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val genericURL =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
mDevice.waitForIdle()
|
||||
longClickPageObject(itemWithText("Link 1"))
|
||||
verifyContextMenuForLocalHostLinks(genericURL.url)
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
verifySnackBarText("New tab opened")
|
||||
clickSnackbarButton("SWITCH")
|
||||
verifyUrl(genericURL.url.toString())
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
verifyNormalBrowsingButtonIsSelected()
|
||||
verifyExistingOpenTabs("Test_Page_1")
|
||||
verifyExistingOpenTabs("Test_Page_4")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun verifyContextOpenLinkPrivateTab() {
|
||||
val pageLinks =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val genericURL =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
mDevice.waitForIdle()
|
||||
longClickPageObject(itemWithText("Link 2"))
|
||||
verifyContextMenuForLocalHostLinks(genericURL.url)
|
||||
clickContextMenuItem("Open link in private tab")
|
||||
verifySnackBarText("New private tab opened")
|
||||
clickSnackbarButton("SWITCH")
|
||||
verifyUrl(genericURL.url.toString())
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
verifyPrivateBrowsingButtonIsSelected()
|
||||
verifyExistingOpenTabs("Test_Page_2")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextCopyLink() {
|
||||
val pageLinks =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val genericURL =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 3)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
mDevice.waitForIdle()
|
||||
longClickPageObject(itemWithText("Link 3"))
|
||||
verifyContextMenuForLocalHostLinks(genericURL.url)
|
||||
clickContextMenuItem("Copy link")
|
||||
verifySnackBarText("Link copied to clipboard")
|
||||
}.openNavigationToolbar {
|
||||
}.visitLinkFromClipboard {
|
||||
verifyUrl(genericURL.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextCopyLinkNotDisplayedAfterApplied() {
|
||||
val pageLinks = TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 3)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
longClickPageObject(itemWithText("Link 3"))
|
||||
verifyContextMenuForLocalHostLinks(genericURL.url)
|
||||
clickContextMenuItem("Copy link")
|
||||
verifySnackBarText("Link copied to clipboard")
|
||||
}.openNavigationToolbar {
|
||||
}.visitLinkFromClipboard {
|
||||
verifyUrl(genericURL.url.toString())
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.openNewTab {
|
||||
}
|
||||
navigationToolbar {
|
||||
verifyClipboardSuggestionsAreDisplayed(shouldBeDisplayed = false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextShareLink() {
|
||||
val pageLinks =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val genericURL =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
mDevice.waitForIdle()
|
||||
longClickPageObject(itemWithText("Link 1"))
|
||||
verifyContextMenuForLocalHostLinks(genericURL.url)
|
||||
clickContextMenuItem("Share link")
|
||||
shareOverlay {
|
||||
verifyShareLinkIntent(genericURL.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextOpenImageNewTab() {
|
||||
val pageLinks =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val imageResource =
|
||||
TestAssetHelper.getImageAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
mDevice.waitForIdle()
|
||||
longClickPageObject(itemWithText("test_link_image"))
|
||||
verifyLinkImageContextMenuItems(imageResource.url)
|
||||
clickContextMenuItem("Open image in new tab")
|
||||
verifySnackBarText("New tab opened")
|
||||
clickSnackbarButton("SWITCH")
|
||||
verifyUrl(imageResource.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextCopyImageLocation() {
|
||||
val pageLinks =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val imageResource =
|
||||
TestAssetHelper.getImageAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
mDevice.waitForIdle()
|
||||
longClickPageObject(itemWithText("test_link_image"))
|
||||
verifyLinkImageContextMenuItems(imageResource.url)
|
||||
clickContextMenuItem("Copy image location")
|
||||
verifySnackBarText("Link copied to clipboard")
|
||||
}.openNavigationToolbar {
|
||||
}.visitLinkFromClipboard {
|
||||
verifyUrl(imageResource.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextSaveImage() {
|
||||
val pageLinks =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val imageResource =
|
||||
TestAssetHelper.getImageAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
mDevice.waitForIdle()
|
||||
longClickPageObject(itemWithText("test_link_image"))
|
||||
verifyLinkImageContextMenuItems(imageResource.url)
|
||||
clickContextMenuItem("Save image")
|
||||
}
|
||||
|
||||
downloadRobot {
|
||||
verifyDownloadNotificationPopup()
|
||||
}.clickOpen("image/jpeg") {} // verify open intent is matched with associated data type
|
||||
downloadRobot {
|
||||
verifyPhotosAppOpens()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextMixedVariations() {
|
||||
val pageLinks =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val genericURL =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val imageResource =
|
||||
TestAssetHelper.getImageAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pageLinks.url) {
|
||||
mDevice.waitForIdle()
|
||||
longClickPageObject(itemWithText("Link 1"))
|
||||
verifyContextMenuForLocalHostLinks(genericURL.url)
|
||||
dismissContentContextMenu()
|
||||
longClickPageObject(itemWithText("test_link_image"))
|
||||
verifyLinkImageContextMenuItems(imageResource.url)
|
||||
dismissContentContextMenu()
|
||||
longClickPageObject(itemWithText("test_no_link_image"))
|
||||
verifyNoLinkImageContextMenuItems(imageResource.url)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextMixedVariationsInPDFTest() {
|
||||
val genericURL =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 3)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericURL.url) {
|
||||
clickPageObject(itemWithText("PDF form file"))
|
||||
waitForPageToLoad()
|
||||
longClickPageObject(itemWithText("Wikipedia link"))
|
||||
verifyContextMenuForLinksToOtherHosts("wikipedia.org".toUri())
|
||||
dismissContentContextMenu()
|
||||
// Some options are missing from the linked and non liked images context menus in PDF files
|
||||
// See https://bugzilla.mozilla.org/show_bug.cgi?id=1012805 for more details
|
||||
longClickPDFImage()
|
||||
verifyContextMenuForLinksToOtherHosts("wikipedia.org".toUri())
|
||||
dismissContentContextMenu()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyContextOpenLinkInAppTest() {
|
||||
val defaultWebPage = TestAssetHelper.getExternalLinksAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
longClickPageObject(itemContainingText("Youtube link"))
|
||||
clickContextMenuItem("Open link in external app")
|
||||
assertYoutubeAppOpens()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,462 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
|
||||
import androidx.test.espresso.Espresso.pressBack
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import mozilla.components.browser.storage.sync.PlacesHistoryStorage
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.ext.settings
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.RecyclerViewIdlingResource
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.helpers.TestHelper.longTapSelectItem
|
||||
import org.mozilla.fenix.helpers.TestHelper.registerAndCleanupIdlingResources
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.historyMenu
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.multipleSelectionToolbar
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of history
|
||||
*
|
||||
*/
|
||||
class ComposeHistoryTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private lateinit var mDevice: UiDevice
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityIntentTestRule.withDefaultSettingsOverrides(
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
InstrumentationRegistry.getInstrumentation().targetContext.settings()
|
||||
.shouldShowJumpBackInCFR = false
|
||||
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
// Clearing all history data after each test to avoid overlapping data
|
||||
val applicationContext: Context = activityTestRule.activity.applicationContext
|
||||
val historyStorage = PlacesHistoryStorage(applicationContext)
|
||||
|
||||
runBlocking {
|
||||
historyStorage.deleteEverything()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun noHistoryItemsInCacheTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
verifyHistoryButton()
|
||||
}.openHistory {
|
||||
verifyHistoryMenuView()
|
||||
verifyEmptyHistoryView()
|
||||
}
|
||||
}
|
||||
|
||||
// Test running on beta/release builds in CI:
|
||||
// caution when making changes to it, so they don't block the builds
|
||||
@Test
|
||||
fun visitedUrlHistoryTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
verifyHistoryMenuView()
|
||||
verifyVisitedTimeTitle()
|
||||
verifyFirstTestPageTitle("Test_Page_1")
|
||||
verifyTestPageUrl(firstWebPage.url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteHistoryItemTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
clickDeleteHistoryButton(firstWebPage.url.toString())
|
||||
}
|
||||
verifyDeleteSnackbarText("Deleted")
|
||||
verifyEmptyHistoryView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun undoDeleteHistoryItemTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
clickDeleteHistoryButton(firstWebPage.url.toString())
|
||||
}
|
||||
verifyUndoDeleteSnackBarButton()
|
||||
clickUndoDeleteButton()
|
||||
verifyHistoryItemExists(true, firstWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun cancelDeleteAllHistoryTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
clickDeleteAllHistoryButton()
|
||||
}
|
||||
verifyDeleteConfirmationMessage()
|
||||
selectEverythingOption()
|
||||
cancelDeleteHistory()
|
||||
verifyHistoryItemExists(true, firstWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteAllHistoryTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
clickDeleteAllHistoryButton()
|
||||
}
|
||||
verifyDeleteConfirmationMessage()
|
||||
selectEverythingOption()
|
||||
confirmDeleteAllHistory()
|
||||
verifyDeleteSnackbarText("Browsing data deleted")
|
||||
verifyEmptyHistoryView()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun historyMultiSelectionToolbarItemsTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
verifyMultiSelectionCheckmark()
|
||||
verifyMultiSelectionCounter()
|
||||
verifyShareHistoryButton()
|
||||
verifyCloseToolbarButton()
|
||||
}.closeToolbarReturnToHistory {
|
||||
verifyHistoryMenuView()
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Failing, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1807268")
|
||||
@Test
|
||||
fun openHistoryInNewTabTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
homeScreen { }.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
}.clickOpenNewTab(activityTestRule) {
|
||||
verifyNormalTabsList()
|
||||
verifyNormalBrowsingButtonIsSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openHistoryInPrivateTabTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
}.clickOpenPrivateTab(activityTestRule) {
|
||||
verifyPrivateTabsList()
|
||||
verifyPrivateBrowsingButtonIsSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteMultipleSelectionTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
verifyUrl(secondWebPage.url.toString())
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 2),
|
||||
) {
|
||||
verifyHistoryItemExists(true, firstWebPage.url.toString())
|
||||
verifyHistoryItemExists(true, secondWebPage.url.toString())
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
longTapSelectItem(secondWebPage.url)
|
||||
openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
clickMultiSelectionDelete()
|
||||
}
|
||||
|
||||
historyMenu {
|
||||
verifyEmptyHistoryView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shareButtonTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
registerAndCleanupIdlingResources(
|
||||
RecyclerViewIdlingResource(activityTestRule.activity.findViewById(R.id.history_list), 1),
|
||||
) {
|
||||
longTapSelectItem(firstWebPage.url)
|
||||
}
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
clickShareHistoryButton()
|
||||
verifyShareOverlay()
|
||||
verifyShareTabFavicon()
|
||||
verifyShareTabTitle()
|
||||
verifyShareTabUrl()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySearchHistoryViewTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.clickSearchButton {
|
||||
verifySearchView()
|
||||
verifySearchToolbar(true)
|
||||
verifySearchSelectorButton()
|
||||
verifySearchEngineIcon("history")
|
||||
verifySearchBarPlaceholder("Search history")
|
||||
verifySearchBarPosition(true)
|
||||
tapOutsideToDismissSearchBar()
|
||||
verifySearchToolbar(false)
|
||||
exitMenu()
|
||||
}
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openCustomizeSubMenu {
|
||||
clickTopToolbarToggle()
|
||||
}
|
||||
|
||||
exitMenu()
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.clickSearchButton {
|
||||
verifySearchView()
|
||||
verifySearchToolbar(true)
|
||||
verifySearchBarPosition(false)
|
||||
pressBack()
|
||||
}
|
||||
historyMenu {
|
||||
verifyHistoryMenuView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyVoiceSearchInHistoryTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.clickSearchButton {
|
||||
verifySearchToolbar(true)
|
||||
verifySearchEngineIcon("history")
|
||||
startVoiceSearch()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySearchForHistoryItemsTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getHTMLControlsFormAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
}.clickSearchButton {
|
||||
// Search for a valid term
|
||||
typeSearch(firstWebPage.title)
|
||||
verifySearchEngineSuggestionResults(activityTestRule, firstWebPage.url.toString())
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, secondWebPage.url.toString())
|
||||
clickClearButton()
|
||||
// Search for invalid term
|
||||
typeSearch("Android")
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, firstWebPage.url.toString())
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, secondWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyDeletedHistoryItemsCanNotBeSearchedTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
val thirdWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 3)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
verifyPageContent(firstWebPage.content)
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
verifyPageContent(secondWebPage.content)
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(thirdWebPage.url) {
|
||||
verifyPageContent(thirdWebPage.content)
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryListExists()
|
||||
clickDeleteHistoryButton(firstWebPage.title)
|
||||
verifyHistoryItemExists(false, firstWebPage.title)
|
||||
clickDeleteHistoryButton(secondWebPage.title)
|
||||
verifyHistoryItemExists(false, secondWebPage.title)
|
||||
}.clickSearchButton {
|
||||
// Search for a valid term
|
||||
typeSearch("generic")
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, firstWebPage.url.toString())
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, secondWebPage.url.toString())
|
||||
verifySearchEngineSuggestionResults(activityTestRule, thirdWebPage.url.toString())
|
||||
pressBack()
|
||||
}
|
||||
historyMenu {
|
||||
clickDeleteHistoryButton(thirdWebPage.title)
|
||||
verifyHistoryItemExists(false, firstWebPage.title)
|
||||
}.clickSearchButton {
|
||||
// Search for a valid term
|
||||
typeSearch("generic")
|
||||
verifyNoSuggestionsAreDisplayed(activityTestRule, thirdWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,272 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.Constants.POCKET_RECOMMENDED_STORIES_UTM_PARAM
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.RetryTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
/**
|
||||
* Tests for verifying the presence of home screen and first-run homescreen elements
|
||||
*
|
||||
* Note: For private browsing, navigation bar and tabs see separate test class
|
||||
*
|
||||
*/
|
||||
|
||||
class ComposeHomeScreenTest {
|
||||
private lateinit var mDevice: UiDevice
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private lateinit var firstPocketStoryPublisher: String
|
||||
|
||||
@get:Rule(order = 0)
|
||||
val activityTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityTestRule.withDefaultSettingsOverrides(
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Rule(order = 1)
|
||||
@JvmField
|
||||
val retryTestRule = RetryTestRule(3)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Ignore("Failing, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1844580")
|
||||
@Test
|
||||
fun homeScreenItemsTest() {
|
||||
homeScreen {}.dismissOnboarding()
|
||||
homeScreen {
|
||||
verifyHomeWordmark()
|
||||
verifyHomePrivateBrowsingButton()
|
||||
verifyExistingTopSitesTabs("Wikipedia")
|
||||
verifyExistingTopSitesTabs("Top Articles")
|
||||
verifyExistingTopSitesTabs("Google")
|
||||
verifyCollectionsHeader()
|
||||
verifyNoCollectionsText()
|
||||
scrollToPocketProvokingStories()
|
||||
verifyThoughtProvokingStories(true)
|
||||
verifyStoriesByTopicItems()
|
||||
verifyCustomizeHomepageButton(true)
|
||||
verifyNavigationToolbar()
|
||||
verifyHomeMenuButton()
|
||||
verifyTabButton()
|
||||
verifyTabCounter("0")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun privateModeScreenItemsTest() {
|
||||
homeScreen { }.dismissOnboarding()
|
||||
homeScreen { }.togglePrivateBrowsingMode()
|
||||
|
||||
homeScreen {
|
||||
verifyPrivateBrowsingHomeScreen()
|
||||
}.openCommonMythsLink {
|
||||
verifyUrl("common-myths-about-private-browsing")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyJumpBackInSectionTest() {
|
||||
activityTestRule.activityRule.applySettingsExceptions {
|
||||
it.isRecentlyVisitedFeatureEnabled = false
|
||||
it.isPocketEnabled = false
|
||||
}
|
||||
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 4)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
verifyPageContent(firstWebPage.content)
|
||||
verifyUrl(firstWebPage.url.toString())
|
||||
}.goToHomescreen {
|
||||
verifyJumpBackInSectionIsDisplayed()
|
||||
verifyJumpBackInItemTitle(activityTestRule, firstWebPage.title)
|
||||
verifyJumpBackInItemWithUrl(activityTestRule, firstWebPage.url.toString())
|
||||
verifyJumpBackInShowAllButton()
|
||||
}.clickJumpBackInShowAllButton(activityTestRule) {
|
||||
verifyExistingOpenTabs(firstWebPage.title)
|
||||
}.closeTabDrawer {
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(secondWebPage.url) {
|
||||
verifyPageContent(secondWebPage.content)
|
||||
verifyUrl(secondWebPage.url.toString())
|
||||
}.goToHomescreen {
|
||||
verifyJumpBackInSectionIsDisplayed()
|
||||
verifyJumpBackInItemTitle(activityTestRule, secondWebPage.title)
|
||||
verifyJumpBackInItemWithUrl(activityTestRule, secondWebPage.url.toString())
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
closeTabWithTitle(secondWebPage.title)
|
||||
}.closeTabDrawer {
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyJumpBackInSectionIsDisplayed()
|
||||
verifyJumpBackInItemTitle(activityTestRule, firstWebPage.title)
|
||||
verifyJumpBackInItemWithUrl(activityTestRule, firstWebPage.url.toString())
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
verifyJumpBackInSectionIsNotDisplayed()
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Failing, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1844580")
|
||||
@Test
|
||||
fun verifyPocketHomepageStoriesTest() {
|
||||
activityTestRule.activityRule.applySettingsExceptions {
|
||||
it.isRecentTabsFeatureEnabled = false
|
||||
it.isRecentlyVisitedFeatureEnabled = false
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.dismissOnboarding()
|
||||
|
||||
homeScreen {
|
||||
verifyThoughtProvokingStories(true)
|
||||
scrollToPocketProvokingStories()
|
||||
verifyPocketRecommendedStoriesItems()
|
||||
// Sponsored Pocket stories are only advertised for a limited time.
|
||||
// See also known issue https://bugzilla.mozilla.org/show_bug.cgi?id=1828629
|
||||
// verifyPocketSponsoredStoriesItems(2, 8)
|
||||
verifyDiscoverMoreStoriesButton()
|
||||
verifyStoriesByTopic(true)
|
||||
verifyPoweredByPocket()
|
||||
}.openThreeDotMenu {
|
||||
}.openCustomizeHome {
|
||||
clickPocketButton()
|
||||
}.goBackToHomeScreen {
|
||||
verifyThoughtProvokingStories(false)
|
||||
verifyStoriesByTopic(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Failing, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1844580")
|
||||
@Test
|
||||
fun openPocketStoryItemTest() {
|
||||
activityTestRule.activityRule.applySettingsExceptions {
|
||||
it.isRecentTabsFeatureEnabled = false
|
||||
it.isRecentlyVisitedFeatureEnabled = false
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.dismissOnboarding()
|
||||
|
||||
homeScreen {
|
||||
verifyThoughtProvokingStories(true)
|
||||
scrollToPocketProvokingStories()
|
||||
firstPocketStoryPublisher = getProvokingStoryPublisher(1)
|
||||
}.clickPocketStoryItem(firstPocketStoryPublisher, 1) {
|
||||
verifyUrl(POCKET_RECOMMENDED_STORIES_UTM_PARAM)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun openPocketDiscoverMoreTest() {
|
||||
activityTestRule.activityRule.applySettingsExceptions {
|
||||
it.isRecentTabsFeatureEnabled = false
|
||||
it.isRecentlyVisitedFeatureEnabled = false
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.dismissOnboarding()
|
||||
|
||||
homeScreen {
|
||||
scrollToPocketProvokingStories()
|
||||
verifyDiscoverMoreStoriesButton()
|
||||
}.clickPocketDiscoverMoreButton {
|
||||
verifyUrl("getpocket.com/explore")
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Failing, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1844580")
|
||||
@Test
|
||||
fun selectStoriesByTopicItemTest() {
|
||||
activityTestRule.activityRule.applySettingsExceptions {
|
||||
it.isRecentTabsFeatureEnabled = false
|
||||
it.isRecentlyVisitedFeatureEnabled = false
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.dismissOnboarding()
|
||||
|
||||
homeScreen {
|
||||
verifyStoriesByTopicItemState(activityTestRule, false, 1)
|
||||
clickStoriesByTopicItem(activityTestRule, 1)
|
||||
verifyStoriesByTopicItemState(activityTestRule, true, 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyPocketLearnMoreLinkTest() {
|
||||
activityTestRule.activityRule.applySettingsExceptions {
|
||||
it.isRecentTabsFeatureEnabled = false
|
||||
it.isRecentlyVisitedFeatureEnabled = false
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.dismissOnboarding()
|
||||
|
||||
homeScreen {
|
||||
verifyPoweredByPocket()
|
||||
}.clickPocketLearnMoreLink(activityTestRule) {
|
||||
verifyUrl("mozilla.org/en-US/firefox/pocket")
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyCustomizeHomepageTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
}.goToHomescreen {
|
||||
}.openCustomizeHomepage {
|
||||
clickJumpBackInButton()
|
||||
clickRecentBookmarksButton()
|
||||
clickRecentSearchesButton()
|
||||
clickPocketButton()
|
||||
}.goBackToHomeScreen {
|
||||
verifyCustomizeHomepageButton(false)
|
||||
}.openThreeDotMenu {
|
||||
}.openCustomizeHome {
|
||||
clickJumpBackInButton()
|
||||
}.goBackToHomeScreen {
|
||||
verifyCustomizeHomepageButton(true)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,173 @@
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.engine.mediasession.MediaSession
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.MatcherHelper
|
||||
import org.mozilla.fenix.helpers.RetryTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.verifySnackBarText
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.clickPageObject
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import org.mozilla.fenix.ui.robots.notificationShade
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of media notifications:
|
||||
* - video and audio playback system notifications appear and can pause/play the media content
|
||||
* - a media notification icon is displayed on the homescreen for the tab playing media content
|
||||
* Note: this test only verifies media notifications, not media itself
|
||||
*/
|
||||
class ComposeMediaNotificationTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private lateinit var mDevice: UiDevice
|
||||
private lateinit var browserStore: BrowserStore
|
||||
|
||||
@get:Rule(order = 0)
|
||||
val composeTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityTestRule.withDefaultSettingsOverrides(
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Rule(order = 1)
|
||||
@JvmField
|
||||
val retryTestRule = RetryTestRule(3)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// Initializing this as part of class construction, below the rule would throw a NPE
|
||||
// So we are initializing this here instead of in all tests.
|
||||
browserStore = composeTestRule.activity.components.core.store
|
||||
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun videoPlaybackSystemNotificationTest() {
|
||||
val videoTestPage = TestAssetHelper.getVideoPageAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(videoTestPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
clickPageObject(MatcherHelper.itemWithText("Play"))
|
||||
assertPlaybackState(browserStore, MediaSession.PlaybackState.PLAYING)
|
||||
}.openNotificationShade {
|
||||
verifySystemNotificationExists(videoTestPage.title)
|
||||
clickMediaNotificationControlButton("Pause")
|
||||
verifyMediaSystemNotificationButtonState("Play")
|
||||
}
|
||||
|
||||
mDevice.pressBack()
|
||||
|
||||
browserScreen {
|
||||
assertPlaybackState(browserStore, MediaSession.PlaybackState.PAUSED)
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
mDevice.openNotification()
|
||||
|
||||
notificationShade {
|
||||
verifySystemNotificationDoesNotExist(videoTestPage.title)
|
||||
}
|
||||
|
||||
// close notification shade before the next test
|
||||
mDevice.pressBack()
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun audioPlaybackSystemNotificationTest() {
|
||||
val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(audioTestPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
clickPageObject(MatcherHelper.itemWithText("Play"))
|
||||
assertPlaybackState(browserStore, MediaSession.PlaybackState.PLAYING)
|
||||
}.openNotificationShade {
|
||||
verifySystemNotificationExists(audioTestPage.title)
|
||||
clickMediaNotificationControlButton("Pause")
|
||||
verifyMediaSystemNotificationButtonState("Play")
|
||||
}
|
||||
|
||||
mDevice.pressBack()
|
||||
|
||||
browserScreen {
|
||||
assertPlaybackState(browserStore, MediaSession.PlaybackState.PAUSED)
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
closeTab()
|
||||
}
|
||||
|
||||
mDevice.openNotification()
|
||||
|
||||
notificationShade {
|
||||
verifySystemNotificationDoesNotExist(audioTestPage.title)
|
||||
}
|
||||
|
||||
// close notification shade before the next test
|
||||
mDevice.pressBack()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mediaSystemNotificationInPrivateModeTest() {
|
||||
val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer)
|
||||
|
||||
homeScreen {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.toggleToPrivateTabs {
|
||||
}.openNewTab {
|
||||
}.submitQuery(audioTestPage.url.toString()) {
|
||||
mDevice.waitForIdle()
|
||||
clickPageObject(MatcherHelper.itemWithText("Play"))
|
||||
assertPlaybackState(browserStore, MediaSession.PlaybackState.PLAYING)
|
||||
}.openNotificationShade {
|
||||
verifySystemNotificationExists("A site is playing media")
|
||||
clickMediaNotificationControlButton("Pause")
|
||||
verifyMediaSystemNotificationButtonState("Play")
|
||||
}
|
||||
|
||||
mDevice.pressBack()
|
||||
|
||||
browserScreen {
|
||||
assertPlaybackState(browserStore, MediaSession.PlaybackState.PAUSED)
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
closeTab()
|
||||
verifySnackBarText("Private tab closed")
|
||||
}
|
||||
|
||||
mDevice.openNotification()
|
||||
|
||||
notificationShade {
|
||||
verifySystemNotificationDoesNotExist("A site is playing media")
|
||||
}
|
||||
|
||||
// close notification shade before and go back to regular mode before the next test
|
||||
mDevice.pressBack()
|
||||
homeScreen { }.togglePrivateBrowsingMode()
|
||||
}
|
||||
}
|
@ -0,0 +1,267 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.itemWithText
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.runWithSystemLocaleChanged
|
||||
import org.mozilla.fenix.ui.robots.clickPageObject
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* Tests for verifying basic functionality of browser navigation and page related interactions
|
||||
*
|
||||
* Including:
|
||||
* - Visiting a URL
|
||||
* - Back and Forward navigation
|
||||
* - Refresh
|
||||
* - Find in page
|
||||
*/
|
||||
|
||||
class ComposeNavigationToolbarTest {
|
||||
private lateinit var mDevice: UiDevice
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityTestRule.withDefaultSettingsOverrides(
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun goBackTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val nextWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(nextWebPage.url) {
|
||||
verifyUrl(nextWebPage.url.toString())
|
||||
}.openThreeDotMenu {
|
||||
}.goToPreviousPage {
|
||||
mDevice.waitForIdle()
|
||||
verifyUrl(defaultWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun goForwardTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val nextWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(nextWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
verifyUrl(nextWebPage.url.toString())
|
||||
}.openThreeDotMenu {
|
||||
}.goToPreviousPage {
|
||||
mDevice.waitForIdle()
|
||||
verifyUrl(defaultWebPage.url.toString())
|
||||
}
|
||||
|
||||
// Re-open the three-dot menu for verification
|
||||
navigationToolbar {
|
||||
}.openThreeDotMenu {
|
||||
verifyThreeDotMenuExists()
|
||||
}.goForward {
|
||||
verifyUrl(nextWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// Swipes the nav bar left/right to switch between tabs
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun swipeToSwitchTabTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.openNewTab {
|
||||
}.submitQuery(secondWebPage.url.toString()) {
|
||||
swipeNavBarRight(secondWebPage.url.toString())
|
||||
verifyUrl(firstWebPage.url.toString())
|
||||
swipeNavBarLeft(firstWebPage.url.toString())
|
||||
verifyUrl(secondWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
// Because it requires changing system prefs, this test will run only on Debug builds
|
||||
@Test
|
||||
fun swipeToSwitchTabInRTLTest() {
|
||||
val firstWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
val arabicLocale = Locale("ar", "AR")
|
||||
|
||||
runWithSystemLocaleChanged(arabicLocale, composeTestRule.activityRule) {
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebPage.url) {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.openNewTab {
|
||||
}.submitQuery(secondWebPage.url.toString()) {
|
||||
swipeNavBarLeft(secondWebPage.url.toString())
|
||||
verifyUrl(firstWebPage.url.toString())
|
||||
swipeNavBarRight(firstWebPage.url.toString())
|
||||
verifyUrl(secondWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Test running on beta/release builds in CI:
|
||||
// caution when making changes to it, so they don't block the builds
|
||||
@Test
|
||||
fun visitURLTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
verifyUrl(defaultWebPage.url.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun findInPageTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 3)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
verifyThreeDotMenuExists()
|
||||
verifyFindInPageButton()
|
||||
}.openFindInPage {
|
||||
verifyFindInPageNextButton()
|
||||
verifyFindInPagePrevButton()
|
||||
verifyFindInPageCloseButton()
|
||||
enterFindInPageQuery("a")
|
||||
verifyFindNextInPageResult("1/3")
|
||||
clickFindInPageNextButton()
|
||||
verifyFindNextInPageResult("2/3")
|
||||
clickFindInPageNextButton()
|
||||
verifyFindNextInPageResult("3/3")
|
||||
clickFindInPagePrevButton()
|
||||
verifyFindPrevInPageResult("2/3")
|
||||
clickFindInPagePrevButton()
|
||||
verifyFindPrevInPageResult("1/3")
|
||||
}.closeFindInPageWithCloseButton {
|
||||
verifyFindInPageBar(false)
|
||||
}.openThreeDotMenu {
|
||||
}.openFindInPage {
|
||||
enterFindInPageQuery("3")
|
||||
verifyFindNextInPageResult("1/1")
|
||||
}.closeFindInPageWithBackButton {
|
||||
verifyFindInPageBar(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun pdfFindInPageTest() {
|
||||
val genericURL =
|
||||
TestAssetHelper.getGenericAsset(mockWebServer, 3)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericURL.url) {
|
||||
clickPageObject(itemWithText("PDF form file"))
|
||||
}.openThreeDotMenu {
|
||||
verifyThreeDotMenuExists()
|
||||
verifyFindInPageButton()
|
||||
}.openFindInPage {
|
||||
verifyFindInPageNextButton()
|
||||
verifyFindInPagePrevButton()
|
||||
verifyFindInPageCloseButton()
|
||||
enterFindInPageQuery("l")
|
||||
verifyFindNextInPageResult("1/2")
|
||||
clickFindInPageNextButton()
|
||||
verifyFindNextInPageResult("2/2")
|
||||
clickFindInPagePrevButton()
|
||||
verifyFindPrevInPageResult("1/2")
|
||||
}.closeFindInPageWithCloseButton {
|
||||
verifyFindInPageBar(false)
|
||||
}.openThreeDotMenu {
|
||||
}.openFindInPage {
|
||||
enterFindInPageQuery("p")
|
||||
verifyFindNextInPageResult("1/1")
|
||||
}.closeFindInPageWithBackButton {
|
||||
verifyFindInPageBar(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifySecurePageSecuritySubMenuTest() {
|
||||
val defaultWebPage = "https://mozilla-mobile.github.io/testapp/loginForm"
|
||||
val defaultWebPageTitle = "Login_form"
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.toUri()) {
|
||||
}.openSiteSecuritySheet {
|
||||
verifyQuickActionSheet(defaultWebPage, true)
|
||||
openSecureConnectionSubMenu(true)
|
||||
verifySecureConnectionSubMenu(defaultWebPageTitle, defaultWebPage, true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyInsecurePageSecuritySubMenuTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
waitForPageToLoad()
|
||||
}.openSiteSecuritySheet {
|
||||
verifyQuickActionSheet(defaultWebPage.url.toString(), false)
|
||||
openSecureConnectionSubMenu(false)
|
||||
verifySecureConnectionSubMenu(defaultWebPage.title, defaultWebPage.url.toString(), false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun verifyClearCookiesFromQuickSettingsTest() {
|
||||
val helpPageUrl = "mozilla.org"
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openHelp {
|
||||
}.openSiteSecuritySheet {
|
||||
clickQuickActionSheetClearSiteData()
|
||||
verifyClearSiteDataPrompt(helpPageUrl)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,620 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.hardware.camera2.CameraManager
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.espresso.Espresso
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Assume
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.Constants
|
||||
import org.mozilla.fenix.helpers.HomeActivityTestRule
|
||||
import org.mozilla.fenix.helpers.MatcherHelper
|
||||
import org.mozilla.fenix.helpers.MockBrowserDataHelper.setCustomSearchEngine
|
||||
import org.mozilla.fenix.helpers.SearchDispatcher
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.ui.robots.clickContextMenuItem
|
||||
import org.mozilla.fenix.ui.robots.clickPageObject
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.longClickPageObject
|
||||
import org.mozilla.fenix.ui.robots.multipleSelectionToolbar
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import org.mozilla.fenix.ui.robots.searchScreen
|
||||
|
||||
/**
|
||||
* Tests for verifying the search fragment
|
||||
*
|
||||
* Including:
|
||||
* - Verify the toolbar, awesomebar, and shortcut bar are displayed
|
||||
* - Select shortcut button
|
||||
* - Select scan button
|
||||
*
|
||||
*/
|
||||
|
||||
class ComposeSearchTest {
|
||||
lateinit var searchMockServer: MockWebServer
|
||||
private val queryString: String = "firefox"
|
||||
private val generalEnginesList = listOf("DuckDuckGo", "Google", "Bing")
|
||||
private val topicEnginesList = listOf("Amazon.com", "Wikipedia", "eBay")
|
||||
|
||||
@get:Rule
|
||||
val activityTestRule = AndroidComposeTestRule(
|
||||
HomeActivityTestRule(
|
||||
skipOnboarding = true,
|
||||
isPocketEnabled = false,
|
||||
isJumpBackInCFREnabled = false,
|
||||
isRecentTabsFeatureEnabled = false,
|
||||
isTCPCFREnabled = false,
|
||||
isWallpaperOnboardingEnabled = false,
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
searchMockServer = MockWebServer().apply {
|
||||
dispatcher = SearchDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
searchMockServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchBarItemsTest() {
|
||||
navigationToolbar {
|
||||
verifyDefaultSearchEngine("Google")
|
||||
verifySearchBarPlaceholder("Search or enter address")
|
||||
}.clickUrlbar {
|
||||
TestHelper.verifyKeyboardVisibility(isExpectedToBeVisible = true)
|
||||
verifyScanButtonVisibility(visible = true)
|
||||
verifyVoiceSearchButtonVisibility(enabled = true)
|
||||
verifySearchBarPlaceholder("Search or enter address")
|
||||
typeSearch("mozilla ")
|
||||
verifyScanButtonVisibility(visible = false)
|
||||
verifyVoiceSearchButtonVisibility(enabled = true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchSelectorMenuItemsTest() {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
verifySearchView()
|
||||
verifySearchToolbar(isDisplayed = true)
|
||||
clickSearchSelectorButton()
|
||||
verifySearchShortcutListContains(
|
||||
"DuckDuckGo", "Google", "Amazon.com", "Wikipedia", "Bing", "eBay",
|
||||
"Bookmarks", "Tabs", "History", "Search settings",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchPlaceholderForDefaultEnginesTest() {
|
||||
generalEnginesList.forEach {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickSearchSelectorButton()
|
||||
}.clickSearchEngineSettings {
|
||||
openDefaultSearchEngineMenu()
|
||||
changeDefaultSearchEngine(it)
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
verifySearchBarPlaceholder("Search or enter address")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchPlaceholderForOtherGeneralSearchEnginesTest() {
|
||||
val generalEnginesList = listOf("DuckDuckGo", "Bing")
|
||||
|
||||
generalEnginesList.forEach {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickSearchSelectorButton()
|
||||
selectTemporarySearchMethod(it)
|
||||
verifySearchBarPlaceholder("Search the web")
|
||||
}.dismissSearchBar {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun searchPlaceholderForTopicSearchEngineTest() {
|
||||
val topicEnginesList = listOf("Amazon.com", "Wikipedia", "eBay")
|
||||
|
||||
topicEnginesList.forEach {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickSearchSelectorButton()
|
||||
selectTemporarySearchMethod(it)
|
||||
verifySearchBarPlaceholder("Enter search terms")
|
||||
}.dismissSearchBar {}
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun scanButtonDenyPermissionTest() {
|
||||
val cameraManager = TestHelper.appContext.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
||||
Assume.assumeTrue(cameraManager.cameraIdList.isNotEmpty())
|
||||
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickScanButton()
|
||||
TestHelper.denyPermission()
|
||||
clickScanButton()
|
||||
clickDismissPermissionRequiredDialog()
|
||||
}
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickScanButton()
|
||||
clickGoToPermissionsSettings()
|
||||
TestHelper.assertNativeAppOpens(Constants.PackageName.ANDROID_SETTINGS)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun scanButtonAllowPermissionTest() {
|
||||
val cameraManager = TestHelper.appContext.getSystemService(Context.CAMERA_SERVICE) as CameraManager
|
||||
Assume.assumeTrue(cameraManager.cameraIdList.isNotEmpty())
|
||||
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickScanButton()
|
||||
TestHelper.grantSystemPermission()
|
||||
verifyScannerOpen()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun scanButtonAvailableOnlyForGeneralSearchEnginesTest() {
|
||||
generalEnginesList.forEach {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickSearchSelectorButton()
|
||||
selectTemporarySearchMethod(it)
|
||||
verifyScanButtonVisibility(visible = true)
|
||||
}.dismissSearchBar {}
|
||||
}
|
||||
|
||||
topicEnginesList.forEach {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickSearchSelectorButton()
|
||||
selectTemporarySearchMethod(it)
|
||||
verifyScanButtonVisibility(visible = false)
|
||||
}.dismissSearchBar {}
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies a temporary change of search engine from the Search shortcut menu
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun selectSearchEnginesShortcutTest() {
|
||||
val enginesList = listOf("DuckDuckGo", "Google", "Amazon.com", "Wikipedia", "Bing", "eBay")
|
||||
|
||||
enginesList.forEach {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
clickSearchSelectorButton()
|
||||
verifySearchShortcutListContains(it)
|
||||
selectTemporarySearchMethod(it)
|
||||
verifySearchEngineIcon(it)
|
||||
}.submitQuery("mozilla ") {
|
||||
verifyUrl("mozilla")
|
||||
}.goToHomescreen {}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun accessSearchSettingFromSearchSelectorMenuTest() {
|
||||
searchScreen {
|
||||
clickSearchSelectorButton()
|
||||
}.clickSearchEngineSettings {
|
||||
verifyToolbarText("Search")
|
||||
openDefaultSearchEngineMenu()
|
||||
changeDefaultSearchEngine("DuckDuckGo")
|
||||
TestHelper.exitMenu()
|
||||
}
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
verifyUrl(queryString)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun clearSearchTest() {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
typeSearch(queryString)
|
||||
clickClearButton()
|
||||
verifySearchBarPlaceholder("Search or enter address")
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test run timing out: https://github.com/mozilla-mobile/fenix/issues/27704")
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun searchGroupShowsInRecentlyVisitedTest() {
|
||||
val searchEngineName = "TestSearchEngine"
|
||||
// setting our custom mockWebServer search URL
|
||||
setCustomSearchEngine(searchMockServer, searchEngineName)
|
||||
|
||||
// Performs a search and opens 2 dummy search results links to create a search group
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
Espresso.pressBack()
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 2"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = true, searchTerm = queryString, groupSize = 3)
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test run timing out: https://github.com/mozilla-mobile/fenix/issues/27704")
|
||||
@Test
|
||||
fun verifySearchGroupHistoryWithNoDuplicatesTest() {
|
||||
val firstPageUrl = TestAssetHelper.getGenericAsset(searchMockServer, 1).url
|
||||
val secondPageUrl = TestAssetHelper.getGenericAsset(searchMockServer, 2).url
|
||||
val originPageUrl =
|
||||
"http://localhost:${searchMockServer.port}/pages/searchResults.html?search=test%20search".toUri()
|
||||
val searchEngineName = "TestSearchEngine"
|
||||
// setting our custom mockWebServer search URL
|
||||
setCustomSearchEngine(searchMockServer, searchEngineName)
|
||||
|
||||
// Performs a search and opens 2 dummy search results links to create a search group
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
Espresso.pressBack()
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
Espresso.pressBack()
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 2"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
Espresso.pressBack()
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = true, searchTerm = queryString, groupSize = 3)
|
||||
}.openRecentlyVisitedSearchGroupHistoryList(queryString) {
|
||||
verifyTestPageUrl(firstPageUrl)
|
||||
verifyTestPageUrl(secondPageUrl)
|
||||
verifyTestPageUrl(originPageUrl)
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Failing due to known bug, see https://github.com/mozilla-mobile/fenix/issues/23818")
|
||||
@Test
|
||||
fun searchGroupGeneratedInTheSameTabTest() {
|
||||
// setting our custom mockWebServer search URL
|
||||
val searchEngineName = "TestSearchEngine"
|
||||
setCustomSearchEngine(searchMockServer, searchEngineName)
|
||||
|
||||
// Performs a search and opens 2 dummy search results links to create a search group
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
clickPageObject(MatcherHelper.itemContainingText("Link 1"))
|
||||
waitForPageToLoad()
|
||||
Espresso.pressBack()
|
||||
clickPageObject(MatcherHelper.itemContainingText("Link 2"))
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = true, searchTerm = queryString, groupSize = 3)
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun noSearchGroupFromPrivateBrowsingTest() {
|
||||
// setting our custom mockWebServer search URL
|
||||
val searchEngineName = "TestSearchEngine"
|
||||
setCustomSearchEngine(searchMockServer, searchEngineName)
|
||||
|
||||
// Performs a search and opens 2 dummy search results links to create a search group
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in private tab")
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 2"))
|
||||
clickContextMenuItem("Open link in private tab")
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.toggleToPrivateTabs {
|
||||
}.openPrivateTab(0) {
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openPrivateTab(1) {
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
togglePrivateBrowsingModeOnOff()
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = false, searchTerm = queryString, groupSize = 3)
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyHistoryItemExists(shouldExist = false, item = "3 sites")
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test run timing out: https://github.com/mozilla-mobile/fenix/issues/27704")
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteItemsFromSearchGroupHistoryTest() {
|
||||
val firstPageUrl = TestAssetHelper.getGenericAsset(searchMockServer, 1).url
|
||||
val secondPageUrl = TestAssetHelper.getGenericAsset(searchMockServer, 2).url
|
||||
// setting our custom mockWebServer search URL
|
||||
val searchEngineName = "TestSearchEngine"
|
||||
setCustomSearchEngine(searchMockServer, searchEngineName)
|
||||
|
||||
// Performs a search and opens 2 dummy search results links to create a search group
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
TestHelper.mDevice.pressBack()
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 2"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = true, searchTerm = queryString, groupSize = 3)
|
||||
}.openRecentlyVisitedSearchGroupHistoryList(queryString) {
|
||||
clickDeleteHistoryButton(firstPageUrl.toString())
|
||||
TestHelper.longTapSelectItem(secondPageUrl)
|
||||
multipleSelectionToolbar {
|
||||
Espresso.openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
clickMultiSelectionDelete()
|
||||
}
|
||||
TestHelper.exitMenu()
|
||||
}
|
||||
homeScreen {
|
||||
// checking that the group is removed when only 1 item is left
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = false, searchTerm = queryString, groupSize = 1)
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test run timing out: https://github.com/mozilla-mobile/fenix/issues/27704")
|
||||
@Test
|
||||
fun deleteSearchGroupFromHistoryTest() {
|
||||
val firstPageUrl = TestAssetHelper.getGenericAsset(searchMockServer, 1).url
|
||||
// setting our custom mockWebServer search URL
|
||||
val searchEngineName = "TestSearchEngine"
|
||||
setCustomSearchEngine(searchMockServer, searchEngineName)
|
||||
|
||||
// Performs a search and opens 2 dummy search results links to create a search group
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
TestHelper.mDevice.pressBack()
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 2"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = true, searchTerm = queryString, groupSize = 3)
|
||||
}.openRecentlyVisitedSearchGroupHistoryList(queryString) {
|
||||
clickDeleteAllHistoryButton()
|
||||
confirmDeleteAllHistory()
|
||||
verifyDeleteSnackbarText("Group deleted")
|
||||
verifyHistoryItemExists(shouldExist = false, firstPageUrl.toString())
|
||||
}.goBack {}
|
||||
homeScreen {
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = false, queryString, groupSize = 3)
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifySearchGroupDisplayed(shouldBeDisplayed = false, queryString, groupSize = 3)
|
||||
verifyEmptyHistoryView()
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test run timing out: https://github.com/mozilla-mobile/fenix/issues/27704")
|
||||
@Test
|
||||
fun reopenTabsFromSearchGroupTest() {
|
||||
val firstPageUrl = TestAssetHelper.getGenericAsset(searchMockServer, 1).url
|
||||
val secondPageUrl = TestAssetHelper.getGenericAsset(searchMockServer, 2).url
|
||||
|
||||
// setting our custom mockWebServer search URL
|
||||
val searchEngineName = "TestSearchEngine"
|
||||
setCustomSearchEngine(searchMockServer, searchEngineName)
|
||||
|
||||
// Performs a search and opens 2 dummy search results links to create a search group
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
TestHelper.mDevice.pressBack()
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 2"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = true, searchTerm = queryString, groupSize = 3)
|
||||
}.openRecentlyVisitedSearchGroupHistoryList(queryString) {
|
||||
}.openWebsite(firstPageUrl) {
|
||||
verifyUrl(firstPageUrl.toString())
|
||||
}.goToHomescreen {
|
||||
}.openRecentlyVisitedSearchGroupHistoryList(queryString) {
|
||||
TestHelper.longTapSelectItem(firstPageUrl)
|
||||
TestHelper.longTapSelectItem(secondPageUrl)
|
||||
Espresso.openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
}.clickOpenNewTab(activityTestRule) {
|
||||
verifyNormalBrowsingButtonIsSelected()
|
||||
}.closeTabDrawer {}
|
||||
Espresso.openActionBarOverflowOrOptionsMenu(activityTestRule.activity)
|
||||
multipleSelectionToolbar {
|
||||
}.clickOpenPrivateTab {
|
||||
verifyPrivateModeSelected()
|
||||
}
|
||||
}
|
||||
|
||||
@Ignore("Test run timing out: https://github.com/mozilla-mobile/fenix/issues/27704")
|
||||
@Test
|
||||
fun sharePageFromASearchGroupTest() {
|
||||
val firstPageUrl = TestAssetHelper.getGenericAsset(searchMockServer, 1).url
|
||||
// setting our custom mockWebServer search URL
|
||||
val searchEngineName = "TestSearchEngine"
|
||||
setCustomSearchEngine(searchMockServer, searchEngineName)
|
||||
|
||||
// Performs a search and opens 2 dummy search results links to create a search group
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 1"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
TestHelper.mDevice.pressBack()
|
||||
longClickPageObject(MatcherHelper.itemWithText("Link 2"))
|
||||
clickContextMenuItem("Open link in new tab")
|
||||
TestHelper.clickSnackbarButton("SWITCH")
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openThreeDotMenu {
|
||||
}.closeAllTabs {
|
||||
verifyRecentlyVisitedSearchGroupDisplayed(shouldBeDisplayed = true, searchTerm = queryString, groupSize = 3)
|
||||
}.openRecentlyVisitedSearchGroupHistoryList(queryString) {
|
||||
TestHelper.longTapSelectItem(firstPageUrl)
|
||||
}
|
||||
|
||||
multipleSelectionToolbar {
|
||||
clickShareHistoryButton()
|
||||
verifyShareOverlay()
|
||||
verifyShareTabFavicon()
|
||||
verifyShareTabTitle()
|
||||
verifyShareTabUrl()
|
||||
}
|
||||
}
|
||||
|
||||
// Default search code for Google-US
|
||||
@Test
|
||||
fun defaultSearchCodeGoogleUS() {
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
waitForPageToLoad()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
// Full URL no longer visible in the nav bar, so we'll check the history record
|
||||
// A search group is sometimes created when searching with Google (probably redirects)
|
||||
try {
|
||||
verifyHistoryItemExists(shouldExist = true, Constants.searchEngineCodes["Google"]!!)
|
||||
} catch (e: AssertionError) {
|
||||
openSearchGroup(queryString)
|
||||
verifyHistoryItemExists(shouldExist = true, Constants.searchEngineCodes["Google"]!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Default search code for Bing-US
|
||||
@Test
|
||||
fun defaultSearchCodeBingUS() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSearchSubMenu {
|
||||
openDefaultSearchEngineMenu()
|
||||
changeDefaultSearchEngine("Bing")
|
||||
TestHelper.exitMenu()
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
waitForPageToLoad()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
// Full URL no longer visible in the nav bar, so we'll check the history record
|
||||
verifyHistoryItemExists(shouldExist = true, Constants.searchEngineCodes["Bing"]!!)
|
||||
}
|
||||
}
|
||||
|
||||
// Default search code for DuckDuckGo-US
|
||||
@Test
|
||||
fun defaultSearchCodeDuckDuckGoUS() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSearchSubMenu {
|
||||
openDefaultSearchEngineMenu()
|
||||
changeDefaultSearchEngine("DuckDuckGo")
|
||||
TestHelper.exitMenu()
|
||||
}
|
||||
homeScreen {
|
||||
}.openSearch {
|
||||
}.submitQuery(queryString) {
|
||||
waitForPageToLoad()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
// Full URL no longer visible in the nav bar, so we'll check the history record
|
||||
// A search group is sometimes created when searching with DuckDuckGo
|
||||
try {
|
||||
verifyHistoryItemExists(shouldExist = true, item = Constants.searchEngineCodes["DuckDuckGo"]!!)
|
||||
} catch (e: AssertionError) {
|
||||
openSearchGroup(queryString)
|
||||
verifyHistoryItemExists(shouldExist = true, item = Constants.searchEngineCodes["DuckDuckGo"]!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,260 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import android.Manifest
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.espresso.Espresso.pressBack
|
||||
import androidx.test.rule.GrantPermissionRule
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.MatcherHelper
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.getStorageTestAsset
|
||||
import org.mozilla.fenix.helpers.TestHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.deleteDownloadedFileOnStorage
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.helpers.TestHelper.mDevice
|
||||
import org.mozilla.fenix.helpers.TestHelper.restartApp
|
||||
import org.mozilla.fenix.helpers.TestHelper.setNetworkEnabled
|
||||
import org.mozilla.fenix.ui.robots.clickPageObject
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
/**
|
||||
* Tests for verifying the Settings for:
|
||||
* Delete Browsing Data on quit
|
||||
*
|
||||
*/
|
||||
class ComposeSettingsDeleteBrowsingDataOnQuitTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
@get:Rule(order = 0)
|
||||
val composeTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityIntentTestRule.withDefaultSettingsOverrides(
|
||||
skipOnboarding = true,
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
// Automatically allows app permissions, avoiding a system dialog showing up.
|
||||
@get:Rule(order = 1)
|
||||
val grantPermissionRule: GrantPermissionRule = GrantPermissionRule.grant(
|
||||
Manifest.permission.RECORD_AUDIO,
|
||||
)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteBrowsingDataOnQuitSettingsItemsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingDataOnQuit {
|
||||
verifyNavigationToolBarHeader()
|
||||
verifyDeleteBrowsingOnQuitEnabled(false)
|
||||
verifyDeleteBrowsingOnQuitButtonSummary()
|
||||
verifyDeleteBrowsingOnQuitEnabled(false)
|
||||
clickDeleteBrowsingOnQuitButtonSwitch()
|
||||
verifyDeleteBrowsingOnQuitEnabled(true)
|
||||
verifyAllTheCheckBoxesText()
|
||||
verifyAllTheCheckBoxesChecked(true)
|
||||
}.goBack {
|
||||
verifySettingsOptionSummary("Delete browsing data on quit", "On")
|
||||
}.goBack {
|
||||
}.openThreeDotMenu {
|
||||
verifyQuitButtonExists()
|
||||
pressBack()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser("test".toUri()) {
|
||||
}.openThreeDotMenu {
|
||||
verifyQuitButtonExists()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteOpenTabsOnQuitTest() {
|
||||
val testPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingDataOnQuit {
|
||||
clickDeleteBrowsingOnQuitButtonSwitch()
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(testPage.url) {
|
||||
}.goToHomescreen {
|
||||
}.openThreeDotMenu {
|
||||
clickQuit()
|
||||
restartApp(composeTestRule.activityRule)
|
||||
}
|
||||
homeScreen {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
verifyNoOpenTabsInNormalBrowsing()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteHistoryAndSiteStorageOnQuitTest() {
|
||||
val storageWritePage =
|
||||
getStorageTestAsset(mockWebServer, "storage_write.html")
|
||||
val storageCheckPage =
|
||||
getStorageTestAsset(mockWebServer, "storage_check.html")
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingDataOnQuit {
|
||||
clickDeleteBrowsingOnQuitButtonSwitch()
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageWritePage.url) {
|
||||
clickPageObject(MatcherHelper.itemWithText("Set cookies"))
|
||||
verifyPageContent("Values written to storage")
|
||||
}.goToHomescreen {
|
||||
}.openThreeDotMenu {
|
||||
clickQuit()
|
||||
restartApp(composeTestRule.activityRule)
|
||||
}
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyEmptyHistoryView()
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageCheckPage.url) {
|
||||
verifyPageContent("Session storage empty")
|
||||
verifyPageContent("Local storage empty")
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageWritePage.url) {
|
||||
verifyPageContent("No cookies set")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteDownloadsOnQuitTest() {
|
||||
val downloadTestPage = "https://storage.googleapis.com/mobile_test_assets/test_app/downloads.html"
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingDataOnQuit {
|
||||
clickDeleteBrowsingOnQuitButtonSwitch()
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(downloadTestPage.toUri()) {
|
||||
}.clickDownloadLink("smallZip.zip") {
|
||||
verifyDownloadPrompt("smallZip.zip")
|
||||
}.clickDownload {
|
||||
verifyDownloadNotificationPopup()
|
||||
}.closeCompletedDownloadPrompt {
|
||||
}.goToHomescreen {
|
||||
}.openThreeDotMenu {
|
||||
clickQuit()
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
restartApp(composeTestRule.activityRule)
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openDownloadsManager {
|
||||
verifyEmptyDownloadsList()
|
||||
}
|
||||
deleteDownloadedFileOnStorage("smallZip.zip")
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteSitePermissionsOnQuitTest() {
|
||||
val testPage = "https://mozilla-mobile.github.io/testapp/permissions"
|
||||
val testPageSubstring = "https://mozilla-mobile.github.io:443"
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingDataOnQuit {
|
||||
clickDeleteBrowsingOnQuitButtonSwitch()
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(testPage.toUri()) {
|
||||
waitForPageToLoad()
|
||||
}.clickStartMicrophoneButton {
|
||||
verifyMicrophonePermissionPrompt(testPageSubstring)
|
||||
selectRememberPermissionDecision()
|
||||
}.clickPagePermissionButton(false) {
|
||||
verifyPageContent("Microphone not allowed")
|
||||
}.goToHomescreen {
|
||||
}.openThreeDotMenu {
|
||||
clickQuit()
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
restartApp(composeTestRule.activityRule)
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(testPage.toUri()) {
|
||||
waitForPageToLoad()
|
||||
}.clickStartMicrophoneButton {
|
||||
verifyMicrophonePermissionPrompt(testPageSubstring)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteCachedFilesOnQuitTest() {
|
||||
val pocketTopArticles = TestHelper.getStringResource(R.string.pocket_pinned_top_articles)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingDataOnQuit {
|
||||
clickDeleteBrowsingOnQuitButtonSwitch()
|
||||
exitMenu()
|
||||
}
|
||||
homeScreen {
|
||||
verifyExistingTopSitesTabs(pocketTopArticles)
|
||||
}.openTopSiteTabWithTitle(pocketTopArticles) {
|
||||
waitForPageToLoad()
|
||||
}.goToHomescreen {
|
||||
}.openThreeDotMenu {
|
||||
clickQuit()
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
// disabling wifi to prevent downloads in the background
|
||||
setNetworkEnabled(enabled = false)
|
||||
restartApp(composeTestRule.activityRule)
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser("about:cache".toUri()) {
|
||||
verifyNetworkCacheIsEmpty("memory")
|
||||
verifyNetworkCacheIsEmpty("disk")
|
||||
}
|
||||
setNetworkEnabled(enabled = true)
|
||||
}
|
||||
}
|
@ -0,0 +1,269 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.itemWithResId
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper.getStorageTestAsset
|
||||
import org.mozilla.fenix.helpers.TestHelper.exitMenu
|
||||
import org.mozilla.fenix.helpers.TestHelper.getStringResource
|
||||
import org.mozilla.fenix.helpers.TestHelper.mDevice
|
||||
import org.mozilla.fenix.helpers.TestHelper.restartApp
|
||||
import org.mozilla.fenix.helpers.TestHelper.setNetworkEnabled
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.clickPageObject
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
import org.mozilla.fenix.ui.robots.settingsScreen
|
||||
|
||||
/**
|
||||
* Tests for verifying the Settings for:
|
||||
* Delete Browsing Data
|
||||
*/
|
||||
|
||||
class ComposeSettingsDeleteBrowsingDataTest {
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
|
||||
@get:Rule
|
||||
val composeTestRule =
|
||||
AndroidComposeTestRule(
|
||||
HomeActivityIntentTestRule.withDefaultSettingsOverrides(
|
||||
skipOnboarding = true,
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteBrowsingDataOptionStatesTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
switchBrowsingHistoryCheckBox()
|
||||
switchCachedFilesCheckBox()
|
||||
verifyOpenTabsCheckBox(true)
|
||||
verifyBrowsingHistoryDetails(false)
|
||||
verifyCookiesCheckBox(true)
|
||||
verifyCachedFilesCheckBox(false)
|
||||
verifySitePermissionsCheckBox(true)
|
||||
verifyDownloadsCheckBox(true)
|
||||
}
|
||||
|
||||
restartApp(composeTestRule.activityRule)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsCheckBox(true)
|
||||
verifyBrowsingHistoryDetails(false)
|
||||
verifyCookiesCheckBox(true)
|
||||
verifyCachedFilesCheckBox(false)
|
||||
verifySitePermissionsCheckBox(true)
|
||||
verifyDownloadsCheckBox(true)
|
||||
switchOpenTabsCheckBox()
|
||||
switchBrowsingHistoryCheckBox()
|
||||
switchCookiesCheckBox()
|
||||
switchCachedFilesCheckBox()
|
||||
switchSitePermissionsCheckBox()
|
||||
switchDownloadsCheckBox()
|
||||
verifyOpenTabsCheckBox(false)
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
verifyCookiesCheckBox(false)
|
||||
verifyCachedFilesCheckBox(true)
|
||||
verifySitePermissionsCheckBox(false)
|
||||
verifyDownloadsCheckBox(false)
|
||||
}
|
||||
|
||||
restartApp(composeTestRule.activityRule)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsCheckBox(false)
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
verifyCookiesCheckBox(false)
|
||||
verifyCachedFilesCheckBox(true)
|
||||
verifySitePermissionsCheckBox(false)
|
||||
verifyDownloadsCheckBox(false)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun deleteTabsDataWithNoOpenTabsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
selectOnlyOpenTabsCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
verifyDeleteBrowsingDataDialog()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
}
|
||||
settingsScreen {
|
||||
verifyGeneralHeading()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteTabsDataTest() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyAllCheckBoxesAreChecked()
|
||||
selectOnlyOpenTabsCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
verifyDeleteBrowsingDataDialog()
|
||||
clickDialogCancelButton()
|
||||
verifyOpenTabsCheckBox(true)
|
||||
clickDeleteBrowsingDataButton()
|
||||
verifyDeleteBrowsingDataDialog()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
}
|
||||
settingsScreen {
|
||||
verifyGeneralHeading()
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyOpenTabsDetails("0")
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
verifyNoOpenTabsInNormalBrowsing()
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteBrowsingHistoryAndSiteDataTest() {
|
||||
val storageWritePage = getStorageTestAsset(mockWebServer, "storage_write.html").url
|
||||
val storageCheckPage = getStorageTestAsset(mockWebServer, "storage_check.html").url
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageWritePage) {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageCheckPage) {
|
||||
verifyPageContent("Session storage has value")
|
||||
verifyPageContent("Local storage has value")
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
verifyBrowsingHistoryDetails("2")
|
||||
selectOnlyBrowsingHistoryCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
verifyDeleteBrowsingDataDialog()
|
||||
clickDialogCancelButton()
|
||||
verifyBrowsingHistoryDetails(true)
|
||||
clickDeleteBrowsingDataButton()
|
||||
verifyDeleteBrowsingDataDialog()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
verifyBrowsingHistoryDetails("0")
|
||||
exitMenu()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyEmptyHistoryView()
|
||||
mDevice.pressBack()
|
||||
}
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(storageCheckPage) {
|
||||
verifyPageContent("Session storage empty")
|
||||
verifyPageContent("Local storage empty")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteCookiesTest() {
|
||||
val genericPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val cookiesTestPage = getStorageTestAsset(mockWebServer, "storage_write.html").url
|
||||
|
||||
// Browsing a generic page to allow GV to load on a fresh run
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericPage.url) {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(cookiesTestPage) {
|
||||
verifyPageContent("No cookies set")
|
||||
clickPageObject(itemWithResId("setCookies"))
|
||||
verifyPageContent("user=android")
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
selectOnlyCookiesCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
verifyDeleteBrowsingDataDialog()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
exitMenu()
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.refreshPage {
|
||||
verifyPageContent("No cookies set")
|
||||
}
|
||||
}
|
||||
|
||||
@SmokeTest
|
||||
@Test
|
||||
fun deleteCachedFilesTest() {
|
||||
val pocketTopArticles = getStringResource(R.string.pocket_pinned_top_articles)
|
||||
|
||||
homeScreen {
|
||||
verifyExistingTopSitesTabs(pocketTopArticles)
|
||||
}.openTopSiteTabWithTitle(pocketTopArticles) {
|
||||
waitForPageToLoad()
|
||||
}.openComposeTabDrawer(composeTestRule) {
|
||||
}.openNewTab {
|
||||
}.submitQuery("about:cache") {
|
||||
// disabling wifi to prevent downloads in the background
|
||||
setNetworkEnabled(enabled = false)
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openSettingsSubMenuDeleteBrowsingData {
|
||||
selectOnlyCachedFilesCheckBox()
|
||||
clickDeleteBrowsingDataButton()
|
||||
verifyDeleteBrowsingDataDialog()
|
||||
confirmDeletionAndAssertSnackbar()
|
||||
exitMenu()
|
||||
}
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.refreshPage {
|
||||
verifyNetworkCacheIsEmpty("memory")
|
||||
verifyNetworkCacheIsEmpty("disk")
|
||||
}
|
||||
setNetworkEnabled(enabled = true)
|
||||
}
|
||||
}
|
@ -0,0 +1,440 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
@file:Suppress("DEPRECATION")
|
||||
|
||||
package org.mozilla.fenix.ui
|
||||
|
||||
import android.view.View
|
||||
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
|
||||
import androidx.core.net.toUri
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.rule.ActivityTestRule
|
||||
import androidx.test.uiautomator.UiDevice
|
||||
import mozilla.components.browser.state.store.BrowserStore
|
||||
import mozilla.components.concept.engine.mediasession.MediaSession
|
||||
import okhttp3.mockwebserver.MockWebServer
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.mozilla.fenix.IntentReceiverActivity
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.customannotations.SmokeTest
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.helpers.AndroidAssetDispatcher
|
||||
import org.mozilla.fenix.helpers.HomeActivityIntentTestRule
|
||||
import org.mozilla.fenix.helpers.MatcherHelper.itemWithText
|
||||
import org.mozilla.fenix.helpers.RetryTestRule
|
||||
import org.mozilla.fenix.helpers.TestAssetHelper
|
||||
import org.mozilla.fenix.helpers.TestHelper.assertYoutubeAppOpens
|
||||
import org.mozilla.fenix.helpers.TestHelper.registerAndCleanupIdlingResources
|
||||
import org.mozilla.fenix.helpers.ViewVisibilityIdlingResource
|
||||
import org.mozilla.fenix.ui.robots.browserScreen
|
||||
import org.mozilla.fenix.ui.robots.clickPageObject
|
||||
import org.mozilla.fenix.ui.robots.homeScreen
|
||||
import org.mozilla.fenix.ui.robots.navigationToolbar
|
||||
|
||||
/**
|
||||
* Test Suite that contains a part of the Smoke and Sanity tests defined in TestRail:
|
||||
* https://testrail.stage.mozaws.net/index.php?/suites/view/3192
|
||||
* Other smoke tests have been marked with the @SmokeTest annotation throughout the ui package in order to limit this class expansion.
|
||||
* These tests will verify different functionalities of the app as a way to quickly detect regressions in main areas
|
||||
*/
|
||||
@Suppress("ForbiddenComment")
|
||||
@SmokeTest
|
||||
class ComposeSmokeTest {
|
||||
private lateinit var mDevice: UiDevice
|
||||
private lateinit var mockWebServer: MockWebServer
|
||||
private val customMenuItem = "TestMenuItem"
|
||||
private lateinit var browserStore: BrowserStore
|
||||
|
||||
@get:Rule(order = 0)
|
||||
val activityTestRule = AndroidComposeTestRule(
|
||||
HomeActivityIntentTestRule.withDefaultSettingsOverrides(
|
||||
tabsTrayRewriteEnabled = true,
|
||||
),
|
||||
) { it.activity }
|
||||
|
||||
@get: Rule(order = 1)
|
||||
val intentReceiverActivityTestRule = ActivityTestRule(
|
||||
IntentReceiverActivity::class.java,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
|
||||
@Rule(order = 2)
|
||||
@JvmField
|
||||
val retryTestRule = RetryTestRule(3)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
// Initializing this as part of class construction, below the rule would throw a NPE
|
||||
// So we are initializing this here instead of in all related tests.
|
||||
browserStore = activityTestRule.activity.components.core.store
|
||||
|
||||
mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
|
||||
mockWebServer = MockWebServer().apply {
|
||||
dispatcher = AndroidAssetDispatcher()
|
||||
start()
|
||||
}
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
mockWebServer.shutdown()
|
||||
}
|
||||
|
||||
/* Verifies the nav bar:
|
||||
- opening a web page
|
||||
- the existence of nav bar items
|
||||
- editing the url bar
|
||||
- the tab drawer button
|
||||
- opening a new search and dismissing the nav bar
|
||||
*/
|
||||
@Test
|
||||
fun verifyBasicNavigationToolbarFunctionality() {
|
||||
val defaultWebPage = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(defaultWebPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
verifyNavURLBarItems()
|
||||
}.openNavigationToolbar {
|
||||
}.goBackToWebsite {
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
verifyNormalTabsList()
|
||||
}.openNewTab {
|
||||
}.dismissSearchBar {
|
||||
verifyHomeScreen()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Device or AVD requires a Google Services Android OS installation with Play Store installed
|
||||
// Verifies the Open in app button when an app is installed
|
||||
@Ignore("Failing, see: https://bugzilla.mozilla.org/show_bug.cgi?id=1849278")
|
||||
@Test
|
||||
fun mainMenuOpenInAppTest() {
|
||||
val youtubeURL = "https://m.youtube.com/user/mozilla?cbrd=1"
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(youtubeURL.toUri()) {
|
||||
verifyNotificationDotOnMainMenu()
|
||||
}.openThreeDotMenu {
|
||||
}.clickOpenInApp {
|
||||
assertYoutubeAppOpens()
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that deleting a Bookmarks folder also removes the item from inside it.
|
||||
@Test
|
||||
fun deleteNonEmptyBookmarkFolderTest() {
|
||||
val website = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
browserScreen {
|
||||
createBookmark(website.url)
|
||||
}.openThreeDotMenu {
|
||||
}.openBookmarks {
|
||||
verifyBookmarkTitle("Test_Page_1")
|
||||
createFolder("My Folder")
|
||||
verifyFolderTitle("My Folder")
|
||||
}.openThreeDotMenu("Test_Page_1") {
|
||||
}.clickEdit {
|
||||
clickParentFolderSelector()
|
||||
selectFolder("My Folder")
|
||||
navigateUp()
|
||||
saveEditBookmark()
|
||||
createFolder("My Folder 2")
|
||||
verifyFolderTitle("My Folder 2")
|
||||
}.openThreeDotMenu("My Folder 2") {
|
||||
}.clickEdit {
|
||||
clickParentFolderSelector()
|
||||
selectFolder("My Folder")
|
||||
navigateUp()
|
||||
saveEditBookmark()
|
||||
}.openThreeDotMenu("My Folder") {
|
||||
}.clickDelete {
|
||||
cancelFolderDeletion()
|
||||
verifyFolderTitle("My Folder")
|
||||
}.openThreeDotMenu("My Folder") {
|
||||
}.clickDelete {
|
||||
confirmDeletion()
|
||||
verifyDeleteSnackBarText()
|
||||
verifyBookmarkIsDeleted("My Folder")
|
||||
verifyBookmarkIsDeleted("My Folder 2")
|
||||
verifyBookmarkIsDeleted("Test_Page_1")
|
||||
navigateUp()
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
}.openThreeDotMenu {
|
||||
verifyAddBookmarkButton()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun shareTabsFromTabsTrayTest() {
|
||||
val firstWebsite = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
val secondWebsite = TestAssetHelper.getGenericAsset(mockWebServer, 2)
|
||||
val firstWebsiteTitle = firstWebsite.title
|
||||
val secondWebsiteTitle = secondWebsite.title
|
||||
val sharingApp = "Gmail"
|
||||
val sharedUrlsString = "${firstWebsite.url}\n\n${secondWebsite.url}"
|
||||
|
||||
homeScreen {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(firstWebsite.url) {
|
||||
verifyPageContent(firstWebsite.content)
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.openNewTab {
|
||||
}.submitQuery(secondWebsite.url.toString()) {
|
||||
verifyPageContent(secondWebsite.content)
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
verifyExistingOpenTabs("Test_Page_1")
|
||||
verifyExistingOpenTabs("Test_Page_2")
|
||||
}.openThreeDotMenu {
|
||||
verifyShareAllTabsButton()
|
||||
}.clickShareAllTabsButton {
|
||||
verifyShareTabsOverlay(firstWebsiteTitle, secondWebsiteTitle)
|
||||
verifySharingWithSelectedApp(
|
||||
sharingApp,
|
||||
sharedUrlsString,
|
||||
"$firstWebsiteTitle, $secondWebsiteTitle",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun emptyTabsTrayViewPrivateBrowsingTest() {
|
||||
homeScreen {
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
}.toggleToPrivateTabs {
|
||||
verifyNormalBrowsingButtonIsSelected(false)
|
||||
verifyPrivateBrowsingButtonIsSelected(true)
|
||||
verifySyncedTabsButtonIsSelected(false)
|
||||
verifyNoOpenTabsInPrivateBrowsing()
|
||||
verifyFab()
|
||||
verifyThreeDotButton()
|
||||
}.openThreeDotMenu {
|
||||
verifyTabSettingsButton()
|
||||
verifyRecentlyClosedTabsButton()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun privateTabsTrayWithOpenedTabTest() {
|
||||
val website = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
}.togglePrivateBrowsingMode()
|
||||
|
||||
homeScreen {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(website.url) {
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
verifyNormalBrowsingButtonIsSelected(false)
|
||||
verifyPrivateBrowsingButtonIsSelected(true)
|
||||
verifySyncedTabsButtonIsSelected(false)
|
||||
verifyThreeDotButton()
|
||||
verifyNormalTabCounter()
|
||||
verifyPrivateTabsList()
|
||||
verifyExistingOpenTabs(website.title)
|
||||
verifyTabCloseButton()
|
||||
verifyTabThumbnail()
|
||||
verifyFab()
|
||||
}
|
||||
}
|
||||
|
||||
// Test running on beta/release builds in CI:
|
||||
// caution when making changes to it, so they don't block the builds
|
||||
@Test
|
||||
fun noHistoryInPrivateBrowsingTest() {
|
||||
val website = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
}.togglePrivateBrowsingMode()
|
||||
|
||||
homeScreen {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(website.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.openThreeDotMenu {
|
||||
}.openHistory {
|
||||
verifyEmptyHistoryView()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun mainMenuInstallPWATest() {
|
||||
val pwaPage = "https://mozilla-mobile.github.io/testapp/"
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(pwaPage.toUri()) {
|
||||
verifyNotificationDotOnMainMenu()
|
||||
}.openThreeDotMenu {
|
||||
}.clickInstall {
|
||||
clickAddAutomaticallyButton()
|
||||
}.openHomeScreenShortcut("TEST_APP") {
|
||||
mDevice.waitForIdle()
|
||||
verifyNavURLBarHidden()
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies that reader mode is detected and the custom appearance controls are displayed
|
||||
@Test
|
||||
fun verifyReaderViewAppearanceUI() {
|
||||
val readerViewPage =
|
||||
TestAssetHelper.getLoremIpsumAsset(mockWebServer)
|
||||
val estimatedReadingTime = "1 - 2 minutes"
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(readerViewPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
}
|
||||
|
||||
registerAndCleanupIdlingResources(
|
||||
ViewVisibilityIdlingResource(
|
||||
activityTestRule.activity.findViewById(R.id.mozac_browser_toolbar_page_actions),
|
||||
View.VISIBLE,
|
||||
),
|
||||
) {}
|
||||
|
||||
navigationToolbar {
|
||||
verifyReaderViewDetected(true)
|
||||
toggleReaderView()
|
||||
}
|
||||
|
||||
browserScreen {
|
||||
waitForPageToLoad()
|
||||
verifyPageContent(estimatedReadingTime)
|
||||
}.openThreeDotMenu {
|
||||
verifyReaderViewAppearance(true)
|
||||
}.openReaderViewAppearance {
|
||||
verifyAppearanceFontGroup(true)
|
||||
verifyAppearanceFontSansSerif(true)
|
||||
verifyAppearanceFontSerif(true)
|
||||
verifyAppearanceFontIncrease(true)
|
||||
verifyAppearanceFontDecrease(true)
|
||||
verifyAppearanceColorGroup(true)
|
||||
verifyAppearanceColorDark(true)
|
||||
verifyAppearanceColorLight(true)
|
||||
verifyAppearanceColorSepia(true)
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tabMediaControlButtonTest() {
|
||||
val audioTestPage = TestAssetHelper.getAudioPageAsset(mockWebServer)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(audioTestPage.url) {
|
||||
mDevice.waitForIdle()
|
||||
clickPageObject(itemWithText("Play"))
|
||||
assertPlaybackState(browserStore, MediaSession.PlaybackState.PLAYING)
|
||||
}.openComposeTabDrawer(activityTestRule) {
|
||||
verifyTabMediaControlButtonState("Pause")
|
||||
clickTabMediaControlButton("Pause")
|
||||
verifyTabMediaControlButtonState("Play")
|
||||
}.openTab(audioTestPage.title) {
|
||||
assertPlaybackState(browserStore, MediaSession.PlaybackState.PAUSED)
|
||||
}
|
||||
}
|
||||
|
||||
// For API>23
|
||||
// Verifies the default browser switch opens the system default apps menu.
|
||||
@Test
|
||||
fun changeDefaultBrowserSetting() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
verifyDefaultBrowserToggle(false)
|
||||
clickDefaultBrowserSwitch()
|
||||
verifyAndroidDefaultAppsMenuAppears()
|
||||
}
|
||||
// Dismiss the request
|
||||
mDevice.pressBack()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun goToHomeScreenBottomToolbarTest() {
|
||||
val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericURL.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.goToHomescreen {
|
||||
verifyHomeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun goToHomeScreenTopToolbarTest() {
|
||||
val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openCustomizeSubMenu {
|
||||
clickTopToolbarToggle()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericURL.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.goToHomescreen {
|
||||
verifyHomeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun goToHomeScreenBottomToolbarPrivateModeTest() {
|
||||
val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
togglePrivateBrowsingModeOnOff()
|
||||
}
|
||||
|
||||
navigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericURL.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.goToHomescreen {
|
||||
verifyHomeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun goToHomeScreenTopToolbarPrivateModeTest() {
|
||||
val genericURL = TestAssetHelper.getGenericAsset(mockWebServer, 1)
|
||||
|
||||
homeScreen {
|
||||
togglePrivateBrowsingModeOnOff()
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openCustomizeSubMenu {
|
||||
clickTopToolbarToggle()
|
||||
}.goBack {
|
||||
}.goBack {
|
||||
}.openNavigationToolbar {
|
||||
}.enterURLAndEnterToBrowser(genericURL.url) {
|
||||
mDevice.waitForIdle()
|
||||
}.goToHomescreen {
|
||||
verifyHomeScreen()
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun tabsSettingsMenuItemsTest() {
|
||||
homeScreen {
|
||||
}.openThreeDotMenu {
|
||||
}.openSettings {
|
||||
}.openTabsSubMenu {
|
||||
verifyTabViewOptions()
|
||||
verifyCloseTabsOptions()
|
||||
verifyMoveOldTabsToInactiveOptions()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,89 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.browser
|
||||
|
||||
import android.app.Activity
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.map
|
||||
import mozilla.components.lib.state.helpers.AbstractBinding
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.components.AppStore
|
||||
import org.mozilla.fenix.components.FenixSnackbar
|
||||
import org.mozilla.fenix.components.appstate.AppAction
|
||||
import org.mozilla.fenix.components.appstate.AppState
|
||||
import org.mozilla.fenix.ext.components
|
||||
import org.mozilla.fenix.ext.getRootView
|
||||
|
||||
/**
|
||||
* A binding that shows standard snackbar error.
|
||||
*/
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
class StandardSnackbarErrorBinding(
|
||||
private val activity: Activity,
|
||||
appStore: AppStore,
|
||||
) : AbstractBinding<AppState>(appStore) {
|
||||
|
||||
override suspend fun onState(flow: Flow<AppState>) {
|
||||
flow.map { state -> state.standardSnackbarError }
|
||||
.distinctUntilChanged()
|
||||
.collect {
|
||||
it?.let { standardSnackbarError ->
|
||||
activity.getRootView()?.let { view ->
|
||||
val snackBar = FenixSnackbar.make(
|
||||
view = view,
|
||||
duration = Snackbar.LENGTH_INDEFINITE,
|
||||
isDisplayedWithBrowserToolbar = true,
|
||||
)
|
||||
snackBar.setText(
|
||||
standardSnackbarError.message,
|
||||
)
|
||||
snackBar.setButtonTextColor(
|
||||
ContextCompat.getColor(
|
||||
activity,
|
||||
R.color.fx_mobile_text_color_primary,
|
||||
),
|
||||
)
|
||||
snackBar.setBackground(
|
||||
AppCompatResources.getDrawable(
|
||||
activity,
|
||||
R.drawable.standard_snackbar_error_background,
|
||||
),
|
||||
)
|
||||
snackBar.setSnackBarTextColor(
|
||||
ContextCompat.getColor(
|
||||
activity,
|
||||
R.color.fx_mobile_text_color_warning,
|
||||
),
|
||||
)
|
||||
snackBar.setAction(
|
||||
text = activity.getString(R.string.standard_snackbar_error_dismiss),
|
||||
action = {
|
||||
view.context.components.appStore.dispatch(
|
||||
AppAction.UpdateStandardSnackbarErrorAction(
|
||||
null,
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
snackBar.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Standard Snackbar Error data class.
|
||||
*
|
||||
* @param message that will appear on the snackbar.
|
||||
*/
|
||||
data class StandardSnackbarError(
|
||||
val message: String,
|
||||
)
|
@ -0,0 +1,181 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.compose
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.SwitchDefaults
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.compositeOver
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.mozilla.fenix.compose.annotation.LightDarkPreview
|
||||
import org.mozilla.fenix.theme.FirefoxTheme
|
||||
import androidx.compose.material.Switch as MaterialSwitch
|
||||
|
||||
private const val DISABLED_ALPHA = 0.5f
|
||||
|
||||
/**
|
||||
* UI for a switch with label that can be on or off.
|
||||
*
|
||||
* @param label Text to be displayed next to the switch.
|
||||
* @param checked Whether or not the switch is checked.
|
||||
* @param onCheckedChange Invoked when Switch is being clicked, therefore the change of checked
|
||||
* state is requested.
|
||||
* @param modifier Modifier to be applied to the switch layout.
|
||||
* @param enabled Whether the switch is enabled or grayed out.
|
||||
*/
|
||||
@Composable
|
||||
fun SwitchWithLabel(
|
||||
label: String,
|
||||
checked: Boolean,
|
||||
onCheckedChange: ((Boolean) -> Unit),
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
text = label,
|
||||
color = if (enabled) {
|
||||
FirefoxTheme.colors.textPrimary
|
||||
} else {
|
||||
FirefoxTheme.colors.textDisabled
|
||||
},
|
||||
style = FirefoxTheme.typography.subtitle1,
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
|
||||
Switch(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
enabled = enabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* UI for a switch that can be on or off.
|
||||
*
|
||||
* @param checked Whether or not the switch is checked.
|
||||
* @param onCheckedChange Invoked when Switch is being clicked, therefore the change of checked
|
||||
* state is requested.
|
||||
* @param modifier Modifier to be applied to the switch layout.
|
||||
* @param enabled Whether the switch is enabled or grayed out.
|
||||
*/
|
||||
@Composable
|
||||
private fun Switch(
|
||||
checked: Boolean,
|
||||
onCheckedChange: ((Boolean) -> Unit),
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
) {
|
||||
MaterialSwitch(
|
||||
checked = checked,
|
||||
onCheckedChange = onCheckedChange,
|
||||
modifier = modifier,
|
||||
enabled = enabled,
|
||||
colors = SwitchDefaults.colors(
|
||||
uncheckedThumbColor = FirefoxTheme.colors.formOff,
|
||||
uncheckedTrackColor = FirefoxTheme.colors.formSurface,
|
||||
checkedThumbColor = FirefoxTheme.colors.formOn,
|
||||
checkedTrackColor = FirefoxTheme.colors.formSurface,
|
||||
disabledUncheckedThumbColor = FirefoxTheme.colors.formOff
|
||||
.copy(alpha = DISABLED_ALPHA)
|
||||
.compositeOver(FirefoxTheme.colors.formSurface),
|
||||
disabledUncheckedTrackColor = FirefoxTheme.colors.formSurface.copy(alpha = DISABLED_ALPHA),
|
||||
disabledCheckedThumbColor = FirefoxTheme.colors.formOn
|
||||
.copy(alpha = DISABLED_ALPHA)
|
||||
.compositeOver(FirefoxTheme.colors.formSurface),
|
||||
disabledCheckedTrackColor = FirefoxTheme.colors.formSurface.copy(alpha = DISABLED_ALPHA),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@LightDarkPreview
|
||||
@Composable
|
||||
private fun SwitchWithLabelPreview() {
|
||||
FirefoxTheme {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.background(FirefoxTheme.colors.layer1)
|
||||
.padding(16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp),
|
||||
) {
|
||||
Text(
|
||||
text = "Enabled",
|
||||
style = FirefoxTheme.typography.headline7,
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
var enabledSwitchState by remember { mutableStateOf(false) }
|
||||
SwitchWithLabel(
|
||||
label = if (enabledSwitchState) "On" else "Off",
|
||||
checked = enabledSwitchState,
|
||||
onCheckedChange = { enabledSwitchState = it },
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Disabled",
|
||||
style = FirefoxTheme.typography.headline7,
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
var disabledSwitchStateOff by remember { mutableStateOf(false) }
|
||||
SwitchWithLabel(
|
||||
label = "Off",
|
||||
checked = disabledSwitchStateOff,
|
||||
enabled = false,
|
||||
onCheckedChange = { disabledSwitchStateOff = it },
|
||||
)
|
||||
|
||||
var disabledSwitchStateOn by remember { mutableStateOf(true) }
|
||||
SwitchWithLabel(
|
||||
label = "On",
|
||||
checked = disabledSwitchStateOn,
|
||||
enabled = false,
|
||||
onCheckedChange = { disabledSwitchStateOn = it },
|
||||
)
|
||||
|
||||
Text(
|
||||
text = "Nested",
|
||||
style = FirefoxTheme.typography.headline7,
|
||||
color = FirefoxTheme.colors.textPrimary,
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
Row {
|
||||
Spacer(Modifier.weight(1f))
|
||||
|
||||
var nestedSwitchState by remember { mutableStateOf(false) }
|
||||
SwitchWithLabel(
|
||||
label = "Nested",
|
||||
checked = nestedSwitchState,
|
||||
onCheckedChange = { nestedSwitchState = it },
|
||||
modifier = Modifier.weight(1f),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,66 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.library.history.state
|
||||
|
||||
import androidx.navigation.NavController
|
||||
import androidx.navigation.NavOptions
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import mozilla.components.lib.state.Middleware
|
||||
import mozilla.components.lib.state.MiddlewareContext
|
||||
import org.mozilla.fenix.R
|
||||
import org.mozilla.fenix.library.history.History
|
||||
import org.mozilla.fenix.library.history.HistoryFragmentAction
|
||||
import org.mozilla.fenix.library.history.HistoryFragmentDirections
|
||||
import org.mozilla.fenix.library.history.HistoryFragmentState
|
||||
|
||||
/**
|
||||
* A [Middleware] for initiating navigation events based on [HistoryFragmentAction]s that are
|
||||
* dispatched to the [HistoryFragmentStore].
|
||||
*
|
||||
* @property navController [NavController] for handling navigation events
|
||||
* @property openToBrowser Callback to open history items in a browser window.
|
||||
*/
|
||||
class HistoryNavigationMiddleware(
|
||||
private val navController: NavController,
|
||||
private val openToBrowser: (item: History.Regular) -> Unit,
|
||||
private val scope: CoroutineScope = CoroutineScope(Dispatchers.Main),
|
||||
) : Middleware<HistoryFragmentState, HistoryFragmentAction> {
|
||||
override fun invoke(
|
||||
context: MiddlewareContext<HistoryFragmentState, HistoryFragmentAction>,
|
||||
next: (HistoryFragmentAction) -> Unit,
|
||||
action: HistoryFragmentAction,
|
||||
) {
|
||||
// Read the current state before letting the chain process the action, so that clicks are
|
||||
// treated correctly in reference to the number of selected items.
|
||||
val currentState = context.state
|
||||
next(action)
|
||||
scope.launch {
|
||||
when (action) {
|
||||
is HistoryFragmentAction.HistoryItemClicked -> {
|
||||
if (currentState.mode.selectedItems.isEmpty()) {
|
||||
when (val item = action.item) {
|
||||
is History.Regular -> openToBrowser(item)
|
||||
is History.Group -> {
|
||||
navController.navigate(
|
||||
HistoryFragmentDirections.actionGlobalHistoryMetadataGroup(
|
||||
title = item.title,
|
||||
historyMetadataItems = item.items.toTypedArray(),
|
||||
),
|
||||
NavOptions.Builder()
|
||||
.setPopUpTo(R.id.historyMetadataGroupFragment, true)
|
||||
.build(),
|
||||
)
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,52 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.fenix.library.history.state
|
||||
|
||||
import mozilla.components.lib.state.Middleware
|
||||
import mozilla.components.lib.state.MiddlewareContext
|
||||
import mozilla.components.service.glean.private.NoExtras
|
||||
import org.mozilla.fenix.library.history.History
|
||||
import org.mozilla.fenix.library.history.HistoryFragmentAction
|
||||
import org.mozilla.fenix.library.history.HistoryFragmentState
|
||||
import org.mozilla.fenix.GleanMetrics.History as GleanHistory
|
||||
|
||||
/**
|
||||
* A [Middleware] for recording telemetry based on [HistoryFragmentAction]s that are dispatched to
|
||||
* the [HistoryFragmentStore].
|
||||
*
|
||||
* @property isInPrivateMode Whether the app is currently in private browsing mode.
|
||||
*/
|
||||
class HistoryTelemetryMiddleware(
|
||||
private val isInPrivateMode: Boolean,
|
||||
) : Middleware<HistoryFragmentState, HistoryFragmentAction> {
|
||||
override fun invoke(
|
||||
context: MiddlewareContext<HistoryFragmentState, HistoryFragmentAction>,
|
||||
next: (HistoryFragmentAction) -> Unit,
|
||||
action: HistoryFragmentAction,
|
||||
) {
|
||||
val currentState = context.state
|
||||
next(action)
|
||||
when (action) {
|
||||
is HistoryFragmentAction.HistoryItemClicked -> {
|
||||
if (currentState.mode.selectedItems.isEmpty()) {
|
||||
when (val item = action.item) {
|
||||
is History.Regular -> {
|
||||
GleanHistory.openedItem.record(
|
||||
GleanHistory.OpenedItemExtra(
|
||||
isRemote = item.isRemote,
|
||||
timeGroup = item.historyTimeGroup.toString(),
|
||||
isPrivate = isInPrivateMode,
|
||||
),
|
||||
)
|
||||
}
|
||||
is History.Group -> GleanHistory.searchTermGroupTapped.record(NoExtras())
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> Unit
|
||||
}
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue