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.
 
 
 
 

253 lines
7.5 KiB

/*
* Copyright 2020-2021 JetBrains s.r.o. and respective authors and developers.
* Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE.txt file.
*/
package org.jetbrains.compose.web.core.tests.elements
import androidx.compose.runtime.*
import kotlinx.browser.document
import org.jetbrains.compose.web.ExperimentalComposeWebApi
import org.jetbrains.compose.web.attributes.AttrsScope
import org.jetbrains.compose.web.dom.*
import org.jetbrains.compose.web.testutils.runTest
import org.w3c.dom.HTMLElement
import org.w3c.dom.get
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertSame
class ElementsTests {
@Test
fun nodeNames() = runTest {
val nodes = listOf<Pair<@Composable () -> Unit, String>>(
Pair({ Address() }, "ADDRESS"),
Pair({ Article() }, "ARTICLE"),
Pair({ Aside() }, "ASIDE"),
Pair({ Header() }, "HEADER"),
Pair({ Area() }, "AREA"),
Pair({ Audio() }, "AUDIO"),
Pair({ HTMLMap() }, "MAP"),
Pair({ Track() }, "TRACK"),
Pair({ Video() }, "VIDEO"),
Pair({ Datalist() }, "DATALIST"),
Pair({ Fieldset() }, "FIELDSET"),
Pair({ Legend() }, "LEGEND"),
Pair({ Meter() }, "METER"),
Pair({ Output() }, "OUTPUT"),
Pair({ Progress() }, "PROGRESS"),
Pair({ Embed() }, "EMBED"),
Pair({ Iframe() }, "IFRAME"),
Pair({ Object() }, "OBJECT"),
Pair({ Param() }, "PARAM"),
Pair({ Picture() }, "PICTURE"),
Pair({ Source() }, "SOURCE"),
Pair({ Canvas() }, "CANVAS"),
Pair({ Div() }, "DIV"),
Pair({ A() }, "A"),
Pair({ Button() }, "BUTTON"),
Pair({ H1() }, "H1"),
Pair({ H2() }, "H2"),
Pair({ H3() }, "H3"),
Pair({ H4() }, "H4"),
Pair({ H5() }, "H5"),
Pair({ H6() }, "H6"),
Pair({ P() }, "P"),
Pair({ Em() }, "EM"),
Pair({ I() }, "I"),
Pair({ B() }, "B"),
Pair({ Small() }, "SMALL"),
Pair({ Sup() }, "SUP"),
Pair({ Sub() }, "SUB"),
Pair({ Blockquote()}, "BLOCKQUOTE"),
Pair({ Span() }, "SPAN"),
Pair({ Br() }, "BR"),
Pair({ Ul() }, "UL"),
Pair({ Ol() }, "OL"),
Pair({ Li() }, "LI"),
Pair({ Img(src = "whatever") }, "IMG"),
Pair({ Form() }, "FORM"),
Pair({ Select() }, "SELECT"),
Pair({ Option("whatever") }, "OPTION"),
Pair({ OptGroup("whatever") }, "OPTGROUP"),
Pair({ Section() }, "SECTION"),
Pair({ TextArea(value = "whatever") }, "TEXTAREA"),
Pair({ Nav() }, "NAV"),
Pair({ Pre() }, "PRE"),
Pair({ Code() }, "CODE"),
Pair({ Main() }, "MAIN"),
Pair({ Footer() }, "FOOTER"),
Pair({ Hr() }, "HR"),
Pair({ Label() }, "LABEL"),
Pair({ Table() }, "TABLE"),
Pair({ Caption() }, "CAPTION"),
Pair({ Col() }, "COL"),
Pair({ Colgroup() }, "COLGROUP"),
Pair({ Tr() }, "TR"),
Pair({ Thead() }, "THEAD"),
Pair({ Th() }, "TH"),
Pair({ Td() }, "TD"),
Pair({ Tbody() }, "TBODY"),
Pair({ Tfoot() }, "TFOOT"),
)
composition {
nodes.forEach {
it.first.invoke()
}
}
nodes.forEachIndexed { index, it ->
assertEquals(it.second, root.children[index]?.nodeName)
}
}
@Test
@OptIn(ExperimentalComposeWebApi::class)
fun rawCreation() = runTest {
@Composable
fun CustomElement(
attrs: AttrsScope<HTMLElement>.() -> Unit,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
tagName = "custom",
applyAttrs = attrs,
content
)
}
composition {
CustomElement({
id("container")
}) {
Text("CUSTOM")
}
}
assertEquals("<div><custom id=\"container\">CUSTOM</custom></div>", root.outerHTML)
}
@Test
fun testElementBuilderCreate() {
val custom = ElementBuilder.createBuilder<HTMLElement>("custom")
val div = ElementBuilder.createBuilder<HTMLElement>("div")
val b = ElementBuilder.createBuilder<HTMLElement>("b")
val abc = ElementBuilder.createBuilder<HTMLElement>("abc")
val expectedKeys = setOf("custom", "div", "b", "abc")
assertEquals(expectedKeys, ElementBuilder.buildersCache.keys.intersect(expectedKeys))
assertEquals("CUSTOM", custom.create().nodeName)
assertEquals("DIV", div.create().nodeName)
assertEquals("B", b.create().nodeName)
assertEquals("ABC", abc.create().nodeName)
}
@Test
@OptIn(ExperimentalComposeWebApi::class)
fun rawCreationAndTagChanges() = runTest {
@Composable
fun CustomElement(
tagName: String,
attrs: AttrsScope<HTMLElement>.() -> Unit,
content: ContentBuilder<HTMLElement>? = null
) {
TagElement(
tagName = tagName,
applyAttrs = attrs,
content
)
}
var tagName by mutableStateOf("custom")
composition {
CustomElement(tagName, {
id("container")
}) {
Text("CUSTOM")
}
}
assertEquals("<div><custom id=\"container\">CUSTOM</custom></div>", root.outerHTML)
tagName = "anothercustom"
waitForRecompositionComplete()
assertEquals("<div><anothercustom id=\"container\">CUSTOM</anothercustom></div>", root.outerHTML)
}
@Test
fun elementBuilderShouldBeCalledOnce() = runTest {
var counter = 0
var flag by mutableStateOf(false)
composition {
TagElement({
counter++
document.createElement("div")
}, null,
if (flag) {
{ Div() { Text("ON") } }
} else null
)
}
assertEquals(1, counter, )
flag = true
waitForRecompositionComplete()
assertEquals(1, counter)
assertEquals("<div><div>ON</div></div>", nextChild().outerHTML)
}
@Test @NoLiveLiterals
fun keyChangesTheOrderButKeepsSameInstances() = runTest {
val items = mutableStateListOf(1, 2, 3)
composition {
items.forEach {
key(it) {
Div { Text("I = $it") }
}
}
}
val refs = listOf(
root.children[0],
root.children[1],
root.children[2],
)
root.children[0]!!.asDynamic().fakeid = "0"
root.children[1]!!.asDynamic().fakeid = "1"
root.children[2]!!.asDynamic().fakeid = "2"
items[0] = 3
items[1] = 2
items[2] = 1
waitForRecompositionComplete()
assertSame(refs[0], root.children[2])
assertSame(refs[1], root.children[1])
assertSame(refs[2], root.children[0])
assertEquals("0", root.children[2].asDynamic().fakeid)
assertEquals("1", root.children[1].asDynamic().fakeid)
assertEquals("2", root.children[0].asDynamic().fakeid)
}
}