You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

83 lines
2.6 KiB

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Composition
import org.jetbrains.compose.web.css.*
import org.jetbrains.compose.web.css.selectors.*
import org.jetbrains.compose.web.attributes.*
import org.jetbrains.compose.web.dom.*
import org.jetbrains.compose.web.*
import kotlinx.browser.document
import kotlinx.browser.window
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.promise
import kotlinx.dom.clear
import org.w3c.dom.HTMLElement
import org.w3c.dom.MutationObserver
import org.w3c.dom.MutationObserverInit
import kotlin.coroutines.resume
import kotlin.coroutines.suspendCoroutine
private val testScope = MainScope()
class TestScope : CoroutineScope by testScope {
val root = "div".asHtmlElement()
fun composition(content: @Composable () -> Unit): Composition {
root.clear()
return renderComposable(root) {
content()
}
}
suspend fun waitChanges() {
waitForChanges(root)
}
}
internal fun runTest(block: suspend TestScope.() -> Unit): dynamic {
val scope = TestScope()
return scope.promise { block(scope) }
}
internal fun runBlockingTest(
block: suspend CoroutineScope.() -> Unit
): dynamic = testScope.promise { this.block() }
internal fun String.asHtmlElement() = document.createElement(this) as HTMLElement
/* Currently, the recompositionRunner relies on AnimationFrame to run the recomposition and
applyChanges. Therefore we can use this method after updating the state and before making
assertions.
If tests get broken, then DefaultMonotonicFrameClock need to be checked if it still
uses window.requestAnimationFrame */
internal suspend fun waitForAnimationFrame() {
suspendCoroutine<Unit> { continuation ->
window.requestAnimationFrame {
continuation.resume(Unit)
}
}
}
private object MutationObserverOptions : MutationObserverInit {
override var childList: Boolean? = true
override var attributes: Boolean? = true
override var characterData: Boolean? = true
override var subtree: Boolean? = true
override var attributeOldValue: Boolean? = true
}
internal suspend fun waitForChanges(elementId: String) {
waitForChanges(document.getElementById(elementId) as HTMLElement)
}
internal suspend fun waitForChanges(element: HTMLElement) {
suspendCoroutine<Unit> { continuation ->
val observer = MutationObserver { mutations, observer ->
continuation.resume(Unit)
observer.disconnect()
}
observer.observe(element, MutationObserverOptions)
}
}