Add tests for exceptions
parent
236b981881
commit
9ae1aa6f16
|
@ -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)
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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() }
|
||||||
|
}
|
||||||
|
}
|
|
@ -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) }
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue