Kotlin é um projeto brilhante. Concebido inicialmente apenas como uma linguagem JVM, mais tarde recebeu suporte de compilação para todas as plataformas convencionais, incluindo JavaScript.
Introdutório. Eu tenho um projeto favorito - um site e uma plataforma de API para a comunidade do jogo Elite: Dangerous. O backend é Kotlin / JVM (Ktor + Hibernate), o frontend é Kotlin / JS (KVision + Fomantic UI). Eu contarei a vocês sobre o projeto de estimação algum tempo depois, e mais na frente.
KVision é uma estrutura de front - end para Kotlin que combina ideias de várias estruturas de desktop (de Swing e JavaFX a WinForms e Flutter) e recursos de sintaxe Kotlin, como construtores DSL.
Fomantic-UI é um fork do Semantic-UI, um componente HTML / JS web framework que pode ser comparado ao Bootstrap, apenas o Fomantic é mais interessante.
Não muito tempo atrás, tive a ideia de ligar esses dois mundos e escrever uma biblioteca para o KVision, o que pelo menos tornaria mais fácil escrever páginas do KVision com elementos do Fomantic. E, como convém a um projeto de código aberto, planejei cobrir a biblioteca com testes. Este artigo será sobre essa aventura.
O código
Em primeiro lugar, vamos definir a tarefa. Temos o seguinte código para um botão Fomantic simples em nossas mãos:
package com.github.kam1sh.kvision.fomantic
import pl.treksoft.kvision.html.*
import pl.treksoft.kvision.panel.SimplePanel
open class FoButton(text: String) : Button(text = text, classes = setOf("ui", "button")) {
var primary: Boolean = false
set(value) {
if (value) addCssClass("primary") else removeCssClass("primary")
field = value
}
}
fun SimplePanel.foButton(
text: String,
init: (FoButton.() -> Unit)? = null
): FoButton {
val btn = FoButton(text)
init?.invoke(btn)
add(btn)
return btn
}
E há alguns testes:
package com.github.kam1sh.kvision.fomantic
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlinx.browser.document
import kotlinx.coroutines.*
import pl.treksoft.kvision.panel.ContainerType
import pl.treksoft.kvision.panel.Root
class FoButtonTest {
lateinit var kvapp: Root
@BeforeTest
fun before() {
kvapp = Root("kvapp", containerType = ContainerType.NONE, addRow = false)
}
@Test
fun genericButtonTest() {
kvapp.foButton("Button")
assertEqualsHtml("""...""")
}
@Test
fun buttonPrimaryTest() {
val btn = kvapp.foButton("Button") { primary = true }
assertEqualsHtml("""...""")
btn.primary = false
assertEqualsHtml("""...""")
}
}
fun assertEqualsHtml(expected: String, message: String? = null) {
val actual = document.getElementById("kvapp")?.innerHTML
assertEquals(expected, actual, message)
}
: "" KVision div id=kvapp, HTML-.
. div? HTML- - document.body?.insertAdjacentHTML(...)
, , - ?
<source lang="html">
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://cdn.jsdelivr.net/npm/jquery@3.3.1/dist/jquery.min.js"></script>
<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.7/dist/semantic.min.css">
<script src="https://cdn.jsdelivr.net/npm/fomantic-ui@2.8.7/dist/semantic.min.js"></script>
</head>
<body>
<main>
<div id="kvapp">
</div>
</main>
</body>
</html>
</source>
You've lost karma
Kotlin/JS.
For browser projects, it downloads and installs the Karma test runner with other required dependencies; for Node.js projects, the Mocha test framework is used.
. Karma Mocha. , Karma js- karma.config.d
.
Karma , -:
// karma.config.d/page.js
config.set({
customContextFile: "../../../../../src/test/resources/test.html"
})
test.html, , src/test/resources/test.html
. - , Karma build/js/packages/kvision-fomantic-test/node_modules
, .
, ? ./gradlew browserTest
, ... Disconnected (0 times), because no message in 30000 ms.
, HTML- , JS-. build/js/node_modules/karma/static/context.html
.
main-:
<!-- The scripts need to be in the body DOM element, as some test running frameworks need the body
to have already been created so they can insert their magic into it. For example, if loaded
before body, Angular Scenario test framework fails to find the body and crashes and burns in
an epic manner. -->
<script src="context.js"></script>
<script type="text/javascript">
// Configure our Karma and set up bindings
%CLIENT_CONFIG%
window.__karma__.setupContext(window);
// All served files with the latest timestamps
%MAPPINGS%
</script>
<!-- Dynamically replaced with <script> tags -->
%SCRIPTS%
<!-- Since %SCRIPTS% might include modules, the `loaded()` call needs to be in a module too.
This ensures all the tests will have been declared before karma tries to run them. -->
<script type="module">
window.__karma__.loaded();
</script>
<script nomodule>
window.__karma__.loaded();
</script>
, ... , .
- . ? , HTTP- Ktor . Python async
, pytest pytest-async, .
suspend .
> Task :compileTestKotlinJs FAILED
e: ...src/test/kotlin/com/github/kam1sh/kvision/fomantic/FoButtonTest.kt: (44, 5): Unsupported [suspend test functions]
- Gradle
, runBlocking {}
. ...
runBlocking Kotlin/JVM.
, , , , , by design. GlobalScope.promise
, runBlocking :
fun runBlocking(block: suspend (scope: CoroutineScope) -> Unit) = GlobalScope.promise { block(this) }
. , . Karma :
config.set({
client: {
mocha: {
timeout: 9000
}
}
})
. workaround. UPD: @ilgonmic, , 0. !
Mocha, , , , :
, done-, .
, Promise.
, , . , kotlin-test-js .
, , . , Promise, Mocha .
, , ? -- ?
- Kotest. .
. , .
// build.gradle.kts
testImplementation("io.kotest:kotest-assertions-core-js:4.3.2")
testImplementation("io.kotest:kotest-framework-api-js:4.3.2")
testImplementation("io.kotest:kotest-framework-engine:4.3.2")
class FoButtonTest : FunSpec({
var kvapp: Root? = null
beforeEach {
kvapp = Root("kvapp", containerType = ContainerType.NONE, addRow = false)
}
test("generic button") {
kvapp!!.foButton("Button")
assertEqualsHtml("""...""")
}
test("primary button") {
val btn = kvapp!!.foButton("Button") { primary = true }
assertEqualsHtml("""...""")
btn.primary = false
delay(200)
assertEqualsHtml("""...""")
}
})
kotest , , FunSpec -- .
, , delay() suspend.
:
Por enquanto, isso é tudo que posso dizer sobre kotest. Eu ainda vou desenvolver a biblioteca e, quando ela estiver pronta, irei anunciar seu lançamento no Fomantic discord e na folga Kotlin / kvision. E em paralelo vou aprender kotest.
Se isso não bastasse para você, então peço desculpas: queria mostrar a conclusão aqui ./gradlew --debug browserTest
, mas a preparação deste material já foi bastante atrasada devido ao aspecto da vida pessoal, então se tiver interesse, contemple você mesmo os logs de depuração do Gradle.
E daí? Bem, nada. Coma Kotlin / JS, beba kotest e cuide-se.