#3601 [structured-commons] Wrong thumbnails at the "depictions" step when uploading (#3696)

* #3601 [structured-commons] Wrong thumbnails at the "depictions" step when uploading - fix imageview rendering code

* #3601 [structured-commons] Wrong thumbnails at the "depictions" step when uploading - show selected depictions at the top - ensure updates to nearby places are reflected

* #3601 [structured-commons] Wrong thumbnails at the "depictions" step when uploading - don't set imageUrl when the url is none

* #3601 [structured-commons] Wrong thumbnails at the "depictions" step when uploading - rewrite unit tests

* #3601 [structured-commons] Wrong thumbnails at the "depictions" step when uploading - minor cleanup

* #3601 [structured-commons] Wrong thumbnails at the "depictions" step when uploading - fix erroneous cache access
This commit is contained in:
Seán Mac Gillicuddy 2020-04-25 10:27:21 +01:00 committed by GitHub
parent 67faa40d8c
commit 707e3145c2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 407 additions and 366 deletions

View file

@ -1,22 +1,32 @@
package fr.free.nrw.commons.upload
import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.jraska.livedata.test
import com.nhaarman.mockitokotlin2.never
import com.nhaarman.mockitokotlin2.times
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.whenever
import fr.free.nrw.commons.category.CategoryItem
import fr.free.nrw.commons.explore.depictions.DepictsClient
import fr.free.nrw.commons.explore.depictions.DepictsClient.NO_DEPICTED_IMAGE
import fr.free.nrw.commons.repository.UploadRepository
import fr.free.nrw.commons.upload.depicts.DepictsContract
import fr.free.nrw.commons.upload.depicts.DepictsFragment
import fr.free.nrw.commons.upload.depicts.DepictsPresenter
import fr.free.nrw.commons.upload.structure.depictions.DepictedItem
import io.reactivex.Observable
import io.reactivex.Flowable
import io.reactivex.Single
import io.reactivex.schedulers.TestScheduler
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.ArgumentMatchers
import org.mockito.Mock
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
class DepictsPresenterTest {
@get:Rule
var testRule = InstantTaskExecutorRule()
@Mock
internal lateinit var repository: UploadRepository
@ -25,15 +35,10 @@ class DepictsPresenterTest {
private lateinit var depictsPresenter: DepictsPresenter
private lateinit var depictsFragment: DepictsFragment
private lateinit var testScheduler: TestScheduler
private val depictedItems: ArrayList<DepictedItem> = ArrayList()
@Mock
lateinit var depictedItem: DepictedItem
lateinit var depictsClient: DepictsClient
/**
* initial setup
@ -43,79 +48,143 @@ class DepictsPresenterTest {
fun setUp() {
MockitoAnnotations.initMocks(this)
testScheduler = TestScheduler()
depictedItem = DepictedItem("label", "desc", "", false, "entityId")
depictedItems.add(depictedItem)
depictsPresenter = DepictsPresenter(repository, testScheduler, testScheduler, null)
depictsFragment = DepictsFragment()
depictsPresenter = DepictsPresenter(repository, testScheduler, testScheduler, depictsClient)
depictsPresenter.onAttachView(view)
}
@Test
fun searchEnglishDepictionsTest() {
whenever(repository.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
whenever(repository.selectedDepictions).thenReturn(depictedItems)
whenever(repository.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
depictsPresenter.searchForDepictions("test")
verify(view).showProgress(true)
verify(view).setDepictsList(null)
fun `Search emission shows view progress`() {
depictsPresenter.searchForDepictions("")
testScheduler.triggerActions()
verify(view).showProgress(false)
}
@Test
fun searchOtherLanguageDepictions() {
whenever(repository.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
whenever(repository.selectedDepictions).thenReturn(depictedItems)
whenever(repository.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
depictsPresenter.searchForDepictions("वी")
verify(view).showProgress(true)
verify(view).setDepictsList(null)
fun `search results emission returns distinct results + selected items`() {
val searchResults = listOf(depictedItem(), depictedItem())
whenever(repository.searchAllEntities("")).thenReturn(Flowable.just(searchResults))
val selectedItem = depictedItem(id = "selected")
whenever(repository.selectedDepictions).thenReturn(listOf(selectedItem))
depictsPresenter.searchForDepictions("")
testScheduler.triggerActions()
verify(view).showProgress(false)
verify(view).showError(false)
depictsPresenter.depictedItems
.test()
.assertValue(listOf(selectedItem, depictedItem()))
}
@Test
fun searchForNonExistingDepictions() {
whenever(repository.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
whenever(repository.selectedDepictions).thenReturn(depictedItems)
whenever(repository.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
depictsPresenter.searchForDepictions("******")
verify(view).showProgress(true)
verify(view).setDepictsList(null)
fun `searchResults retrieve imageUrls from cache`() {
val depictedItem = depictedItem()
whenever(depictsClient.getP18ForItem(depictedItem.id)).thenReturn(Single.just("url"))
depictsPresenter.fetchThumbnailForEntityId(depictedItem)
testScheduler.triggerActions()
val searchResults = listOf(depictedItem(), depictedItem())
whenever(repository.searchAllEntities("")).thenReturn(Flowable.just(searchResults))
depictsPresenter.searchForDepictions("")
testScheduler.triggerActions()
depictsPresenter.depictedItems
.test()
.assertValue(listOf(depictedItem(imageUrl = "url")))
}
@Test
fun `empty search results with empty term do not show error`() {
whenever(repository.searchAllEntities("")).thenReturn(Flowable.just(emptyList()))
depictsPresenter.searchForDepictions("")
testScheduler.triggerActions()
verify(view).setDepictsList(null)
verify(view).showProgress(false)
verify(view).showError(false)
depictsPresenter.depictedItems
.test()
.assertValue(emptyList())
}
@Test
fun setSingleDepiction() {
whenever(repository.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
whenever(repository.selectedDepictions).thenReturn(depictedItems)
whenever(repository.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
depictsPresenter.onDepictItemClicked(depictedItem)
depictsPresenter.verifyDepictions()
verify(view).goToNextScreen()
}
@Test
fun setMultipleDepictions() {
whenever(repository.sortBySimilarity(ArgumentMatchers.anyString())).thenReturn(Comparator<CategoryItem> { _, _ -> 1 })
whenever(repository.selectedDepictions).thenReturn(depictedItems)
whenever(repository.searchAllEntities(ArgumentMatchers.anyString())).thenReturn(Observable.empty())
depictsPresenter.onDepictItemClicked(depictedItem)
val depictedItem2 = DepictedItem("label2", "desc2", "", false, "entityid2")
depictsPresenter.onDepictItemClicked(depictedItem2)
depictsPresenter.verifyDepictions()
verify(view).goToNextScreen()
}
@Test
fun `on Search Exception Show Error And Stop Progress`() {
whenever(repository.searchAllEntities(ArgumentMatchers.anyString()))
.thenReturn(Observable.error(Exception()))
depictsPresenter.searchForDepictions("******")
fun `empty search results with non empty term do show error`() {
whenever(repository.searchAllEntities("a")).thenReturn(Flowable.just(emptyList()))
depictsPresenter.searchForDepictions("a")
testScheduler.triggerActions()
verify(view).showProgress(false)
verify(view).showError(true)
depictsPresenter.depictedItems
.test()
.assertValue(emptyList())
}
@Test
fun `search error shows error`() {
whenever(repository.searchAllEntities("")).thenReturn(Flowable.error(Exception()))
depictsPresenter.searchForDepictions("")
testScheduler.triggerActions()
verify(view).showProgress(false)
verify(view).showError(true)
}
@Test
fun `onPreviousButtonClicked goes to previous screen`() {
depictsPresenter.onPreviousButtonClicked()
verify(view).goToPreviousScreen()
}
@Test
fun `onDepictItemClicked calls repository`() {
val depictedItem = depictedItem()
depictsPresenter.onDepictItemClicked(depictedItem)
verify(repository).onDepictItemClicked(depictedItem)
}
@Test
fun `verifyDepictions with non empty selectedDepictions goes to next screen`() {
whenever(repository.selectedDepictions).thenReturn(listOf(depictedItem()))
depictsPresenter.verifyDepictions()
verify(view).goToNextScreen()
}
@Test
fun `verifyDepictions with empty selectedDepictions goes to noDepictionSelected`() {
whenever(repository.selectedDepictions).thenReturn(emptyList())
depictsPresenter.verifyDepictions()
verify(view).noDepictionSelected()
}
@Test
fun `image urls fetched from network update the view`() {
val depictedItem = depictedItem()
whenever(depictsClient.getP18ForItem(depictedItem.id)).thenReturn(Single.just("url"))
depictsPresenter.fetchThumbnailForEntityId(depictedItem)
testScheduler.triggerActions()
verify(view).onUrlFetched(depictedItem, "url")
}
@Test
fun `image urls fetched from network filter NO_DEPICTED_IMAGE`() {
val depictedItem = depictedItem()
whenever(depictsClient.getP18ForItem(depictedItem.id))
.thenReturn(Single.just(NO_DEPICTED_IMAGE))
depictsPresenter.fetchThumbnailForEntityId(depictedItem)
testScheduler.triggerActions()
verify(view, never()).onUrlFetched(depictedItem, NO_DEPICTED_IMAGE)
}
@Test
fun `successive image urls fetched from cache`() {
val depictedItem = depictedItem()
whenever(depictsClient.getP18ForItem(depictedItem.id)).thenReturn(Single.just("url"))
depictsPresenter.fetchThumbnailForEntityId(depictedItem)
testScheduler.triggerActions()
verify(view).onUrlFetched(depictedItem, "url")
depictsPresenter.fetchThumbnailForEntityId(depictedItem)
testScheduler.triggerActions()
verify(view, times(2)).onUrlFetched(depictedItem, "url")
}
}
fun depictedItem(
name: String = "label",
description: String = "desc",
imageUrl: String = "",
isSelected: Boolean = false,
id: String = "entityId"
) = DepictedItem(name, description, imageUrl, isSelected, id)