adding missing image gallery reducer tests

This commit is contained in:
Adam Brown 2022-11-03 12:34:05 +00:00
parent 42761c0899
commit 6492afe063
3 changed files with 99 additions and 20 deletions

View File

@ -39,6 +39,8 @@ class ReducerTestScope<S, E>(
private val expectTestScope: ExpectTestScope private val expectTestScope: ExpectTestScope
) : ExpectTestScope by expectTestScope, Reducer<S> { ) : ExpectTestScope by expectTestScope, Reducer<S> {
private var invalidateCapturedState: Boolean = false
private val actionSideEffects = mutableMapOf<Action, () -> S>()
private var manualState: S? = null private var manualState: S? = null
private var capturedResult: S? = null private var capturedResult: S? = null
@ -47,6 +49,10 @@ class ReducerTestScope<S, E>(
override val coroutineScope = CoroutineScope(UnconfinedTestDispatcher()) override val coroutineScope = CoroutineScope(UnconfinedTestDispatcher())
override fun dispatch(action: Action) { override fun dispatch(action: Action) {
actionCaptures.add(action) actionCaptures.add(action)
if (actionSideEffects.containsKey(action)) {
setState(actionSideEffects.getValue(action).invoke(), invalidateCapturedState = true)
}
} }
override fun getState() = manualState ?: reducerFactory.initialState() override fun getState() = manualState ?: reducerFactory.initialState()
@ -54,15 +60,20 @@ class ReducerTestScope<S, E>(
private val reducer: Reducer<S> = reducerFactory.create(reducerScope) private val reducer: Reducer<S> = reducerFactory.create(reducerScope)
override fun reduce(action: Action) = reducer.reduce(action).also { override fun reduce(action: Action) = reducer.reduce(action).also {
capturedResult = it capturedResult = if (invalidateCapturedState) manualState else it
} }
fun setState(state: S) { fun actionSideEffect(action: Action, handler: () -> S) {
actionSideEffects[action] = handler
}
fun setState(state: S, invalidateCapturedState: Boolean = false) {
manualState = state manualState = state
this.invalidateCapturedState = invalidateCapturedState
} }
fun setState(block: (S) -> S) { fun setState(block: (S) -> S) {
manualState = block(reducerScope.getState()) setState(block(reducerScope.getState()))
} }
fun assertInitialState(expected: S) { fun assertInitialState(expected: S) {

View File

@ -46,7 +46,6 @@ fun imageGalleryReducer(
parent = ImageGalleryPage.Routes.folders, parent = ImageGalleryPage.Routes.folders,
state = ImageGalleryPage.Files(Lce.Loading(), action.folder) state = ImageGalleryPage.Files(Lce.Loading(), action.folder)
) )
dispatch(PageAction.GoTo(page)) dispatch(PageAction.GoTo(page))
jobBag.replace(ImageGalleryPage.Files::class, coroutineScope.launch { jobBag.replace(ImageGalleryPage.Files::class, coroutineScope.launch {

View File

@ -1,54 +1,123 @@
package app.dapk.st.messenger.gallery.state package app.dapk.st.messenger.gallery.state
import android.net.Uri
import app.dapk.st.core.Lce import app.dapk.st.core.Lce
import app.dapk.st.core.page.PageAction
import app.dapk.st.core.page.PageContainer
import app.dapk.st.core.page.PageStateChange
import app.dapk.st.design.components.SpiderPage import app.dapk.st.design.components.SpiderPage
import app.dapk.st.messenger.gallery.FetchMediaFoldersUseCase import app.dapk.st.messenger.gallery.FetchMediaFoldersUseCase
import app.dapk.st.messenger.gallery.FetchMediaUseCase import app.dapk.st.messenger.gallery.FetchMediaUseCase
import app.dapk.st.core.page.PageContainer import app.dapk.st.messenger.gallery.Folder
import app.dapk.st.messenger.gallery.Media
import app.dapk.state.Combined2 import app.dapk.state.Combined2
import fake.FakeJobBag import fake.FakeJobBag
import fake.FakeUri
import io.mockk.coEvery
import io.mockk.mockk import io.mockk.mockk
import org.junit.Test import org.junit.Test
import test.assertOnlyDispatches
import test.delegateReturn
import test.expect
import test.testReducer import test.testReducer
private const val A_ROOM_NAME = "a room name" private const val A_ROOM_NAME = "a room name"
private val A_FOLDER = Folder(
bucketId = "a-bucket-id",
title = "a title",
thumbnail = FakeUri().instance,
)
private val A_MEDIA_RESULT = listOf(aMedia())
private val A_FOLDERS_RESULT = listOf(aFolder())
private val AN_INITIAL_FILES_PAGE = SpiderPage(
route = ImageGalleryPage.Routes.files,
label = "Send to $A_ROOM_NAME",
parent = ImageGalleryPage.Routes.folders,
state = ImageGalleryPage.Files(Lce.Loading(), A_FOLDER)
)
private val AN_INITIAL_FOLDERS_PAGE = SpiderPage(
route = ImageGalleryPage.Routes.folders,
label = "Send to $A_ROOM_NAME",
parent = null,
state = ImageGalleryPage.Folders(Lce.Loading())
)
class ImageGalleryReducerTest { class ImageGalleryReducerTest {
private val fakeJobBag = FakeJobBag() private val fakeJobBag = FakeJobBag()
private val fakeFetchMediaFoldersUseCase = FakeFetchMediaFoldersUseCase()
private val fakeFetchMediaUseCase = FakeFetchMediaUseCase()
private val runReducerTest = testReducer { _: (Unit) -> Unit -> private val runReducerTest = testReducer { _: (Unit) -> Unit ->
imageGalleryReducer( imageGalleryReducer(
A_ROOM_NAME, A_ROOM_NAME,
FakeFetchMediaFoldersUseCase().instance, fakeFetchMediaFoldersUseCase.instance,
FakeFetchMediaUseCase().instance, fakeFetchMediaUseCase.instance,
fakeJobBag.instance, fakeJobBag.instance,
) )
} }
@Test @Test
fun `initial state is folders page`() = runReducerTest { fun `initial state is folders page`() = runReducerTest {
assertInitialState( assertInitialState(pageState(AN_INITIAL_FOLDERS_PAGE))
Combined2( }
state1 = PageContainer(
SpiderPage( @Test
route = ImageGalleryPage.Routes.folders, fun `when Visible, then updates Folders content`() = runReducerTest {
label = "Send to $A_ROOM_NAME", fakeJobBag.instance.expect { it.replace(ImageGalleryPage.Folders::class, any()) }
parent = null, fakeFetchMediaFoldersUseCase.givenFolders().returns(A_FOLDERS_RESULT)
state = ImageGalleryPage.Folders(Lce.Loading())
) reduce(ImageGalleryActions.Visible)
),
state2 = Unit assertOnlyDispatches(
) PageStateChange.UpdatePage(AN_INITIAL_FOLDERS_PAGE.state.copy(content = Lce.Content(A_FOLDERS_RESULT)))
)
}
@Test
fun `when SelectFolder, then goes to Folder page and fetches content`() = runReducerTest {
fakeJobBag.instance.expect { it.replace(ImageGalleryPage.Files::class, any()) }
fakeFetchMediaUseCase.givenMedia(A_FOLDER.bucketId).returns(A_MEDIA_RESULT)
val goToFolderPage = PageAction.GoTo(AN_INITIAL_FILES_PAGE)
actionSideEffect(goToFolderPage) { pageState(goToFolderPage.page) }
reduce(ImageGalleryActions.SelectFolder(A_FOLDER))
assertOnlyDispatches(
goToFolderPage,
PageStateChange.UpdatePage(goToFolderPage.page.state.copy(content = Lce.Content(A_MEDIA_RESULT)))
) )
} }
} }
private fun <P> pageState(page: SpiderPage<out P>) = Combined2(PageContainer(page), Unit)
class FakeFetchMediaFoldersUseCase { class FakeFetchMediaFoldersUseCase {
val instance = mockk<FetchMediaFoldersUseCase>() val instance = mockk<FetchMediaFoldersUseCase>()
fun givenFolders() = coEvery { instance.fetchFolders() }.delegateReturn()
} }
class FakeFetchMediaUseCase { class FakeFetchMediaUseCase {
val instance = mockk<FetchMediaUseCase>() val instance = mockk<FetchMediaUseCase>()
fun givenMedia(bucketId: String) = coEvery { instance.getMediaInBucket(bucketId) }.delegateReturn()
} }
fun aMedia(
id: Long = 1L,
uri: Uri = FakeUri().instance,
mimeType: String = "image/png",
width: Int = 100,
height: Int = 250,
size: Long = 1000L,
dateModifiedEpochMillis: Long = 5000L,
) = Media(id, uri, mimeType, width, height, size, dateModifiedEpochMillis)
fun aFolder(
bucketId: String = "a-bucket-id",
title: String = "a title",
thumbnail: Uri = FakeUri().instance,
) = Folder(bucketId, title, thumbnail)