@@ -10,6 +10,7 @@ import com.squareup.workflow1.Worker
10
10
import com.squareup.workflow1.Workflow
11
11
import com.squareup.workflow1.WorkflowAction
12
12
import com.squareup.workflow1.WorkflowAction.Companion.noAction
13
+ import com.squareup.workflow1.WorkflowExperimentalApi
13
14
import com.squareup.workflow1.WorkflowIdentifier
14
15
import com.squareup.workflow1.WorkflowOutput
15
16
import com.squareup.workflow1.action
@@ -19,14 +20,18 @@ import com.squareup.workflow1.identifier
19
20
import com.squareup.workflow1.renderChild
20
21
import com.squareup.workflow1.rendering
21
22
import com.squareup.workflow1.runningWorker
23
+ import com.squareup.workflow1.sessionWorkflow
22
24
import com.squareup.workflow1.stateful
23
25
import com.squareup.workflow1.stateless
24
26
import com.squareup.workflow1.testing.RenderTester.ChildWorkflowMatch.Matched
25
27
import com.squareup.workflow1.unsnapshottableIdentifier
26
28
import com.squareup.workflow1.workflowIdentifier
27
29
import io.mockk.mockk
30
+ import kotlinx.coroutines.CoroutineScope
28
31
import kotlinx.coroutines.flow.Flow
29
32
import kotlinx.coroutines.flow.emptyFlow
33
+ import kotlinx.coroutines.sync.Mutex
34
+ import kotlinx.coroutines.test.runTest
30
35
import org.mockito.kotlin.mock
31
36
import kotlin.reflect.typeOf
32
37
import kotlin.test.Test
@@ -1263,6 +1268,121 @@ internal class RealRenderTesterTest {
1263
1268
assertEquals(2 , renderCount)
1264
1269
}
1265
1270
1271
+ @OptIn(WorkflowExperimentalApi ::class )
1272
+ @Test
1273
+ fun `testRender with SessionWorkflow throws exception` () {
1274
+ class TestAction : WorkflowAction <Unit , String , String >() {
1275
+ override fun Updater.apply () {
1276
+ state = " new state"
1277
+ setOutput(" output" )
1278
+ }
1279
+ }
1280
+
1281
+ val workflow = Workflow .sessionWorkflow<Unit , String , String , Sink <TestAction >>(
1282
+ initialState = { _, _: CoroutineScope -> " initial" },
1283
+ render = { _, _ ->
1284
+ actionSink.contraMap { it }
1285
+ }
1286
+ )
1287
+
1288
+ val exception = assertFailsWith<IllegalArgumentException > {
1289
+ workflow.testRender(Unit )
1290
+ .render { sink ->
1291
+ sink.send(TestAction ())
1292
+ }
1293
+ }
1294
+
1295
+ assertEquals(
1296
+ exception.message,
1297
+ " Called testRender on a SessionWorkflow without a CoroutineScope. Use the version that passes a CoroutineScope."
1298
+ )
1299
+ }
1300
+
1301
+ @OptIn(WorkflowExperimentalApi ::class )
1302
+ @Test
1303
+ fun `testRender with CoroutineScope works for SessionWorkflow` () = runTest {
1304
+ class TestAction : WorkflowAction <Unit , String , String >() {
1305
+ override fun Updater.apply () {
1306
+ state = " new state"
1307
+ setOutput(" output" )
1308
+ }
1309
+ }
1310
+
1311
+ val workflow = Workflow .sessionWorkflow<Unit , String , String , Sink <TestAction >>(
1312
+ initialState = { _, _: CoroutineScope -> " initial" },
1313
+ render = { _, _ ->
1314
+ actionSink.contraMap { it }
1315
+ }
1316
+ )
1317
+
1318
+ val testResult = workflow.testRender(Unit , this )
1319
+ .render { sink ->
1320
+ sink.send(TestAction ())
1321
+ }
1322
+
1323
+ testResult.verifyActionResult { state, output ->
1324
+ assertEquals(" new state" , state)
1325
+ assertEquals(" output" , output?.value)
1326
+ }
1327
+ }
1328
+
1329
+ @OptIn(WorkflowExperimentalApi ::class )
1330
+ @Test
1331
+ fun `testRender with CoroutineScope uses the correct scope` () = runTest {
1332
+ val signalMutex = Mutex (locked = true )
1333
+ class TestAction : WorkflowAction <Unit , String , String >() {
1334
+ override fun Updater.apply () {
1335
+ state = " new state"
1336
+ setOutput(" output" )
1337
+ }
1338
+ }
1339
+
1340
+ val workflow = Workflow .sessionWorkflow<Unit , String , String , Sink <TestAction >>(
1341
+ initialState = { _, workflowScope: CoroutineScope ->
1342
+ assertEquals(workflowScope, this @runTest)
1343
+ signalMutex.unlock()
1344
+ " initial"
1345
+ },
1346
+ render = { _, _ ->
1347
+ actionSink.contraMap { it }
1348
+ }
1349
+ )
1350
+
1351
+ workflow.testRender(Unit , this )
1352
+ .render { sink ->
1353
+ sink.send(TestAction ())
1354
+ }
1355
+
1356
+ // Assertion happens in the `initialState` call above.
1357
+ signalMutex.lock()
1358
+ }
1359
+
1360
+ @Test fun `testRender with CoroutineScope does not work if not SessionWorkflow` () = runTest {
1361
+ class TestAction : WorkflowAction <Unit , String , String >() {
1362
+ override fun Updater.apply () {
1363
+ state = " new state"
1364
+ setOutput(" output" )
1365
+ }
1366
+ }
1367
+
1368
+ val workflow = Workflow .stateful<Unit , String , String , Sink <TestAction >>(
1369
+ initialState = { " initial" },
1370
+ render = { _, _ -> actionSink.contraMap { it } }
1371
+ )
1372
+
1373
+ val exception = assertFailsWith<IllegalArgumentException > {
1374
+ workflow.testRender(Unit , this )
1375
+ .render { sink ->
1376
+ sink.send(TestAction ())
1377
+ }
1378
+ }
1379
+
1380
+ assertEquals(
1381
+ exception.message,
1382
+ " testRender with workflowScope called on non-Session Workflow. Use the version of testScope with a CoroutineScope to test SessionWorkflow."
1383
+ )
1384
+ }
1385
+
1266
1386
@Test fun `createRenderChildInvocation() for Workflow-stateless{}` () {
1267
1387
val workflow = Workflow .stateless<String , Int , Unit > {}
1268
1388
val invocation = createRenderChildInvocation(workflow, " props" , " key" )
0 commit comments