1
0
Fork 0

Add tests for onboarding viewholders (#11522)

master
Tiger Oakes 2020-07-08 07:53:22 -07:00 committed by GitHub
parent cf94906e9d
commit 67ce5ad5d9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 380 additions and 32 deletions

View File

@ -5,12 +5,14 @@
package org.mozilla.fenix.home.sessioncontrol.viewholders.onboarding
import android.view.View
import android.widget.Button
import androidx.annotation.VisibleForTesting
import androidx.appcompat.content.res.AppCompatResources.getDrawable
import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.onboarding_automatic_signin.view.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import mozilla.components.service.fxa.manager.SignInWithShareableAccountResult
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.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 val headerText = view.header_text
init {
view.turn_on_sync_button.setOnClickListener {
it.context.components.analytics.metrics.track(Event.OnboardingAutoSignIn)
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'.
}
}
scope.launch {
onClick(it.turn_on_sync_button)
}
}
}
@ -69,6 +47,34 @@ class OnboardingAutomaticSignInViewHolder(view: View) : RecyclerView.ViewHolder(
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 {
const val LAYOUT_ID = R.layout.onboarding_automatic_signin
}

View File

@ -18,7 +18,7 @@ import mozilla.components.feature.pwa.WebAppShortcutManager
class TestCore(context: Context) : Core(context) {
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 store = mockk<BrowserStore>()

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)
}
}