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.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

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