1
0
Fork 0

Copione merged onto master
continuous-integration/drone/push Build is passing Details

master
blallo 2020-07-09 00:00:47 +02:00
commit acf33e754f
43 changed files with 1388 additions and 150 deletions

4
.gitignore vendored
View File

@ -100,3 +100,7 @@ test_artifacts/
/build/test-tools/google-cloud-sdk/ /build/test-tools/google-cloud-sdk/
/build/test-tools/*.jar /build/test-tools/*.jar
/build/test-tools/*.gz /build/test-tools/*.gz
# Web extensions: manifest.json files are generated
manifest.json

View File

@ -186,6 +186,10 @@ android {
// GeckoView must uncompress it before it can do anything else which // GeckoView must uncompress it before it can do anything else which
// causes a significant delay on startup. // causes a significant delay on startup.
noCompress 'ja' noCompress 'ja'
// manifest.template.json is converted to manifest.json at build time.
// No need to package the template in the APK.
ignoreAssetsPattern "manifest.template.json"
} }
testOptions { testOptions {
@ -755,3 +759,34 @@ if (gradle.hasProperty('localProperties.autoPublish.application-services.dir'))
ext.appServicesSrcDir = gradle."localProperties.autoPublish.application-services.dir" ext.appServicesSrcDir = gradle."localProperties.autoPublish.application-services.dir"
apply from: "../${appServicesSrcDir}/build-scripts/substitute-local-appservices.gradle" apply from: "../${appServicesSrcDir}/build-scripts/substitute-local-appservices.gradle"
} }
// Define a reusable task for updating the versions of our built-in web extensions. We automate this
// to make sure we never forget to update the version, either in local development or for releases.
// In both cases, we want to make sure the latest version of all extensions (including their latest
// changes) are installed on first start-up.
// We're using the A-C version here as we want to uplift all built-in extensions to A-C (Once that's
// done we can also remove the task below):
// https://github.com/mozilla-mobile/android-components/issues/7249
ext.updateExtensionVersion = { task, extDir ->
configure(task) {
from extDir
include 'manifest.template.json'
rename { 'manifest.json' }
into extDir
def values = ['version': AndroidComponents.VERSION + "." + new Date().format('MMddHHmmss')]
inputs.properties(values)
expand(values)
}
}
tasks.register("updateAdsExtensionVersion", Copy) { task ->
updateExtensionVersion(task, 'src/main/assets/extensions/ads')
}
tasks.register("updateCookiesExtensionVersion", Copy) { task ->
updateExtensionVersion(task, 'src/main/assets/extensions/cookies')
}
preBuild.dependsOn updateAdsExtensionVersion
preBuild.dependsOn updateCookiesExtensionVersion

View File

@ -4,12 +4,14 @@
package org.mozilla.fenix.screenshots package org.mozilla.fenix.screenshots
import android.os.SystemClock
import androidx.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import org.junit.After import org.junit.After
import org.junit.Rule import org.junit.Rule
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.HomeActivity import org.mozilla.fenix.HomeActivity
import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.ui.robots.homeScreen import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.mDevice import org.mozilla.fenix.ui.robots.mDevice
import tools.fastlane.screengrab.Screengrab import tools.fastlane.screengrab.Screengrab
@ -30,15 +32,17 @@ class DefaultHomeScreenTest : ScreenshotTest() {
fun showDefaultHomeScreen() { fun showDefaultHomeScreen() {
homeScreen { homeScreen {
verifyAccountsSignInButton() verifyAccountsSignInButton()
Screengrab.screenshot("HomeScreenRobot_home-screen")
swipeToBottom() swipeToBottom()
Screengrab.screenshot("HomeScreenRobot_home-screen-scroll") Screengrab.screenshot("HomeScreenRobot_home-screen-scroll")
TestAssetHelper.waitingTime
} }
} }
@Test @Test
fun privateBrowsingTest() { fun privateBrowsingTest() {
homeScreen { homeScreen {
SystemClock.sleep(TestAssetHelper.waitingTimeShort)
Screengrab.screenshot("HomeScreenRobot_home-screen")
}.openThreeDotMenu { }.openThreeDotMenu {
}.openSettings { } }.openSettings { }
// To get private screenshot, // To get private screenshot,
@ -48,6 +52,7 @@ class DefaultHomeScreenTest : ScreenshotTest() {
togglePrivateBrowsingModeOnOff() togglePrivateBrowsingModeOnOff()
Screengrab.screenshot("HomeScreenRobot_private-browsing-menu") Screengrab.screenshot("HomeScreenRobot_private-browsing-menu")
togglePrivateBrowsingModeOnOff() togglePrivateBrowsingModeOnOff()
Screengrab.screenshot("HomeScreenRobot_after-onboarding")
} }
} }
} }

View File

@ -4,10 +4,13 @@
package org.mozilla.fenix.screenshots package org.mozilla.fenix.screenshots
import android.os.SystemClock
import androidx.test.espresso.Espresso.onView import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withId
import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.espresso.matcher.ViewMatchers.withText
import androidx.test.rule.ActivityTestRule import androidx.test.rule.ActivityTestRule
import androidx.test.uiautomator.By
import androidx.test.uiautomator.Until
import tools.fastlane.screengrab.Screengrab import tools.fastlane.screengrab.Screengrab
import tools.fastlane.screengrab.locale.LocaleTestRule import tools.fastlane.screengrab.locale.LocaleTestRule
import okhttp3.mockwebserver.MockWebServer import okhttp3.mockwebserver.MockWebServer
@ -21,6 +24,7 @@ import org.mozilla.fenix.helpers.AndroidAssetDispatcher
import org.mozilla.fenix.helpers.HomeActivityTestRule import org.mozilla.fenix.helpers.HomeActivityTestRule
import org.mozilla.fenix.helpers.TestAssetHelper import org.mozilla.fenix.helpers.TestAssetHelper
import org.mozilla.fenix.helpers.click import org.mozilla.fenix.helpers.click
import org.mozilla.fenix.helpers.ext.waitNotNull
import org.mozilla.fenix.ui.robots.homeScreen import org.mozilla.fenix.ui.robots.homeScreen
import org.mozilla.fenix.ui.robots.bookmarksMenu import org.mozilla.fenix.ui.robots.bookmarksMenu
import org.mozilla.fenix.ui.robots.mDevice import org.mozilla.fenix.ui.robots.mDevice
@ -125,13 +129,13 @@ class MenuScreenShotTest : ScreenshotTest() {
openBookmarksThreeDotMenu() openBookmarksThreeDotMenu()
Screengrab.screenshot("BookmarksRobot_bookmarks-menu") Screengrab.screenshot("BookmarksRobot_bookmarks-menu")
bookmarksMenu { bookmarksMenu {
clickAddFolderButton() clickAddFolderButtonUsingId()
Screengrab.screenshot("BookmarksRobot_add-folder-view") Screengrab.screenshot("BookmarksRobot_add-folder-view")
saveNewFolder() saveNewFolder()
Screengrab.screenshot("BookmarksRobot_error-empty-folder-name") Screengrab.screenshot("BookmarksRobot_error-empty-folder-name")
addNewFolderName("test") addNewFolderName("test")
saveNewFolder() saveNewFolder()
}.openThreeDotMenu { }.openThreeDotMenu("test") {
Screengrab.screenshot("ThreeDotMenuBookmarksRobot_folder-menu") Screengrab.screenshot("ThreeDotMenuBookmarksRobot_folder-menu")
} }
editBookmarkFolder() editBookmarkFolder()
@ -139,7 +143,7 @@ class MenuScreenShotTest : ScreenshotTest() {
// It may be needed to wait here to have the screenshot // It may be needed to wait here to have the screenshot
mDevice.pressBack() mDevice.pressBack()
bookmarksMenu { bookmarksMenu {
}.openThreeDotMenu { }.openThreeDotMenu("test") {
deleteBookmarkFolder() deleteBookmarkFolder()
Screengrab.screenshot("ThreeDotMenuBookmarksRobot_delete-bookmark-folder-menu") Screengrab.screenshot("ThreeDotMenuBookmarksRobot_delete-bookmark-folder-menu")
} }
@ -152,21 +156,12 @@ class MenuScreenShotTest : ScreenshotTest() {
Screengrab.screenshot("NavigationToolbarRobot_navigation-toolbar") Screengrab.screenshot("NavigationToolbarRobot_navigation-toolbar")
}.enterURLAndEnterToBrowser(defaultWebPage.url) { }.enterURLAndEnterToBrowser(defaultWebPage.url) {
Screengrab.screenshot("BrowserRobot_enter-url") Screengrab.screenshot("BrowserRobot_enter-url")
} }.openTabDrawer {
tapOnTabCounter() TestAssetHelper.waitingTime
// Homescreen with visited tabs Screengrab.screenshot("TabDrawerRobot_one-tab-open")
Screengrab.screenshot("HomeScreenRobot_homescreen-with-tabs-open")
homeScreen {
}.openTabsListThreeDotMenu { }.openTabsListThreeDotMenu {
Screengrab.screenshot("open-tabs-menu") TestAssetHelper.waitingTime
}.close { Screengrab.screenshot("TabDrawerRobot_three-dot-menu")
// It may be needed to wait here for tests working on Firebase
saveToCollectionButton()
Screengrab.screenshot("HomeScreenRobot_save-collection-view")
typeCollectionName("CollectionName")
mDevice.pressBack()
// It may be needed to wait here for tests working on Firebase
Screengrab.screenshot("HomeScreenRobot_saved-collection")
} }
} }
@ -176,12 +171,13 @@ class MenuScreenShotTest : ScreenshotTest() {
navigationToolbar { navigationToolbar {
}.enterURLAndEnterToBrowser(defaultWebPage.url) { }.enterURLAndEnterToBrowser(defaultWebPage.url) {
}.openThreeDotMenu { }.openThreeDotMenu {
Screengrab.screenshot("browser-tab-menu") Screengrab.screenshot("TabDrawerRobot_browser-tab-menu")
}.closeBrowserMenuToBrowser { }.closeBrowserMenuToBrowser {
}.openTabDrawer { }.openTabDrawer {
Screengrab.screenshot("tab-drawer-with-tabs") Screengrab.screenshot("TabDrawerRobot_tab-drawer-with-tabs")
closeTab() closeTab()
Screengrab.screenshot("remove-tab") TestAssetHelper.waitingTime
Screengrab.screenshot("TabDrawerRobot_remove-tab")
} }
} }
@ -189,12 +185,11 @@ class MenuScreenShotTest : ScreenshotTest() {
fun saveLoginPromptTest() { fun saveLoginPromptTest() {
val saveLoginTest = val saveLoginTest =
TestAssetHelper.getSaveLoginAsset(mockWebServer) TestAssetHelper.getSaveLoginAsset(mockWebServer)
TestAssetHelper.waitingTimeShort
navigationToolbar { navigationToolbar {
}.enterURLAndEnterToBrowser(saveLoginTest.url) { }.enterURLAndEnterToBrowser(saveLoginTest.url) {
verifySaveLoginPromptIsShownNotSave()
SystemClock.sleep(TestAssetHelper.waitingTimeShort)
Screengrab.screenshot("save-login-prompt") Screengrab.screenshot("save-login-prompt")
TestAssetHelper.waitingTime
// verifySaveLoginPromptIsShown()
} }
} }
} }
@ -236,3 +231,11 @@ fun loginsAndPassword() = onView(withText(R.string.preferences_passwords_logins_
fun addOns() = onView(withText(R.string.preferences_addons)).click() fun addOns() = onView(withText(R.string.preferences_addons)).click()
fun settingsLanguage() = onView(withText(R.string.preferences_language)).click() fun settingsLanguage() = onView(withText(R.string.preferences_language)).click()
fun verifySaveLoginPromptIsShownNotSave() {
mDevice.waitNotNull(Until.findObjects(By.text("test@example.com")), TestAssetHelper.waitingTime)
val submitButton = mDevice.findObject(By.res("submit"))
submitButton.clickAndWait(Until.newWindow(), TestAssetHelper.waitingTime)
}
fun clickAddFolderButtonUsingId() = onView(withId(R.id.add_bookmark_folder)).click()

View File

@ -131,7 +131,7 @@ class ContextMenusTest {
clickContextShareLink(genericURL.url) // verify share intent is matched with associated URL clickContextShareLink(genericURL.url) // verify share intent is matched with associated URL
} }
} }
@Ignore("Intermittent: https://github.com/mozilla-mobile/fenix/issues/12367")
@Test @Test
fun verifyContextOpenImageNewTab() { fun verifyContextOpenImageNewTab() {
val pageLinks = val pageLinks =

View File

@ -1,7 +1,12 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"applications": {
"gecko": {
"id": "ads@mozac.org"
}
},
"name": "Mozilla Android Components - Ads", "name": "Mozilla Android Components - Ads",
"version": "1.0", "version": "${version}",
"content_scripts": [ "content_scripts": [
{ {
"matches": ["https://*/*"], "matches": ["https://*/*"],
@ -16,6 +21,7 @@
], ],
"permissions": [ "permissions": [
"geckoViewAddons", "geckoViewAddons",
"nativeMessaging" "nativeMessaging",
"nativeMessagingFromContent"
] ]
} }

View File

@ -1,7 +1,12 @@
{ {
"manifest_version": 2, "manifest_version": 2,
"applications": {
"gecko": {
"id": "cookies@mozac.org"
}
},
"name": "Mozilla Android Components - Cookies", "name": "Mozilla Android Components - Cookies",
"version": "1.0", "version": "${version}",
"content_scripts": [ "content_scripts": [
{ {
"matches": ["https://*/*"], "matches": ["https://*/*"],
@ -23,6 +28,7 @@
"permissions": [ "permissions": [
"geckoViewAddons", "geckoViewAddons",
"nativeMessaging", "nativeMessaging",
"nativeMessagingFromContent",
"webNavigation", "webNavigation",
"webRequest", "webRequest",
"webRequestBlocking", "webRequestBlocking",

View File

@ -277,8 +277,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity {
EngineView::class.java.name -> components.core.engine.createView(context, attrs).apply { EngineView::class.java.name -> components.core.engine.createView(context, attrs).apply {
selectionActionDelegate = DefaultSelectionActionDelegate( selectionActionDelegate = DefaultSelectionActionDelegate(
getSearchAdapter(components.core.store), getSearchAdapter(components.core.store),
resources = context.resources, resources = context.resources
appName = getString(R.string.app_name)
) { ) {
share(it) share(it)
} }

View File

@ -9,8 +9,8 @@ import androidx.annotation.StringRes
import mozilla.components.feature.contextmenu.ContextMenuCandidate import mozilla.components.feature.contextmenu.ContextMenuCandidate
import org.mozilla.fenix.components.FenixSnackbar import org.mozilla.fenix.components.FenixSnackbar
class FenixSnackbarDelegate(val view: View) : class FenixSnackbarDelegate(private val view: View) : ContextMenuCandidate.SnackbarDelegate {
ContextMenuCandidate.SnackbarDelegate {
override fun show( override fun show(
snackBarParentView: View, snackBarParentView: View,
@StringRes text: Int, @StringRes text: Int,

View File

@ -13,6 +13,7 @@ import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import android.widget.PopupWindow import android.widget.PopupWindow
import androidx.annotation.LayoutRes import androidx.annotation.LayoutRes
import androidx.annotation.VisibleForTesting
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.isVisible import androidx.core.view.isVisible
@ -29,6 +30,8 @@ import kotlinx.android.synthetic.main.component_browser_top_toolbar.*
import kotlinx.android.synthetic.main.component_browser_top_toolbar.view.* import kotlinx.android.synthetic.main.component_browser_top_toolbar.view.*
import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider import mozilla.components.browser.domains.autocomplete.ShippedDomainsProvider
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.state.selector.selectedTab
import mozilla.components.browser.state.store.BrowserStore
import mozilla.components.browser.toolbar.BrowserToolbar import mozilla.components.browser.toolbar.BrowserToolbar
import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior import mozilla.components.browser.toolbar.behavior.BrowserToolbarBottomBehavior
import mozilla.components.browser.toolbar.display.DisplayToolbar import mozilla.components.browser.toolbar.display.DisplayToolbar
@ -94,9 +97,6 @@ class BrowserToolbarView(
view.context.resources.getDimensionPixelSize(R.dimen.context_menu_height), view.context.resources.getDimensionPixelSize(R.dimen.context_menu_height),
true true
) )
val selectedSession = container.context.components.core.sessionManager.selectedSession
popupWindow.elevation = popupWindow.elevation =
view.context.resources.getDimension(R.dimen.mozac_browser_menu_elevation) view.context.resources.getDimension(R.dimen.mozac_browser_menu_elevation)
@ -110,11 +110,7 @@ class BrowserToolbarView(
customView.copy.setOnClickListener { customView.copy.setOnClickListener {
popupWindow.dismiss() popupWindow.dismiss()
if (isCustomTabSession) { clipboard.text = getUrlForClipboard(it.context.components.core.store, customTabSession)
clipboard.text = customTabSession?.url
} else {
clipboard.text = selectedSession?.url
}
FenixSnackbar.make( FenixSnackbar.make(
view = view, view = view,
@ -300,5 +296,15 @@ class BrowserToolbarView(
companion object { companion object {
private const val TOOLBAR_ELEVATION = 16 private const val TOOLBAR_ELEVATION = 16
@VisibleForTesting
internal fun getUrlForClipboard(store: BrowserStore, customTabSession: Session? = null): String? {
return if (customTabSession != null) {
customTabSession.url
} else {
val selectedTab = store.state.selectedTab
selectedTab?.readerState?.activeUrl ?: selectedTab?.content?.url
}
}
} }
} }

View File

@ -14,19 +14,13 @@ import org.mozilla.fenix.exceptions.viewholders.ExceptionsDeleteButtonViewHolder
import org.mozilla.fenix.exceptions.viewholders.ExceptionsHeaderViewHolder import org.mozilla.fenix.exceptions.viewholders.ExceptionsHeaderViewHolder
import org.mozilla.fenix.exceptions.viewholders.ExceptionsListItemViewHolder import org.mozilla.fenix.exceptions.viewholders.ExceptionsListItemViewHolder
sealed class AdapterItem {
object DeleteButton : AdapterItem()
object Header : AdapterItem()
data class Item(val item: TrackingProtectionException) : AdapterItem()
}
/** /**
* Adapter for a list of sites that are exempted from Tracking Protection, * Adapter for a list of sites that are exempted from Tracking Protection,
* along with controls to remove the exception. * along with controls to remove the exception.
*/ */
class ExceptionsAdapter( class ExceptionsAdapter(
private val interactor: ExceptionsInteractor private val interactor: ExceptionsInteractor
) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(DiffCallback) { ) : ListAdapter<ExceptionsAdapter.AdapterItem, RecyclerView.ViewHolder>(DiffCallback) {
/** /**
* Change the list of items that are displayed. * Change the list of items that are displayed.
@ -67,6 +61,12 @@ class ExceptionsAdapter(
} }
} }
sealed class AdapterItem {
object DeleteButton : AdapterItem()
object Header : AdapterItem()
data class Item(val item: TrackingProtectionException) : AdapterItem()
}
private object DiffCallback : DiffUtil.ItemCallback<AdapterItem>() { private object DiffCallback : DiffUtil.ItemCallback<AdapterItem>() {
override fun areItemsTheSame(oldItem: AdapterItem, newItem: AdapterItem) = override fun areItemsTheSame(oldItem: AdapterItem, newItem: AdapterItem) =
areContentsTheSame(oldItem, newItem) areContentsTheSame(oldItem, newItem)

View File

@ -28,6 +28,7 @@ import org.mozilla.fenix.settings.SupportUtils
* along with controls to remove the exception. * along with controls to remove the exception.
*/ */
class ExceptionsFragment : Fragment() { class ExceptionsFragment : Fragment() {
private lateinit var exceptionsStore: ExceptionsFragmentStore private lateinit var exceptionsStore: ExceptionsFragmentStore
private lateinit var exceptionsView: ExceptionsView private lateinit var exceptionsView: ExceptionsView
private lateinit var exceptionsInteractor: ExceptionsInteractor private lateinit var exceptionsInteractor: ExceptionsInteractor
@ -48,7 +49,7 @@ class ExceptionsFragment : Fragment() {
exceptionsStore = StoreProvider.get(this) { exceptionsStore = StoreProvider.get(this) {
ExceptionsFragmentStore( ExceptionsFragmentStore(
ExceptionsFragmentState( ExceptionsFragmentState(
items = listOf() items = emptyList()
) )
) )
} }
@ -61,7 +62,6 @@ class ExceptionsFragment : Fragment() {
@ExperimentalCoroutinesApi @ExperimentalCoroutinesApi
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
consumeFrom(exceptionsStore) { consumeFrom(exceptionsStore) {
exceptionsView.update(it) exceptionsView.update(it)
} }

View File

@ -4,16 +4,16 @@
package org.mozilla.fenix.exceptions package org.mozilla.fenix.exceptions
import android.text.SpannableString
import android.text.method.LinkMovementMethod import android.text.method.LinkMovementMethod
import android.text.style.UnderlineSpan import android.text.style.UnderlineSpan
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.FrameLayout import android.widget.FrameLayout
import androidx.core.text.toSpannable
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import kotlinx.android.extensions.LayoutContainer import kotlinx.android.extensions.LayoutContainer
import kotlinx.android.synthetic.main.component_exceptions.view.* import kotlinx.android.synthetic.main.component_exceptions.*
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
import org.mozilla.fenix.R import org.mozilla.fenix.R
@ -42,35 +42,36 @@ interface ExceptionsViewInteractor {
* View that contains and configures the Exceptions List * View that contains and configures the Exceptions List
*/ */
class ExceptionsView( class ExceptionsView(
override val containerView: ViewGroup, container: ViewGroup,
val interactor: ExceptionsInteractor interactor: ExceptionsInteractor
) : LayoutContainer { ) : LayoutContainer {
val view: FrameLayout = LayoutInflater.from(containerView.context) override val containerView: FrameLayout = LayoutInflater.from(container.context)
.inflate(R.layout.component_exceptions, containerView, true) .inflate(R.layout.component_exceptions, container, true)
.findViewById(R.id.exceptions_wrapper) .findViewById(R.id.exceptions_wrapper)
private val exceptionsAdapter = ExceptionsAdapter(interactor) private val exceptionsAdapter = ExceptionsAdapter(interactor)
init { init {
view.exceptions_list.apply { exceptions_list.apply {
adapter = exceptionsAdapter adapter = exceptionsAdapter
layoutManager = LinearLayoutManager(containerView.context) layoutManager = LinearLayoutManager(container.context)
} }
val learnMoreText = view.exceptions_learn_more.text.toString()
val textWithLink = SpannableString(learnMoreText).apply { with(exceptions_learn_more) {
setSpan(UnderlineSpan(), 0, learnMoreText.length, 0) val learnMoreText = text
} text = learnMoreText.toSpannable().apply {
with(view.exceptions_learn_more) { setSpan(UnderlineSpan(), 0, learnMoreText.length, 0)
}
movementMethod = LinkMovementMethod.getInstance() movementMethod = LinkMovementMethod.getInstance()
text = textWithLink
setOnClickListener { interactor.onLearnMore() } setOnClickListener { interactor.onLearnMore() }
} }
} }
fun update(state: ExceptionsFragmentState) { fun update(state: ExceptionsFragmentState) {
view.exceptions_empty_view.isVisible = state.items.isEmpty() exceptions_empty_view.isVisible = state.items.isEmpty()
view.exceptions_list.isVisible = state.items.isNotEmpty() exceptions_list.isVisible = state.items.isNotEmpty()
exceptionsAdapter.updateData(state.items) exceptionsAdapter.updateData(state.items)
} }
} }

View File

@ -5,12 +5,14 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
import android.view.View import android.view.View
import android.widget.Button
import androidx.annotation.VisibleForTesting
import androidx.appcompat.content.res.AppCompatResources.getDrawable import androidx.appcompat.content.res.AppCompatResources.getDrawable
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.onboarding_automatic_signin.view.* import kotlinx.android.synthetic.main.onboarding_automatic_signin.view.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import mozilla.components.service.fxa.manager.SignInWithShareableAccountResult import mozilla.components.service.fxa.manager.SignInWithShareableAccountResult
import mozilla.components.service.fxa.sharing.ShareableAccount import mozilla.components.service.fxa.sharing.ShareableAccount
@ -20,42 +22,18 @@ import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
import org.mozilla.fenix.ext.components import org.mozilla.fenix.ext.components
class OnboardingAutomaticSignInViewHolder(view: View) : RecyclerView.ViewHolder(view) { class OnboardingAutomaticSignInViewHolder(
view: View,
private val scope: CoroutineScope = MainScope()
) : RecyclerView.ViewHolder(view) {
private lateinit var shareableAccount: ShareableAccount private lateinit var shareableAccount: ShareableAccount
private val headerText = view.header_text private val headerText = view.header_text
init { init {
view.turn_on_sync_button.setOnClickListener { view.turn_on_sync_button.setOnClickListener {
it.context.components.analytics.metrics.track(Event.OnboardingAutoSignIn) scope.launch {
onClick(it.turn_on_sync_button)
it.turn_on_sync_button.text = it.context.getString(
R.string.onboarding_firefox_account_signing_in
)
it.turn_on_sync_button.isEnabled = false
CoroutineScope(Dispatchers.Main).launch {
val result = view.context.components.backgroundServices.accountManager
.signInWithShareableAccountAsync(shareableAccount).await()
when (result) {
SignInWithShareableAccountResult.Failure -> {
// Failed to sign-in (e.g. bad credentials). Allow to try again.
it.turn_on_sync_button.text = it.context.getString(
R.string.onboarding_firefox_account_auto_signin_confirm
)
it.turn_on_sync_button.isEnabled = true
FenixSnackbar.make(
view = it,
duration = Snackbar.LENGTH_SHORT,
isDisplayedWithBrowserToolbar = false
).setText(
it.context.getString(R.string.onboarding_firefox_account_automatic_signin_failed)
).show()
}
SignInWithShareableAccountResult.WillRetry, SignInWithShareableAccountResult.Success -> {
// We consider both of these as a 'success'.
}
}
} }
} }
} }
@ -69,6 +47,34 @@ class OnboardingAutomaticSignInViewHolder(view: View) : RecyclerView.ViewHolder(
headerText.putCompoundDrawablesRelativeWithIntrinsicBounds(start = icon) headerText.putCompoundDrawablesRelativeWithIntrinsicBounds(start = icon)
} }
@VisibleForTesting
internal suspend fun onClick(button: Button) {
val context = button.context
context.components.analytics.metrics.track(Event.OnboardingAutoSignIn)
button.text = context.getString(R.string.onboarding_firefox_account_signing_in)
button.isEnabled = false
val accountManager = context.components.backgroundServices.accountManager
when (accountManager.signInWithShareableAccountAsync(shareableAccount).await()) {
SignInWithShareableAccountResult.Failure -> {
// Failed to sign-in (e.g. bad credentials). Allow to try again.
button.text = context.getString(R.string.onboarding_firefox_account_auto_signin_confirm)
button.isEnabled = true
FenixSnackbar.make(
view = button,
duration = Snackbar.LENGTH_SHORT,
isDisplayedWithBrowserToolbar = false
).setText(
context.getString(R.string.onboarding_firefox_account_automatic_signin_failed)
).show()
}
SignInWithShareableAccountResult.WillRetry, SignInWithShareableAccountResult.Success -> {
// We consider both of these as a 'success'.
}
}
}
companion object { companion object {
const val LAYOUT_ID = R.layout.onboarding_automatic_signin const val LAYOUT_ID = R.layout.onboarding_automatic_signin
} }

View File

@ -93,7 +93,6 @@ abstract class BaseSearchTelemetry {
engine.installWebExtension( engine.installWebExtension(
id = extensionInfo.id, id = extensionInfo.id,
url = extensionInfo.resourceUrl, url = extensionInfo.resourceUrl,
allowContentMessaging = true,
onSuccess = { extension -> onSuccess = { extension ->
store.flowScoped { flow -> subscribeToUpdates(flow, extension, extensionInfo) } store.flowScoped { flow -> subscribeToUpdates(flow, extension, extensionInfo) }
}, },

View File

@ -51,7 +51,7 @@ class AdsTelemetry(private val metrics: MetricController) : BaseSearchTelemetry(
companion object { companion object {
@VisibleForTesting @VisibleForTesting
internal const val ADS_EXTENSION_ID = "mozacBrowserAds" internal const val ADS_EXTENSION_ID = "ads@mozac.org"
@VisibleForTesting @VisibleForTesting
internal const val ADS_EXTENSION_RESOURCE_URL = "resource://android/assets/extensions/ads/" internal const val ADS_EXTENSION_RESOURCE_URL = "resource://android/assets/extensions/ads/"
@VisibleForTesting @VisibleForTesting

View File

@ -131,7 +131,7 @@ class InContentTelemetry(private val metrics: MetricController) : BaseSearchTele
companion object { companion object {
@VisibleForTesting @VisibleForTesting
internal const val COOKIES_EXTENSION_ID = "BrowserCookiesExtension" internal const val COOKIES_EXTENSION_ID = "cookies@mozac.org"
@VisibleForTesting @VisibleForTesting
internal const val COOKIES_EXTENSION_RESOURCE_URL = internal const val COOKIES_EXTENSION_RESOURCE_URL =
"resource://android/assets/extensions/cookies/" "resource://android/assets/extensions/cookies/"

View File

@ -192,6 +192,7 @@ class TabTrayDialogFragment : AppCompatDialogFragment() {
getString(R.string.snackbar_deleted_undo), getString(R.string.snackbar_deleted_undo),
{ {
sessionManager.add(snapshot.session, isSelected, engineSessionState = state) sessionManager.add(snapshot.session, isSelected, engineSessionState = state)
tabTrayView.scrollToTab(snapshot.session.id)
}, },
operation = { }, operation = { },
elevation = ELEVATION, elevation = ELEVATION,

View File

@ -126,7 +126,7 @@ class TabTrayView(
} }
if (!hasLoaded) { if (!hasLoaded) {
hasLoaded = true hasLoaded = true
scrollToSelectedTab() scrollToTab(view.context.components.core.store.state.selectedTabId)
if (view.context.settings().accessibilityServicesEnabled) { if (view.context.settings().accessibilityServicesEnabled) {
lifecycleScope.launch { lifecycleScope.launch {
delay(SELECTION_DELAY.toLong()) delay(SELECTION_DELAY.toLong())
@ -145,27 +145,30 @@ class TabTrayView(
} }
} }
tabTrayItemMenu = TabTrayItemMenu(view.context, { view.tab_layout.selectedTabPosition == 0 }) { tabTrayItemMenu =
when (it) { TabTrayItemMenu(view.context, { view.tab_layout.selectedTabPosition == 0 }) {
is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsClicked( when (it) {
isPrivateModeSelected is TabTrayItemMenu.Item.ShareAllTabs -> interactor.onShareTabsClicked(
) isPrivateModeSelected
is TabTrayItemMenu.Item.SaveToCollection -> interactor.onSaveToCollectionClicked() )
is TabTrayItemMenu.Item.CloseAllTabs -> interactor.onCloseAllTabsClicked( is TabTrayItemMenu.Item.SaveToCollection -> interactor.onSaveToCollectionClicked()
isPrivateModeSelected is TabTrayItemMenu.Item.CloseAllTabs -> interactor.onCloseAllTabsClicked(
) isPrivateModeSelected
)
}
} }
}
view.tab_tray_overflow.setOnClickListener { view.tab_tray_overflow.setOnClickListener {
container.context.components.analytics.metrics.track(Event.TabsTrayMenuOpened) container.context.components.analytics.metrics.track(Event.TabsTrayMenuOpened)
menu = tabTrayItemMenu.menuBuilder.build(container.context) menu = tabTrayItemMenu.menuBuilder.build(container.context)
menu?.show(it) menu?.show(it)
?.also { pu -> ?.also { pu ->
(pu.contentView as? CardView)?.setCardBackgroundColor(ContextCompat.getColor( (pu.contentView as? CardView)?.setCardBackgroundColor(
view.context, ContextCompat.getColor(
R.color.foundation_normal_theme view.context,
)) R.color.foundation_normal_theme
)
)
} }
} }
@ -211,7 +214,7 @@ class TabTrayView(
filterTabs.invoke(filter) filterTabs.invoke(filter)
updateState(view.context.components.core.store.state) updateState(view.context.components.core.store.state)
scrollToSelectedTab() scrollToTab(view.context.components.core.store.state.selectedTabId)
if (isPrivateModeSelected) { if (isPrivateModeSelected) {
container.context.components.analytics.metrics.track(Event.TabsTrayPrivateModeTapped) container.context.components.analytics.metrics.track(Event.TabsTrayPrivateModeTapped)
@ -220,8 +223,11 @@ class TabTrayView(
} }
} }
override fun onTabReselected(tab: TabLayout.Tab?) { /*noop*/ } override fun onTabReselected(tab: TabLayout.Tab?) { /*noop*/
override fun onTabUnselected(tab: TabLayout.Tab?) { /*noop*/ } }
override fun onTabUnselected(tab: TabLayout.Tab?) { /*noop*/
}
fun updateState(state: BrowserState) { fun updateState(state: BrowserState) {
view.let { view.let {
@ -266,14 +272,16 @@ class TabTrayView(
private fun toggleFabText(private: Boolean) { private fun toggleFabText(private: Boolean) {
if (private) { if (private) {
fabView.new_tab_button.extend() fabView.new_tab_button.extend()
fabView.new_tab_button.contentDescription = view.context.resources.getString(R.string.add_private_tab) fabView.new_tab_button.contentDescription =
view.context.resources.getString(R.string.add_private_tab)
} else { } else {
fabView.new_tab_button.shrink() fabView.new_tab_button.shrink()
fabView.new_tab_button.contentDescription = view.context.resources.getString(R.string.add_tab) fabView.new_tab_button.contentDescription =
view.context.resources.getString(R.string.add_tab)
} }
} }
private fun scrollToSelectedTab() { fun scrollToTab(sessionId: String?) {
(view.tabsTray as? BrowserTabsTray)?.also { tray -> (view.tabsTray as? BrowserTabsTray)?.also { tray ->
val tabs = if (isPrivateModeSelected) { val tabs = if (isPrivateModeSelected) {
view.context.components.core.store.state.privateTabs view.context.components.core.store.state.privateTabs
@ -282,7 +290,7 @@ class TabTrayView(
} }
val selectedBrowserTabIndex = tabs val selectedBrowserTabIndex = tabs
.indexOfFirst { it.id == view.context.components.core.store.state.selectedTabId } .indexOfFirst { it.id == sessionId }
tray.layoutManager?.scrollToPosition(selectedBrowserTabIndex) tray.layoutManager?.scrollToPosition(selectedBrowserTabIndex)
} }

View File

@ -7,7 +7,7 @@
android:height="24dp" android:height="24dp"
android:viewportWidth="24" android:viewportWidth="24"
android:viewportHeight="24"> android:viewportHeight="24">
<path <path
android:pathData="M13.36 11.98l4.38-4.38a0.95 0.95 0 0 0-1.34-1.34l-4.38 4.38-4.38-4.38A0.95 0.95 0 0 0 6.3 7.6l4.38 4.38-4.38 4.38a0.95 0.95 0 1 0 1.34 1.34l4.38-4.38 4.38 4.38a0.95 0.95 0 0 0 1.34-1.34l-4.38-4.38z" android:fillColor="?primaryText"
android:fillColor="?primaryText" /> android:pathData="m18.5,17.0962 l-5.1,-5.0962 5.1,-5.0962c0.3905,-0.3905 0.3905,-1.0133 0,-1.4038s-1.0133,-0.3905 -1.4038,0l-5.0962,5.1 -5.0962,-5.1c-0.3905,-0.3905 -1.0133,-0.3905 -1.4038,0s-0.3905,1.0133 0,1.4038l5.1,5.0962 -5.1,5.0962c-0.3905,0.3905 -0.3905,1.0133 0,1.4038s1.0133,0.3905 1.4038,0l5.0962,-5.1 5.0962,5.1c0.3905,0.3905 1.0133,0.3905 1.4038,0s0.3905,-1.0133 0,-1.4038z" />
</vector> </vector>

View File

@ -11,12 +11,15 @@
<string name="content_description_disable_private_browsing_button">عطّل التصفّح الخاص</string> <string name="content_description_disable_private_browsing_button">عطّل التصفّح الخاص</string>
<!-- Placeholder text shown in the search bar before a user enters text --> <!-- Placeholder text shown in the search bar before a user enters text -->
<string name="search_hint">ابحث أو أدخِل عنوانا</string> <string name="search_hint">ابحث أو أدخِل عنوانا</string>
<!-- No Open Tabs Message Header -->
<string name="no_open_tabs_header_2">لا ألسنة مفتوحة</string>
<!-- No Open Tabs Message Description --> <!-- No Open Tabs Message Description -->
<string name="no_open_tabs_description">ستظهر الألسنة المفتوحة هنا.</string> <string name="no_open_tabs_description">ستظهر الألسنة المفتوحة هنا.</string>
<!-- No Private Tabs Message Description -->
<string name="no_private_tabs_description">ستظهر الألسنة الخاصة هنا.</string>
<!-- Message announced to the user when tab tray is selected with 1 tab -->
<string name="open_tab_tray_single">لسان واحد مفتوح. انقر لتبديل الألسنة.</string>
<!-- Private Browsing --> <!-- Private Browsing -->
<!-- Title for private session option --> <!-- Title for private session option -->
<string name="private_browsing_title">أنت في جلسة خاصة</string> <string name="private_browsing_title">أنت في جلسة خاصة</string>
@ -28,7 +31,7 @@
<!-- Delete session button to erase your history in a private session --> <!-- Delete session button to erase your history in a private session -->
<string name="private_browsing_delete_session">احذف الجلسة</string> <string name="private_browsing_delete_session">احذف الجلسة</string>
<!-- Private mode shortcut "contextual feature recommender" (CFR) --> <!-- Private mode shortcut "contextual feature recommendation" (CFR) -->
<!-- Text for the main message --> <!-- Text for the main message -->
<string name="cfr_message">أضِف اختصارًا لفتح الألسنة الخاصة من الشاشة الرئيسية.</string> <string name="cfr_message">أضِف اختصارًا لفتح الألسنة الخاصة من الشاشة الرئيسية.</string>
<!-- Text for the positive button --> <!-- Text for the positive button -->
@ -48,6 +51,10 @@
<!-- Browser Fragment --> <!-- Browser Fragment -->
<!-- Content description (not visible, for screen readers etc.): Navigate to open tabs --> <!-- Content description (not visible, for screen readers etc.): Navigate to open tabs -->
<string name="browser_tabs_button">الألسنة المفتوحة</string> <string name="browser_tabs_button">الألسنة المفتوحة</string>
<!-- Content description (not visible, for screen readers etc.): Navigate backward (browsing history) -->
<string name="browser_menu_back">السابق</string>
<!-- Content description (not visible, for screen readers etc.): Navigate forward (browsing history) -->
<string name="browser_menu_forward">التالي</string>
<!-- Content description (not visible, for screen readers etc.): Refresh current website --> <!-- Content description (not visible, for screen readers etc.): Refresh current website -->
<string name="browser_menu_refresh">أعِد التحميل</string> <string name="browser_menu_refresh">أعِد التحميل</string>
<!-- Content description (not visible, for screen readers etc.): Stop loading current website --> <!-- Content description (not visible, for screen readers etc.): Stop loading current website -->
@ -82,8 +89,6 @@
<string name="browser_menu_new_tab">لسان جديد</string> <string name="browser_menu_new_tab">لسان جديد</string>
<!-- Browser menu button that saves the current tab to a collection --> <!-- Browser menu button that saves the current tab to a collection -->
<string name="browser_menu_save_to_collection_2">احفظ في التجميعة</string> <string name="browser_menu_save_to_collection_2">احفظ في التجميعة</string>
<!-- Browser menu button that opens a dialog to report issues with the current site -->
<string name="browser_menu_report_issue">أبلِغ عن مشكلة بالموقع</string>
<!-- Browser menu button that open a share menu to share the current site --> <!-- Browser menu button that open a share menu to share the current site -->
<string name="browser_menu_share">شارِك</string> <string name="browser_menu_share">شارِك</string>
<!-- Share menu title, displayed when a user is sharing their current site --> <!-- Share menu title, displayed when a user is sharing their current site -->
@ -97,13 +102,17 @@
<!-- Browser menu text shown in custom tabs to indicate this is a Fenix tab <!-- Browser menu text shown in custom tabs to indicate this is a Fenix tab
The first parameter is the name of the app defined in app_name (for example: Fenix) --> The first parameter is the name of the app defined in app_name (for example: Fenix) -->
<string name="browser_menu_powered_by2">تدعمها %1$s</string> <string name="browser_menu_powered_by2">تدعمها %1$s</string>
<!-- Browser menu button to put the the current page in reader mode --> <!-- Browser menu button to put the current page in reader mode -->
<string name="browser_menu_read">منظور القارئ</string> <string name="browser_menu_read">منظور القارئ</string>
<!-- Browser menu button to open the current page in an external app --> <!-- Browser menu button to open the current page in an external app -->
<string name="browser_menu_open_app_link">افتح في التطبيق</string> <string name="browser_menu_open_app_link">افتح في التطبيق</string>
<!-- Browser menu button to configure reader mode appearance e.g. the used font type and size --> <!-- Browser menu button to configure reader mode appearance e.g. the used font type and size -->
<string name="browser_menu_read_appearance">المظهر</string> <string name="browser_menu_read_appearance">المظهر</string>
<!-- Error message to show when the user tries to access a scheme not
handled by the app (Ex: blob, tel etc) -->
<string name="unknown_scheme_error_message">تعذّر الاتصال، لم أتعرّف على مخطّط المسار.</string>
<!-- Locale Settings Fragment --> <!-- Locale Settings Fragment -->
<!-- Content description for tick mark on selected language --> <!-- Content description for tick mark on selected language -->
<string name="a11y_selected_locale_content_description">اللغة المحددة</string> <string name="a11y_selected_locale_content_description">اللغة المحددة</string>
@ -335,6 +344,11 @@
<!-- Preference for using bottom toolbar --> <!-- Preference for using bottom toolbar -->
<string name="preference_bottom_toolbar">بالأسفل</string> <string name="preference_bottom_toolbar">بالأسفل</string>
<!-- Theme Preferences -->
<!-- Preference for using light theme -->
<string name="preference_light_theme">فاتحة</string>
<!-- Preference for using dark theme -->
<string name="preference_dark_theme">داكنة</string>
<!-- Preference for using using dark or light theme automatically set by battery --> <!-- Preference for using using dark or light theme automatically set by battery -->
<string name="preference_auto_battery_theme">حسب وضع توفير الطاقة</string> <string name="preference_auto_battery_theme">حسب وضع توفير الطاقة</string>
<!-- Preference for using following device theme --> <!-- Preference for using following device theme -->

View File

@ -489,6 +489,11 @@
<!-- Text shown when no history exists --> <!-- Text shown when no history exists -->
<string name="history_empty_message">Няма гісторыі</string> <string name="history_empty_message">Няма гісторыі</string>
<!-- Crashes -->
<!-- Title text displayed on the tab crash page. This first parameter is the name of the application (For example: Fenix) -->
<string name="tab_crash_title_2">Выбачайце. %1$s не можа загрузіць гэтую старонку.</string>
<!-- Description text displayed on the tab crash page -->
<string name="tab_crash_description">Вы можаце паспрабаваць аднавіць ці закрыць гэтую картку ніжэй.</string>
<!-- Send crash report checkbox text on the tab crash page --> <!-- Send crash report checkbox text on the tab crash page -->
<string name="tab_crash_send_report">Адправіць справаздачу аб краху ў Mozilla</string> <string name="tab_crash_send_report">Адправіць справаздачу аб краху ў Mozilla</string>
<!-- Close tab button text on the tab crash page --> <!-- Close tab button text on the tab crash page -->

View File

@ -19,6 +19,11 @@
<!-- No Private Tabs Message Description --> <!-- No Private Tabs Message Description -->
<string name="no_private_tabs_description">Tus pestañas privadas aparecerán aquí.</string> <string name="no_private_tabs_description">Tus pestañas privadas aparecerán aquí.</string>
<!-- Message announced to the user when tab tray is selected with 1 tab -->
<string name="open_tab_tray_single">1 pestaña abierta. Tocar para cambiar de pestaña.</string>
<!-- Message announced to the user when tab tray is selected with 0 or 2+ tabs -->
<string name="open_tab_tray_plural">%1$s pestañas abiertas. Tocar para cambiar de pestaña.</string>
<!-- About content. The first parameter is the name of the application. (For example: Fenix) --> <!-- About content. The first parameter is the name of the application. (For example: Fenix) -->
<string name="about_content">%1$s es producido por Mozilla.</string> <string name="about_content">%1$s es producido por Mozilla.</string>
@ -116,6 +121,8 @@
<string name="browser_menu_powered_by2">Patrocinado por %1$s</string> <string name="browser_menu_powered_by2">Patrocinado por %1$s</string>
<!-- Browser menu button to put the current page in reader mode --> <!-- Browser menu button to put the current page in reader mode -->
<string name="browser_menu_read">Vista de lectura</string> <string name="browser_menu_read">Vista de lectura</string>
<!-- Browser menu button content description to close reader mode and return the user to the regular browser -->
<string name="browser_menu_read_close">Salir de vista de lectura</string>
<!-- Browser menu button to open the current page in an external app --> <!-- Browser menu button to open the current page in an external app -->
<string name="browser_menu_open_app_link">Abrir en la aplicación</string> <string name="browser_menu_open_app_link">Abrir en la aplicación</string>
@ -288,6 +295,8 @@
<string name="preferences_sync_bookmarks">Marcadores</string> <string name="preferences_sync_bookmarks">Marcadores</string>
<!-- Preference for syncing logins --> <!-- Preference for syncing logins -->
<string name="preferences_sync_logins">Inicios de sesión</string> <string name="preferences_sync_logins">Inicios de sesión</string>
<!-- Preference for syncing tabs -->
<string name="preferences_sync_tabs_2">Pestañas abiertas</string>
<!-- Preference for signing out --> <!-- Preference for signing out -->
<string name="preferences_sign_out">Cerrar sesión</string> <string name="preferences_sign_out">Cerrar sesión</string>
<!-- Preference displays and allows changing current FxA device name --> <!-- Preference displays and allows changing current FxA device name -->
@ -442,6 +451,24 @@
<string name="tabs_header_private_tabs_title">Pestañas privadas</string> <string name="tabs_header_private_tabs_title">Pestañas privadas</string>
<!-- Content description (not visible, for screen readers etc.): Add tab button. Adds a news tab when pressed --> <!-- Content description (not visible, for screen readers etc.): Add tab button. Adds a news tab when pressed -->
<string name="add_tab">Agregar pestaña</string> <string name="add_tab">Agregar pestaña</string>
<!-- Content description (not visible, for screen readers etc.): Add tab button. Adds a news tab when pressed -->
<string name="add_private_tab">Agregar pestaña privada</string>
<!-- Text for the new tab button to indicate adding a new private tab in the tab -->
<string name="tab_drawer_fab_content">Privada</string>
<!-- Text shown as the title of the open tab tray -->
<string name="tab_tray_title">Pestañas abiertas</string>
<!-- Text shown in the menu for saving tabs to a collection -->
<string name="tab_tray_menu_item_save">Guardar en colección</string>
<!-- Text shown in the menu for sharing all tabs -->
<string name="tab_tray_menu_item_share">Compartir todas las pestañas</string>
<!-- Text shown in the menu for closing all tabs -->
<string name="tab_tray_menu_item_close">Cerrar todas las pestañas</string>
<!-- Shortcut action to open new tab -->
<string name="tab_tray_menu_open_new_tab">Nueva pestaña</string>
<!-- Shortcut action to open the home screen -->
<string name="tab_tray_menu_home">Ir a Inicio</string>
<!-- Shortcut action to toggle private mode -->
<string name="tab_tray_menu_toggle">Alternar modo de pestaña</string>
<!-- Content description (not visible, for screen readers etc.): Removes tab from collection button. Removes the selected tab from collection when pressed --> <!-- Content description (not visible, for screen readers etc.): Removes tab from collection button. Removes the selected tab from collection when pressed -->
<string name="remove_tab_from_collection">Eliminar la pestaña de la colección</string> <string name="remove_tab_from_collection">Eliminar la pestaña de la colección</string>
<!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed --> <!-- Content description (not visible, for screen readers etc.): Close tab button. Closes the current session when pressed -->
@ -454,6 +481,8 @@
<string name="tabs_menu_close_all_tabs">Cerrar todas las pestañas</string> <string name="tabs_menu_close_all_tabs">Cerrar todas las pestañas</string>
<!-- Open tabs menu item to share all tabs --> <!-- Open tabs menu item to share all tabs -->
<string name="tabs_menu_share_tabs">Compartir pestañas</string> <string name="tabs_menu_share_tabs">Compartir pestañas</string>
<!-- Open tabs menu item to save tabs to collection -->
<string name="tabs_menu_save_to_collection1">Guardar pestañas en la colección</string>
<!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed --> <!-- Content description (not visible, for screen readers etc.): Opens the tab menu when pressed -->
<string name="tab_menu">Menú de pestaña</string> <string name="tab_menu">Menú de pestaña</string>
<!-- Tab menu item to share the tab --> <!-- Tab menu item to share the tab -->
@ -668,6 +697,10 @@
<string name="collections_header">Colecciones</string> <string name="collections_header">Colecciones</string>
<!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed --> <!-- Content description (not visible, for screen readers etc.): Opens the collection menu when pressed -->
<string name="collection_menu_button_content_description">Menú de la colección</string> <string name="collection_menu_button_content_description">Menú de la colección</string>
<!-- No Open Tabs Message Header -->
<string name="no_collections_header1">Colecciona las cosas que te importan</string>
<!-- Label to describe what collections are to a new user without any collections -->
<string name="no_collections_description1">Agrupa búsquedas, sitios y pestañas similares para un acceso rápido después.</string>
<!-- Title for the "select tabs" step of the collection creator --> <!-- Title for the "select tabs" step of the collection creator -->
<string name="create_collection_select_tabs">Seleccionar pestañas</string> <string name="create_collection_select_tabs">Seleccionar pestañas</string>
<!-- Title for the "select collection" step of the collection creator --> <!-- Title for the "select collection" step of the collection creator -->
@ -697,6 +730,9 @@
<!-- Button to save currently selected tabs in the "select tabs" step of the collection creator--> <!-- Button to save currently selected tabs in the "select tabs" step of the collection creator-->
<string name="create_collection_save">Guardar</string> <string name="create_collection_save">Guardar</string>
<!-- Snackbar action to view the collection the user just created or updated -->
<string name="create_collection_view">Ver</string>
<!-- Default name for a new collection in "name new collection" step of the collection creator. %d is a placeholder for the number of collections--> <!-- Default name for a new collection in "name new collection" step of the collection creator. %d is a placeholder for the number of collections-->
<string name="create_collection_default_name">Colección %d</string> <string name="create_collection_default_name">Colección %d</string>
@ -845,8 +881,6 @@
<string name="preference_summary_delete_browsing_data_on_quit">Eliminar automáticamente los datos de navegación cuando selecciones &quot;Salir&quot; en el menú principal</string> <string name="preference_summary_delete_browsing_data_on_quit">Eliminar automáticamente los datos de navegación cuando selecciones &quot;Salir&quot; en el menú principal</string>
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. --> <!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
<string name="preference_summary_delete_browsing_data_on_quit_2">Eliminar automáticamente los datos de navegación cuando selecciones \&quot;Salir\&quot; en el menú principal</string> <string name="preference_summary_delete_browsing_data_on_quit_2">Eliminar automáticamente los datos de navegación cuando selecciones \&quot;Salir\&quot; en el menú principal</string>
<!-- Category for history items to delete on quit in delete browsing data on quit -->
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Historial de navegación</string>
<!-- Action item in menu for the Delete browsing data on quit feature --> <!-- Action item in menu for the Delete browsing data on quit feature -->
<string name="delete_browsing_data_on_quit_action">Salir</string> <string name="delete_browsing_data_on_quit_action">Salir</string>
@ -872,9 +906,26 @@
Sin embargo, puede ser menos estable. Descarga nuestro navegador beta para una experiencia más estable.</string> Sin embargo, puede ser menos estable. Descarga nuestro navegador beta para una experiencia más estable.</string>
<!-- text for firefox preview moving tip button. "Firefox for Android Beta" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button_2">Obtener Firefox para Android Beta</string>
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded --> <!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly se movió</string> <string name="tip_firefox_preview_moved_header_preview_installed">Firefox Nightly se movió</string>
<!-- text for firefox preview moving tip description -->
<string name="tip_firefox_preview_moved_description_preview_installed">Esta aplicación ya no recibirá actualizaciones de seguridad. Deja de usarla y cambia al nuevo Nightly.
\n\nPara transferir tus marcadores, inicios de sesión e historial a otra aplicación, crea una cuenta de Firefox.</string>
<!-- text for firefox preview moving tip button -->
<string name="tip_firefox_preview_moved_button_preview_installed">Cambia al nuevo Nightly</string>
<!-- text for firefox preview moving tip header. "Firefox Nightly" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_header_preview_not_installed">Firefox Nightly se ha trasladado</string>
<!-- text for firefox preview moving tip description -->
<string name="tip_firefox_preview_moved_description_preview_not_installed">Esta aplicación ya no recibirá actualizaciones de seguridad. Obtén el nuevo Nightly y deja de usar esta aplicación.
\n\nPara transferir tus marcadores, inicios de sesión e historial a otra aplicación, crea una cuenta de Firefox.</string>
<!-- text for firefox preview moving tip button -->
<string name="tip_firefox_preview_moved_button_preview_not_installed">Obtén el nuevo Nightly</string>
<!-- Onboarding --> <!-- Onboarding -->
<!-- Text for onboarding welcome message <!-- Text for onboarding welcome message
The first parameter is the name of the app (e.g. Firefox Preview) --> The first parameter is the name of the app (e.g. Firefox Preview) -->
@ -910,11 +961,22 @@
<string name="onboarding_firefox_account_sync_is_on">Sync está activado</string> <string name="onboarding_firefox_account_sync_is_on">Sync está activado</string>
<!-- text to display in the snackbar if automatic sign-in fails. user may try again --> <!-- text to display in the snackbar if automatic sign-in fails. user may try again -->
<string name="onboarding_firefox_account_automatic_signin_failed">Error al iniciar sesión</string> <string name="onboarding_firefox_account_automatic_signin_failed">Error al iniciar sesión</string>
<!-- text for the tracking protection onboarding card header -->
<string name="onboarding_tracking_protection_header_2">Privacidad automática</string>
<!-- text for the tracking protection card description
The first parameter is the name of the app (e.g. Firefox Preview) -->
<string name="onboarding_tracking_protection_description_2">La configuración de privacidad y seguridad bloquea los rastreadores, el malware y las compañías que te siguen.</string>
<!-- text for tracking protection radio button option for standard level of blocking -->
<string name="onboarding_tracking_protection_standard_button_2">Estándar (predeterminado)</string>
<!-- text for standard blocking option button description -->
<string name="onboarding_tracking_protection_standard_button_description_2">Bloquea menos rastreadores. Las páginas se cargarán normalmente.</string>
<!-- text for tracking protection radio button option for strict level of blocking --> <!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_button">Estricta (recomendada)</string> <string name="onboarding_tracking_protection_strict_button">Estricta (recomendada)</string>
<!-- text for tracking protection radio button option for strict level of blocking --> <!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_option">Estricto</string> <string name="onboarding_tracking_protection_strict_option">Estricto</string>
<!-- text for strict blocking option button description -->
<string name="onboarding_tracking_protection_strict_button_description_2">Bloquear más rastreadores, publicidad y ventanas emergentes. Las páginas cargan más rápido, pero pueden tener problemas de funcionalidad.</string>
<!-- text for the toolbar position card header <!-- text for the toolbar position card header
In English this is an idiom for "choose a side as in an argument or fight" In English this is an idiom for "choose a side as in an argument or fight"
but it is ok to make this more literally about "choosing a position in a physical space --> but it is ok to make this more literally about "choosing a position in a physical space -->
@ -999,14 +1061,22 @@
<string name="preference_enhanced_tracking_protection_explanation">Protege tus datos. %s te protege de muchos de los rastreadores más comunes que siguen lo que haces en línea.</string> <string name="preference_enhanced_tracking_protection_explanation">Protege tus datos. %s te protege de muchos de los rastreadores más comunes que siguen lo que haces en línea.</string>
<!-- Text displayed that links to website about enhanced tracking protection --> <!-- Text displayed that links to website about enhanced tracking protection -->
<string name="preference_enhanced_tracking_protection_explanation_learn_more">Saber más</string> <string name="preference_enhanced_tracking_protection_explanation_learn_more">Saber más</string>
<!-- Preference for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_default_1">Estándar (predeterminado)</string>
<!-- Preference description for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_description_3">Bloquea menos rastreadores. Las páginas se cargarán normalmente.</string>
<!-- Accessibility text for the Standard protection information icon --> <!-- Accessibility text for the Standard protection information icon -->
<string name="preference_enhanced_tracking_protection_standard_info_button">Qué es lo que está bloqueado por la protección estándar contra el rastreo</string> <string name="preference_enhanced_tracking_protection_standard_info_button">Qué es lo que está bloqueado por la protección estándar contra el rastreo</string>
<!-- Preference for enhanced tracking protection for the strict protection settings --> <!-- Preference for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict">Estricto</string> <string name="preference_enhanced_tracking_protection_strict">Estricto</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict_description_2">Bloquear más rastreadores, publicidad y ventanas emergentes. Las páginas cargan más rápido, pero pueden tener problemas de funcionalidad.</string>
<!-- Accessibility text for the Strict protection information icon --> <!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_strict_info_button">Qué es lo que está bloqueado por la protección estricta contra el rastreo</string> <string name="preference_enhanced_tracking_protection_strict_info_button">Qué es lo que está bloqueado por la protección estricta contra el rastreo</string>
<!-- Preference for enhanced tracking protection for the custom protection settings --> <!-- Preference for enhanced tracking protection for the custom protection settings -->
<string name="preference_enhanced_tracking_protection_custom">Personalizado</string> <string name="preference_enhanced_tracking_protection_custom">Personalizado</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_custom_description_2">Elige qué rastreadores y secuencias de comandos bloquear</string>
<!-- Accessibility text for the Strict protection information icon --> <!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_custom_info_button">Esto es lo que está bloqueado por la protección de rastreo estándar</string> <string name="preference_enhanced_tracking_protection_custom_info_button">Esto es lo que está bloqueado por la protección de rastreo estándar</string>
<!-- Header for categories that are being blocked by current Enhanced Tracking Protection settings --> <!-- Header for categories that are being blocked by current Enhanced Tracking Protection settings -->
@ -1057,6 +1127,8 @@
<!-- Description of tracking content that can be blocked by Enhanced Tracking Protection --> <!-- Description of tracking content that can be blocked by Enhanced Tracking Protection -->
<string name="etp_tracking_content_description">Bloquea la carga de anuncios externos, vídeos y contenido que contenga código de rastreo. Puede afectar a la funcionalidad del sitio web.</string> <string name="etp_tracking_content_description">Bloquea la carga de anuncios externos, vídeos y contenido que contenga código de rastreo. Puede afectar a la funcionalidad del sitio web.</string>
<!-- Enhanced Tracking Protection Onboarding Message shown in a dialog above the toolbar. The first parameter is the name of the application (For example: Fenix) -->
<string name="etp_onboarding_cfr_message">Cada vez que el escudo es morado, %s bloqueó rastreadores en un sitio. Toca para más información.</string>
<!-- Enhanced Tracking Protection message that protection is currently on for this site --> <!-- Enhanced Tracking Protection message that protection is currently on for this site -->
<string name="etp_panel_on">Las protecciones están ACTIVAS para este sitio</string> <string name="etp_panel_on">Las protecciones están ACTIVAS para este sitio</string>
@ -1065,4 +1137,305 @@
<!-- Header for exceptions list for which sites enhanced tracking protection is always off --> <!-- Header for exceptions list for which sites enhanced tracking protection is always off -->
<string name="enhanced_tracking_protection_exceptions">La protección de rastreo mejorada está desactivada para estos sitios</string> <string name="enhanced_tracking_protection_exceptions">La protección de rastreo mejorada está desactivada para estos sitios</string>
</resources> <!-- Content description (not visible, for screen readers etc.): Navigate
back from ETP details (Ex: Tracking content) -->
<string name="etp_back_button_content_description">Regresar</string>
<!-- About page Your rights link text -->
<string name="about_your_rights">Tus derechos</string>
<!-- About page link text to open open source licenses screen -->
<string name="about_open_source_licenses">Bibliotecas de código abierto que utilizamos</string>
<!-- About page link text to open what's new link -->
<string name="about_whats_new">Novedades de %s</string>
<!-- Open source licenses page title
The first parameter is the app name -->
<string name="open_source_licenses_title">%s | Bibliotecas OSS</string>
<!-- About page link text to open support link -->
<string name="about_support">Ayuda</string>
<!-- About page link text to list of past crashes (like about:crashes on desktop) -->
<string name="about_crashes">Fallos</string>
<!-- About page link text to open privacy notice link -->
<string name="about_privacy_notice">Aviso de privacidad</string>
<!-- About page link text to open know your rights link -->
<string name="about_know_your_rights">Conoce tus derechos</string>
<!-- About page link text to open licensing information link -->
<string name="about_licensing_information">Información de licencia</string>
<!-- About page link text to open a screen with libraries that are used -->
<string name="about_other_open_source_libraries">Bibliotecas que usamos</string>
<!-- Toast shown to the user when they are activating the secret dev menu
The first parameter is number of long clicks left to enable the menu -->
<string name="about_debug_menu_toast_progress">Menú de depuración: quedan %1$d click(s) para habilitarlo</string>
<string name="about_debug_menu_toast_done">Menú de depuración habilitado</string>
<!-- Content description of the tab counter toolbar button when one tab is open -->
<string name="tab_counter_content_description_one_tab">1 pestaña</string>
<!-- Content description of the tab counter toolbar button when multiple tabs are open. First parameter will be replaced with the number of tabs (always more than one) -->
<string name="tab_counter_content_description_multi_tab">%d pestañas</string>
<!-- Browser long press popup menu -->
<!-- Copy the current url -->
<string name="browser_toolbar_long_press_popup_copy">Copiar</string>
<!-- Paste & go the text in the clipboard. '&amp;' is replaced with the ampersand symbol: & -->
<string name="browser_toolbar_long_press_popup_paste_and_go">Pegar e ir</string>
<!-- Paste the text in the clipboard -->
<string name="browser_toolbar_long_press_popup_paste">Pegar</string>
<!-- Snackbar message shown after an URL has been copied to clipboard. -->
<string name="browser_toolbar_url_copied_to_clipboard_snackbar">URL copiada al portapapeles</string>
<!-- Title text for the Add To Homescreen dialog -->
<string name="add_to_homescreen_title">Agregar a la pantalla de inicio</string>
<!-- Cancel button text for the Add to Homescreen dialog -->
<string name="add_to_homescreen_cancel">Cancelar</string>
<!-- Add button text for the Add to Homescreen dialog -->
<string name="add_to_homescreen_add">Agregar</string>
<!-- Continue to website button text for the first-time Add to Homescreen dialog -->
<string name="add_to_homescreen_continue">Continuar al sitio web</string>
<!-- Placeholder text for the TextView in the Add to Homescreen dialog -->
<string name="add_to_homescreen_text_placeholder">Nombre del acceso directo</string>
<!-- Describes the add to homescreen functionality -->
<string name="add_to_homescreen_description">Puedes agregar fácilmente este sitio web a la página de inicio de tu teléfono para navegar más rápido con una experiencia similar a una app.</string>
<!-- Preference for managing the settings for logins and passwords in Fenix -->
<string name="preferences_passwords_logins_and_passwords">Inicios de sesión y contraseñas</string>
<!-- Preference for managing the saving of logins and passwords in Fenix -->
<string name="preferences_passwords_save_logins">Guardar inicios de sesión y contraseñas</string>
<!-- Preference option for asking to save passwords in Fenix -->
<string name="preferences_passwords_save_logins_ask_to_save">Preguntar para guardar</string>
<!-- Preference option for never saving passwords in Fenix -->
<string name="preferences_passwords_save_logins_never_save">Nunca guardar</string>
<!-- Preference for autofilling saved logins in Fenix -->
<string name="preferences_passwords_autofill">Autocompletar</string>
<!-- Preference for syncing saved logins in Fenix -->
<string name="preferences_passwords_sync_logins">Sincronizar inicios de sesión</string>
<!-- Syncing saved logins in Fenix is on -->
<string name="preferences_passwords_sync_logins_on">Habilitado</string>
<!-- Syncing saved logins in Fenix is off -->
<string name="preferences_passwords_sync_logins_off">Deshabilitado</string>
<!-- Syncing saved logins in Fenix needs reconnect to sync -->
<string name="preferences_passwords_sync_logins_reconnect">Reconectar</string>
<!-- Syncing saved logins in Fenix needs login -->
<string name="preferences_passwords_sync_logins_sign_in">Iniciar sesión en Sync</string>
<!-- Preference to access list of saved logins -->
<string name="preferences_passwords_saved_logins">Inicios de sesión guardados</string>
<!-- Description of empty list of saved passwords. Placeholder is replaced with app name. -->
<string name="preferences_passwords_saved_logins_description_empty_text">Los inicios de sesión que guardes o sincronices en %s se mostrarán aquí</string>
<!-- Preference to access list of saved logins -->
<string name="preferences_passwords_saved_logins_description_empty_learn_more_link">Saber más acerca de Sync.</string>
<!-- Preference to access list of login exceptions that we never save logins for -->
<string name="preferences_passwords_exceptions">Excepciones</string>
<!-- Empty description of list of login exceptions that we never save logins for -->
<string name="preferences_passwords_exceptions_description_empty">Los inicios de sesión y contraseñas no guardados se mostrarán aquí.</string>
<!-- Description of list of login exceptions that we never save logins for -->
<string name="preferences_passwords_exceptions_description">Los inicios de sesión y contraseñas no serán guardados para estos sitios.</string>
<!-- Hint for search box in logins list -->
<string name="preferences_passwords_saved_logins_search">Buscar inicios de sesión</string>
<!-- Option to sort logins list A-Z, alphabetically -->
<string name="preferences_passwords_saved_logins_alphabetically">Alfabéticamente</string>
<!-- Option to sort logins list by most recently used -->
<string name="preferences_passwords_saved_logins_recently_used">Usados recientemente</string>
<!-- The header for the site that a login is for -->
<string name="preferences_passwords_saved_logins_site">Sitio</string>
<!-- The header for the username for a login -->
<string name="preferences_passwords_saved_logins_username">Nombre de usuario</string>
<!-- The header for the password for a login -->
<string name="preferences_passwords_saved_logins_password">Contraseña</string>
<!-- Message displayed in security prompt to reenter a secret pin to access saved logins -->
<string name="preferences_passwords_saved_logins_enter_pin">Vuelve a ingresar tu PIN</string>
<!-- Message displayed in security prompt to access saved logins -->
<string name="preferences_passwords_saved_logins_enter_pin_description">Desbloquear para ver los inicios de sesión guardados</string>
<!-- Message displayed when a connection is insecure and we detect the user is entering a password -->
<string name="logins_insecure_connection_warning">Esta conexión no es segura. Los inicios de sesión ingresados aquí pueden verse comprometidos.</string>
<!-- Learn more link that will link to a page with more information displayed when a connection is insecure and we detect the user is entering a password -->
<string name="logins_insecure_connection_warning_learn_more">Saber más</string>
<!-- Prompt message displayed when Fenix detects a user has entered a password and user decides if Fenix should save it. The first parameter is the name of the application (For example: Fenix) -->
<string name="logins_doorhanger_save">¿Quieres que %s guarde este inicio de sesión?</string>
<!-- Positive confirmation that Fenix should save the new or updated login -->
<string name="logins_doorhanger_save_confirmation">Guardar</string>
<!-- Negative confirmation that Fenix should not save the new or updated login -->
<string name="logins_doorhanger_save_dont_save">No guardar</string>
<!-- Shown in snackbar to tell user that the password has been copied -->
<string name="logins_password_copied">Contraseña copiada al portapapeles</string>
<!-- Shown in snackbar to tell user that the username has been copied -->
<string name="logins_username_copied">Nombre de usuario copiado al portapapeles</string>
<!-- Shown in snackbar to tell user that the site has been copied -->
<string name="logins_site_copied">Sitio copiado al portapapeles</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a password in logins-->
<string name="saved_logins_copy_password">Copiar contraseña</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a username in logins -->
<string name="saved_login_copy_username">Copiar nombre de usuario</string>
<!-- Content Description (for screenreaders etc) read for the button to copy a site in logins -->
<string name="saved_login_copy_site">Copiar sitio</string>
<!-- Content Description (for screenreaders etc) read for the button to reveal a password in logins -->
<string name="saved_login_reveal_password">Mostrar contraseña</string>
<!-- Content Description (for screenreaders etc) read for the button to hide a password in logins -->
<string name="saved_login_hide_password">Ocultar contraseña</string>
<!-- Message displayed in biometric prompt displayed for authentication before allowing users to view their logins -->
<string name="logins_biometric_prompt_message">Desbloquear para ver tus inicios de sesión guardados</string>
<!-- Title of warning dialog if users have no device authentication set up -->
<string name="logins_warning_dialog_title">Asegurar tus usuarios y contraseñas</string>
<!-- Message of warning dialog if users have no device authentication set up -->
<string name="logins_warning_dialog_message">Configura un patrón de bloqueo de dispositivo, PIN o contraseña para proteger el acceso a tus inicios de sesión y contraseñas guardadas si alguien más tiene tu dispositivo.</string>
<!-- Negative button to ignore warning dialog if users have no device authentication set up -->
<string name="logins_warning_dialog_later">Más tarde</string>
<!-- Positive button to send users to set up a pin of warning dialog if users have no device authentication set up -->
<string name="logins_warning_dialog_set_up_now">Configurar ahora</string>
<!-- Title of PIN verification dialog to direct users to re-enter their device credentials to access their logins -->
<string name="logins_biometric_prompt_message_pin">Desbloquear tu dispositivo</string>
<!-- Title for Accessibility Force Enable Zoom Preference -->
<string name="preference_accessibility_force_enable_zoom">Zoom en todos los sitios</string>
<!-- Summary for Accessibility Force Enable Zoom Preference -->
<string name="preference_accessibility_force_enable_zoom_summary">Habilitar para permitir pellizcar y hacer zoom, incluso en sitios web que previenen este gesto.</string>
<!-- Saved logins sorting strategy menu item -by name- (if selected, it will sort saved logins alphabetically) -->
<string name="saved_logins_sort_strategy_alphabetically">Nombre (A-Z)</string>
<!-- Saved logins sorting strategy menu item -by last used- (if selected, it will sort saved logins by last used) -->
<string name="saved_logins_sort_strategy_last_used">Último uso</string>
<!-- Content description (not visible, for screen readers etc.): Sort saved logins dropdown menu chevron icon -->
<string name="saved_logins_menu_dropdown_chevron_icon_content_description">Ordenar menú de inicios de sesión</string>
<!-- Title of the Add search engine screen -->
<string name="search_engine_add_custom_search_engine_title">Agregar motor de búsqueda</string>
<!-- Title of the Edit search engine screen -->
<string name="search_engine_edit_custom_search_engine_title">Editar motor de búsqueda</string>
<!-- Content description (not visible, for screen readers etc.): Title for the button to add a search engine in the action bar -->
<string name="search_engine_add_button_content_description">Agregar</string>
<!-- Content description (not visible, for screen readers etc.): Title for the button to save a search engine in the action bar -->
<string name="search_engine_add_custom_search_engine_edit_button_content_description">Guardar</string>
<!-- Text for the menu button to edit a search engine -->
<string name="search_engine_edit">Editar</string>
<!-- Text for the menu button to delete a search engine -->
<string name="search_engine_delete">Eliminar</string>
<!-- Text for the button to create a custom search engine on the Add search engine screen -->
<string name="search_add_custom_engine_label_other">Otro</string>
<!-- Placeholder text shown in the Search Engine Name TextField before a user enters text -->
<string name="search_add_custom_engine_name_hint">Nombre</string>
<!-- Placeholder text shown in the Search String TextField before a user enters text -->
<string name="search_add_custom_engine_search_string_hint">Cadena de búsqueda a usar</string>
<!-- Description text for the Search String TextField. The %s is part of the string -->
<string name="search_add_custom_engine_search_string_example">Reemplazar la consulta con “%s”. Ejemplo:\n https://www.google.com/search?q=%s</string>
<!-- Text for the button to learn more about adding a custom search engine -->
<string name="search_add_custom_engine_learn_more_label">Saber más</string>
<!-- Accessibility description for the form in which details about the custom search engine are entered -->
<string name="search_add_custom_engine_form_description">Detalles del motor de búsqueda personalizado</string>
<!-- Accessibility description for the 'Learn more' link -->
<string name="search_add_custom_engine_learn_more_description">Enlace para saber más</string>
<!-- Text shown when a user leaves the name field empty -->
<string name="search_add_custom_engine_error_empty_name">Introducir nombre de motor de búsqueda</string>
<!-- Text shown when a user tries to add a search engine that already exists -->
<string name="search_add_custom_engine_error_existing_name">Ya existe un motor de búsqueda con nombre “%s”.</string>
<!-- Text shown when a user leaves the search string field empty -->
<string name="search_add_custom_engine_error_empty_search_string">Ingresa una cadena de búsqueda</string>
<!-- Text shown when a user leaves out the required template string -->
<string name="search_add_custom_engine_error_missing_template">Verifica que la cadena de búsqueda corresponde con el formato de ejemplo</string>
<!-- Text shown when we aren't able to validate the custom search query. The first parameter is the url of the custom search engine -->
<string name="search_add_custom_engine_error_cannot_reach">Error al conectarse a “%s”</string>
<!-- Text shown when a user creates a new search engine -->
<string name="search_add_custom_engine_success_message">Se creó %s</string>
<!-- Text shown when a user successfully edits a custom search engine -->
<string name="search_edit_custom_engine_success_message">Se guardó %s</string>
<!-- Text shown when a user successfully deletes a custom search engine -->
<string name="search_delete_search_engine_success_message">Se eliminó %s</string>
<!-- Title text shown for the migration screen to the new browser. Placeholder replaced with app name -->
<string name="migration_title">Bienvenido a un %s completamente nuevo</string>
<!-- Description text followed by a list of things migrating (e.g. Bookmarks, History). Placeholder replaced with app name-->
<string name="migration_description">Un navegador completamente re-diseñado te espera, con un mejor desempeño y funciones que te ayudarán a hacer más en línea.\n\nPor favor espera mientras actualizamos %s con tus</string>
<!-- Text on the disabled button while in progress. Placeholder replaced with app name -->
<string name="migration_updating_app_button_text">Actualizando %s…</string>
<!-- Text on the enabled button. Placeholder replaced with app name-->
<string name="migration_update_app_button">Iniciar %s</string>
<!-- Accessibility description text for a completed migration item -->
<string name="migration_icon_description">Migración completada</string>
<!--Text on list of migrated items (e.g. Settings, History, etc.)-->
<string name="migration_text_passwords">Contraseñas</string>
<!-- Heading for the instructions to allow a permission -->
<string name="phone_feature_blocked_intro">Para permitirlo:</string>
<!-- First step for the allowing a permission -->
<string name="phone_feature_blocked_step_settings">1. Ve a los ajustes de Android</string>
<!-- Second step for the allowing a permission -->
<string name="phone_feature_blocked_step_permissions"><![CDATA[2. Toca en <b>Permisos</b>]]></string>
<!-- Third step for the allowing a permission (Fore example: Camera) -->
<string name="phone_feature_blocked_step_feature"><![CDATA[3. Cambia <b>%1$s</b> a activado]]></string>
<!-- Label that indicates a site is using a secure connection -->
<string name="quick_settings_sheet_secure_connection">Conexión segura</string>
<!-- Label that indicates a site is using a insecure connection -->
<string name="quick_settings_sheet_insecure_connection">Conexión insegura</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete all the permissions for all sites-->
<string name="confirm_clear_permissions_on_all_sites">¿Seguro que quieres borrar todos los permisos de todos los sitios?</string>
<!-- Confirmation message for a dialog confirming if the user wants to delete all the permissions for a site-->
<string name="confirm_clear_permissions_site">¿Seguro que quieres eliminar todos los permisos para este sitio?</string>
<!-- Confirmation message for a dialog confirming if the user wants to set default value a permission for a site-->
<string name="confirm_clear_permission_site">¿Seguro que quieres eliminar este permiso para este sitio?</string>
<!-- label shown when there are not site exceptions to show in the site exception settings -->
<string name="no_site_exceptions">Sin excepciones de sitio</string>
<!-- Label for the Pocket default top site -->
<string name="pocket_top_articles">Artículos principales</string>
<!-- Bookmark deletion confirmation -->
<string name="bookmark_deletion_confirmation">¿Seguro que quiere eliminar este marcador?</string>
<!-- Browser menu button that adds a top site to the home fragment -->
<string name="browser_menu_add_to_top_sites">Agregar a los sitios principales</string>
<!-- text shown before the issuer name to indicate who its verified by, parameter is the name of
the certificate authority that verified the ticket-->
<string name="certificate_info_verified_by">Verificado por: %1$s</string>
<!-- Login overflow menu delete button -->
<string name="login_menu_delete_button">Eliminar</string>
<!-- Login overflow menu edit button -->
<string name="login_menu_edit_button">Editar</string>
<!-- Message in delete confirmation dialog for logins -->
<string name="login_deletion_confirmation">¿Seguro que quieres eliminar este inicio de sesión?</string>
<!-- Positive action of a dialog asking to delete -->
<string name="dialog_delete_positive">Eliminar</string>
<!-- The saved login options menu description. -->
<string name="login_options_menu">Opciones de inicio de sesión</string>
<!-- The editable text field for a login's web address. -->
<string name="saved_login_hostname_description">El campo de texto editable para la dirección web del inicio de sesión.</string>
<!-- The editable text field for a login's username. -->
<string name="saved_login_username_description">El campo de texto editable para el nombre de usuario del inicio de sesión.</string>
<!-- The editable text field for a login's password. -->
<string name="saved_login_password_description">El campo de texto editable para la contraseña del inicio de sesión.</string>
<!-- The button description to save changes to an edited login. -->
<string name="save_changes_to_login">Guardar cambios al inicio de sesión.</string>
<!-- The button description to discard changes to an edited login. -->
<string name="discard_changes">Descartar cambios</string>
<!-- The page title for editing a saved login. -->
<string name="edit">Editar</string>
<!-- The error message in edit login view when password field is blank. -->
<string name="saved_login_password_required">Se requiere contraseña</string>
<!-- Voice search button content description -->
<string name="voice_search_content_description">Búsqueda por voz</string>
<!-- Voice search prompt description displayed after the user presses the voice search button -->
<string name="voice_search_explainer">Habla ahora</string>
<!-- The error message in edit login view when a duplicate username exists. -->
<string name="saved_login_duplicate">Ya existe un inicio de sesión con ese nombre de usuario</string>
<!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account -->
<string name="synced_tabs_connect_to_sync_account">Conectar con una cuenta de Firefox.</string>
<!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">Conectar otro dispositivo.</string>
<!-- Text displayed asking user to re-authenticate -->
<string name="synced_tabs_reauth">Por favor, vuelve a autenticarte.</string>
<!-- Text displayed when user has disabled tab syncing in Firefox Sync Account -->
<string name="synced_tabs_enable_tab_syncing">Por favor habilita la sincronización de pestañas.</string>
<!-- Text displayed when user has no tabs that have been synced -->
<string name="synced_tabs_no_tabs">No tienes ninguna pestaña abierta en Firefox en tus otros dispositivos.</string>
<!-- Text displayed in the synced tabs screen when a user is not signed in to Firefox Sync describing Synced Tabs -->
<string name="synced_tabs_sign_in_message">Ver una lista de pestañas de tus otros dispositivos.</string>
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">Iniciar sesión para sincronizar</string>
<!-- Top Sites -->
<!-- Title text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_title">Límite de sitios frecuentes alcanzado</string>
<!-- Content description text displayed in the dialog when top sites limit is reached. -->
<string name="top_sites_max_limit_content">Para agregar un nuevo sitio frecuente, elimina uno. Mantén presionado el sitio y selecciona eliminar.</string>
<!-- Confirmation dialog button text when top sites limit is reached. -->
<string name="top_sites_max_limit_confirmation_button">Vale, entendido</string>
</resources>

View File

@ -370,7 +370,7 @@
<!-- Title for experiments preferences --> <!-- Title for experiments preferences -->
<string name="preference_experiments">Eksperymenty</string> <string name="preference_experiments">Eksperymenty</string>
<!-- Summary for experiments preferences --> <!-- Summary for experiments preferences -->
<string name="preference_experiments_summary">Pozwala Mozilli instalować i zbierać dane dla funkcji eksperymentalnych</string> <string name="preference_experiments_summary">Pozwala Mozilli instalować i zbierać dane dla funkcji eksperymentalnych.</string>
<!-- Preference switch for crash reporter --> <!-- Preference switch for crash reporter -->
<string name="preferences_crash_reporter">Zgłaszanie awarii</string> <string name="preferences_crash_reporter">Zgłaszanie awarii</string>
<!-- Preference switch for Mozilla location service --> <!-- Preference switch for Mozilla location service -->
@ -892,8 +892,6 @@
<string name="preference_summary_delete_browsing_data_on_quit">Automatycznie usuwa dane przeglądania po wybraniu „Zakończ” z głównego menu</string> <string name="preference_summary_delete_browsing_data_on_quit">Automatycznie usuwa dane przeglądania po wybraniu „Zakończ” z głównego menu</string>
<!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. --> <!-- Summary for the Delete browsing data on quit preference. "Quit" translation should match delete_browsing_data_on_quit_action translation. -->
<string name="preference_summary_delete_browsing_data_on_quit_2">Automatycznie usuwa dane przeglądania po wybraniu „Zakończ” z głównego menu</string> <string name="preference_summary_delete_browsing_data_on_quit_2">Automatycznie usuwa dane przeglądania po wybraniu „Zakończ” z głównego menu</string>
<!-- Category for history items to delete on quit in delete browsing data on quit -->
<string name="preferences_delete_browsing_data_on_quit_browsing_history">Historia przeglądania</string>
<!-- Action item in menu for the Delete browsing data on quit feature --> <!-- Action item in menu for the Delete browsing data on quit feature -->
<string name="delete_browsing_data_on_quit_action">Zakończ</string> <string name="delete_browsing_data_on_quit_action">Zakończ</string>

View File

@ -954,10 +954,14 @@
<string name="onboarding_firefox_account_automatic_signin_failed">ไม่สามารถเข้าสู่ระบบ</string> <string name="onboarding_firefox_account_automatic_signin_failed">ไม่สามารถเข้าสู่ระบบ</string>
<!-- text for the tracking protection onboarding card header --> <!-- text for the tracking protection onboarding card header -->
<string name="onboarding_tracking_protection_header_2">ความเป็นส่วนตัวอัตโนมัติ</string> <string name="onboarding_tracking_protection_header_2">ความเป็นส่วนตัวอัตโนมัติ</string>
<!-- text for tracking protection radio button option for standard level of blocking -->
<string name="onboarding_tracking_protection_standard_button_2">มาตรฐาน (ค่าเริ่มต้น)</string>
<!-- text for tracking protection radio button option for strict level of blocking --> <!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_button">เข้มงวด (แนะนำ)</string> <string name="onboarding_tracking_protection_strict_button">เข้มงวด (แนะนำ)</string>
<!-- text for tracking protection radio button option for strict level of blocking --> <!-- text for tracking protection radio button option for strict level of blocking -->
<string name="onboarding_tracking_protection_strict_option">เข้มงวด</string> <string name="onboarding_tracking_protection_strict_option">เข้มงวด</string>
<!-- text for strict blocking option button description -->
<string name="onboarding_tracking_protection_strict_button_description_2">ปิดกั้นตัวติดตาม โฆษณา และป๊อปอัปมากขึ้น เว็บโหลดเร็วขึ้น แต่ฟังก์ชั่นบางอย่างอาจไม่ทำงาน</string>
<!-- text for the toolbar position card header <!-- text for the toolbar position card header
In English this is an idiom for "choose a side as in an argument or fight" In English this is an idiom for "choose a side as in an argument or fight"
but it is ok to make this more literally about "choosing a position in a physical space --> but it is ok to make this more literally about "choosing a position in a physical space -->
@ -1043,14 +1047,20 @@
<string name="preference_enhanced_tracking_protection_explanation">เก็บข้อมูลของคุณไว้กับตัวคุณเอง %s ปกป้องคุณจากตัวติดตามที่พบบ่อยที่สุดซึ่งติดตามสิ่งที่คุณทำทางออนไลน์</string> <string name="preference_enhanced_tracking_protection_explanation">เก็บข้อมูลของคุณไว้กับตัวคุณเอง %s ปกป้องคุณจากตัวติดตามที่พบบ่อยที่สุดซึ่งติดตามสิ่งที่คุณทำทางออนไลน์</string>
<!-- Text displayed that links to website about enhanced tracking protection --> <!-- Text displayed that links to website about enhanced tracking protection -->
<string name="preference_enhanced_tracking_protection_explanation_learn_more">เรียนรู้เพิ่มเติม</string> <string name="preference_enhanced_tracking_protection_explanation_learn_more">เรียนรู้เพิ่มเติม</string>
<!-- Preference for enhanced tracking protection for the standard protection settings -->
<string name="preference_enhanced_tracking_protection_standard_default_1">มาตรฐาน (ค่าเริ่มต้น)</string>
<!-- Accessibility text for the Standard protection information icon --> <!-- Accessibility text for the Standard protection information icon -->
<string name="preference_enhanced_tracking_protection_standard_info_button">สิ่งที่ถูกปิดกั้นโดยการป้องกันการติดตามแบบมาตรฐาน</string> <string name="preference_enhanced_tracking_protection_standard_info_button">สิ่งที่ถูกปิดกั้นโดยการป้องกันการติดตามแบบมาตรฐาน</string>
<!-- Preference for enhanced tracking protection for the strict protection settings --> <!-- Preference for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict">เข้มงวด</string> <string name="preference_enhanced_tracking_protection_strict">เข้มงวด</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_strict_description_2">ปิดกั้นตัวติดตาม โฆษณา และป๊อปอัปมากขึ้น เว็บโหลดเร็วขึ้น แต่ฟังก์ชั่นบางอย่างอาจไม่ทำงาน</string>
<!-- Accessibility text for the Strict protection information icon --> <!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_strict_info_button">สิ่งที่ถูกปิดกั้นโดยการป้องกันการติดตามแบบเข้มงวด</string> <string name="preference_enhanced_tracking_protection_strict_info_button">สิ่งที่ถูกปิดกั้นโดยการป้องกันการติดตามแบบเข้มงวด</string>
<!-- Preference for enhanced tracking protection for the custom protection settings --> <!-- Preference for enhanced tracking protection for the custom protection settings -->
<string name="preference_enhanced_tracking_protection_custom">กำหนดเอง</string> <string name="preference_enhanced_tracking_protection_custom">กำหนดเอง</string>
<!-- Preference description for enhanced tracking protection for the strict protection settings -->
<string name="preference_enhanced_tracking_protection_custom_description_2">เลือกตัวติดตามหรือสคริปต์ที่ต้องการปิดกั้น</string>
<!-- Accessibility text for the Strict protection information icon --> <!-- Accessibility text for the Strict protection information icon -->
<string name="preference_enhanced_tracking_protection_custom_info_button">สิ่งที่ถูกปิดกั้นโดยการป้องกันการติดตามแบบกำหนดเอง</string> <string name="preference_enhanced_tracking_protection_custom_info_button">สิ่งที่ถูกปิดกั้นโดยการป้องกันการติดตามแบบกำหนดเอง</string>
<!-- Header for categories that are being blocked by current Enhanced Tracking Protection settings --> <!-- Header for categories that are being blocked by current Enhanced Tracking Protection settings -->
@ -1365,6 +1375,8 @@
<string name="dialog_delete_positive">ลบ</string> <string name="dialog_delete_positive">ลบ</string>
<!-- The saved login options menu description. --> <!-- The saved login options menu description. -->
<string name="login_options_menu">ตัวเลือกการเข้าสู่ระบบ</string> <string name="login_options_menu">ตัวเลือกการเข้าสู่ระบบ</string>
<!-- The button description to save changes to an edited login. -->
<string name="save_changes_to_login">บันทึกการเปลี่ยนแปลงเพื่อเข้าสู่ระบบ</string>
<!-- The button description to discard changes to an edited login. --> <!-- The button description to discard changes to an edited login. -->
<string name="discard_changes">ละทิ้งการเปลี่ยนแปลง</string> <string name="discard_changes">ละทิ้งการเปลี่ยนแปลง</string>
<!-- The page title for editing a saved login. --> <!-- The page title for editing a saved login. -->
@ -1376,12 +1388,19 @@
<!-- Voice search prompt description displayed after the user presses the voice search button --> <!-- Voice search prompt description displayed after the user presses the voice search button -->
<string name="voice_search_explainer">พูดเลย</string> <string name="voice_search_explainer">พูดเลย</string>
<!-- The error message in edit login view when a duplicate username exists. -->
<string name="saved_login_duplicate">การเข้าสู่ระบบที่มีชื่อผู้ใช้นี้มีอยู่แล้ว</string>
<!-- Synced Tabs --> <!-- Synced Tabs -->
<!-- Text displayed when user is not logged into a Firefox Account --> <!-- Text displayed when user is not logged into a Firefox Account -->
<string name="synced_tabs_connect_to_sync_account">เชื่อมต่อกับบัญชี Firefox</string> <string name="synced_tabs_connect_to_sync_account">เชื่อมต่อกับบัญชี Firefox</string>
<!-- Text displayed to ask user to connect another device as no devices found with account --> <!-- Text displayed to ask user to connect another device as no devices found with account -->
<string name="synced_tabs_connect_another_device">เชื่อมต่ออุปกรณ์อื่น</string> <string name="synced_tabs_connect_another_device">เชื่อมต่ออุปกรณ์อื่น</string>
<!-- Text displayed when user has no tabs that have been synced -->
<string name="synced_tabs_no_tabs">คุณไม่มีแท็บใด ๆ ที่เปิดอยู่ใน Firefox บนอุปกรณ์อื่น ๆ ของคุณ</string>
<!-- Text displayed in the synced tabs screen when a user is not signed in to Firefox Sync describing Synced Tabs -->
<string name="synced_tabs_sign_in_message">ดูรายการแท็บจากอุปกรณ์อื่น ๆ ของคุณ</string>
<!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync --> <!-- Text displayed on a button in the synced tabs screen to link users to sign in when a user is not signed in to Firefox Sync -->
<string name="synced_tabs_sign_in_button">ลงชื่อเข้าใช้เพื่อซิงค์</string> <string name="synced_tabs_sign_in_button">ลงชื่อเข้าใช้เพื่อซิงค์</string>

View File

@ -940,7 +940,9 @@
<!-- text for firefox preview moving tip header "Firefox Preview" and "Firefox Nightly" are intentionally hardcoded --> <!-- text for firefox preview moving tip header "Firefox Preview" and "Firefox Nightly" are intentionally hardcoded -->
<string name="tip_firefox_preview_moved_header">Firefox Nightly 已接手原 Firefox Preview 功能</string> <string name="tip_firefox_preview_moved_header">Firefox Nightly 已接手原 Firefox Preview 功能</string>
<!-- text for firefox preview moving tip description --> <!-- text for firefox preview moving tip description -->
<string name="tip_firefox_preview_moved_description">Firefox Nightly 每天都會更新,當中包含實驗中的新功能,然而這些新功能可能會較不穩定。\n\n若想要有較穩定的使用體驗請下載我們的 Beta 測試版。</string> <string name="tip_firefox_preview_moved_description">Firefox Nightly 每天都會更新,當中包含實驗中的新功能,然而這些新功能可能會較不穩定。\n\n
若想要有較穩定的使用體驗,請下載我們的 Beta 測試版。</string>
<!-- text for firefox preview moving tip button. "Firefox for Android Beta" is intentionally hardcoded --> <!-- text for firefox preview moving tip button. "Firefox for Android Beta" is intentionally hardcoded -->
<string name="tip_firefox_preview_moved_button_2">下載 Firefox for Android Beta 測試版</string> <string name="tip_firefox_preview_moved_button_2">下載 Firefox for Android Beta 測試版</string>

View File

@ -0,0 +1,112 @@
/* 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.view.View
import io.mockk.MockKAnnotations
import io.mockk.every
import io.mockk.impl.annotations.MockK
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkObject
import io.mockk.verify
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.mozilla.fenix.R
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.components.FenixSnackbar.Companion.LENGTH_SHORT
class FenixSnackbarDelegateTest {
@MockK private lateinit var view: View
@MockK(relaxed = true) private lateinit var snackbar: FenixSnackbar
private lateinit var delegate: FenixSnackbarDelegate
@Before
fun setup() {
MockKAnnotations.init(this)
mockkObject(FenixSnackbar.Companion)
delegate = FenixSnackbarDelegate(view)
every {
FenixSnackbar.make(view, LENGTH_SHORT, isDisplayedWithBrowserToolbar = true)
} returns snackbar
every { snackbar.setText(any()) } returns snackbar
every { snackbar.setAction(any(), any()) } returns snackbar
every { view.context.getString(R.string.app_name) } returns "Firefox"
every { view.context.getString(R.string.edit) } returns "Edit"
}
@After
fun teardown() {
unmockkObject(FenixSnackbar.Companion)
}
@Test
fun `show with no listener nor action`() {
delegate.show(
snackBarParentView = mockk(),
text = R.string.app_name,
duration = 0,
action = 0,
listener = null
)
verify { snackbar.setText("Firefox") }
verify(exactly = 0) { snackbar.setAction(any(), any()) }
verify { snackbar.show() }
}
@Test
fun `show with listener but no action`() {
delegate.show(
snackBarParentView = mockk(),
text = R.string.app_name,
duration = 0,
action = 0,
listener = {}
)
verify { snackbar.setText("Firefox") }
verify(exactly = 0) { snackbar.setAction(any(), any()) }
verify { snackbar.show() }
}
@Test
fun `show with action but no listener`() {
delegate.show(
snackBarParentView = mockk(),
text = R.string.app_name,
duration = 0,
action = R.string.edit,
listener = null
)
verify { snackbar.setText("Firefox") }
verify(exactly = 0) { snackbar.setAction(any(), any()) }
verify { snackbar.show() }
}
@Test
fun `show with listener and action`() {
val listener = mockk<(View) -> Unit>(relaxed = true)
delegate.show(
snackBarParentView = mockk(),
text = R.string.app_name,
duration = 0,
action = R.string.edit,
listener = listener
)
verify { snackbar.setText("Firefox") }
verify { snackbar.setAction("Edit", withArg {
verify(exactly = 0) { listener(view) }
it.invoke()
verify { listener(view) }
}) }
verify { snackbar.show() }
}
}

View File

@ -10,7 +10,8 @@ import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
import mozilla.components.browser.session.SessionManager import mozilla.components.browser.session.SessionManager
import org.junit.Assert import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.mozilla.fenix.components.metrics.Event import org.mozilla.fenix.components.metrics.Event
@ -59,8 +60,8 @@ class TelemetrySessionObserverTest {
triggeredByRedirect = false, triggeredByRedirect = false,
triggeredByWebContent = false triggeredByWebContent = false
) )
Assert.assertEquals(sessionUrl, singleSessionObserver.originSessionUrl) assertEquals(sessionUrl, singleSessionObserver.originSessionUrl)
Assert.assertEquals(url, singleSessionObserver.redirectChain[0]) assertEquals(url, singleSessionObserver.redirectChain[0])
} }
@Test @Test
@ -77,9 +78,9 @@ class TelemetrySessionObserverTest {
triggeredByRedirect = false, triggeredByRedirect = false,
triggeredByWebContent = false triggeredByWebContent = false
) )
Assert.assertEquals(url, singleSessionObserver.originSessionUrl) assertEquals(url, singleSessionObserver.originSessionUrl)
Assert.assertEquals(url, singleSessionObserver.redirectChain[0]) assertEquals(url, singleSessionObserver.redirectChain[0])
Assert.assertEquals(newUrl, singleSessionObserver.redirectChain[1]) assertEquals(newUrl, singleSessionObserver.redirectChain[1])
} }
@Test @Test
@ -93,8 +94,8 @@ class TelemetrySessionObserverTest {
triggeredByRedirect = false, triggeredByRedirect = false,
triggeredByWebContent = false triggeredByWebContent = false
) )
Assert.assertNull(singleSessionObserver.originSessionUrl) assertNull(singleSessionObserver.originSessionUrl)
Assert.assertEquals(0, singleSessionObserver.redirectChain.size) assertEquals(0, singleSessionObserver.redirectChain.size)
} }
@Test @Test
@ -116,7 +117,7 @@ class TelemetrySessionObserverTest {
redirectChain redirectChain
) )
} }
Assert.assertNull(singleSessionObserver.originSessionUrl) assertNull(singleSessionObserver.originSessionUrl)
Assert.assertEquals(0, singleSessionObserver.redirectChain.size) assertEquals(0, singleSessionObserver.redirectChain.size)
} }
} }

View File

@ -5,6 +5,7 @@
package org.mozilla.fenix.browser package org.mozilla.fenix.browser
import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.LifecycleOwner
import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.verify import io.mockk.verify
import mozilla.components.browser.session.Session import mozilla.components.browser.session.Session
@ -41,7 +42,34 @@ class UriOpenedObserverTest {
observer.onSessionAdded(session) observer.onSessionAdded(session)
verify { session.register(observer.singleSessionObserver, owner) } verify { session.register(observer.singleSessionObserver, owner) }
observer.onSessionSelected(session)
verify { session.register(observer.singleSessionObserver, owner) }
observer.onSessionRemoved(session) observer.onSessionRemoved(session)
verify { session.unregister(observer.singleSessionObserver) } verify { session.unregister(observer.singleSessionObserver) }
} }
@Test
fun `registers when all sessions are restored`() {
val session1: Session = mockk(relaxed = true)
val session2: Session = mockk(relaxed = true)
every { sessionManager.sessions } returns listOf(session1, session2)
observer.onSessionsRestored()
verify { session1.register(observer.singleSessionObserver, owner) }
verify { session2.register(observer.singleSessionObserver, owner) }
}
@Test
fun `unregisters when all sessions are removed`() {
val session1: Session = mockk(relaxed = true)
val session2: Session = mockk(relaxed = true)
every { sessionManager.sessions } returns listOf(session1, session2)
observer.onAllSessionsRemoved()
verify { session1.unregister(observer.singleSessionObserver) }
verify { session2.unregister(observer.singleSessionObserver) }
}
} }

View File

@ -18,7 +18,7 @@ import mozilla.components.feature.pwa.WebAppShortcutManager
class TestCore(context: Context) : Core(context) { class TestCore(context: Context) : Core(context) {
override val engine = mockk<Engine>(relaxed = true) { override val engine = mockk<Engine>(relaxed = true) {
every { this@mockk getProperty "settings" } returns mockk<Settings>() every { this@mockk getProperty "settings" } returns mockk<Settings>(relaxed = true)
} }
override val sessionManager = SessionManager(engine) override val sessionManager = SessionManager(engine)
override val store = mockk<BrowserStore>() override val store = mockk<BrowserStore>()

View File

@ -0,0 +1,43 @@
/* 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.components.toolbar
import io.mockk.every
import io.mockk.mockk
import mozilla.components.browser.session.Session
import mozilla.components.browser.state.state.BrowserState
import mozilla.components.browser.state.state.ReaderState
import mozilla.components.browser.state.state.createTab
import mozilla.components.browser.state.store.BrowserStore
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.components.toolbar.BrowserToolbarView.Companion.getUrlForClipboard
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class BrowserToolbarViewTest {
@Test
fun getUrlForClipboard() {
val customTabSession: Session = mockk()
every { customTabSession.url } returns "https://mozilla.org"
// Custom tab
assertEquals("https://mozilla.org", getUrlForClipboard(mockk(), customTabSession))
// Regular tab
val regularTab = createTab(url = "http://firefox.com")
var store = BrowserStore(BrowserState(tabs = listOf(regularTab), selectedTabId = regularTab.id))
assertEquals(regularTab.content.url, getUrlForClipboard(store))
// Reader Tab
val readerTab = createTab(url = "moz-extension://1234",
readerState = ReaderState(active = true, activeUrl = "https://blog.mozilla.org/123")
)
store = BrowserStore(BrowserState(tabs = listOf(readerTab), selectedTabId = readerTab.id))
assertEquals(readerTab.readerState.activeUrl, getUrlForClipboard(store))
}
}

View File

@ -0,0 +1,42 @@
/* 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.exceptions
import io.mockk.mockk
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runBlockingTest
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.exceptions.viewholders.ExceptionsDeleteButtonViewHolder
import org.mozilla.fenix.exceptions.viewholders.ExceptionsHeaderViewHolder
import org.mozilla.fenix.exceptions.viewholders.ExceptionsListItemViewHolder
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@ExperimentalCoroutinesApi
@RunWith(FenixRobolectricTestRunner::class)
class ExceptionsAdapterTest {
private lateinit var interactor: ExceptionsInteractor
private lateinit var adapter: ExceptionsAdapter
@Before
fun setup() {
interactor = mockk()
adapter = ExceptionsAdapter(interactor)
}
@Test
fun `binds header and delete button with other adapter items`() = runBlockingTest {
adapter.updateData(listOf(mockk(), mockk()))
assertEquals(4, adapter.itemCount)
assertEquals(ExceptionsHeaderViewHolder.LAYOUT_ID, adapter.getItemViewType(0))
assertEquals(ExceptionsListItemViewHolder.LAYOUT_ID, adapter.getItemViewType(1))
assertEquals(ExceptionsListItemViewHolder.LAYOUT_ID, adapter.getItemViewType(2))
assertEquals(ExceptionsDeleteButtonViewHolder.LAYOUT_ID, adapter.getItemViewType(3))
}
}

View File

@ -0,0 +1,82 @@
/* 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.exceptions
import android.text.Spannable
import android.text.method.LinkMovementMethod
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.view.isVisible
import io.mockk.Runs
import io.mockk.every
import io.mockk.just
import io.mockk.mockk
import io.mockk.mockkConstructor
import io.mockk.unmockkConstructor
import io.mockk.verify
import kotlinx.android.synthetic.main.component_exceptions.*
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
import mozilla.components.support.test.robolectric.testContext
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class ExceptionsViewTest {
private lateinit var container: ViewGroup
private lateinit var interactor: ExceptionsInteractor
private lateinit var exceptionsView: ExceptionsView
@Before
fun setup() {
mockkConstructor(ExceptionsAdapter::class)
container = FrameLayout(testContext)
interactor = mockk()
exceptionsView = ExceptionsView(container, interactor)
every { anyConstructed<ExceptionsAdapter>().updateData(any()) } just Runs
}
@After
fun teardown() {
unmockkConstructor(ExceptionsAdapter::class)
}
@Test
fun `binds exception text`() {
assertTrue(exceptionsView.exceptions_learn_more.movementMethod is LinkMovementMethod)
assertTrue(exceptionsView.exceptions_learn_more.text is Spannable)
assertEquals("Learn more", exceptionsView.exceptions_learn_more.text.toString())
every { interactor.onLearnMore() } just Runs
exceptionsView.exceptions_learn_more.performClick()
verify { interactor.onLearnMore() }
}
@Test
fun `binds empty list to adapter`() {
exceptionsView.update(ExceptionsFragmentState(emptyList()))
assertTrue(exceptionsView.exceptions_empty_view.isVisible)
assertFalse(exceptionsView.exceptions_list.isVisible)
verify { anyConstructed<ExceptionsAdapter>().updateData(emptyList()) }
}
@Test
fun `binds list with items to adapter`() {
val items = listOf<TrackingProtectionException>(mockk(), mockk())
exceptionsView.update(ExceptionsFragmentState(items))
assertFalse(exceptionsView.exceptions_empty_view.isVisible)
assertTrue(exceptionsView.exceptions_list.isVisible)
verify { anyConstructed<ExceptionsAdapter>().updateData(items) }
}
}

View File

@ -0,0 +1,43 @@
/* 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.exceptions.viewholders
import android.view.LayoutInflater
import android.view.View
import androidx.appcompat.view.ContextThemeWrapper
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.delete_exceptions_button.view.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.R
import org.mozilla.fenix.exceptions.ExceptionsInteractor
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class ExceptionsDeleteButtonViewHolderTest {
private lateinit var view: View
private lateinit var interactor: ExceptionsInteractor
private lateinit var viewHolder: ExceptionsDeleteButtonViewHolder
@Before
fun setup() {
val appCompatContext = ContextThemeWrapper(testContext, R.style.NormalTheme)
view = LayoutInflater.from(appCompatContext)
.inflate(ExceptionsDeleteButtonViewHolder.LAYOUT_ID, null)
interactor = mockk(relaxed = true)
viewHolder = ExceptionsDeleteButtonViewHolder(view, interactor)
}
@Test
fun `calls onDeleteAll on click`() {
view.removeAllExceptions.performClick()
verify { interactor.onDeleteAll() }
}
}

View File

@ -0,0 +1,56 @@
/* 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.exceptions.viewholders
import android.view.LayoutInflater
import android.view.View
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.exception_item.view.*
import mozilla.components.concept.engine.content.blocking.TrackingProtectionException
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.exceptions.ExceptionsInteractor
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class ExceptionsListItemViewHolderTest {
private lateinit var view: View
private lateinit var interactor: ExceptionsInteractor
private lateinit var viewHolder: ExceptionsListItemViewHolder
@Before
fun setup() {
view = LayoutInflater.from(testContext)
.inflate(ExceptionsListItemViewHolder.LAYOUT_ID, null)
interactor = mockk(relaxed = true)
viewHolder = ExceptionsListItemViewHolder(view, interactor)
}
@Test
fun `bind url and icon`() {
val exception = object : TrackingProtectionException {
override val url = "https://example.com/icon.svg"
}
viewHolder.bind(exception)
assertEquals(exception.url, view.webAddressView.text)
}
@Test
fun `calls onDeleteOne on click`() {
val exception = object : TrackingProtectionException {
override val url = "https://example.com/icon.svg"
}
viewHolder.bind(exception)
view.delete_exception.performClick()
verify { interactor.onDeleteOne(exception) }
}
}

View File

@ -0,0 +1,105 @@
/* 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.home.sessioncontrol.viewholders.onboarding
import android.view.LayoutInflater
import android.view.View
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkObject
import io.mockk.unmockkObject
import io.mockk.verify
import kotlinx.android.synthetic.main.onboarding_automatic_signin.view.*
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runBlockingTest
import mozilla.components.service.fxa.manager.SignInWithShareableAccountResult
import mozilla.components.service.fxa.sharing.ShareableAccount
import mozilla.components.support.test.robolectric.testContext
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.components.BackgroundServices
import org.mozilla.fenix.components.FenixSnackbar
import org.mozilla.fenix.ext.components
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@ExperimentalCoroutinesApi
@RunWith(FenixRobolectricTestRunner::class)
class OnboardingAutomaticSignInViewHolderTest {
private lateinit var view: View
private lateinit var backgroundServices: BackgroundServices
private lateinit var snackbar: FenixSnackbar
@Before
fun setup() {
view = LayoutInflater.from(testContext)
.inflate(OnboardingAutomaticSignInViewHolder.LAYOUT_ID, null)
snackbar = mockk(relaxed = true)
mockkObject(FenixSnackbar.Companion)
backgroundServices = testContext.components.backgroundServices
every { FenixSnackbar.make(any(), any(), any(), any()) } returns snackbar
}
@After
fun teardown() {
unmockkObject(FenixSnackbar.Companion)
}
@Test
fun `bind updates header text`() {
val holder = OnboardingAutomaticSignInViewHolder(view)
holder.bind(mockk {
every { email } returns "email@example.com"
})
assertEquals(
"You are signed in as email@example.com on another Firefox browser on this phone. Would you like to sign in with this account?",
view.header_text.text
)
assertTrue(view.turn_on_sync_button.isEnabled)
}
@Test
fun `sign in on click`() = runBlocking {
val account = mockk<ShareableAccount> {
every { email } returns "email@example.com"
}
every {
backgroundServices.accountManager.signInWithShareableAccountAsync(account)
} returns CompletableDeferred(SignInWithShareableAccountResult.Success)
val holder = OnboardingAutomaticSignInViewHolder(view, scope = this)
holder.bind(account)
holder.onClick(view.turn_on_sync_button)
assertEquals("Signing in…", view.turn_on_sync_button.text)
assertFalse(view.turn_on_sync_button.isEnabled)
}
@Test
fun `show error if sign in fails`() = runBlockingTest {
val account = mockk<ShareableAccount> {
every { email } returns "email@example.com"
}
every {
backgroundServices.accountManager.signInWithShareableAccountAsync(account)
} returns CompletableDeferred(SignInWithShareableAccountResult.Failure)
val holder = OnboardingAutomaticSignInViewHolder(view, scope = this)
holder.bind(account)
holder.onClick(view.turn_on_sync_button)
assertEquals("Yes, sign me in", view.turn_on_sync_button.text)
assertTrue(view.turn_on_sync_button.isEnabled)
verify { snackbar.setText("Failed to sign-in") }
}
}

View File

@ -0,0 +1,39 @@
/* 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.home.sessioncontrol.viewholders.onboarding
import android.view.LayoutInflater
import android.view.View
import io.mockk.mockk
import io.mockk.verify
import kotlinx.android.synthetic.main.onboarding_finish.view.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.sessioncontrol.OnboardingInteractor
@RunWith(FenixRobolectricTestRunner::class)
class OnboardingFinishViewHolderTest {
private lateinit var view: View
private lateinit var interactor: OnboardingInteractor
@Before
fun setup() {
view = LayoutInflater.from(testContext)
.inflate(OnboardingFinishViewHolder.LAYOUT_ID, null)
interactor = mockk(relaxed = true)
}
@Test
fun `call interactor on click`() {
OnboardingFinishViewHolder(view, interactor)
view.finish_button.performClick()
verify { interactor.onStartBrowsingClicked() }
}
}

View File

@ -0,0 +1,34 @@
/* 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.home.sessioncontrol.viewholders.onboarding
import android.view.LayoutInflater
import android.view.View
import kotlinx.android.synthetic.main.onboarding_header.view.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class OnboardingHeaderViewHolderTest {
private lateinit var view: View
@Before
fun setup() {
view = LayoutInflater.from(testContext)
.inflate(OnboardingHeaderViewHolder.LAYOUT_ID, null)
}
@Test
fun `bind header text`() {
OnboardingHeaderViewHolder(view)
assertEquals("Welcome to Firefox Preview!", view.header_text.text)
}
}

View File

@ -0,0 +1,61 @@
/* 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.home.sessioncontrol.viewholders.onboarding
import android.view.LayoutInflater
import android.view.View
import androidx.navigation.NavController
import androidx.navigation.Navigation
import io.mockk.every
import io.mockk.mockk
import io.mockk.mockkStatic
import io.mockk.unmockkStatic
import io.mockk.verify
import kotlinx.android.synthetic.main.onboarding_manual_signin.view.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.home.HomeFragmentDirections
@RunWith(FenixRobolectricTestRunner::class)
class OnboardingManualSignInViewHolderTest {
private lateinit var view: View
private lateinit var navController: NavController
@Before
fun setup() {
view = LayoutInflater.from(testContext)
.inflate(OnboardingManualSignInViewHolder.LAYOUT_ID, null)
navController = mockk(relaxed = true)
mockkStatic(Navigation::class)
every { Navigation.findNavController(view) } returns navController
}
@After
fun teardown() {
unmockkStatic(Navigation::class)
}
@Test
fun `bind header text`() {
OnboardingManualSignInViewHolder(view).bind()
assertEquals("Get the most out of Firefox Preview.", view.header_text.text)
}
@Test
fun `navigate on click`() {
OnboardingManualSignInViewHolder(view)
view.turn_on_sync_button.performClick()
verify { navController.navigate(HomeFragmentDirections.actionGlobalTurnOnSync()) }
}
}

View File

@ -0,0 +1,38 @@
/* 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.home.sessioncontrol.viewholders.onboarding
import android.view.LayoutInflater
import android.view.View
import kotlinx.android.synthetic.main.onboarding_section_header.view.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
@RunWith(FenixRobolectricTestRunner::class)
class OnboardingSectionHeaderViewHolderTest {
private lateinit var view: View
@Before
fun setup() {
view = LayoutInflater.from(testContext)
.inflate(OnboardingSectionHeaderViewHolder.LAYOUT_ID, null)
}
@Test
fun `bind text`() {
val holder = OnboardingSectionHeaderViewHolder(view)
holder.bind { "Hello world" }
assertEquals(
"Hello world",
view.section_header_text.text
)
}
}

View File

@ -0,0 +1,65 @@
/* 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.home.sessioncontrol.viewholders.onboarding
import android.view.LayoutInflater
import android.view.View
import io.mockk.every
import io.mockk.mockk
import kotlinx.android.synthetic.main.onboarding_toolbar_position_picker.view.*
import mozilla.components.support.test.robolectric.testContext
import org.junit.After
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mozilla.fenix.helpers.FenixRobolectricTestRunner
import org.mozilla.fenix.utils.Settings
@RunWith(FenixRobolectricTestRunner::class)
class OnboardingToolbarPositionPickerViewHolderTest {
private lateinit var view: View
private lateinit var settings: Settings
@Before
fun setup() {
view = LayoutInflater.from(testContext)
.inflate(OnboardingToolbarPositionPickerViewHolder.LAYOUT_ID, null)
settings = mockk(relaxed = true)
Settings.instance = settings
}
@After
fun teardown() {
Settings.instance = null
}
@Test
fun `bottom illustration should select corresponding radio button`() {
every { settings.shouldUseBottomToolbar } returns false
OnboardingToolbarPositionPickerViewHolder(view)
assertTrue(view.toolbar_top_radio_button.isChecked)
assertFalse(view.toolbar_bottom_radio_button.isChecked)
view.toolbar_bottom_image.performClick()
assertFalse(view.toolbar_top_radio_button.isChecked)
assertTrue(view.toolbar_bottom_radio_button.isChecked)
}
@Test
fun `top illustration should select corresponding radio button`() {
every { settings.shouldUseBottomToolbar } returns true
OnboardingToolbarPositionPickerViewHolder(view)
assertFalse(view.toolbar_top_radio_button.isChecked)
assertTrue(view.toolbar_bottom_radio_button.isChecked)
view.toolbar_top_image.performClick()
assertTrue(view.toolbar_top_radio_button.isChecked)
assertFalse(view.toolbar_bottom_radio_button.isChecked)
}
}

View File

@ -44,7 +44,6 @@ class BaseSearchTelemetryTest {
engine.installWebExtension( engine.installWebExtension(
id = id, id = id,
url = resourceUrl, url = resourceUrl,
allowContentMessaging = true,
onSuccess = any(), onSuccess = any(),
onError = any() onError = any()
) )

View File

@ -3,5 +3,5 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
object AndroidComponents { object AndroidComponents {
const val VERSION = "49.0.20200707131055" const val VERSION = "50.0.20200708130551"
} }