Implement specification testing for StoryViewerViewModel.

fork-5.53.8
Alex Hart 2022-04-01 15:36:54 -03:00 zatwierdzone przez Cody Henthorne
rodzic 469879c211
commit 19861ef0d1
5 zmienionych plików z 166 dodań i 13 usunięć

Wyświetl plik

@ -4,6 +4,7 @@ import android.os.Bundle
import android.view.View
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.LiveDataReactiveStreams
import androidx.viewpager2.widget.ViewPager2
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.recipients.RecipientId
@ -40,7 +41,7 @@ class StoryViewerFragment : Fragment(R.layout.stories_viewer_fragment), StoryVie
storyPager.isUserInputEnabled = !it
}
viewModel.state.observe(viewLifecycleOwner) { state ->
LiveDataReactiveStreams.fromPublisher(viewModel.state).observe(viewLifecycleOwner) { state ->
adapter.setPages(state.pages)
if (state.pages.isNotEmpty() && storyPager.currentItem != state.page) {
storyPager.setCurrentItem(state.page, state.previousPage > -1)
@ -65,11 +66,11 @@ class StoryViewerFragment : Fragment(R.layout.stories_viewer_fragment), StoryVie
}
override fun onGoToPreviousStory(recipientId: RecipientId) {
viewModel.onGoToPreviousStory(recipientId)
viewModel.onGoToPrevious(recipientId)
}
override fun onFinishedPosts(recipientId: RecipientId) {
viewModel.onFinishedPosts(recipientId)
viewModel.onGoToNext(recipientId)
}
override fun onStoryHidden(recipientId: RecipientId) {

Wyświetl plik

@ -8,7 +8,10 @@ import org.thoughtcrime.securesms.database.model.StoryResult
import org.thoughtcrime.securesms.recipients.Recipient
import org.thoughtcrime.securesms.recipients.RecipientId
class StoryViewerRepository {
/**
* Open for testing
*/
open class StoryViewerRepository {
fun getStories(): Single<List<RecipientId>> {
return Single.fromCallable {
val storyResults: List<StoryResult> = SignalDatabase.mms.orderedStoryRecipientsAndIds.distinctBy { it.recipientId }

Wyświetl plik

@ -4,21 +4,23 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.kotlin.plusAssign
import org.thoughtcrime.securesms.recipients.RecipientId
import org.thoughtcrime.securesms.util.livedata.Store
import kotlin.math.min
import org.thoughtcrime.securesms.util.rx.RxStore
import kotlin.math.max
class StoryViewerViewModel(
private val startRecipientId: RecipientId,
private val repository: StoryViewerRepository
) : ViewModel() {
private val store = Store(StoryViewerState())
private val store = RxStore(StoryViewerState())
private val disposables = CompositeDisposable()
val state: LiveData<StoryViewerState> = store.stateLiveData
val stateSnapshot: StoryViewerState get() = store.state
val state: Flowable<StoryViewerState> = store.stateFlowable
private val scrollStatePublisher: MutableLiveData<Boolean> = MutableLiveData(false)
val isScrolling: LiveData<Boolean> = scrollStatePublisher
@ -66,7 +68,7 @@ class StoryViewerViewModel(
}
}
fun onFinishedPosts(recipientId: RecipientId) {
fun onGoToNext(recipientId: RecipientId) {
store.update {
if (it.pages[it.page] == recipientId) {
updatePages(it, it.page + 1)
@ -76,10 +78,10 @@ class StoryViewerViewModel(
}
}
fun onGoToPreviousStory(recipientId: RecipientId) {
fun onGoToPrevious(recipientId: RecipientId) {
store.update {
if (it.pages[it.page] == recipientId) {
updatePages(it, min(0, it.page - 1))
updatePages(it, max(0, it.page - 1))
} else {
it
}

Wyświetl plik

@ -238,7 +238,7 @@ class StoryViewerPageFragment :
viewModel.setIsUserScrollingParent(isScrolling)
}
sharedViewModel.state.observe(viewLifecycleOwner) { parentState ->
LiveDataReactiveStreams.fromPublisher(sharedViewModel.state).observe(viewLifecycleOwner) { parentState ->
if (parentState.pages.size <= parentState.page) {
viewModel.setIsSelectedPage(false)
} else if (storyRecipientId == parentState.pages[parentState.page]) {
@ -753,7 +753,7 @@ class StoryViewerPageFragment :
}
override fun onScroll(e1: MotionEvent, e2: MotionEvent, distanceX: Float, distanceY: Float): Boolean {
val isFirstStory = sharedViewModel.state.value?.page == 0
val isFirstStory = sharedViewModel.stateSnapshot.page == 0
val isXMagnitudeGreaterThanYMagnitude = abs(distanceX) > abs(distanceY) || viewToTranslate.translationX > 0f
val isFirstAndHasYTranslationOrNegativeY = isFirstStory && (viewToTranslate.translationY > 0f || distanceY < 0f)

Wyświetl plik

@ -0,0 +1,147 @@
package org.thoughtcrime.securesms.stories.viewer
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.plugins.RxJavaPlugins
import io.reactivex.rxjava3.schedulers.TestScheduler
import org.junit.After
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.mock
import org.mockito.kotlin.whenever
import org.thoughtcrime.securesms.recipients.RecipientId
class StoryViewerViewModelTest {
private val testScheduler = TestScheduler()
private val repository: StoryViewerRepository = mock()
@Before
fun setUp() {
RxJavaPlugins.setInitComputationSchedulerHandler { testScheduler }
RxJavaPlugins.setComputationSchedulerHandler { testScheduler }
}
@After
fun tearDown() {
RxJavaPlugins.reset()
}
@Test
fun `Given five stories, when I initialize with story 2, then I expect to be on the right page`() {
// GIVEN
val stories: List<RecipientId> = (1L..5L).map(RecipientId::from)
val startStory = RecipientId.from(2L)
whenever(repository.getStories()).doReturn(Single.just(stories))
// WHEN
val testSubject = StoryViewerViewModel(startStory, repository)
testScheduler.triggerActions()
// THEN
val expectedStartIndex = testSubject.stateSnapshot.pages.indexOf(startStory)
val actualStartIndex = testSubject.stateSnapshot.page
assertEquals(expectedStartIndex, actualStartIndex)
}
@Test
fun `Given five stories and am on 1, when I onGoToNext, then I expect to go to 2`() {
// GIVEN
val stories: List<RecipientId> = (1L..5L).map(RecipientId::from)
val startStory = RecipientId.from(1L)
whenever(repository.getStories()).doReturn(Single.just(stories))
val testSubject = StoryViewerViewModel(startStory, repository)
testScheduler.triggerActions()
// WHEN
testSubject.onGoToNext(RecipientId.from(1L))
testScheduler.triggerActions()
// THEN
val expectedIndex = 1
val actualIndex = testSubject.stateSnapshot.page
assertEquals(expectedIndex, actualIndex)
}
@Test
fun `Given five stories and am on last, when I onGoToNext, then I expect to go to size`() {
// GIVEN
val stories: List<RecipientId> = (1L..5L).map(RecipientId::from)
val startStory = stories.last()
whenever(repository.getStories()).doReturn(Single.just(stories))
val testSubject = StoryViewerViewModel(startStory, repository)
testScheduler.triggerActions()
// WHEN
testSubject.onGoToNext(startStory)
testScheduler.triggerActions()
// THEN
val expectedIndex = stories.size
val actualIndex = testSubject.stateSnapshot.page
assertEquals(expectedIndex, actualIndex)
}
@Test
fun `Given five stories and am on last, when I onGoToPrevious, then I expect to go to last - 1`() {
// GIVEN
val stories: List<RecipientId> = (1L..5L).map(RecipientId::from)
val startStory = stories.last()
whenever(repository.getStories()).doReturn(Single.just(stories))
val testSubject = StoryViewerViewModel(startStory, repository)
testScheduler.triggerActions()
// WHEN
testSubject.onGoToPrevious(startStory)
testScheduler.triggerActions()
// THEN
val expectedIndex = stories.lastIndex - 1
val actualIndex = testSubject.stateSnapshot.page
assertEquals(expectedIndex, actualIndex)
}
@Test
fun `Given five stories and am on first, when I onGoToPrevious, then I expect stay at 0`() {
// GIVEN
val stories: List<RecipientId> = (1L..5L).map(RecipientId::from)
val startStory = stories.first()
whenever(repository.getStories()).doReturn(Single.just(stories))
val testSubject = StoryViewerViewModel(startStory, repository)
testScheduler.triggerActions()
// WHEN
testSubject.onGoToPrevious(startStory)
testScheduler.triggerActions()
// THEN
val expectedIndex = 0
val actualIndex = testSubject.stateSnapshot.page
assertEquals(expectedIndex, actualIndex)
}
@Test
fun `Given five stories and am on first, when I setSelectedPage, then I expect to go to the page I selected`() {
// GIVEN
val stories: List<RecipientId> = (1L..5L).map(RecipientId::from)
val startStory = stories.first()
whenever(repository.getStories()).doReturn(Single.just(stories))
val testSubject = StoryViewerViewModel(startStory, repository)
testScheduler.triggerActions()
// WHEN
testSubject.setSelectedPage(2)
testScheduler.triggerActions()
// THEN
val expectedIndex = 2
val actualIndex = testSubject.stateSnapshot.page
assertEquals(expectedIndex, actualIndex)
}
}