@@ -65,15 +65,16 @@ internal class ComposeViewTreeIntegrationTest {
65
65
66
66
@Before fun setUp () {
67
67
scenario.onActivity {
68
- it.viewEnvironment = ViewEnvironment .EMPTY + ViewRegistry (NoTransitionBackStackContainer )
68
+ it.viewEnvironment = (ViewEnvironment .EMPTY + ViewRegistry (NoTransitionBackStackContainer ))
69
+ .withComposeInteropSupport()
69
70
}
70
71
}
71
72
72
73
@Test fun compose_view_assertions_work () {
73
- val firstScreen = TestComposeRendering (" first" ) {
74
+ val firstScreen = VanillaComposeRendering (" first" ) {
74
75
BasicText (" First Screen" )
75
76
}
76
- val secondScreen = TestComposeRendering (" second" ) {}
77
+ val secondScreen = VanillaComposeRendering (" second" ) {}
77
78
78
79
scenario.onActivity {
79
80
it.setBackstack(firstScreen)
@@ -89,18 +90,87 @@ internal class ComposeViewTreeIntegrationTest {
89
90
composeRule.onNodeWithText(" First Screen" ).assertDoesNotExist()
90
91
}
91
92
92
- @Test fun composition_is_disposed_when_navigated_away_dispose_on_detach_strategy () {
93
+ @Test fun composition_is_disposed_when_navigated_away_stock_class () {
93
94
var composedCount = 0
94
95
var disposedCount = 0
95
- val firstScreen = TestComposeRendering (" first" , disposeStrategy = DisposeOnDetachedFromWindow ) {
96
- DisposableEffect (Unit ) {
97
- composedCount++
98
- onDispose {
99
- disposedCount++
96
+ val firstScreen =
97
+ VanillaComposeRendering (" first" ) {
98
+ DisposableEffect (Unit ) {
99
+ composedCount++
100
+ onDispose {
101
+ disposedCount++
102
+ }
103
+ }
104
+ }
105
+ val secondScreen = VanillaComposeRendering (" second" ) {}
106
+
107
+ scenario.onActivity {
108
+ it.setBackstack(firstScreen)
109
+ }
110
+
111
+ composeRule.runOnIdle {
112
+ assertThat(composedCount).isEqualTo(1 )
113
+ assertThat(disposedCount).isEqualTo(0 )
114
+ }
115
+
116
+ // Navigate away.
117
+ scenario.onActivity {
118
+ it.setBackstack(firstScreen, secondScreen)
119
+ }
120
+
121
+ composeRule.runOnIdle {
122
+ assertThat(composedCount).isEqualTo(1 )
123
+ assertThat(disposedCount).isEqualTo(1 )
124
+ }
125
+ }
126
+
127
+ @Test fun composition_is_disposed_when_navigated_away_default_strategy () {
128
+ var composedCount = 0
129
+ var disposedCount = 0
130
+ val firstScreen =
131
+ BespokeComposeRendering (" first" , disposeStrategy = ViewCompositionStrategy .Default ) {
132
+ DisposableEffect (Unit ) {
133
+ composedCount++
134
+ onDispose {
135
+ disposedCount++
136
+ }
100
137
}
101
138
}
139
+ val secondScreen = VanillaComposeRendering (" second" ) {}
140
+
141
+ scenario.onActivity {
142
+ it.setBackstack(firstScreen)
143
+ }
144
+
145
+ composeRule.runOnIdle {
146
+ assertThat(composedCount).isEqualTo(1 )
147
+ assertThat(disposedCount).isEqualTo(0 )
102
148
}
103
- val secondScreen = TestComposeRendering (" second" ) {}
149
+
150
+ // Navigate away.
151
+ scenario.onActivity {
152
+ it.setBackstack(firstScreen, secondScreen)
153
+ }
154
+
155
+ composeRule.runOnIdle {
156
+ assertThat(composedCount).isEqualTo(1 )
157
+ assertThat(disposedCount).isEqualTo(1 )
158
+ }
159
+ }
160
+
161
+ @Test fun composition_is_disposed_when_navigated_away_dispose_on_detach_strategy () {
162
+ var composedCount = 0
163
+ var disposedCount = 0
164
+ val firstScreen =
165
+ BespokeComposeRendering (" first" , disposeStrategy = DisposeOnDetachedFromWindow ) {
166
+ DisposableEffect (Unit ) {
167
+ composedCount++
168
+ onDispose {
169
+ disposedCount++
170
+ }
171
+ }
172
+ }
173
+ val secondScreen = VanillaComposeRendering (" second" ) {}
104
174
105
175
scenario.onActivity {
106
176
it.setBackstack(firstScreen)
@@ -126,15 +196,15 @@ internal class ComposeViewTreeIntegrationTest {
126
196
var composedCount = 0
127
197
var disposedCount = 0
128
198
val firstScreen =
129
- TestComposeRendering (" first" , disposeStrategy = DisposeOnViewTreeLifecycleDestroyed ) {
199
+ BespokeComposeRendering (" first" , disposeStrategy = DisposeOnViewTreeLifecycleDestroyed ) {
130
200
DisposableEffect (Unit ) {
131
201
composedCount++
132
202
onDispose {
133
203
disposedCount++
134
204
}
135
205
}
136
206
}
137
- val secondScreen = TestComposeRendering (" second" ) {}
207
+ val secondScreen = VanillaComposeRendering (" second" ) {}
138
208
139
209
scenario.onActivity {
140
210
it.setBackstack(firstScreen)
@@ -157,7 +227,7 @@ internal class ComposeViewTreeIntegrationTest {
157
227
}
158
228
159
229
@Test fun composition_state_is_restored_after_config_change () {
160
- val firstScreen = TestComposeRendering (" first" ) {
230
+ val firstScreen = VanillaComposeRendering (" first" ) {
161
231
var counter by rememberSaveable { mutableStateOf(0 ) }
162
232
BasicText (
163
233
" Counter: $counter " ,
@@ -184,7 +254,7 @@ internal class ComposeViewTreeIntegrationTest {
184
254
}
185
255
186
256
@Test fun composition_state_is_restored_after_navigating_back () {
187
- val firstScreen = TestComposeRendering (" first" ) {
257
+ val firstScreen = VanillaComposeRendering (" first" ) {
188
258
var counter by rememberSaveable { mutableStateOf(0 ) }
189
259
BasicText (
190
260
" Counter: $counter " ,
@@ -193,7 +263,7 @@ internal class ComposeViewTreeIntegrationTest {
193
263
.testTag(CounterTag )
194
264
)
195
265
}
196
- val secondScreen = TestComposeRendering (" second" ) {
266
+ val secondScreen = VanillaComposeRendering (" second" ) {
197
267
BasicText (" nothing to see here" )
198
268
}
199
269
@@ -225,7 +295,7 @@ internal class ComposeViewTreeIntegrationTest {
225
295
226
296
@Test
227
297
fun composition_state_is_restored_after_config_change_then_navigating_back () {
228
- val firstScreen = TestComposeRendering (" first" ) {
298
+ val firstScreen = VanillaComposeRendering (" first" ) {
229
299
var counter by rememberSaveable { mutableStateOf(0 ) }
230
300
BasicText (
231
301
" Counter: $counter " ,
@@ -234,7 +304,7 @@ internal class ComposeViewTreeIntegrationTest {
234
304
.testTag(CounterTag )
235
305
)
236
306
}
237
- val secondScreen = TestComposeRendering (" second" ) {
307
+ val secondScreen = VanillaComposeRendering (" second" ) {
238
308
BasicText (" nothing to see here" )
239
309
}
240
310
@@ -267,7 +337,7 @@ internal class ComposeViewTreeIntegrationTest {
267
337
}
268
338
269
339
@Test fun composition_state_is_not_restored_after_screen_is_removed_from_backstack () {
270
- val firstScreen = TestComposeRendering (" first" ) {
340
+ val firstScreen = VanillaComposeRendering (" first" ) {
271
341
var counter by rememberSaveable { mutableStateOf(0 ) }
272
342
BasicText (
273
343
" Counter: $counter " ,
@@ -276,7 +346,7 @@ internal class ComposeViewTreeIntegrationTest {
276
346
.testTag(CounterTag )
277
347
)
278
348
}
279
- val secondScreen = TestComposeRendering (" second" ) {
349
+ val secondScreen = VanillaComposeRendering (" second" ) {
280
350
BasicText (" nothing to see here" )
281
351
}
282
352
@@ -312,7 +382,7 @@ internal class ComposeViewTreeIntegrationTest {
312
382
313
383
@Test
314
384
fun composition_state_is_not_restored_after_screen_is_removed_and_replaced_from_backstack () {
315
- val firstScreen = TestComposeRendering (" first" ) {
385
+ val firstScreen = VanillaComposeRendering (" first" ) {
316
386
var counter by rememberSaveable { mutableStateOf(0 ) }
317
387
BasicText (
318
388
" Counter: $counter " ,
@@ -321,7 +391,7 @@ internal class ComposeViewTreeIntegrationTest {
321
391
.testTag(CounterTag )
322
392
)
323
393
}
324
- val secondScreen = TestComposeRendering (" second" ) {
394
+ val secondScreen = VanillaComposeRendering (" second" ) {
325
395
BasicText (" nothing to see here" )
326
396
}
327
397
@@ -360,8 +430,8 @@ internal class ComposeViewTreeIntegrationTest {
360
430
.assertTextEquals(" Counter: 0" )
361
431
}
362
432
363
- @Test fun composition_is_restored_in_modal_after_config_change () {
364
- val firstScreen: Screen = TestComposeRendering (compatibilityKey = " " ) {
433
+ @Test fun composition_is_restored_in_overlay_after_config_change () {
434
+ val firstScreen: Screen = VanillaComposeRendering (compatibilityKey = " " ) {
365
435
var counter by rememberSaveable { mutableStateOf(0 ) }
366
436
BasicText (
367
437
" Counter: $counter " ,
@@ -392,8 +462,8 @@ internal class ComposeViewTreeIntegrationTest {
392
462
.assertTextEquals(" Counter: 1" )
393
463
}
394
464
395
- @Test fun composition_is_restored_in_multiple_modals_after_config_change () {
396
- val firstScreen: Screen = TestComposeRendering (compatibilityKey = " 0" ) {
465
+ @Test fun composition_is_restored_in_multiple_overlays_after_config_change () {
466
+ val firstScreen: Screen = VanillaComposeRendering (compatibilityKey = " 0" ) {
397
467
var counter by rememberSaveable { mutableStateOf(0 ) }
398
468
BasicText (
399
469
" Counter: $counter " ,
@@ -403,7 +473,7 @@ internal class ComposeViewTreeIntegrationTest {
403
473
)
404
474
}
405
475
406
- val secondScreen: Screen = TestComposeRendering (compatibilityKey = " 1" ) {
476
+ val secondScreen: Screen = VanillaComposeRendering (compatibilityKey = " 1" ) {
407
477
var counter by rememberSaveable { mutableStateOf(0 ) }
408
478
BasicText (
409
479
" Counter2: $counter " ,
@@ -413,7 +483,7 @@ internal class ComposeViewTreeIntegrationTest {
413
483
)
414
484
}
415
485
416
- val thirdScreen: Screen = TestComposeRendering (compatibilityKey = " 2" ) {
486
+ val thirdScreen: Screen = VanillaComposeRendering (compatibilityKey = " 2" ) {
417
487
var counter by rememberSaveable { mutableStateOf(0 ) }
418
488
BasicText (
419
489
" Counter3: $counter " ,
@@ -464,12 +534,12 @@ internal class ComposeViewTreeIntegrationTest {
464
534
.assertTextEquals(" Counter3: 1" )
465
535
}
466
536
467
- @Test fun composition_is_restored_in_multiple_modals_backstacks_after_config_change () {
537
+ @Test fun composition_is_restored_in_multiple_overlays_backstacks_after_config_change () {
468
538
fun createRendering (
469
539
layer : Int ,
470
540
screen : Int
471
- ) = TestComposeRendering (
472
- // Use the same compatibility key across layers – these screens are in different modals , so
541
+ ) = VanillaComposeRendering (
542
+ // Use the same compatibility key across layers – these screens are in different overlays , so
473
543
// they won't conflict.
474
544
compatibilityKey = screen.toString()
475
545
) {
@@ -494,7 +564,7 @@ internal class ComposeViewTreeIntegrationTest {
494
564
EmptyRendering ,
495
565
listOf (
496
566
TestOverlay (BackStackScreen (EmptyRendering , layer0Screen0)),
497
- // A SavedStateRegistry is set up for each modal . Each registry needs a unique name,
567
+ // A SavedStateRegistry is set up for each overlay . Each registry needs a unique name,
498
568
// and these names default to their `Compatible.keyFor` value. When we show two
499
569
// of the same type at the same time, we need to give them unique names.
500
570
TestOverlay (NamedScreen (BackStackScreen (EmptyRendering , layer1Screen0), " another" ))
@@ -522,7 +592,7 @@ internal class ComposeViewTreeIntegrationTest {
522
592
EmptyRendering ,
523
593
listOf (
524
594
TestOverlay (BackStackScreen (EmptyRendering , layer0Screen0, layer0Screen1)),
525
- // A SavedStateRegistry is set up for each modal . Each registry needs a unique name,
595
+ // A SavedStateRegistry is set up for each overlay . Each registry needs a unique name,
526
596
// and these names default to their `Compatible.keyFor` value. When we show two
527
597
// of the same type at the same time, we need to give them unique names.
528
598
TestOverlay (
@@ -566,7 +636,7 @@ internal class ComposeViewTreeIntegrationTest {
566
636
EmptyRendering ,
567
637
listOf (
568
638
TestOverlay (BackStackScreen (EmptyRendering , layer0Screen0)),
569
- // A SavedStateRegistry is set up for each modal . Each registry needs a unique name,
639
+ // A SavedStateRegistry is set up for each overlay . Each registry needs a unique name,
570
640
// and these names default to their `Compatible.keyFor` value. When we show two
571
641
// of the same type at the same time, we need to give them unique names.
572
642
TestOverlay (NamedScreen (BackStackScreen (EmptyRendering , layer1Screen0), " another" ))
@@ -581,7 +651,7 @@ internal class ComposeViewTreeIntegrationTest {
581
651
.assertIsDisplayed()
582
652
}
583
653
584
- private fun WorkflowUiTestActivity.setBackstack (vararg backstack : TestComposeRendering ) {
654
+ private fun WorkflowUiTestActivity.setBackstack (vararg backstack : Screen ) {
585
655
setRendering(
586
656
BackStackScreen .fromList(listOf<AndroidScreen <* >>(EmptyRendering ) + backstack.asList())
587
657
)
@@ -598,20 +668,27 @@ internal class ComposeViewTreeIntegrationTest {
598
668
}
599
669
}
600
670
601
- data class TestComposeRendering (
671
+ /* *
672
+ * This is our own custom lovingly handcrafted implementation that creates [ComposeView]
673
+ * itself, bypassing [ScreenComposableFactory] entirely. Allows us to mess with alternative
674
+ * [ViewCompositionStrategy] approaches.
675
+ */
676
+ data class BespokeComposeRendering (
602
677
override val compatibilityKey : String ,
603
678
val disposeStrategy : ViewCompositionStrategy ? = null ,
604
679
val content : @Composable () -> Unit
605
- ) : Compatible, AndroidScreen<TestComposeRendering>, ScreenViewFactory<TestComposeRendering> {
606
- override val type: KClass <in TestComposeRendering > = TestComposeRendering ::class
607
- override val viewFactory: ScreenViewFactory <TestComposeRendering > get() = this
680
+ ) : Compatible,
681
+ AndroidScreen <BespokeComposeRendering >,
682
+ ScreenViewFactory <BespokeComposeRendering > {
683
+ override val type: KClass <in BespokeComposeRendering > = BespokeComposeRendering ::class
684
+ override val viewFactory: ScreenViewFactory <BespokeComposeRendering > get() = this
608
685
609
686
override fun buildView (
610
- initialRendering : TestComposeRendering ,
687
+ initialRendering : BespokeComposeRendering ,
611
688
initialEnvironment : ViewEnvironment ,
612
689
context : Context ,
613
690
container : ViewGroup ?
614
- ): ScreenViewHolder <TestComposeRendering > {
691
+ ): ScreenViewHolder <BespokeComposeRendering > {
615
692
var lastCompositionStrategy = initialRendering.disposeStrategy
616
693
617
694
return ComposeView (context).let { view ->
@@ -629,6 +706,19 @@ internal class ComposeViewTreeIntegrationTest {
629
706
}
630
707
}
631
708
709
+ /* *
710
+ * Bog standard [ComposeScreen], as opposed to [BespokeComposeRendering].
711
+ * Requires [ViewEnvironment.withComposeInteropSupport].
712
+ */
713
+ data class VanillaComposeRendering (
714
+ override val compatibilityKey : String ,
715
+ val content : @Composable () -> Unit
716
+ ) : Compatible, ComposeScreen {
717
+ @Composable override fun Content () {
718
+ content()
719
+ }
720
+ }
721
+
632
722
object EmptyRendering : AndroidScreen<EmptyRendering> {
633
723
override val viewFactory: ScreenViewFactory <EmptyRendering >
634
724
get() = ScreenViewFactory .fromCode { _, e, c, _ ->
0 commit comments