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.
100 lines
2.8 KiB
100 lines
2.8 KiB
3 years ago
|
# Using test-utils for DOM DSL unit testing
|
||
|
|
||
|
### Dependencies
|
||
|
|
||
|
It's necessary to add `compose.web.testUtils` to jsTest dependencies:
|
||
|
|
||
|
``` kotlin
|
||
|
sourceSets {
|
||
|
val jsMain by getting {
|
||
|
dependencies {
|
||
|
implementation(compose.web.core)
|
||
|
implementation(compose.runtime)
|
||
|
//....
|
||
|
}
|
||
|
}
|
||
|
val jsTest by getting {
|
||
|
implementation(kotlin("test-js"))
|
||
|
implementation(compose.web.testUtils)
|
||
|
//...
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
|
||
|
### Example
|
||
|
|
||
|
``` kotlin
|
||
|
// This is a function that we want to test
|
||
|
@Composable
|
||
|
fun TestButton(text: String, onButtonClick: () -> Unit) {
|
||
|
Button(attrs = {
|
||
|
onClick { onButtonClick() }
|
||
|
}) {
|
||
|
Text(text)
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
Let's add a test to ensure that button has correct text, and it's onClick works properly.
|
||
|
``` kotlin
|
||
|
import org.jetbrains.compose.web.testutils.ComposeWebExperimentalTestsApi
|
||
|
import org.jetbrains.compose.web.testutils.runTest
|
||
|
import androidx.compose.runtime.getValue
|
||
|
import androidx.compose.runtime.mutableStateOf
|
||
|
import androidx.compose.runtime.setValue
|
||
|
import org.w3c.dom.HTMLButtonElement
|
||
|
import kotlin.test.Test
|
||
|
import kotlin.test.assertEquals
|
||
|
|
||
|
@OptIn(ComposeWebExperimentalTestsApi::class)
|
||
|
class TestsForButton {
|
||
|
|
||
|
@Test
|
||
|
fun testButton() = runTest {
|
||
|
var counter by mutableStateOf(1)
|
||
|
|
||
|
composition {
|
||
|
TestButton(text = "$counter") {
|
||
|
counter++
|
||
|
}
|
||
|
}
|
||
|
|
||
|
assertEquals("<button>1</button>", root.innerHTML)
|
||
|
|
||
|
(root.firstChild!! as HTMLButtonElement).click()
|
||
|
waitForRecompositionComplete()
|
||
|
assertEquals("<button>2</button>", root.innerHTML)
|
||
|
|
||
|
counter = 10
|
||
|
waitForRecompositionComplete()
|
||
|
assertEquals("<button>10</button>", root.innerHTML)
|
||
|
}
|
||
|
}
|
||
|
```
|
||
|
|
||
|
### Let's break it down:
|
||
|
|
||
|
### `runTest { ... }`
|
||
|
Provides the TestScope with useful functions to configure the test.
|
||
|
|
||
|
### `composition { ... }`
|
||
|
Takes a @Composable block with a content that we want to test.
|
||
|
It will automatically build and mount DOM into `root` element.
|
||
|
|
||
|
### `root`
|
||
|
It's not supposed to be used for elements manipulation.
|
||
|
It's mostly useful to make assertions on the html content (e.g. `root.innerHtml`)
|
||
|
|
||
|
### `nextChild() and currentChild()`
|
||
|
Under the hood `nextChild()` iterates over `root` children, providing convenient access to them.
|
||
|
|
||
|
`currentChild()` doesn't move the iterator and returns the same element every time until `nextChild()` called.
|
||
|
|
||
|
### `waitForRecompositionComplete()`
|
||
|
It suspends until recomposition completes. It's useful when state changes, and we want to test that content updates as well. `waitForRecompositionComplete` needs to be called after state change and before assertions.
|
||
|
|
||
|
### `waitForChanges(id: String)`
|
||
|
It suspends until any change occur in the element with `id`.
|
||
|
It's also useful to ensure that state changes make corresponding updates to the content.
|