Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make HTML TestUtils wait functions cancellable #3320

Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.MonotonicFrameClock
import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.promise
Expand Down Expand Up @@ -84,21 +85,31 @@ class TestScope : CoroutineScope by MainScope() {
* Suspends until [element] observes any change to its html.
*/
suspend fun waitForChanges(element: HTMLElement = root) {
suspendCoroutine<Unit> { continuation ->
suspendCancellableCoroutine<Unit> { continuation ->
val observer = MutationObserver { _, observer ->
continuation.resume(Unit)
observer.disconnect()
}
observer.observe(element, MutationObserverOptions)

continuation.invokeOnCancellation {
observer.disconnect()
}
}
}

/**
* Suspends until recomposition completes.
*/
suspend fun waitForRecompositionComplete() {
suspendCoroutine<Unit> { continuation ->
suspendCancellableCoroutine<Unit> { continuation ->
eymar marked this conversation as resolved.
Show resolved Hide resolved
waitForRecompositionCompleteContinuation = continuation

continuation.invokeOnCancellation {
if (waitForRecompositionCompleteContinuation === continuation) {
waitForRecompositionCompleteContinuation = null
}
}
}
}
}
Expand Down
52 changes: 52 additions & 0 deletions html/test-utils/src/jsTest/kotlin/TestsForTestUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.currentRecomposeScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.withTimeout
import org.jetbrains.compose.web.testutils.ComposeWebExperimentalTestsApi
import org.jetbrains.compose.web.testutils.runTest
import kotlin.test.Test
Expand Down Expand Up @@ -79,4 +81,54 @@ class TestsForTestUtils {
assertEquals(true, waitForChangesContinued)
assertEquals("<div>Hello World!</div>", root.outerHTML)
}

@Test
fun waitForChanges_cancels_with_timeout() = runTest {

var cancelled = false

val job = launch {
try {
withTimeout(1000) {
waitForChanges(root)
}
} catch (t: TimeoutCancellationException) {
cancelled = true
throw t
}
}

delay(100) // to check that `waitForChanges` is suspended after delay
assertEquals(false, cancelled)

delay(1000) // to check that `waitForChanges` is cancelled after timeout
assertEquals(true, cancelled)

job.join()
}

@Test
fun waitForRecompositionComplete_cancels_with_timeout() = runTest {

var cancelled = false

val job = launch {
try {
withTimeout(1000) {
eymar marked this conversation as resolved.
Show resolved Hide resolved
waitForRecompositionComplete()
}
} catch (t: TimeoutCancellationException) {
cancelled = true
throw t
}
}

delay(100) // to check that `waitForRecompositionComplete` is suspended after delay
assertEquals(false, cancelled)

delay(1000) // to check that `waitForRecompositionComplete` is cancelled after timeout
assertEquals(true, cancelled)

job.join()
}
}