diff --git a/redwood-compose/src/commonTest/kotlin/app/cash/redwood/compose/ChangeListenerTest.kt b/redwood-compose/src/commonTest/kotlin/app/cash/redwood/compose/ChangeListenerTest.kt index 6da0d4945c..388d3942c5 100644 --- a/redwood-compose/src/commonTest/kotlin/app/cash/redwood/compose/ChangeListenerTest.kt +++ b/redwood-compose/src/commonTest/kotlin/app/cash/redwood/compose/ChangeListenerTest.kt @@ -25,7 +25,6 @@ import app.cash.redwood.lazylayout.widget.RedwoodLazyLayoutTestingWidgetFactory import app.cash.redwood.protocol.widget.ProtocolBridge import app.cash.redwood.testing.TestRedwoodComposition import app.cash.redwood.testing.WidgetValue -import app.cash.redwood.ui.UiConfiguration import app.cash.redwood.widget.MutableListChildren import assertk.assertThat import assertk.assertions.containsExactly @@ -42,14 +41,13 @@ import com.example.redwood.testing.widget.TestSchemaWidgetFactories import com.example.redwood.testing.widget.TestSchemaWidgetFactory import kotlin.test.Test import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest class DirectChangeListenerTest : AbstractChangeListenerTest() { override fun CoroutineScope.launchComposition( factories: TestSchemaWidgetFactories, snapshot: () -> T, - ) = TestRedwoodComposition(this, factories, MutableListChildren(), MutableStateFlow(UiConfiguration()), snapshot) + ) = TestRedwoodComposition(this, factories, MutableListChildren(), createSnapshot = snapshot) } class ProtocolChangeListenerTest : AbstractChangeListenerTest() { @@ -61,7 +59,7 @@ class ProtocolChangeListenerTest : AbstractChangeListenerTest() { val widgetBridge = ProtocolBridge(MutableListChildren(), TestSchemaProtocolNodeFactory(factories)) { throw AssertionError() } - return TestRedwoodComposition(this, composeBridge.provider, composeBridge.root, MutableStateFlow(UiConfiguration())) { + return TestRedwoodComposition(this, composeBridge.provider, composeBridge.root) { composeBridge.getChangesOrNull()?.let { changes -> widgetBridge.sendChanges(changes) } diff --git a/redwood-lazylayout-compose/src/commonTest/kotlin/app/cash/redwood/lazylayout/compose/LazyListTest.kt b/redwood-lazylayout-compose/src/commonTest/kotlin/app/cash/redwood/lazylayout/compose/LazyListTest.kt index f0a1b6309f..d9c97a55fe 100644 --- a/redwood-lazylayout-compose/src/commonTest/kotlin/app/cash/redwood/lazylayout/compose/LazyListTest.kt +++ b/redwood-lazylayout-compose/src/commonTest/kotlin/app/cash/redwood/lazylayout/compose/LazyListTest.kt @@ -29,7 +29,6 @@ import app.cash.redwood.lazylayout.widget.WindowedLazyList import app.cash.redwood.testing.TestRedwoodComposition import app.cash.redwood.testing.WidgetValue import app.cash.redwood.ui.Margin -import app.cash.redwood.ui.UiConfiguration import app.cash.redwood.ui.dp import app.cash.redwood.widget.MutableListChildren import assertk.assertThat @@ -40,7 +39,6 @@ import com.example.redwood.testing.widget.TestSchemaWidgetFactories import com.example.redwood.testing.widget.TextValue import kotlin.test.Test import kotlinx.coroutines.coroutineScope -import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.test.runTest class LazyListTest { @@ -148,7 +146,6 @@ private suspend fun TestSchemaTester( this, factories, container, - MutableStateFlow(UiConfiguration()), ) { container.map { it.value } } diff --git a/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/ProtocolTest.kt b/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/ProtocolTest.kt index 097dcaee17..b0babf3aa3 100644 --- a/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/ProtocolTest.kt +++ b/redwood-protocol-compose/src/commonTest/kotlin/app/cash/redwood/protocol/compose/ProtocolTest.kt @@ -237,7 +237,6 @@ class ProtocolTest { scope = backgroundScope, provider = bridge.provider, container = bridge.root, - uiConfigurations = MutableStateFlow(UiConfiguration()), ) { bridge.getChangesOrNull() ?: emptyList() } diff --git a/redwood-testing/src/commonMain/kotlin/app/cash/redwood/testing/TestRedwoodComposition.kt b/redwood-testing/src/commonMain/kotlin/app/cash/redwood/testing/TestRedwoodComposition.kt index 93c5a114e9..bedc07a14a 100644 --- a/redwood-testing/src/commonMain/kotlin/app/cash/redwood/testing/TestRedwoodComposition.kt +++ b/redwood-testing/src/commonMain/kotlin/app/cash/redwood/testing/TestRedwoodComposition.kt @@ -18,6 +18,7 @@ package app.cash.redwood.testing import androidx.compose.runtime.BroadcastFrameClock import androidx.compose.runtime.Composable import app.cash.redwood.compose.RedwoodComposition +import app.cash.redwood.compose.current import app.cash.redwood.ui.Cancellable import app.cash.redwood.ui.OnBackPressedCallback import app.cash.redwood.ui.OnBackPressedDispatcher @@ -30,7 +31,7 @@ import kotlinx.coroutines.Job import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.delay -import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.plus import kotlinx.coroutines.withTimeout @@ -42,10 +43,10 @@ public fun TestRedwoodComposition( scope: CoroutineScope, provider: Widget.Provider, container: Widget.Children, - uiConfigurations: StateFlow, + initialUiConfiguration: UiConfiguration = UiConfiguration(), createSnapshot: () -> S, ): TestRedwoodComposition { - return RealTestRedwoodComposition(scope, provider, container, uiConfigurations, createSnapshot) + return RealTestRedwoodComposition(scope, provider, container, initialUiConfiguration, createSnapshot) } public interface TestRedwoodComposition : RedwoodComposition { @@ -55,6 +56,12 @@ public interface TestRedwoodComposition : RedwoodComposition { * @throws TimeoutCancellationException if no new snapshot is produced before [timeout]. */ public suspend fun awaitSnapshot(timeout: Duration = 1.seconds): S + + /** + * The mutable [UiConfiguration] instance bound to [UiConfiguration.current][current] + * inside the composition. + */ + public val uiConfigurations: MutableStateFlow } /** Performs Redwood composition strictly for testing. */ @@ -62,7 +69,7 @@ private class RealTestRedwoodComposition( scope: CoroutineScope, provider: Widget.Provider, container: Widget.Children, - uiConfigurations: StateFlow, + initialUiConfiguration: UiConfiguration, createSnapshot: () -> S, ) : TestRedwoodComposition { /** Emit frames manually in [sendFrames]. */ @@ -73,6 +80,8 @@ private class RealTestRedwoodComposition( /** Channel with the most recent snapshot, if any. */ private val snapshots = Channel(Channel.CONFLATED) + override val uiConfigurations = MutableStateFlow(initialUiConfiguration) + private val composition = RedwoodComposition( scope = scope + clock, container = container, diff --git a/redwood-testing/src/commonTest/kotlin/app/cash/redwood/testing/ViewTreesTest.kt b/redwood-testing/src/commonTest/kotlin/app/cash/redwood/testing/ViewTreesTest.kt index aaf74f3c27..44a3164dc1 100644 --- a/redwood-testing/src/commonTest/kotlin/app/cash/redwood/testing/ViewTreesTest.kt +++ b/redwood-testing/src/commonTest/kotlin/app/cash/redwood/testing/ViewTreesTest.kt @@ -18,6 +18,7 @@ package app.cash.redwood.testing import androidx.compose.runtime.BroadcastFrameClock import androidx.compose.runtime.Composable import app.cash.redwood.RedwoodCodegenApi +import app.cash.redwood.compose.current import app.cash.redwood.layout.widget.RedwoodLayoutTestingWidgetFactory import app.cash.redwood.lazylayout.widget.RedwoodLazyLayoutTestingWidgetFactory import app.cash.redwood.protocol.Change @@ -37,6 +38,7 @@ import app.cash.redwood.ui.OnBackPressedDispatcher import app.cash.redwood.ui.UiConfiguration import app.cash.redwood.widget.MutableListChildren import assertk.assertThat +import assertk.assertions.containsExactly import assertk.assertions.isEqualTo import com.example.redwood.testing.compose.TestRow import com.example.redwood.testing.compose.TestSchemaProtocolBridge @@ -45,6 +47,7 @@ import com.example.redwood.testing.widget.TestSchemaProtocolNodeFactory import com.example.redwood.testing.widget.TestSchemaTester import com.example.redwood.testing.widget.TestSchemaTestingWidgetFactory import com.example.redwood.testing.widget.TestSchemaWidgetFactories +import com.example.redwood.testing.widget.TextValue import kotlin.test.Test import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.plus @@ -142,4 +145,20 @@ class ViewTreesTest { assertThat(widgetContainer.map { it.value }).isEqualTo(snapshot) } + + @Test fun uiConfigurationWorks() = runTest { + TestSchemaTester { + setContent { + Text("Dark: ${UiConfiguration.current.darkMode}") + } + + val first = awaitSnapshot() + assertThat(first).containsExactly(TextValue(text = "Dark: false")) + + uiConfigurations.value = UiConfiguration(darkMode = true) + + val second = awaitSnapshot() + assertThat(second).containsExactly(TextValue(text = "Dark: true")) + } + } } diff --git a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/testingGeneration.kt b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/testingGeneration.kt index d7f878f8bc..2a0b7a567d 100644 --- a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/testingGeneration.kt +++ b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/testingGeneration.kt @@ -50,7 +50,7 @@ suspend fun ExampleTester( RedwoodLayout = RedwoodLayoutTestingWidgetFactory(), ) val container = MutableListChildren() - val tester = TestRedwoodComposition(this, factories, container, MutableStateFlow(UiConfiguration())) { + val tester = TestRedwoodComposition(this, factories, container) { container.map { it.value } } try { @@ -86,7 +86,7 @@ internal fun generateTester(schemaSet: SchemaSet): FileSpec { } .addCode("⇤)\n") .addStatement("val container = %T<%T>()", RedwoodWidget.MutableListChildren, RedwoodTesting.WidgetValue) - .beginControlFlow("val tester = %T(this, factories, container, %M(%T()))", RedwoodTesting.TestRedwoodComposition, KotlinxCoroutines.MutableStateFlow, RedwoodRuntime.UiConfiguration) + .beginControlFlow("val tester = %T(this, factories, container)", RedwoodTesting.TestRedwoodComposition) .addStatement("container.map { it.value }") .endControlFlow() .beginControlFlow("try") diff --git a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/types.kt b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/types.kt index b1e508c95d..7fa4a66905 100644 --- a/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/types.kt +++ b/redwood-tooling-codegen/src/main/kotlin/app/cash/redwood/tooling/codegen/types.kt @@ -67,10 +67,6 @@ internal object Redwood { .build() } -internal object RedwoodRuntime { - val UiConfiguration = ClassName("app.cash.redwood.ui", "UiConfiguration") -} - internal object RedwoodTesting { val TestRedwoodComposition = ClassName("app.cash.redwood.testing", "TestRedwoodComposition") val WidgetValue = ClassName("app.cash.redwood.testing", "WidgetValue") @@ -144,5 +140,4 @@ internal object KotlinxSerialization { internal object KotlinxCoroutines { val coroutineScope = MemberName("kotlinx.coroutines", "coroutineScope") - val MutableStateFlow = MemberName("kotlinx.coroutines.flow", "MutableStateFlow") }