Skip to content

Commit 0d529f7

Browse files
committed
User waitUntil instead of delay
1 parent d7f1d30 commit 0d529f7

2 files changed

Lines changed: 62 additions & 59 deletions

File tree

compose/ui/ui/src/uikitInstrumentedTest/kotlin/androidx/compose/ui/interop/UIKitNavigationSwipeBackTest.kt

Lines changed: 36 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import androidx.compose.foundation.layout.systemBarsPadding
2525
import androidx.compose.foundation.pager.HorizontalPager
2626
import androidx.compose.foundation.pager.rememberPagerState
2727
import androidx.compose.runtime.Composable
28+
import androidx.compose.runtime.LaunchedEffect
2829
import androidx.compose.runtime.MutableIntState
2930
import androidx.compose.runtime.mutableIntStateOf
3031
import androidx.compose.ui.Modifier
@@ -41,7 +42,6 @@ import androidx.compose.ui.unit.dp
4142
import kotlin.test.Test
4243
import kotlin.test.assertEquals
4344
import kotlin.test.assertNotNull
44-
import kotlin.test.assertNull
4545
import kotlinx.cinterop.ExperimentalForeignApi
4646
import org.jetbrains.skiko.OS
4747
import org.jetbrains.skiko.OSVersion
@@ -55,38 +55,36 @@ internal abstract class UIKitNavigationSwipeBackTest(
5555
) {
5656
@Test
5757
fun testSwipeRightOnPagerDoesNotPopController() = runUIKitInstrumentedTest {
58-
val currentPage = mutableIntStateOf(0)
58+
val initialPage = 0
59+
val currentPage = mutableIntStateOf(initialPage)
5960

6061
setNavigationControllerContent {
6162
TestContent(currentPage = currentPage)
6263
}
6364

64-
delay(10)
65-
6665
findNodeWithTag("pager").swipeRight()
6766

68-
delay(500)
67+
waitForIdle()
6968

7069
assertEquals(2, navigationController.viewControllers.size)
7170
assertNotNull(findNodeWithTagOrNull("pager"))
72-
assertEquals(0, currentPage.value)
71+
assertEquals(initialPage, currentPage.value)
7372
}
7473

7574
@Test
7675
fun testSwipeLeftOnPagerChangesPage() = runUIKitInstrumentedTest {
77-
val currentPage = mutableIntStateOf(0)
76+
val initialPage = 0
77+
val currentPage = mutableIntStateOf(initialPage)
7878

7979
setNavigationControllerContent {
8080
TestContent(currentPage = currentPage)
8181
}
8282

83-
delay(10)
84-
8583
findNodeWithTag("pager").swipeLeft()
8684

8785
waitForIdle()
8886

89-
assertEquals(1, currentPage.value)
87+
assertEquals(initialPage + 1, currentPage.value)
9088
assertEquals(2, navigationController.viewControllers.size)
9189
}
9290

@@ -99,8 +97,6 @@ internal abstract class UIKitNavigationSwipeBackTest(
9997
TestContent(currentPage = currentPage)
10098
}
10199

102-
delay(10)
103-
104100
findNodeWithTag("outsideBox").swipeLeft()
105101

106102
assertEquals(initialPage, currentPage.value)
@@ -113,16 +109,11 @@ internal abstract class UIKitNavigationSwipeBackTest(
113109
TestContent(currentPage = mutableIntStateOf(1))
114110
}
115111

116-
delay(10)
117-
118112
swipeRightFromEdge()
119113

120-
// wait for pop animation to finish
121-
delay(500)
122-
123-
assertNull(findNodeWithTagOrNull("pager"))
124-
assertNull(findNodeWithTagOrNull("outsideBox"))
125-
assertEquals(1, navigationController.viewControllers.size)
114+
waitUntil("Waiting for view controller to be popped") {
115+
navigationController.viewControllers.size == 1
116+
}
126117
}
127118

128119
@Test
@@ -137,15 +128,12 @@ internal abstract class UIKitNavigationSwipeBackTest(
137128
TestContent(currentPage = currentPage)
138129
}
139130

140-
delay(10)
141-
142131
findNodeWithTag("outsideBox").swipe(
143132
fromPosition = { center() },
144133
toPosition = { rightCenter() },
145134
)
146135

147-
// wait for pop animation to finish if any
148-
delay(500)
136+
waitForIdle()
149137

150138
assertNotNull(findNodeWithTagOrNull("pager"))
151139
assertNotNull(findNodeWithTagOrNull("outsideBox"))
@@ -164,16 +152,11 @@ internal abstract class UIKitNavigationSwipeBackTest(
164152
TestContent(currentPage = currentPage)
165153
}
166154

167-
delay(10)
168-
169155
findNodeWithTag("outsideBox").swipeRight()
170156

171-
// wait for pop animation to finish
172-
delay(500)
173-
174-
assertNull(findNodeWithTagOrNull("pager"))
175-
assertNull(findNodeWithTagOrNull("outsideBox"))
176-
assertEquals(1, navigationController.viewControllers.size)
157+
waitUntil("Waiting for view controller to be popped") {
158+
navigationController.viewControllers.size == 1
159+
}
177160
}
178161

179162
@Test
@@ -188,16 +171,11 @@ internal abstract class UIKitNavigationSwipeBackTest(
188171
TestContent(currentPage = currentPage)
189172
}
190173

191-
delay(10)
192-
193174
swipeRightFromEdge()
194175

195-
// wait for pop animation to finish
196-
delay(500)
197-
198-
assertNull(findNodeWithTagOrNull("pager"))
199-
assertNull(findNodeWithTagOrNull("outsideBox"))
200-
assertEquals(1, navigationController.viewControllers.size)
176+
waitUntil("Waiting for view controller to be popped") {
177+
navigationController.viewControllers.size == 1
178+
}
201179
}
202180

203181
@Test
@@ -212,19 +190,16 @@ internal abstract class UIKitNavigationSwipeBackTest(
212190
TestContent(currentPage = currentPage)
213191
}
214192

215-
delay(10)
216-
217193
findNodeWithTag("outsideBox").swipe(
218194
fromPosition = { center() },
219195
toPosition = { rightCenter() },
220196
)
221197

222-
// wait for pop animation to finish
223-
delay(500)
198+
waitForIdle()
224199

225-
assertNull(findNodeWithTagOrNull("pager"))
226-
assertNull(findNodeWithTagOrNull("outsideBox"))
227-
assertEquals(1, navigationController.viewControllers.size)
200+
waitUntil("Waiting for view controller to be popped") {
201+
navigationController.viewControllers.size == 1
202+
}
228203
}
229204

230205
private val UIKitInstrumentedTest.navigationController: UINavigationController get() {
@@ -233,12 +208,17 @@ internal abstract class UIKitNavigationSwipeBackTest(
233208

234209
private fun UIKitInstrumentedTest.setNavigationControllerContent(
235210
content: @Composable () -> Unit = {}
236-
) = setupWindow {
237-
val firstViewController = UIViewController()
238-
val secondViewController = createRootViewController(content = content)
211+
) {
212+
val composeViewController = createViewControllerHostingCompose(content = content)
213+
214+
setupWindow {
215+
UINavigationController().also {
216+
it.setViewControllers(listOf(UIViewController(), composeViewController), false)
217+
}
218+
}
239219

240-
UINavigationController().also {
241-
it.setViewControllers(listOf(firstViewController, secondViewController), false)
220+
waitUntil {
221+
composeViewController.view.window != null
242222
}
243223
}
244224

@@ -260,6 +240,10 @@ private fun TestContent(
260240
val pagerColors = listOf(Color.Red, Color.Green, Color.Blue)
261241
val pagerState = rememberPagerState(initialPage = currentPage.value) { 3 }
262242

243+
LaunchedEffect(pagerState.currentPage) {
244+
currentPage.value = pagerState.currentPage
245+
}
246+
263247
Column(
264248
modifier = Modifier
265249
.fillMaxSize()

compose/ui/ui/src/uikitInstrumentedTest/kotlin/androidx/compose/ui/test/UIKitInstrumentedTest.kt

Lines changed: 26 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,16 @@ internal class UIKitInstrumentedTest(
265265
content: @Composable () -> Unit
266266
) = setupWindow(
267267
interfaceOrientation = interfaceOrientation,
268-
rootViewController = { createRootViewController(configure, content) }
268+
rootViewController = { createViewControllerHostingCompose(configure, content) }
269269
)
270270

271+
/**
272+
* Installs [rootViewController] into the test window and waits until the Compose scene owned by
273+
* this [UIKitInstrumentedTest] is idle.
274+
*
275+
* Use this when a test needs a custom UIKit hierarchy around Compose, for example a navigation
276+
* controller or a view controller presented by UIKit.
277+
*/
271278
fun setupWindow(
272279
interfaceOrientation: UIInterfaceOrientation = UIInterfaceOrientationPortrait,
273280
rootViewController: () -> UIViewController,
@@ -286,18 +293,26 @@ internal class UIKitInstrumentedTest(
286293
}
287294
}
288295

289-
fun createRootViewController(
296+
/**
297+
* Creates a [UIViewController] that hosts [content] using the container variant selected by
298+
* [useHostingView].
299+
*/
300+
fun createViewControllerHostingCompose(
290301
configure: ComposeContainerConfiguration.() -> Unit = {},
291302
content: @Composable () -> Unit
292303
): UIViewController = if (useHostingView) {
293304
UIViewController().also {
294-
it.view.embedSubview(createHostingView(configure, content))
305+
it.view.embedSubview(createComposeHostingView(configure, content))
295306
}
296307
} else {
297-
createHostingViewController(configure, content)
308+
createComposeHostingViewController(configure, content)
298309
}
299310

300-
fun createHostingView(
311+
/**
312+
* Creates a [ComposeHostingView] for [content] and records it as the active Compose container
313+
* for idleness and redrawer checks.
314+
*/
315+
fun createComposeHostingView(
301316
configure: ComposeUIViewConfiguration.() -> Unit = {},
302317
content: @Composable () -> Unit
303318
): ComposeHostingView {
@@ -314,7 +329,11 @@ internal class UIKitInstrumentedTest(
314329
}
315330
}
316331

317-
fun createHostingViewController(
332+
/**
333+
* Creates a [ComposeHostingViewController] for [content] and records it as the active Compose
334+
* container for idleness and redrawer checks.
335+
*/
336+
fun createComposeHostingViewController(
318337
configure: ComposeUIViewControllerConfiguration.() -> Unit = {},
319338
content: @Composable () -> Unit
320339
): ComposeHostingViewController {
@@ -812,4 +831,4 @@ internal fun UIKitInstrumentedTest.waitForContextMenu() {
812831
} != null
813832
}
814833
delay(500) // wait for toolbar animation
815-
}
834+
}

0 commit comments

Comments
 (0)