mirror of
https://github.com/commons-app/apps-android-commons.git
synced 2025-10-27 21:03:54 +01:00
parent
63ab4a25aa
commit
f4d81eb4ca
32 changed files with 956 additions and 685 deletions
|
|
@ -1,11 +1,10 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
package fr.free.nrw.commons.explore
|
||||
|
||||
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
import com.jraska.livedata.test
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import io.reactivex.schedulers.TestScheduler
|
||||
import org.junit.Before
|
||||
|
|
@ -14,25 +13,25 @@ import org.junit.Test
|
|||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class SearchDepictionsFragmentPresenterTest {
|
||||
class BaseSearchPresenterTest {
|
||||
|
||||
@Rule
|
||||
@JvmField
|
||||
var instantTaskExecutorRule = InstantTaskExecutorRule()
|
||||
|
||||
@Mock
|
||||
internal lateinit var view: SearchDepictionsFragmentContract.View
|
||||
internal lateinit var view: SearchFragmentContract.View<String>
|
||||
|
||||
private lateinit var searchDepictionsFragmentPresenter: SearchDepictionsFragmentPresenter
|
||||
private lateinit var baseSearchPresenter: BaseSearchPresenter<String>
|
||||
|
||||
private lateinit var testScheduler: TestScheduler
|
||||
|
||||
@Mock
|
||||
private lateinit var searchableDepictionsDataSourceFactory: SearchableDepictionsDataSourceFactory
|
||||
private lateinit var pageableDataSource: PageableDataSource<String>
|
||||
|
||||
private var loadingStates: PublishProcessor<LoadingState> = PublishProcessor.create()
|
||||
|
||||
private var searchResults: PublishProcessor<LiveData<PagedList<DepictedItem>>> =
|
||||
private var searchResults: PublishProcessor<LiveData<PagedList<String>>> =
|
||||
PublishProcessor.create()
|
||||
|
||||
private var noItemLoadedEvent: PublishProcessor<Unit> = PublishProcessor.create()
|
||||
|
|
@ -41,21 +40,19 @@ class SearchDepictionsFragmentPresenterTest {
|
|||
@Throws(Exception::class)
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
whenever(searchableDepictionsDataSourceFactory.searchResults).thenReturn(searchResults)
|
||||
whenever(searchableDepictionsDataSourceFactory.loadingStates).thenReturn(loadingStates)
|
||||
whenever(searchableDepictionsDataSourceFactory.noItemsLoadedEvent)
|
||||
whenever(pageableDataSource.searchResults).thenReturn(searchResults)
|
||||
whenever(pageableDataSource.loadingStates).thenReturn(loadingStates)
|
||||
whenever(pageableDataSource.noItemsLoadedEvent)
|
||||
.thenReturn(noItemLoadedEvent)
|
||||
testScheduler = TestScheduler()
|
||||
searchDepictionsFragmentPresenter = SearchDepictionsFragmentPresenter(
|
||||
testScheduler,
|
||||
searchableDepictionsDataSourceFactory
|
||||
)
|
||||
searchDepictionsFragmentPresenter.onAttachView(view)
|
||||
baseSearchPresenter =
|
||||
object : BaseSearchPresenter<String>(testScheduler, pageableDataSource) {}
|
||||
baseSearchPresenter.onAttachView(view)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `searchResults emission updates the view`() {
|
||||
val pagedListLiveData = mock<LiveData<PagedList<DepictedItem>>>()
|
||||
val pagedListLiveData = mock<LiveData<PagedList<String>>>()
|
||||
searchResults.offer(pagedListLiveData)
|
||||
verify(view).observeSearchResults(pagedListLiveData)
|
||||
}
|
||||
|
|
@ -63,14 +60,13 @@ class SearchDepictionsFragmentPresenterTest {
|
|||
@Test
|
||||
fun `Loading offers a loading list item`() {
|
||||
onLoadingState(LoadingState.Loading)
|
||||
searchDepictionsFragmentPresenter.listFooterData.test()
|
||||
.assertValue(listOf(FooterItem.LoadingItem))
|
||||
baseSearchPresenter.listFooterData.test().assertValue(listOf(FooterItem.LoadingItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `Complete offers an empty list item and hides initial loader`() {
|
||||
onLoadingState(LoadingState.Complete)
|
||||
searchDepictionsFragmentPresenter.listFooterData.test()
|
||||
baseSearchPresenter.listFooterData.test()
|
||||
.assertValue(emptyList())
|
||||
verify(view).hideInitialLoadProgress()
|
||||
}
|
||||
|
|
@ -83,13 +79,12 @@ class SearchDepictionsFragmentPresenterTest {
|
|||
|
||||
@Test
|
||||
fun `Error offers a refresh list item, hides initial loader and shows error with a set text`() {
|
||||
searchDepictionsFragmentPresenter.onQueryUpdated("test")
|
||||
baseSearchPresenter.onQueryUpdated("test")
|
||||
onLoadingState(LoadingState.Error)
|
||||
verify(view).setEmptyViewText("test")
|
||||
verify(view).showSnackbar()
|
||||
verify(view).hideInitialLoadProgress()
|
||||
searchDepictionsFragmentPresenter.listFooterData.test()
|
||||
.assertValue(listOf(FooterItem.RefreshItem))
|
||||
baseSearchPresenter.listFooterData.test().assertValue(listOf(FooterItem.RefreshItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
@ -98,35 +93,33 @@ class SearchDepictionsFragmentPresenterTest {
|
|||
verify(view, never()).setEmptyViewText(any())
|
||||
verify(view).showSnackbar()
|
||||
verify(view).hideInitialLoadProgress()
|
||||
searchDepictionsFragmentPresenter.listFooterData.test()
|
||||
.assertValue(listOf(FooterItem.RefreshItem))
|
||||
baseSearchPresenter.listFooterData.test().assertValue(listOf(FooterItem.RefreshItem))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `no Items event sets empty view text`() {
|
||||
searchDepictionsFragmentPresenter.onQueryUpdated("test")
|
||||
baseSearchPresenter.onQueryUpdated("test")
|
||||
noItemLoadedEvent.offer(Unit)
|
||||
verify(view).setEmptyViewText("test")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `retryFailedRequest calls retry`() {
|
||||
searchDepictionsFragmentPresenter.retryFailedRequest()
|
||||
verify(searchableDepictionsDataSourceFactory).retryFailedRequest()
|
||||
baseSearchPresenter.retryFailedRequest()
|
||||
verify(pageableDataSource).retryFailedRequest()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onDetachView stops subscriptions`() {
|
||||
searchDepictionsFragmentPresenter.onDetachView()
|
||||
baseSearchPresenter.onDetachView()
|
||||
onLoadingState(LoadingState.Loading)
|
||||
searchDepictionsFragmentPresenter.listFooterData.test()
|
||||
.assertValue(emptyList())
|
||||
baseSearchPresenter.listFooterData.test().assertValue(emptyList())
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onQueryUpdated updates dataSourceFactory`() {
|
||||
searchDepictionsFragmentPresenter.onQueryUpdated("test")
|
||||
verify(searchableDepictionsDataSourceFactory).onQueryUpdated("test")
|
||||
baseSearchPresenter.onQueryUpdated("test")
|
||||
verify(pageableDataSource).onQueryUpdated("test")
|
||||
}
|
||||
|
||||
private fun onLoadingState(loadingState: LoadingState) {
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.paging.PagedList
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
import fr.free.nrw.commons.explore.depictions.LoadFunction
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class PageableDataSourceTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var liveDataConverter: LiveDataConverter
|
||||
|
||||
private lateinit var pageableDataSource: PageableDataSource<String>
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
pageableDataSource = object: PageableDataSource<String>(liveDataConverter){
|
||||
override val loadFunction: LoadFunction<String>
|
||||
get() = mock()
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onQueryUpdated emits new liveData`() {
|
||||
val (_, liveData) = expectNewLiveData()
|
||||
pageableDataSource.searchResults.test()
|
||||
.also { pageableDataSource.onQueryUpdated("test") }
|
||||
.assertValue(liveData)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `onQueryUpdated invokes livedatconverter with no items emitter`() {
|
||||
val (zeroItemsFuncCaptor, _) = expectNewLiveData()
|
||||
pageableDataSource.onQueryUpdated("test")
|
||||
pageableDataSource.noItemsLoadedEvent.test()
|
||||
.also { zeroItemsFuncCaptor.firstValue.invoke() }
|
||||
.assertValue(Unit)
|
||||
}
|
||||
|
||||
/*
|
||||
* Just for coverage, no way to really assert this
|
||||
* */
|
||||
@Test
|
||||
fun `retryFailedRequest does nothing without a factory`() {
|
||||
pageableDataSource.retryFailedRequest()
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Rewrite with Mockk constructor mocks")
|
||||
fun `retryFailedRequest retries with a factory`() {
|
||||
val (_, _, dataSourceFactoryCaptor) = expectNewLiveData()
|
||||
pageableDataSource.onQueryUpdated("test")
|
||||
val dataSourceFactory = spy(dataSourceFactoryCaptor.firstValue)
|
||||
pageableDataSource.retryFailedRequest()
|
||||
verify(dataSourceFactory).retryFailedRequest()
|
||||
}
|
||||
|
||||
private fun expectNewLiveData(): Triple<KArgumentCaptor<() -> Unit>, LiveData<PagedList<String>>, KArgumentCaptor<SearchDataSourceFactory<String>>> {
|
||||
val captor = argumentCaptor<() -> Unit>()
|
||||
val dataSourceFactoryCaptor = argumentCaptor<SearchDataSourceFactory<String>>()
|
||||
val liveData: LiveData<PagedList<String>> = mock()
|
||||
whenever(liveDataConverter.convert(dataSourceFactoryCaptor.capture(), captor.capture()))
|
||||
.thenReturn(liveData)
|
||||
return Triple(captor, liveData, dataSourceFactoryCaptor)
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.spy
|
||||
import com.nhaarman.mockitokotlin2.verify
|
||||
import fr.free.nrw.commons.explore.depictions.DepictsClient
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers.instanceOf
|
||||
import org.junit.Before
|
||||
import org.junit.Ignore
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.Mockito
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class SearchDataSourceFactoryTest {
|
||||
|
||||
@Mock
|
||||
private lateinit var depictsClient: DepictsClient
|
||||
|
||||
@Mock
|
||||
private lateinit var loadingStates: PublishProcessor<LoadingState>
|
||||
private lateinit var factory: SearchDataSourceFactory<String>
|
||||
|
||||
private var function: (Int, Int) -> List<String> = mock()
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
factory = object : SearchDataSourceFactory<String>(loadingStates) {
|
||||
override val loadFunction get() = function
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `create returns a dataSource`() {
|
||||
assertThat(
|
||||
factory.create(),
|
||||
instanceOf(SearchDataSource::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@Ignore("Rewrite with Mockk constructor mocks")
|
||||
fun `retryFailedRequest invokes method if not null`() {
|
||||
val spyFactory = spy(factory)
|
||||
val dataSource = mock<SearchDataSource<String>>()
|
||||
Mockito.doReturn(dataSource).`when`(spyFactory).create()
|
||||
factory.retryFailedRequest()
|
||||
verify(dataSource).retryFailedRequest()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `retryFailedRequest does not invoke method if null`() {
|
||||
factory.retryFailedRequest()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,120 @@
|
|||
package fr.free.nrw.commons.explore
|
||||
|
||||
import androidx.paging.PositionalDataSource
|
||||
import com.nhaarman.mockitokotlin2.*
|
||||
import fr.free.nrw.commons.explore.depictions.LoadingStates
|
||||
import io.reactivex.plugins.RxJavaPlugins
|
||||
import io.reactivex.processors.PublishProcessor
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.mockito.Mock
|
||||
import org.mockito.MockitoAnnotations
|
||||
|
||||
class SearchDataSourceTest {
|
||||
|
||||
private lateinit var loadingStates: PublishProcessor<LoadingState>
|
||||
private lateinit var searchDepictionsDataSource: TestSearchDataSource
|
||||
|
||||
@Mock
|
||||
private lateinit var mockGetItems: MockGetItems
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }
|
||||
MockitoAnnotations.initMocks(this)
|
||||
loadingStates = PublishProcessor.create()
|
||||
searchDepictionsDataSource =
|
||||
TestSearchDataSource(
|
||||
loadingStates,
|
||||
mockGetItems
|
||||
)
|
||||
}
|
||||
|
||||
@After
|
||||
fun tearDown() {
|
||||
RxJavaPlugins.reset()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loadInitial returns results and emits InitialLoad & Complete`() {
|
||||
val params = PositionalDataSource.LoadInitialParams(0, 1, 2, false)
|
||||
val callback = mock<PositionalDataSource.LoadInitialCallback<String>>()
|
||||
whenever(mockGetItems.getItems(1, 0)).thenReturn(emptyList())
|
||||
val testSubscriber = loadingStates.test()
|
||||
searchDepictionsDataSource.loadInitial(params, callback)
|
||||
verify(callback).onResult(emptyList(), 0)
|
||||
testSubscriber.assertValues(LoadingState.InitialLoad, LoadingState.Complete)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loadInitial onError does not return results and emits InitialLoad & Error`() {
|
||||
val params = PositionalDataSource.LoadInitialParams(0, 1, 2, false)
|
||||
val callback = mock<PositionalDataSource.LoadInitialCallback<String>>()
|
||||
whenever(mockGetItems.getItems(1, 0)).thenThrow(RuntimeException())
|
||||
val testSubscriber = loadingStates.test()
|
||||
searchDepictionsDataSource.loadInitial(params, callback)
|
||||
verify(callback, never()).onResult(any(), any())
|
||||
testSubscriber.assertValues(LoadingState.InitialLoad, LoadingState.Error)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loadRange returns results and emits Loading & Complete`() {
|
||||
val callback: PositionalDataSource.LoadRangeCallback<String> = mock()
|
||||
val params = PositionalDataSource.LoadRangeParams(0, 1)
|
||||
whenever(mockGetItems.getItems(params.loadSize, params.startPosition))
|
||||
.thenReturn(emptyList())
|
||||
val testSubscriber = loadingStates.test()
|
||||
searchDepictionsDataSource.loadRange(params, callback)
|
||||
verify(callback).onResult(emptyList())
|
||||
testSubscriber.assertValues(LoadingState.Loading, LoadingState.Complete)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `loadRange onError does not return results and emits Loading & Error`() {
|
||||
val callback: PositionalDataSource.LoadRangeCallback<String> = mock()
|
||||
val params = PositionalDataSource.LoadRangeParams(0, 1)
|
||||
whenever(mockGetItems.getItems(params.loadSize, params.startPosition))
|
||||
.thenThrow(RuntimeException())
|
||||
val testSubscriber = loadingStates.test()
|
||||
searchDepictionsDataSource.loadRange(params, callback)
|
||||
verify(callback, never()).onResult(any())
|
||||
testSubscriber.assertValues(LoadingState.Loading, LoadingState.Error)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `retryFailedRequest does nothing when null`() {
|
||||
searchDepictionsDataSource.retryFailedRequest()
|
||||
verifyNoMoreInteractions(mockGetItems)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `retryFailedRequest retries last request`() {
|
||||
val callback: PositionalDataSource.LoadRangeCallback<String> = mock()
|
||||
val params = PositionalDataSource.LoadRangeParams(0, 1)
|
||||
whenever(mockGetItems.getItems(params.loadSize, params.startPosition))
|
||||
.thenThrow(RuntimeException()).thenReturn(emptyList())
|
||||
val testSubscriber = loadingStates.test()
|
||||
searchDepictionsDataSource.loadRange(params, callback)
|
||||
verify(callback, never()).onResult(any())
|
||||
searchDepictionsDataSource.retryFailedRequest()
|
||||
verify(callback).onResult(emptyList())
|
||||
testSubscriber.assertValues(
|
||||
LoadingState.Loading,
|
||||
LoadingState.Error,
|
||||
LoadingState.Loading,
|
||||
LoadingState.Complete
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
class TestSearchDataSource(loadingStates: LoadingStates, val mockGetItems: MockGetItems) :
|
||||
SearchDataSource<String>(loadingStates) {
|
||||
override fun getItems(loadSize: Int, startPosition: Int): List<String> =
|
||||
mockGetItems.getItems(loadSize, startPosition)
|
||||
}
|
||||
|
||||
interface MockGetItems {
|
||||
fun getItems(loadSize: Int, startPosition: Int): List<String>
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package fr.free.nrw.commons.explore.categroies
|
||||
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import fr.free.nrw.commons.category.CategoryClient
|
||||
import fr.free.nrw.commons.explore.categories.PageableCategoriesDataSource
|
||||
import io.reactivex.Observable
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers
|
||||
import org.junit.Test
|
||||
|
||||
class PageableCategoriesDataSourceTest {
|
||||
@Test
|
||||
fun `loadFunction loads categories`() {
|
||||
val categoryClient: CategoryClient = mock()
|
||||
whenever(categoryClient.searchCategories("test", 0, 1))
|
||||
.thenReturn(Observable.just(emptyList()))
|
||||
val pageableCategoriesDataSource = PageableCategoriesDataSource(mock(), categoryClient)
|
||||
pageableCategoriesDataSource.onQueryUpdated("test")
|
||||
assertThat(pageableCategoriesDataSource.loadFunction(0, 1), Matchers.`is`(emptyList()))
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
package fr.free.nrw.commons.explore.depictions
|
||||
|
||||
import com.nhaarman.mockitokotlin2.mock
|
||||
import com.nhaarman.mockitokotlin2.whenever
|
||||
import io.reactivex.Single
|
||||
import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers
|
||||
import org.junit.Test
|
||||
|
||||
class PageableDepictionsDataSourceTest {
|
||||
|
||||
@Test
|
||||
fun `loadFunction loads depictions`() {
|
||||
val depictsClient: DepictsClient = mock()
|
||||
whenever(depictsClient.searchForDepictions("test", 0, 1)).thenReturn(Single.just(emptyList()))
|
||||
val pageableDepictionsDataSource = PageableDepictionsDataSource(mock(), depictsClient)
|
||||
pageableDepictionsDataSource.onQueryUpdated("test")
|
||||
assertThat(pageableDepictionsDataSource.loadFunction.invoke(0, 1), Matchers.`is`(emptyList()))
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue