aboutsummaryrefslogtreecommitdiff
path: root/dokka-subprojects/plugin-base/src/test/kotlin/renderers
diff options
context:
space:
mode:
Diffstat (limited to 'dokka-subprojects/plugin-base/src/test/kotlin/renderers')
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/BasicTest.kt24
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/BreadcrumbsTest.kt88
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/CoverPageTest.kt51
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/CustomFooterTest.kt48
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/DivergentTest.kt316
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/FooterMessageTest.kt31
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/FormattingUtilsTest.kt86
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/GroupWrappingTest.kt82
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/HeaderTest.kt102
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt68
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/ListStylesTest.kt45
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/NavigationIconTest.kt292
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/NavigationTest.kt414
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SearchbarDataInstallerTest.kt50
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt139
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SourceSetFilterTest.kt68
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/TabbedContentTest.kt188
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/TextStylesTest.kt113
18 files changed, 2205 insertions, 0 deletions
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/BasicTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/BasicTest.kt
new file mode 100644
index 00000000..9653b7bb
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/BasicTest.kt
@@ -0,0 +1,24 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.links.DRI
+import renderers.testPage
+import utils.Span
+import utils.match
+import kotlin.test.Test
+
+class BasicTest : HtmlRenderingOnlyTestBase() {
+ @Test
+ fun `unresolved DRI link should render as text`() {
+ val page = testPage {
+ link("linkText", DRI("nonexistentPackage", "nonexistentClass"))
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Span("linkText"))
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/BreadcrumbsTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/BreadcrumbsTest.kt
new file mode 100644
index 00000000..4bb0d41f
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/BreadcrumbsTest.kt
@@ -0,0 +1,88 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jsoup.nodes.Element
+import signatures.renderedContent
+import utils.*
+import kotlin.test.Test
+
+class BreadcrumbsTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ @Test
+ fun `should add breadcrumbs with current element`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/basic/TestClass.kt
+ |package testpackage
+ |
+ |class TestClass {
+ | fun foo() {}
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/testpackage/-test-class/foo.html").selectBreadcrumbs().match(
+ link("root"),
+ delimiter(),
+ link("testpackage"),
+ delimiter(),
+ link("TestClass"),
+ delimiter(),
+ current("foo"),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should mark only one element as current even if more elements have the same name`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/basic/TestClass.kt
+ |package testpackage
+ |
+ |class testname {
+ | val testname: String = ""
+ |}
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ writerPlugin.writer.renderedContent("root/testpackage/testname/testname.html").selectBreadcrumbs().match(
+ link("root"),
+ delimiter(),
+ link("testpackage"),
+ delimiter(),
+ link("testname"),
+ delimiter(),
+ current("testname"),
+ ignoreSpanWithTokenStyle = true
+ )
+ }
+ }
+ }
+
+ private fun Element.selectBreadcrumbs() = this.select("div.breadcrumbs").single()
+
+ private fun link(text: String): Tag = A(text)
+ private fun delimiter(): Tag = Span().withClasses("delimiter")
+ private fun current(text: String): Tag = Span(text).withClasses("current")
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/CoverPageTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/CoverPageTest.kt
new file mode 100644
index 00000000..6b3ce2eb
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/CoverPageTest.kt
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import signatures.renderedContent
+import utils.TestOutputWriterPlugin
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class CoverPageTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOf(commonStdlibPath!!)
+ externalDocumentationLinks = listOf(stdlibExternalDocumentationLink)
+ }
+ }
+ }
+
+ @Test
+ fun `names of nested inheritors`() {
+ val source = """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ | sealed class Result{
+ | class Success(): Result()
+ | class Failed(): Result()
+ | }
+ """
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.renderedContent("root/example/-result/index.html")
+ val tableInheritors = content.select("div.table").single { it.previousElementSibling()?.text() == "Inheritors" && it.childrenSize() == 2 }
+ assertEquals(tableInheritors.getElementsContainingOwnText("Failed").singleOrNull()?.tagName(), "a")
+ assertEquals(tableInheritors.getElementsContainingOwnText("Success").singleOrNull()?.tagName(), "a")
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/CustomFooterTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/CustomFooterTest.kt
new file mode 100644
index 00000000..ff562c38
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/CustomFooterTest.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.DokkaConfigurationImpl
+import org.jetbrains.dokka.PluginConfigurationImpl
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.DokkaBaseConfiguration
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.base.templating.toJsonString
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import renderers.testPage
+import utils.A
+import utils.Div
+import utils.Span
+import utils.match
+import kotlin.test.Test
+
+class CustomFooterTest : HtmlRenderingOnlyTestBase() {
+ @Test
+ fun `should include message from custom footer`() {
+ val page = testPage { }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(
+ Span(A()),
+ Span(Div("Custom message")),
+ Span(Span("Generated by "), A(Span("dokka"), Span()))
+ )
+ }
+
+ override val configuration: DokkaConfigurationImpl
+ get() = super.configuration.copy(
+ pluginsConfiguration = listOf(
+ PluginConfigurationImpl(
+ DokkaBase::class.java.canonicalName,
+ DokkaConfiguration.SerializationFormat.JSON,
+ toJsonString(DokkaBaseConfiguration(footerMessage = """<div style="color: red">Custom message</div>"""))
+ )
+ )
+ )
+
+ override val renderedContent: Element
+ get() = files.contents.getValue("test-page.html").let { Jsoup.parse(it) }.select(".footer").single()
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/DivergentTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/DivergentTest.kt
new file mode 100644
index 00000000..ccc43f12
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/DivergentTest.kt
@@ -0,0 +1,316 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.pages.ContentDivergentGroup
+import renderers.testPage
+import utils.Br
+import utils.match
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class DivergentTest : HtmlRenderingOnlyTestBase() {
+
+ @Test
+ fun simpleWrappingCase() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ }
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.select("[data-togglable=DEFAULT/js]").single().match("a")
+ }
+
+ @Test
+ fun noPlatformHintCase() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test"), implicitlySourceSetHinted = false) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ }
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match("a")
+ }
+
+ @Test
+ fun divergentBetweenSourceSets() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("c")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ val content = renderedContent
+ content.select("[data-togglable=DEFAULT/js]").single().match("a")
+ content.select("[data-togglable=DEFAULT/jvm]").single().match("b")
+ content.select("[data-togglable=DEFAULT/native]").single().match("c")
+ }
+
+ @Test
+ fun divergentInOneSourceSet() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(js)) {
+ divergent {
+ text("c")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.select("[data-togglable=DEFAULT/js]").single().match("abc")
+ }
+
+ @Test
+ fun divergentInAndBetweenSourceSets() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("c")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("d")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(native)) {
+ divergent {
+ text("e")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ val content = renderedContent
+ val orderOfTabs = content.select(".platform-bookmarks-row").single().children().map { it.attr("data-toggle") }
+
+ assertEquals(listOf("DEFAULT/js", "DEFAULT/jvm", "DEFAULT/native"), orderOfTabs)
+
+ content.select("[data-togglable=DEFAULT/native]").single().match("ae")
+ content.select("[data-togglable=DEFAULT/js]").single().match("bd")
+ content.select("[data-togglable=DEFAULT/jvm]").single().match("c")
+ }
+
+ @Test
+ fun divergentInAndBetweenSourceSetsWithGrouping() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ after {
+ text("a+")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(js)) {
+ divergent {
+ text("b")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test")), setOf(jvm)) {
+ divergent {
+ text("c")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(js)) {
+ divergent {
+ text("d")
+ }
+ after {
+ text("bd+")
+ }
+ }
+ instance(setOf(DRI("test", "Test3")), setOf(native)) {
+ divergent {
+ text("e")
+ }
+ after {
+ text("e+")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ val content = renderedContent
+ content.select("[data-togglable=DEFAULT/native]").single().match("aa+", Br, "ee+")
+ content.select("[data-togglable=DEFAULT/js]").single().match("bdbd+")
+ content.select("[data-togglable=DEFAULT/jvm]").single().match("c")
+ }
+
+ @Test
+ fun divergentSameBefore() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("a")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("b")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.select("[data-togglable=DEFAULT/native]").single().match("ab-ab")
+ }
+
+ @Test
+ fun divergentSameAfter() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.select("[data-togglable=DEFAULT/native]").single().match("abab+")
+ }
+
+ @Test
+ fun divergentGroupedByBeforeAndAfter() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("ab-")
+ }
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.select("[data-togglable=DEFAULT/native]").single().match("ab-abab+")
+ }
+
+ @Test
+ fun divergentDifferentBeforeAndAfter() {
+ val page = testPage {
+ divergentGroup(ContentDivergentGroup.GroupID("test")) {
+ instance(setOf(DRI("test", "Test")), setOf(native)) {
+ before {
+ text("a-")
+ }
+ divergent {
+ text("a")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ instance(setOf(DRI("test", "Test2")), setOf(native)) {
+ before {
+ text("b-")
+ }
+ divergent {
+ text("b")
+ }
+ after {
+ text("ab+")
+ }
+ }
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.select("[data-togglable=DEFAULT/native]").single().match("a-aab+", Br, "b-bab+")
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/FooterMessageTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/FooterMessageTest.kt
new file mode 100644
index 00000000..149f970c
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/FooterMessageTest.kt
@@ -0,0 +1,31 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.DokkaBaseConfiguration.Companion.defaultFooterMessage
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import renderers.testPage
+import utils.A
+import utils.Span
+import utils.match
+import kotlin.test.Test
+
+class FooterMessageTest : HtmlRenderingOnlyTestBase() {
+ @Test
+ fun `should include defaultFooter`() {
+ val page = testPage { }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(
+ Span(A()),
+ Span(defaultFooterMessage),
+ Span(Span("Generated by "), A(Span("dokka"), Span()))
+ )
+ }
+
+ override val renderedContent: Element
+ get() = files.contents.getValue("test-page.html").let { Jsoup.parse(it) }.select(".footer").single()
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/FormattingUtilsTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/FormattingUtilsTest.kt
new file mode 100644
index 00000000..028ffa77
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/FormattingUtilsTest.kt
@@ -0,0 +1,86 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import kotlinx.html.body
+import kotlinx.html.html
+import kotlinx.html.stream.createHTML
+import org.jetbrains.dokka.base.renderers.html.buildBreakableText
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class FormattingUtilsTest {
+ @Test
+ fun `should build breakable text`(){
+ val testedText = "kotlinx.collections.immutable"
+ val expectedHtml = """
+ <html>
+ <body><span>kotlinx.</span><wbr></wbr><span>collections.</span><wbr></wbr><span>immutable</span></body>
+ </html>
+ """.trimIndent()
+
+ val html = createHTML(prettyPrint = true).html {
+ body {
+ buildBreakableText(testedText)
+ }
+ }
+
+ assertEquals(expectedHtml.trim(), html.trim())
+ }
+
+ @Test
+ fun `should build breakable text without empty spans`(){
+ val testedText = "Package org.jetbrains.dokka.it.moduleC"
+ val expectedHtml = """
+ <html>
+ <body><span><span>Package</span></span> <span>org.</span><wbr></wbr><span>jetbrains.</span><wbr></wbr><span>dokka.</span><wbr></wbr><span>it.</span><wbr></wbr><span>moduleC</span></body>
+ </html>
+ """.trimIndent()
+
+ val html = createHTML(prettyPrint = true).html {
+ body {
+ buildBreakableText(testedText)
+ }
+ }
+
+ assertEquals(expectedHtml.trim(), html.trim())
+ }
+
+ @Test
+ fun `should build breakable text for text with braces`(){
+ val testedText = "[Common]kotlinx.collections.immutable"
+ val expectedHtml = """
+ <html>
+ <body><span>[Common]kotlinx.</span><wbr></wbr><span>collections.</span><wbr></wbr><span>immutable</span></body>
+ </html>
+ """.trimIndent()
+
+ val html = createHTML(prettyPrint = true).html {
+ body {
+ buildBreakableText(testedText)
+ }
+ }
+
+ assertEquals(expectedHtml.trim(), html.trim())
+ }
+
+ @Test
+ fun `should build breakable text for camel case notation`(){
+ val testedText = "DokkkkkkkaIsTheBest"
+ val expectedHtml = """
+ <html>
+ <body><span>Dokkkkkkka</span><wbr></wbr><span>Is</span><wbr></wbr><span>The</span><wbr></wbr><span><span>Best</span></span></body>
+ </html>
+ """.trimIndent()
+
+ val html = createHTML(prettyPrint = true).html {
+ body {
+ buildBreakableText(testedText)
+ }
+ }
+
+ assertEquals(expectedHtml.trim(), html.trim())
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/GroupWrappingTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/GroupWrappingTest.kt
new file mode 100644
index 00000000..cc9b763d
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/GroupWrappingTest.kt
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.pages.TextStyle
+import renderers.testPage
+import utils.Div
+import utils.P
+import utils.match
+import kotlin.test.Test
+
+class GroupWrappingTest : HtmlRenderingOnlyTestBase() {
+
+ @Test
+ fun notWrapped() {
+ val page = testPage {
+ group {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ HtmlRenderer(context).render(page)
+
+ renderedContent.match("abc")
+ }
+
+ @Test
+ fun paragraphWrapped() {
+ val page = testPage {
+ group(styles = setOf(TextStyle.Paragraph)) {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ HtmlRenderer(context).render(page)
+
+ renderedContent.match(P("ab"), "c")
+ }
+
+ @Test
+ fun blockWrapped() {
+ val page = testPage {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a")
+ text("b")
+ }
+ text("c")
+ }
+
+ HtmlRenderer(context).render(page)
+
+ renderedContent.match(Div("ab"), "c")
+ }
+
+ @Test
+ fun nested() {
+ val page = testPage {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a")
+ group(styles = setOf(TextStyle.Block)) {
+ group(styles = setOf(TextStyle.Block)) {
+ text("b")
+ text("c")
+ }
+ }
+ text("d")
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+
+ renderedContent.match(Div("a", Div(Div("bc")), "d"))
+ }
+
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/HeaderTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/HeaderTest.kt
new file mode 100644
index 00000000..c19f965f
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/HeaderTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.DokkaConfigurationImpl
+import org.jetbrains.dokka.PluginConfigurationImpl
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.DokkaBaseConfiguration
+import org.jetbrains.dokka.base.templating.toJsonString
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.pages.RootPageNode
+import org.jetbrains.dokka.plugability.DokkaContext
+import org.jsoup.Jsoup
+import utils.TestOutputWriter
+import utils.TestOutputWriterPlugin
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+
+class HeaderTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ name = "jvm"
+ sourceRoots = listOf("src/jvm")
+ }
+ sourceSet {
+ name = "js"
+ sourceRoots = listOf("src/js")
+ }
+ }
+ }
+
+ @Test
+ fun `should include homepage link if homepageLink is provided`() {
+ testRendering(
+ DokkaBaseConfiguration(homepageLink = "https://github.com/Kotlin/dokka/")
+ ) { _, _, writer ->
+ val renderedContent = navigationElement(writer)
+
+ val sourceLinkElement =
+ assertNotNull(renderedContent.getElementById("homepage-link"), "Source link element not found")
+ val aElement = assertNotNull(sourceLinkElement.selectFirst("a"))
+ assertEquals("https://github.com/Kotlin/dokka/", aElement.attr("href"))
+ }
+ }
+
+ @Test
+ fun `should not include homepage link by default`() {
+ testRendering(null) { _, _, writer ->
+ val renderedContent = navigationElement(writer)
+ assertNull(renderedContent.getElementById("homepage-link"), "Source link element found")
+ }
+ }
+
+ private fun testRendering(
+ baseConfiguration: DokkaBaseConfiguration?,
+ block: (RootPageNode, DokkaContext, writer: TestOutputWriter) -> Unit
+ ) {
+ fun configuration(): DokkaConfigurationImpl {
+ baseConfiguration ?: return configuration
+ return configuration.copy(
+ pluginsConfiguration = listOf(
+ PluginConfigurationImpl(
+ DokkaBase::class.java.canonicalName,
+ DokkaConfiguration.SerializationFormat.JSON,
+ toJsonString(baseConfiguration)
+ )
+ )
+ )
+ }
+
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/jvm/Test.kt
+ |fun test() {}
+ |/src/js/Test.kt
+ |fun test() {}
+ """,
+ configuration(),
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { node, context ->
+ block(node, context, writerPlugin.writer)
+ }
+ }
+ }
+
+ private fun navigationElement(writer: TestOutputWriter) =
+ writer
+ .contents
+ .getValue("index.html")
+ .let(Jsoup::parse)
+ .select(".navigation")
+ .single()
+
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt
new file mode 100644
index 00000000..4e098371
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/HtmlRenderingOnlyTestBase.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.DokkaConfigurationImpl
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.renderers.RootCreator
+import org.jetbrains.dokka.base.resolvers.external.DefaultExternalLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.external.javadoc.JavadocExternalLocationProviderFactory
+import org.jetbrains.dokka.base.resolvers.local.DokkaLocationProviderFactory
+import org.jetbrains.dokka.testApi.context.MockContext
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import renderers.RenderingOnlyTestBase
+import testApi.testRunner.defaultSourceSet
+import utils.TestOutputWriter
+import java.io.File
+
+abstract class HtmlRenderingOnlyTestBase : RenderingOnlyTestBase<Element>() {
+
+ protected val js = defaultSourceSet.copy(
+ "JS",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "js"),
+ analysisPlatform = Platform.js,
+ sourceRoots = setOf(File("pl1"))
+ )
+ protected val jvm = defaultSourceSet.copy(
+ "JVM",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "jvm"),
+
+ analysisPlatform = Platform.jvm,
+ sourceRoots = setOf(File("pl1"))
+ )
+ protected val native = defaultSourceSet.copy(
+ "NATIVE",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "native"),
+ analysisPlatform = Platform.native,
+ sourceRoots = setOf(File("pl1"))
+ )
+
+ val files = TestOutputWriter()
+
+ open val configuration = DokkaConfigurationImpl(
+ sourceSets = listOf(js, jvm, native),
+ finalizeCoroutines = false
+ )
+
+ override val context = MockContext(
+ DokkaBase().outputWriter to { files },
+ DokkaBase().locationProviderFactory to ::DokkaLocationProviderFactory,
+ DokkaBase().htmlPreprocessors to { RootCreator },
+ DokkaBase().externalLocationProviderFactory to ::JavadocExternalLocationProviderFactory,
+ DokkaBase().externalLocationProviderFactory to ::DefaultExternalLocationProviderFactory,
+ testConfiguration = configuration
+ )
+
+ override val renderedContent: Element by lazy {
+ files.contents.getValue("test-page.html").let { Jsoup.parse(it) }.select("#content").single()
+ }
+
+ protected fun linesAfterContentTag() =
+ files.contents.getValue("test-page.html").lines()
+ .dropWhile { !it.contains("""<div id="content">""") }
+ .joinToString(separator = "") { it.trim() }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/ListStylesTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/ListStylesTest.kt
new file mode 100644
index 00000000..f8afb54c
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/ListStylesTest.kt
@@ -0,0 +1,45 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.pages.ListStyle
+import renderers.testPage
+import utils.Dd
+import utils.Dl
+import utils.Dt
+import utils.match
+import kotlin.test.Test
+
+
+class ListStylesTest : HtmlRenderingOnlyTestBase() {
+
+ @Test
+ fun `description list render`() {
+ val page = testPage {
+ descriptionList {
+ item(styles = setOf(ListStyle.DescriptionTerm)) {
+ text("Description term #1")
+ }
+ item(styles = setOf(ListStyle.DescriptionTerm)) {
+ text("Description term #2")
+ }
+ item(styles = setOf(ListStyle.DescriptionDetails)) {
+ text("Description details describing terms #1 and #2")
+ }
+ }
+ }
+
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(
+ Dl(
+ Dt("Description term #1"),
+ Dt("Description term #2"),
+ Dd("Description details describing terms #1 and #2")
+ )
+ )
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/NavigationIconTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/NavigationIconTest.kt
new file mode 100644
index 00000000..d57f84df
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/NavigationIconTest.kt
@@ -0,0 +1,292 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import utils.TestOutputWriterPlugin
+import utils.navigationHtml
+import utils.selectNavigationGrid
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class NavigationIconTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ @Test
+ fun `should include all navigation icons`() {
+ val source = """
+ |/src/main/kotlin/com/example/Empty.kt
+ |package com.example
+ |
+ |class Empty {}
+ """
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val navIconAssets = writerPlugin.writer.contents
+ .filterKeys { it.startsWith("images/nav-icons") }
+ .keys.sorted()
+
+ assertEquals(16, navIconAssets.size)
+ assertEquals("images/nav-icons/abstract-class-kotlin.svg", navIconAssets[0])
+ assertEquals("images/nav-icons/abstract-class.svg", navIconAssets[1])
+ assertEquals("images/nav-icons/annotation-kotlin.svg", navIconAssets[2])
+ assertEquals("images/nav-icons/annotation.svg", navIconAssets[3])
+ assertEquals("images/nav-icons/class-kotlin.svg", navIconAssets[4])
+ assertEquals("images/nav-icons/class.svg", navIconAssets[5])
+ assertEquals("images/nav-icons/enum-kotlin.svg", navIconAssets[6])
+ assertEquals("images/nav-icons/enum.svg", navIconAssets[7])
+ assertEquals("images/nav-icons/exception-class.svg", navIconAssets[8])
+ assertEquals("images/nav-icons/field-value.svg", navIconAssets[9])
+ assertEquals("images/nav-icons/field-variable.svg", navIconAssets[10])
+ assertEquals("images/nav-icons/function.svg", navIconAssets[11])
+ assertEquals("images/nav-icons/interface-kotlin.svg", navIconAssets[12])
+ assertEquals("images/nav-icons/interface.svg", navIconAssets[13])
+ assertEquals("images/nav-icons/object.svg", navIconAssets[14])
+ assertEquals("images/nav-icons/typealias-kotlin.svg", navIconAssets[15])
+ }
+ }
+ }
+
+ @Test
+ fun `should add icon styles to kotlin class navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("class Clazz {}"),
+ expectedIconClass = "class-kt",
+ expectedNavLinkText = "Clazz"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to java class navigation item`() {
+ assertNavigationIcon(
+ source = javaSource(
+ className = "JavaClazz",
+ source = "public class JavaClazz {}"
+ ),
+ expectedIconClass = "class",
+ expectedNavLinkText = "JavaClazz"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin abstract class navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("abstract class AbstractClazz {}"),
+ expectedIconClass = "abstract-class-kt",
+ expectedNavLinkText = "AbstractClazz"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to java abstract class navigation item`() {
+ assertNavigationIcon(
+ source = javaSource(
+ className = "AbstractJavaClazz",
+ source = "public abstract class AbstractJavaClazz {}"
+ ),
+ expectedIconClass = "abstract-class",
+ expectedNavLinkText = "AbstractJavaClazz"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin typealias navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("typealias KotlinTypealias = String"),
+ expectedIconClass = "typealias-kt",
+ expectedNavLinkText = "KotlinTypealias"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin enum navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("enum class KotlinEnum {}"),
+ expectedIconClass = "enum-class-kt",
+ expectedNavLinkText = "KotlinEnum"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to java enum class navigation item`() {
+ assertNavigationIcon(
+ source = javaSource(
+ className = "JavaEnum",
+ source = "public enum JavaEnum {}"
+ ),
+ expectedIconClass = "enum-class",
+ expectedNavLinkText = "JavaEnum"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin annotation navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("annotation class KotlinAnnotation"),
+ expectedIconClass = "annotation-class-kt",
+ expectedNavLinkText = "KotlinAnnotation"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to java annotation navigation item`() {
+ assertNavigationIcon(
+ source = javaSource(
+ className = "JavaAnnotation",
+ source = "public @interface JavaAnnotation {}"
+ ),
+ expectedIconClass = "annotation-class",
+ expectedNavLinkText = "JavaAnnotation"
+ )
+ }
+
+
+ @Test
+ fun `should add icon styles to kotlin interface navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("interface KotlinInterface"),
+ expectedIconClass = "interface-kt",
+ expectedNavLinkText = "KotlinInterface"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to java interface navigation item`() {
+ assertNavigationIcon(
+ source = javaSource(
+ className = "JavaInterface",
+ source = "public interface JavaInterface {}"
+ ),
+ expectedIconClass = "interface",
+ expectedNavLinkText = "JavaInterface"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin function navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("fun ktFunction() {}"),
+ expectedIconClass = "function",
+ expectedNavLinkText = "ktFunction()"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin exception class navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("class KotlinException : Exception() {}"),
+ expectedIconClass = "exception-class",
+ expectedNavLinkText = "KotlinException"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin object navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("object KotlinObject {}"),
+ expectedIconClass = "object",
+ expectedNavLinkText = "KotlinObject"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin val navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("val value: String? = null"),
+ expectedIconClass = "val",
+ expectedNavLinkText = "value"
+ )
+ }
+
+ @Test
+ fun `should add icon styles to kotlin var navigation item`() {
+ assertNavigationIcon(
+ source = kotlinSource("var variable: String? = null"),
+ expectedIconClass = "var",
+ expectedNavLinkText = "variable"
+ )
+ }
+
+ private fun kotlinSource(source: String): String {
+ return """
+ |/src/main/kotlin/com/example/Example.kt
+ |package com.example
+ |
+ |$source
+ """.trimIndent()
+ }
+
+ private fun javaSource(className: String, source: String): String {
+ return """
+ |/src/main/java/com/example/$className.java
+ |package com.example;
+ |
+ |$source
+ """.trimIndent()
+ }
+
+ private fun assertNavigationIcon(source: String, expectedIconClass: String, expectedNavLinkText: String) {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ val navigationGrid = content.selectNavigationGrid()
+
+ val classNames = navigationGrid.child(0).classNames().toList()
+ assertEquals("nav-link-child", classNames[0])
+ assertEquals("nav-icon", classNames[1])
+ assertEquals(expectedIconClass, classNames[2])
+
+ val navLinkText = navigationGrid.child(1).text()
+ assertEquals(expectedNavLinkText, navLinkText)
+ }
+ }
+ }
+
+ @Test
+ fun `should not generate nav link grids or icons for packages and modules`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/Example.kt
+ |package com.example
+ |
+ |class Example {}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+
+ assertEquals(3, content.size)
+ assertEquals("root-nav-submenu", content[0].id())
+ assertEquals("root-nav-submenu-0", content[1].id())
+ assertEquals("root-nav-submenu-0-0", content[2].id())
+
+ // there's 3 nav items, but only one icon
+ val navLinkGrids = content.select("span.nav-icon")
+ assertEquals(1, navLinkGrids.size)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/NavigationTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/NavigationTest.kt
new file mode 100644
index 00000000..02074810
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/NavigationTest.kt
@@ -0,0 +1,414 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.renderers.html.NavigationNodeIcon
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jsoup.nodes.Element
+import utils.TestOutputWriterPlugin
+import utils.navigationHtml
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+
+class NavigationTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ }
+ }
+ }
+
+ @Test
+ fun `should sort alphabetically ignoring case`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/Sequences.kt
+ |package com.example
+ |
+ |fun <T> sequence(): Sequence<T>
+ |
+ |fun <T> Sequence(): Sequence<T>
+ |
+ |fun <T> Sequence<T>.any() {}
+ |
+ |interface Sequence<T>
+ """.trimMargin(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(6, content.size)
+
+ // Navigation menu should be the following:
+ // - root
+ // - com.example
+ // - any()
+ // - Sequence interface
+ // - Sequence()
+ // - sequence()
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "any()",
+ address = "root/com.example/any.html",
+ icon = NavigationNodeIcon.FUNCTION
+ )
+
+ content[3].assertNavigationLink(
+ id = "root-nav-submenu-0-1",
+ text = "Sequence",
+ address = "root/com.example/-sequence/index.html",
+ icon = NavigationNodeIcon.INTERFACE_KT
+ )
+
+ content[4].assertNavigationLink(
+ id = "root-nav-submenu-0-2",
+ text = "Sequence()",
+ address = "root/com.example/-sequence.html",
+ icon = NavigationNodeIcon.FUNCTION
+ )
+
+ content[5].assertNavigationLink(
+ id = "root-nav-submenu-0-3",
+ text = "sequence()",
+ address = "root/com.example/sequence.html",
+ icon = NavigationNodeIcon.FUNCTION
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should strike deprecated class link`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/SimpleDeprecatedClass.kt
+ |package com.example
+ |
+ |@Deprecated("reason")
+ |class SimpleDeprecatedClass {}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(3, content.size)
+
+ // Navigation menu should be the following:
+ // - root
+ // - com.example
+ // - SimpleDeprecatedClass
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "SimpleDeprecatedClass",
+ address = "root/com.example/-simple-deprecated-class/index.html",
+ icon = NavigationNodeIcon.CLASS_KT,
+ isStrikethrough = true
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should not strike pages where only one of N documentables is deprecated`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/File.kt
+ |package com.example
+ |
+ |/**
+ | * First
+ | */
+ |@Deprecated("reason")
+ |fun functionWithCommonName()
+ |
+ |/**
+ | * Second
+ | */
+ |fun functionWithCommonName()
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(3, content.size)
+
+ // Navigation menu should be the following:
+ // - root
+ // - com.example
+ // - functionWithCommonName
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "functionWithCommonName()",
+ address = "root/com.example/function-with-common-name.html",
+ icon = NavigationNodeIcon.FUNCTION,
+ isStrikethrough = false
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should have expandable classlikes`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/WithInner.kt
+ |package com.example
+ |
+ |class WithInner {
+ | // in-class functions should not be in navigation
+ | fun a() {}
+ | fun b() {}
+ | fun c() {}
+ |
+ | class InnerClass {}
+ | interface InnerInterface {}
+ | enum class InnerEnum {}
+ | object InnerObject {}
+ | annotation class InnerAnnotation {}
+ | companion object CompanionObject {}
+ |}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(9, content.size)
+
+ // Navigation menu should be the following, sorted by name:
+ // - root
+ // - com.example
+ // - WithInner
+ // - CompanionObject
+ // - InnerAnnotation
+ // - InnerClass
+ // - InnerEnum
+ // - InnerInterface
+ // - InnerObject
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "WithInner",
+ address = "root/com.example/-with-inner/index.html",
+ icon = NavigationNodeIcon.CLASS_KT
+ )
+
+ content[3].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0",
+ text = "CompanionObject",
+ address = "root/com.example/-with-inner/-companion-object/index.html",
+ icon = NavigationNodeIcon.OBJECT
+ )
+
+ content[4].assertNavigationLink(
+ id = "root-nav-submenu-0-0-1",
+ text = "InnerAnnotation",
+ address = "root/com.example/-with-inner/-inner-annotation/index.html",
+ icon = NavigationNodeIcon.ANNOTATION_CLASS_KT
+ )
+
+ content[5].assertNavigationLink(
+ id = "root-nav-submenu-0-0-2",
+ text = "InnerClass",
+ address = "root/com.example/-with-inner/-inner-class/index.html",
+ icon = NavigationNodeIcon.CLASS_KT
+ )
+
+ content[6].assertNavigationLink(
+ id = "root-nav-submenu-0-0-3",
+ text = "InnerEnum",
+ address = "root/com.example/-with-inner/-inner-enum/index.html",
+ icon = NavigationNodeIcon.ENUM_CLASS_KT
+ )
+
+ content[7].assertNavigationLink(
+ id = "root-nav-submenu-0-0-4",
+ text = "InnerInterface",
+ address = "root/com.example/-with-inner/-inner-interface/index.html",
+ icon = NavigationNodeIcon.INTERFACE_KT
+ )
+
+ content[8].assertNavigationLink(
+ id = "root-nav-submenu-0-0-5",
+ text = "InnerObject",
+ address = "root/com.example/-with-inner/-inner-object/index.html",
+ icon = NavigationNodeIcon.OBJECT
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `should be able to have deeply nested classlikes`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/com/example/DeeplyNested.kt
+ |package com.example
+ |
+ |class DeeplyNested {
+ | class FirstLevelClass {
+ | interface SecondLevelInterface {
+ | object ThirdLevelObject {
+ | annotation class FourthLevelAnnotation {}
+ | }
+ | }
+ | }
+ |}
+ """.trimIndent(),
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.writer.navigationHtml().select("div.sideMenuPart")
+ assertEquals(7, content.size)
+
+ // Navigation menu should be the following
+ // - root
+ // - com.example
+ // - DeeplyNested
+ // - FirstLevelClass
+ // - SecondLevelInterface
+ // - ThirdLevelObject
+ // - FourthLevelAnnotation
+
+ content[0].assertNavigationLink(
+ id = "root-nav-submenu",
+ text = "root",
+ address = "index.html",
+ )
+
+ content[1].assertNavigationLink(
+ id = "root-nav-submenu-0",
+ text = "com.example",
+ address = "root/com.example/index.html",
+ )
+
+ content[2].assertNavigationLink(
+ id = "root-nav-submenu-0-0",
+ text = "DeeplyNested",
+ address = "root/com.example/-deeply-nested/index.html",
+ icon = NavigationNodeIcon.CLASS_KT
+ )
+
+ content[3].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0",
+ text = "FirstLevelClass",
+ address = "root/com.example/-deeply-nested/-first-level-class/index.html",
+ icon = NavigationNodeIcon.CLASS_KT
+ )
+
+ content[4].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0-0",
+ text = "SecondLevelInterface",
+ address = "root/com.example/-deeply-nested/-first-level-class/-second-level-interface/index.html",
+ icon = NavigationNodeIcon.INTERFACE_KT
+ )
+
+ content[5].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0-0-0",
+ text = "ThirdLevelObject",
+ address = "root/com.example/-deeply-nested/-first-level-class/-second-level-interface/" +
+ "-third-level-object/index.html",
+ icon = NavigationNodeIcon.OBJECT
+ )
+
+ content[6].assertNavigationLink(
+ id = "root-nav-submenu-0-0-0-0-0-0",
+ text = "FourthLevelAnnotation",
+ address = "root/com.example/-deeply-nested/-first-level-class/-second-level-interface/" +
+ "-third-level-object/-fourth-level-annotation/index.html",
+ icon = NavigationNodeIcon.ANNOTATION_CLASS_KT
+ )
+ }
+ }
+ }
+
+ private fun Element.assertNavigationLink(
+ id: String, text: String, address: String, icon: NavigationNodeIcon? = null, isStrikethrough: Boolean = false
+ ) {
+ assertEquals(id, this.id())
+
+ val link = this.selectFirst("a")
+ assertNotNull(link)
+ assertEquals(text, link.text())
+ assertEquals(address, link.attr("href"))
+ if (icon != null) {
+ val iconStyles =
+ this.selectFirst("div.overview span.nav-link-grid")?.child(0)?.classNames()?.toList() ?: emptyList()
+ assertEquals(3, iconStyles.size)
+ assertEquals("nav-link-child", iconStyles[0])
+ assertEquals(icon.style(), "${iconStyles[1]} ${iconStyles[2]}")
+ }
+ if (isStrikethrough) {
+ val textInsideStrikethrough = link.selectFirst("strike")?.text()
+ assertEquals(text, textInsideStrikethrough)
+ } else {
+ assertNull(link.selectFirst("strike"))
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SearchbarDataInstallerTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SearchbarDataInstallerTest.kt
new file mode 100644
index 00000000..a5f5feb5
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SearchbarDataInstallerTest.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import utils.TestOutputWriterPlugin
+import utils.pagesJson
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class SearchbarDataInstallerTest: BaseAbstractTest() {
+
+ @Test // see #2289
+ fun `should display description of root declarations without a leading dot`() {
+ val configuration = dokkaConfiguration {
+ moduleName = "Dokka Module"
+
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/kotlin/Test.kt")
+ }
+ }
+ }
+
+ val source = """
+ |/src/kotlin/Test.kt
+ |
+ |class Test
+ |
+ """.trimIndent()
+
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val searchRecords = writerPlugin.writer.pagesJson()
+
+ assertEquals(
+ "Test",
+ searchRecords.find { record -> record.name == "class Test" }?.description ?: ""
+ )
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt
new file mode 100644
index 00000000..e3c28984
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SourceSetDependentHintTest.kt
@@ -0,0 +1,139 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.pages.TextStyle
+import renderers.testPage
+import testApi.testRunner.defaultSourceSet
+import utils.Div
+import utils.match
+import java.io.File
+import kotlin.test.Test
+
+class SourceSetDependentHintTest : HtmlRenderingOnlyTestBase() {
+
+ private val pl1 = defaultSourceSet.copy(
+ "pl1",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "pl1"),
+ analysisPlatform = Platform.js,
+ sourceRoots = setOf(File("pl1"))
+ )
+ private val pl2 = defaultSourceSet.copy(
+ "pl2",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "pl2"),
+ analysisPlatform = Platform.jvm,
+ sourceRoots = setOf(File("pl1"))
+ )
+ private val pl3 = defaultSourceSet.copy(
+ "pl3",
+ defaultSourceSet.sourceSetID.copy(sourceSetName = "pl3"),
+ analysisPlatform = Platform.native,
+ sourceRoots = setOf(File("pl1"))
+ )
+
+ @Test
+ fun platformIndependentCase() {
+ val page = testPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a")
+ text("b")
+ text("c")
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("abc"))))
+ }
+
+ @Test
+ fun completelyDivergentCase() {
+ val page = testPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl2))
+ text("c", sourceSets = setOf(pl3))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("a")), Div(Div("b")), Div(Div("c"))))
+ }
+
+ @Test
+ fun overlappingCase() {
+ val page = testPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl1, pl2))
+ text("c", sourceSets = setOf(pl2))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("ab")), Div(Div("bc"))))
+ }
+
+ @Test
+ fun caseThatCanBeSimplified() {
+ val page = testPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("ab"))))
+ }
+
+ @Test
+ fun caseWithGroupBreakingSimplification() {
+ val page = testPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2), styles = setOf(TextStyle.Block)) {
+ group(styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ }
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div(Div("ab"))), Div(Div(Div("a"), "b"))))
+ }
+
+ @Test
+ fun caseWithGroupNotBreakingSimplification() {
+ val page = testPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2)) {
+ group {
+ text("a", sourceSets = setOf(pl1, pl2))
+ text("b", sourceSets = setOf(pl1))
+ }
+ text("b", sourceSets = setOf(pl2))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div("ab")))
+ }
+
+ @Test
+ fun partiallyUnifiedCase() {
+ val page = testPage {
+ sourceSetDependentHint(sourceSets = setOf(pl1, pl2, pl3), styles = setOf(TextStyle.Block)) {
+ text("a", sourceSets = setOf(pl1))
+ text("a", sourceSets = setOf(pl2))
+ text("b", sourceSets = setOf(pl3))
+ }
+ }
+
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Div(Div(Div("a")), Div(Div("b"))))
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SourceSetFilterTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SourceSetFilterTest.kt
new file mode 100644
index 00000000..b461bfcd
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/SourceSetFilterTest.kt
@@ -0,0 +1,68 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import signatures.renderedContent
+import utils.TestOutputWriterPlugin
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class SourceSetFilterTest : BaseAbstractTest() {
+
+ @Test // see #3011
+ fun `should separate multiple data-filterable attribute values with comma`() {
+ val configuration = dokkaConfiguration {
+ moduleName = "Dokka Module"
+
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/testing/Test.kt")
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/testing/Test.kt")
+ }
+ }
+ }
+
+ val source = """
+ |/src/commonMain/kotlin/testing/Test.kt
+ |package testing
+ |
+ |expect open class Test
+ |
+ |/src/jvmMain/kotlin/testing/Test.kt
+ |package testing
+ |
+ |actual open class Test
+ """.trimIndent()
+
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val packagePage = writerPlugin.writer.renderedContent("-dokka -module/testing/index.html")
+
+ val testClassRow = packagePage
+ .select("div[data-togglable=TYPE]")
+ .select("div[class=table-row]")
+ .single()
+
+ assertEquals("Dokka Module/common,Dokka Module/jvm", testClassRow.attr("data-filterable-current"))
+ assertEquals("Dokka Module/common,Dokka Module/jvm", testClassRow.attr("data-filterable-set"))
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/TabbedContentTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/TabbedContentTest.kt
new file mode 100644
index 00000000..090127fd
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/TabbedContentTest.kt
@@ -0,0 +1,188 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jsoup.nodes.Element
+import signatures.renderedContent
+import utils.TestOutputWriterPlugin
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class TabbedContentTest : BaseAbstractTest() {
+
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOf(commonStdlibPath!!)
+ externalDocumentationLinks = listOf(stdlibExternalDocumentationLink)
+ }
+ }
+ }
+
+ private fun Element.getTabbedRow(type: String) = select(".table-row[data-togglable=$type]")
+ private fun Element.getTabbedTable(type: String) = select("div[data-togglable=$type] .table")
+ private fun Element.getMainContentDataType() = selectFirst(".main-content")?.attr("data-page-type")
+
+ @Test
+ fun `should have correct tabbed content type`() {
+ val source = """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ |val p = 0
+ |fun foo() = 0
+ |
+ | class A(val d: Int = 0) {
+ | class Success(): Result()
+ | class Failed(): Result()
+ |
+ | fun fn() = 0
+ | }
+ |
+ | fun A.fn() = 0
+ | fun A.fn2() = 0
+ | fun A.fn3() = 0
+ | val A.p = 0
+ | val A.p2 = 0
+ """
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val classContent = writerPlugin.writer.renderedContent("root/example/-a/index.html")
+ assertEquals(1, classContent.getTabbedTable("CONSTRUCTOR").size)
+ assertEquals(1, classContent.getTabbedTable("PROPERTY").size)
+ assertEquals(1, classContent.getTabbedTable("CONSTRUCTOR").size)
+ assertEquals(1, classContent.getTabbedTable("FUNCTION").size)
+ assertEquals(1, classContent.getTabbedTable("TYPE").size)
+ assertEquals(3, classContent.getTabbedRow("EXTENSION_FUNCTION").size)
+ assertEquals(2, classContent.getTabbedRow("EXTENSION_PROPERTY").size)
+ assertEquals("classlike", classContent.getMainContentDataType())
+
+ val packagePage = writerPlugin.writer.renderedContent("root/example/index.html")
+ assertEquals(1, packagePage.getTabbedTable("TYPE").size)
+ assertEquals(1, packagePage.getTabbedTable("PROPERTY").size)
+ assertEquals(1, packagePage.getTabbedTable("FUNCTION").size)
+ assertEquals(3, packagePage.getTabbedRow("EXTENSION_FUNCTION").size)
+ assertEquals(2, packagePage.getTabbedRow("EXTENSION_PROPERTY").size)
+ assertEquals("package", packagePage.getMainContentDataType())
+ }
+ }
+ }
+
+ @Test
+ fun `should not have Types-tab where there are not types`() {
+ val source = """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ |val p = 0
+ |fun foo() = 0
+ |
+ |/src/main/kotlin/test/PackageTwo.kt
+ |package example2
+ |
+ |class A
+ """
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val packagePage = writerPlugin.writer.renderedContent("root/example/index.html")
+ assertEquals(0, packagePage.select("*[data-togglable=TYPE]").size)
+ assertEquals(1, packagePage.getTabbedTable("PROPERTY").size)
+ assertEquals(1, packagePage.getTabbedTable("FUNCTION").size)
+
+ val packagePage2 = writerPlugin.writer.renderedContent("root/example2/index.html")
+ assertEquals(2, packagePage2.select("*[data-togglable=TYPE]").size)
+ assertEquals(0, packagePage2.getTabbedTable("PROPERTY").size)
+ assertEquals(0, packagePage2.getTabbedTable("FUNCTION").size)
+ }
+ }
+ }
+
+ @Test
+ fun `should have correct order of members and extensions`() {
+ val source = """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ |val p = 0
+ |fun foo() = 0
+ |
+ |class A(val d: Int = 0) {
+ | fun fn() = 0
+ | fun a() = 0
+ | fun g() = 0
+ |}
+ |
+ | fun A.fn() = 0
+ """
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val classContent = writerPlugin.writer.renderedContent("root/example/-a/index.html")
+ val funTable = classContent.select("div[data-togglable=FUNCTION] .table")
+ val orders =
+ funTable.select(".table-row").map { it.attr("data-togglable") }
+ assertEquals(listOf("", "", "EXTENSION_FUNCTION", ""), orders)
+ val names =
+ funTable.select(".main-subrow .inline-flex a").map { it.text() }
+ assertEquals(listOf("a", "fn", "fn", "g"), names)
+ }
+ }
+ }
+
+ @Test
+ fun `should have expected order of content types within a members tab`() {
+ val source = """
+ |/src/main/kotlin/test/Result.kt
+ |package example
+ |
+ |class Result(val d: Int = 0) {
+ | class Success(): Result()
+ |
+ | val isFailed = false
+ | fun reset() = 0
+ | fun String.extension() = 0
+ |}
+ """
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ source,
+ configuration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val classContent = writerPlugin.writer.renderedContent("root/example/-result/index.html")
+ val tabSectionNames = classContent.select("div .tabs-section-body > div[data-togglable]")
+ .map { it.attr("data-togglable") }
+
+ val expectedOrder = listOf("CONSTRUCTOR", "TYPE", "PROPERTY", "FUNCTION")
+
+ assertEquals(expectedOrder.size, tabSectionNames.size)
+ expectedOrder.forEachIndexed { index, element ->
+ assertEquals(element, tabSectionNames[index])
+ }
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/TextStylesTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/TextStylesTest.kt
new file mode 100644
index 00000000..0ca4e245
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/renderers/html/TextStylesTest.kt
@@ -0,0 +1,113 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package renderers.html
+
+import org.jetbrains.dokka.base.renderers.html.HtmlRenderer
+import org.jetbrains.dokka.pages.TextStyle
+import org.jetbrains.dokka.pages.TokenStyle
+import org.jsoup.Jsoup
+import org.jsoup.nodes.Element
+import renderers.testPage
+import utils.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class TextStylesTest : HtmlRenderingOnlyTestBase() {
+ @Test
+ fun `should include bold`(){
+ val page = testPage {
+ text("bold text", styles = setOf(TextStyle.Bold))
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(B("bold text"))
+ }
+
+ @Test
+ fun `should include italics`(){
+ val page = testPage {
+ text("italics text", styles = setOf(TextStyle.Italic))
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(I("italics text"))
+ }
+
+ @Test
+ fun `should include strikethrought`(){
+ val page = testPage {
+ text("strike text", styles = setOf(TextStyle.Strikethrough))
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(STRIKE("strike text"))
+ }
+
+ @Test
+ fun `should include token styles`(){
+ val page = testPage {
+ text("keyword", styles = setOf(TokenStyle.Keyword))
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(Span("keyword"))
+ val lastChild = renderedContent.children().last() ?: throw IllegalStateException("No element found")
+ assertEquals(lastChild.attr("class"), "token keyword")
+ }
+
+ @Test
+ fun `should include multiple styles at one`(){
+ val page = testPage {
+ text(
+ "styled text",
+ styles = setOf(
+ TextStyle.Strikethrough,
+ TextStyle.Bold,
+ TextStyle.Indented,
+ TextStyle.UnderCoverText,
+ TextStyle.BreakableAfter
+ )
+ )
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(STRIKE(B("styled text")))
+ //Our dsl swallows nbsp so i manually check for it
+ files.contents.getValue("test-page.html").contains("&nbsp;<strike><b>styled text</b></strike>")
+ }
+
+ @Test
+ fun `should include blockquote`() {
+ val page = testPage {
+ group(styles = setOf(TextStyle.Quotation)) {
+ text("blockquote text")
+ }
+ }
+ HtmlRenderer(context).render(page)
+ renderedContent.match(BlockQuote("blockquote text"))
+ }
+
+ @Test
+ fun `should include var`() {
+ val page = testPage {
+ group(styles = setOf(TextStyle.Var)) {
+ text("variable")
+ }
+ }
+ HtmlRenderer(context).render(page)
+ println(renderedContent)
+ renderedContent.match(Var("variable"))
+ }
+
+ @Test
+ fun `should include underlined text`() {
+ val page = testPage {
+ group(styles = setOf(TextStyle.Underlined)) {
+ text("underlined text")
+ }
+ }
+ HtmlRenderer(context).render(page)
+ println(renderedContent)
+ renderedContent.match(U("underlined text"))
+ }
+
+ override val renderedContent: Element
+ get() = files.contents.getValue("test-page.html").let { Jsoup.parse(it) }.select("#content").single()
+}