1
0
Fork 0

Add tests for exceptions

master
Tiger Oakes 2020-07-08 12:07:59 -07:00 committed by Emily Kager
parent 236b981881
commit 9ae1aa6f16
7 changed files with 249 additions and 25 deletions

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.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,
* along with controls to remove the exception.
*/
class ExceptionsAdapter(
private val interactor: ExceptionsInteractor
) : ListAdapter<AdapterItem, RecyclerView.ViewHolder>(DiffCallback) {
) : ListAdapter<ExceptionsAdapter.AdapterItem, RecyclerView.ViewHolder>(DiffCallback) {
/**
* 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>() {
override fun areItemsTheSame(oldItem: AdapterItem, newItem: AdapterItem) =
areContentsTheSame(oldItem, newItem)

View File

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

View File

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

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