Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions mvi/gradle.properties
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
POM_ARTIFACT_ID=mvi
GROUP=com.adidas.mvi
VERSION_CODE=1
VERSION_NAME=1.9.4
VERSION_NAME=1.9.5
POM_NAME=Adidas MVI
POM_DESCRIPTION=Adidas MVI
POM_DESCRIPTION=Adidas MVI
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ import com.adidas.mvi.sideeffects.SideEffects
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map

public fun <TIntent : Intent, TInnerState : LoggableState, TAction> Reducer(
coroutineScope: CoroutineScope,
Expand All @@ -34,3 +37,14 @@ public inline fun <reified TView : Any> Reducer<*, *>.requireView(): TView =
throw ClassCastException("Required view of ${TView::class} type, but found $this")
}
}

/**
* Wait until reducer's view is [T] and then return that View. Note that this function can potentially suspend
* indefinitely if the view is never reached.
*
* To use, you have to specify two parameters, first one is the target state, the second one can be left blank. Like this:
* `val state = reducer.awaitView<MyLoadedState, _>()`
*/
public suspend inline fun <reified T : P, P> Reducer<*, out State<P, *>>.awaitView(): T {
Comment thread
matejdro marked this conversation as resolved.
return state.map { it.view }.filterIsInstance<T>().first()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,68 @@ package com.adidas.mvi.reducer

import com.adidas.mvi.Intent
import com.adidas.mvi.State
import com.adidas.mvi.product.FakeProductViewTransform
import com.adidas.mvi.product.ProductSideEffect
import com.adidas.mvi.product.ProductState
import com.adidas.mvi.sideeffects.SideEffects
import com.adidas.mvi.transform.StateTransform
import io.kotest.core.spec.style.ShouldSpec
import io.kotest.engine.coroutines.backgroundScope
import io.kotest.engine.coroutines.testScheduler
import io.kotest.matchers.shouldBe
import kotlinx.coroutines.async
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.test.TestScope

class ReducerExtensionsTest : ShouldSpec({
context("A reducer instantiated with the extension") {
val reducer =
Reducer(
coroutineScope = TestScope(),
initialInnerState = ProductState.Loading,
intentExecutor = { _: Intent ->
emptyFlow<StateTransform<State<ProductState, Unit>>>()
},
)

should("The initial inner state should be Loading") {
reducer.state.value.view shouldBe ProductState.Loading
class ReducerExtensionsTest : ShouldSpec(
{
coroutineTestScope = true

context("A reducer instantiated with the extension") {
val reducer =
Reducer(
coroutineScope = TestScope(),
initialInnerState = ProductState.Loading,
intentExecutor = { _: Intent ->
emptyFlow<StateTransform<State<ProductState, Unit>>>()
},
)

should("The initial inner state should be Loading") {
reducer.state.value.view shouldBe ProductState.Loading
}
}

context("A reducer instantiated ") {
val reducer =
Reducer<Intent, ProductState, ProductSideEffect>(
coroutineScope = backgroundScope,
initialInnerState = ProductState.Loading,
intentExecutor = { intent: Intent ->
println("got intent $intent")
if (intent is TestIntent.CainIntent) {
flowOf<StateTransform<State<ProductState, ProductSideEffect>>>(FakeProductViewTransform(State(ProductState.Loaded, SideEffects())))
} else {
emptyFlow<StateTransform<State<ProductState, ProductSideEffect>>>()
}
},
)

val awaitJob = backgroundScope.async { reducer.awaitView<ProductState.Loaded, _>() }
testScheduler.runCurrent()

should("The initial await state should not return yet") {
awaitJob.isCompleted shouldBe false
}

should("return after emitting Loaded") {
reducer.executeIntent(TestIntent.CainIntent)
testScheduler.runCurrent()

awaitJob.isCompleted shouldBe true
awaitJob.await() shouldBe ProductState.Loaded
}
}
}
})
},
)
Loading