aboutsummaryrefslogtreecommitdiff
path: root/dokka-subprojects/plugin-base/src/test/kotlin/content
diff options
context:
space:
mode:
Diffstat (limited to 'dokka-subprojects/plugin-base/src/test/kotlin/content')
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/ContentInDescriptionTest.kt142
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/HighlightingTest.kt83
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt351
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/FileLevelJvmNameTest.kt115
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt144
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt401
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/SinceKotlinTest.kt350
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/exceptions/ContentForExceptions.kt439
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/functions/ContentForBriefTest.kt388
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/functions/ContentForConstructors.kt53
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt499
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/params/ContentForParamsTest.kt1529
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/properties/ContentForClassWithParamsAndPropertiesTest.kt272
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/receiver/ContentForReceiverTest.kt61
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/samples/ContentForSamplesTest.kt207
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt866
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt469
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt515
-rw-r--r--dokka-subprojects/plugin-base/src/test/kotlin/content/typealiases/TypealiasTest.kt83
19 files changed, 6967 insertions, 0 deletions
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/ContentInDescriptionTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/ContentInDescriptionTest.kt
new file mode 100644
index 00000000..a278795d
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/ContentInDescriptionTest.kt
@@ -0,0 +1,142 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.doc.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+class ContentInDescriptionTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ val expectedDescription = Description(
+ CustomDocTag(
+ listOf(
+ P(
+ listOf(
+ Text("Hello World! Docs with period issue, e.g."),
+ Text(String(Character.toChars(160)), params = mapOf("content-type" to "html")),
+ Text("this.")
+ )
+ )
+ ),
+ params = emptyMap(),
+ name = "MARKDOWN_FILE"
+ )
+ )
+
+ @Test
+ fun `nbsp is handled as code in kotlin`() {
+ testInline(
+ """
+ |/src/main/kotlin/sample/ParentKt.kt
+ |package sample;
+ |/**
+ | * Hello World! Docs with period issue, e.g. this.
+ | */
+ |public class ParentKt {
+ |}
+ """.trimIndent(), configuration
+ ) {
+ documentablesMergingStage = {
+ val classlike = it.packages.flatMap { it.classlikes }.find { it.name == "ParentKt" }
+
+ assertTrue(classlike != null)
+ assertEquals(expectedDescription, classlike.documentation.values.first().children.first())
+ }
+ }
+ }
+
+ @Test
+ fun `nbsp is handled as code in java`() {
+ testInline(
+ """
+ |/src/main/kotlin/sample/Parent.java
+ |package sample;
+ |/**
+ | * Hello World! Docs with period issue, e.g. this.
+ | */
+ |public class Parent {
+ |}
+ """.trimIndent(), configuration
+ ) {
+ documentablesMergingStage = {
+ val classlike = it.packages.flatMap { it.classlikes }.find { it.name == "Parent" }
+
+ assertTrue(classlike != null)
+ assertEquals(expectedDescription, classlike.documentation.values.first().children.first())
+ }
+ }
+ }
+
+ @Test
+ fun `same documentation in java and kotlin when nbsp is present`() {
+ testInline(
+ """
+ |/src/main/kotlin/sample/Parent.java
+ |package sample;
+ |/**
+ | * Hello World! Docs with period issue, e.g. this.
+ | */
+ |public class Parent {
+ |}
+ |
+ |/src/main/kotlin/sample/ParentKt.kt
+ |package sample;
+ |/**
+ | * Hello World! Docs with period issue, e.g. this.
+ | */
+ |public class ParentKt {
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val java = module.packages.flatMap { it.classlikes }.first { it.name == "Parent" }
+ val kotlin = module.packages.flatMap { it.classlikes }.first { it.name == "ParentKt" }
+
+ assertEquals(java.documentation.values.first(), kotlin.documentation.values.first())
+ }
+ }
+ }
+
+ @Test
+ fun `text surrounded by angle brackets is not removed`() {
+ testInline(
+ """
+ |/src/main/kotlin/sample/Foo.kt
+ |package sample
+ |/**
+ | * My example `CodeInline<Bar>`
+ | * ```
+ | * CodeBlock<Bar>
+ | * ```
+ | */
+ |class Foo {
+ |}
+ """.trimIndent(),
+ configuration
+ ) {
+ documentablesMergingStage = { module ->
+ val cls = module.packages.flatMap { it.classlikes }.first { it.name == "Foo" }
+ val documentation = cls.documentation.values.first()
+ val docTags = documentation.children.single().root.children
+
+ assertEquals("CodeInline<Bar>", ((docTags[0].children[1] as CodeInline).children.first() as Text).body)
+ assertEquals("CodeBlock<Bar>", ((docTags[1] as CodeBlock).children.first() as Text).body)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/HighlightingTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/HighlightingTest.kt
new file mode 100644
index 00000000..a7fb2bde
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/HighlightingTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.pages.*
+import kotlin.test.Test
+import kotlin.test.assertTrue
+
+class HighlightingTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOf(commonStdlibPath!!, jvmStdlibPath!!)
+ externalDocumentationLinks = listOf(stdlibExternalDocumentationLink)
+ }
+ }
+ }
+
+ @Test
+ fun `open suspend fun`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ | open suspend fun simpleFun(): String = "Celebrimbor"
+ """,
+ configuration
+ ) {
+ pagesTransformationStage = { module ->
+ val symbol = (module.dfs { it.name == "simpleFun" } as MemberPageNode).content
+ .dfs { it is ContentGroup && it.dci.kind == ContentKind.Symbol }
+ val children = symbol?.children
+
+ for (it in listOf(
+ Pair(0, TokenStyle.Keyword), Pair(1, TokenStyle.Keyword), Pair(2, TokenStyle.Keyword),
+ Pair(4, TokenStyle.Punctuation), Pair(5, TokenStyle.Punctuation), Pair(6, TokenStyle.Operator)
+ ))
+ assertTrue(children?.get(it.first)?.style?.contains(it.second) == true)
+ assertTrue(children?.get(3)?.children?.first()?.style?.contains(TokenStyle.Function) == true)
+ }
+ }
+ }
+
+ @Test
+ fun `plain typealias of plain class with annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/common/Test.kt
+ |package example
+ |
+ |@MustBeDocumented
+ |@Target(AnnotationTarget.TYPEALIAS)
+ |annotation class SomeAnnotation
+ |
+ |@SomeAnnotation
+ |typealias PlainTypealias = Int
+ |
+ """.trimMargin(),
+ configuration
+ ) {
+ pagesTransformationStage = { module ->
+ val symbol = (module.dfs { it.name == "example" } as PackagePageNode).content
+ .dfs { it is ContentGroup && it.dci.kind == ContentKind.Symbol }
+ val children = symbol?.children
+
+ for (it in listOf(
+ Pair(1, TokenStyle.Keyword), Pair(3, TokenStyle.Operator)
+ ))
+ assertTrue(children?.get(it.first)?.style?.contains(it.second) == true)
+ val annotation = children?.first()?.children?.first()
+
+ assertTrue(annotation?.children?.get(0)?.style?.contains(TokenStyle.Annotation) == true)
+ assertTrue(annotation?.children?.get(1)?.children?.first()?.style?.contains(TokenStyle.Annotation) == true)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
new file mode 100644
index 00000000..7293b53c
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/ContentForAnnotationsTest.kt
@@ -0,0 +1,351 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.annotations
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.base.utils.firstNotNullOfOrNull
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.*
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.ContentText
+import org.jetbrains.dokka.pages.MemberPageNode
+import org.jetbrains.dokka.pages.PackagePageNode
+import utils.ParamAttributes
+import utils.assertNotNull
+import utils.bareSignature
+import utils.propertySignature
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertTrue
+
+
+class ContentForAnnotationsTest : BaseAbstractTest() {
+
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ @Test
+ fun `function with documented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
+ | AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD
+ |)
+ |@Retention(AnnotationRetention.SOURCE)
+ |@MustBeDocumented
+ |annotation class Fancy
+ |
+ |
+ |@Fancy
+ |fun function(@Fancy abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ mapOf("Fancy" to emptySet()),
+ "",
+ "",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(mapOf("Fancy" to emptySet()), emptySet(), "String")
+ )
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function with undocumented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION,
+ | AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION, AnnotationTarget.CONSTRUCTOR, AnnotationTarget.FIELD
+ |)
+ |@Retention(AnnotationRetention.SOURCE)
+ |annotation class Fancy
+ |
+ |@Fancy
+ |fun function(@Fancy abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `property with undocumented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@Suppress
+ |val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "", "", emptySet(), "val", "property", "Int", "6")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `property with documented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@MustBeDocumented
+ |annotation class Fancy
+ |
+ |@Fancy
+ |val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(mapOf("Fancy" to emptySet()), "", "", emptySet(), "val", "property", "Int", "6")
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `rich documented annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@MustBeDocumented
+ |@Retention(AnnotationRetention.SOURCE)
+ |@Target(AnnotationTarget.PROPERTY)
+ |annotation class BugReport(
+ | val assignedTo: String = "[none]",
+ | val testCase: KClass<ABC> = ABC::class,
+ | val status: Status = Status.UNCONFIRMED,
+ | val ref: Reference = Reference(value = 1),
+ | val reportedBy: Array<Reference>,
+ | val showStopper: Boolean = false
+ | val previousReport: BugReport?
+ |) {
+ | enum class Status {
+ | UNCONFIRMED, CONFIRMED, FIXED, NOTABUG
+ | }
+ | class ABC
+ |}
+ |annotation class Reference(val value: Long)
+ |annotation class ReferenceReal(val value: Double)
+ |
+ |
+ |@BugReport(
+ | assignedTo = "me",
+ | testCase = BugReport.ABC::class,
+ | status = BugReport.Status.FIXED,
+ | ref = Reference(value = 2u),
+ | reportedBy = [Reference(value = 2UL), Reference(value = 4L),
+ | ReferenceReal(value = 4.9), ReferenceReal(value = 2f)],
+ | showStopper = true,
+ | previousReport = null
+ |)
+ |val ltint: Int = 5
+ """.trimIndent(), testConfiguration
+ ) {
+ documentablesCreationStage = { modules ->
+
+ fun expectedAnnotationValue(name: String, value: AnnotationParameterValue) = AnnotationValue(Annotations.Annotation(
+ dri = DRI("test", name),
+ params = mapOf("value" to value),
+ scope = Annotations.AnnotationScope.DIRECT,
+ mustBeDocumented = false
+ ))
+ val property = modules.flatMap { it.packages }.flatMap { it.properties }.first()
+ val annotation = property.extra[Annotations]?.let {
+ it.directAnnotations.entries.firstNotNullOfOrNull { (_, annotations) -> annotations.firstOrNull() }
+ }
+ val annotationParams = annotation?.params ?: emptyMap()
+
+ assertEquals(expectedAnnotationValue("Reference", IntValue(2)), annotationParams["ref"])
+
+ val reportedByParam = ArrayValue(listOf(
+ expectedAnnotationValue("Reference", LongValue(2)),
+ expectedAnnotationValue("Reference", LongValue(4)),
+ expectedAnnotationValue("ReferenceReal", DoubleValue(4.9)),
+ expectedAnnotationValue("ReferenceReal", FloatValue(2f))
+ ))
+ assertEquals(reportedByParam, annotationParams["reportedBy"])
+ assertEquals(BooleanValue(true), annotationParams["showStopper"])
+ assertEquals(NullValue, annotationParams["previousReport"])
+ }
+
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(
+ mapOf(
+ "BugReport" to setOf(
+ "assignedTo",
+ "testCase",
+ "status",
+ "ref",
+ "reportedBy",
+ "showStopper",
+ "previousReport"
+ )
+ ), "", "", emptySet(), "val", "ltint", "Int", "5"
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `JvmName for property with setter and getter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |@get:JvmName("xd")
+ |@set:JvmName("asd")
+ |var property: String
+ | get() = ""
+ | set(value) {}
+ """.trimIndent(), testConfiguration
+ ) {
+ documentablesCreationStage = { modules ->
+ fun expectedAnnotation(name: String) = Annotations.Annotation(
+ dri = DRI("kotlin.jvm", "JvmName"),
+ params = mapOf("name" to StringValue(name)),
+ scope = Annotations.AnnotationScope.DIRECT,
+ mustBeDocumented = true
+ )
+
+ val property = modules.flatMap { it.packages }.flatMap { it.properties }.first()
+ val getterAnnotation = property.getter?.extra?.get(Annotations)?.let {
+ it.directAnnotations.entries.firstNotNullOfOrNull { (_, annotations) -> annotations.firstOrNull() }
+ }
+ val setterAnnotation = property.getter?.extra?.get(Annotations)?.let {
+ it.directAnnotations.entries.firstNotNullOfOrNull { (_, annotations) -> annotations.firstOrNull() }
+ }
+
+ assertEquals(expectedAnnotation("xd"), getterAnnotation)
+ assertTrue(getterAnnotation?.mustBeDocumented!!)
+ assertEquals(Annotations.AnnotationScope.DIRECT, getterAnnotation.scope)
+
+ assertEquals(expectedAnnotation("asd"), setterAnnotation)
+ assertTrue(setterAnnotation?.mustBeDocumented!!)
+ assertEquals(Annotations.AnnotationScope.DIRECT, setterAnnotation.scope)
+ }
+ }
+ }
+
+ @Test
+ fun `annotated bounds in Kotlin`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |@MustBeDocumented
+ |@Target(AnnotationTarget.TYPE_PARAMETER)
+ |annotation class Hello(val bar: String)
+ |fun <T: @Hello("abc") String> foo(arg: String): List<T> = TODO()
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { root ->
+ val fooPage = root.dfs { it.name == "foo" } as MemberPageNode
+ fooPage.content.dfs { it is ContentText && it.text == "Hello" }.assertNotNull()
+ }
+ }
+ }
+
+ @Test
+ fun `annotated bounds in Java`() {
+ testInline(
+ """
+ |/src/main/java/demo/AnnotationTest.java
+ |package demo;
+ |import java.lang.annotation.*;
+ |import java.util.List;
+ |@Documented
+ |@Target({ElementType.TYPE_USE, ElementType.TYPE})
+ |@interface Hello {
+ | public String bar() default "";
+ |}
+ |public class AnnotationTest {
+ | public <T extends @Hello(bar = "baz") String> List<T> foo() {
+ | return null;
+ | }
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { root ->
+ val fooPage = root.dfs { it.name == "foo" } as MemberPageNode
+ fooPage.content.dfs { it is ContentText && it.text == "Hello" }.assertNotNull()
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/FileLevelJvmNameTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/FileLevelJvmNameTest.kt
new file mode 100644
index 00000000..5809d7df
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/FileLevelJvmNameTest.kt
@@ -0,0 +1,115 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.annotations
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.model.Annotations
+import org.jetbrains.dokka.model.StringValue
+import org.junit.jupiter.params.ParameterizedTest
+import org.junit.jupiter.params.provider.ValueSource
+import kotlin.test.assertEquals
+
+class FileLevelJvmNameTest : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ classpath += jvmStdlibPath!!
+ }
+ }
+ }
+
+ companion object {
+ private const val functionTest =
+ """
+ |/src/main/kotlin/test/source.kt
+ |@file:JvmName("CustomJvmName")
+ |package test
+ |
+ |fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """
+
+ private const val extensionFunctionTest =
+ """
+ |/src/main/kotlin/test/source.kt
+ |@file:JvmName("CustomJvmName")
+ |package test
+ |
+ |fun String.function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """
+
+ private const val propertyTest =
+ """
+ |/src/main/kotlin/test/source.kt
+ |@file:JvmName("CustomJvmName")
+ |package test
+ |
+ |val property: String
+ | get() = ""
+ """
+
+ private const val extensionPropertyTest =
+ """
+ |/src/main/kotlin/test/source.kt
+ |@file:JvmName("CustomJvmName")
+ |package test
+ |
+ |val String.property: String
+ | get() = ""
+ """
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = [functionTest, extensionFunctionTest])
+ fun `jvm name should be included in functions extra`(query: String) {
+ testInline(
+ query.trimIndent(), testConfiguration
+ ) {
+ documentablesCreationStage = { modules ->
+ val expectedAnnotation = Annotations.Annotation(
+ dri = DRI("kotlin.jvm", "JvmName"),
+ params = mapOf("name" to StringValue("CustomJvmName")),
+ scope = Annotations.AnnotationScope.FILE,
+ mustBeDocumented = true
+ )
+ val function = modules.flatMap { it.packages }.first().functions.first()
+ val annotation = function.extra[Annotations]?.fileLevelAnnotations?.entries?.first()?.value?.single()
+ assertEquals(emptyMap(), function.extra[Annotations]?.directAnnotations)
+ assertEquals(expectedAnnotation, annotation)
+ assertEquals(expectedAnnotation.scope, annotation?.scope)
+ assertEquals(expectedAnnotation.mustBeDocumented, annotation?.mustBeDocumented)
+ }
+ }
+ }
+
+ @ParameterizedTest
+ @ValueSource(strings = [propertyTest, extensionPropertyTest])
+ fun `jvm name should be included in properties extra`(query: String) {
+ testInline(
+ query.trimIndent(), testConfiguration
+ ) {
+ documentablesCreationStage = { modules ->
+ val expectedAnnotation = Annotations.Annotation(
+ dri = DRI("kotlin.jvm", "JvmName"),
+ params = mapOf("name" to StringValue("CustomJvmName")),
+ scope = Annotations.AnnotationScope.FILE,
+ mustBeDocumented = true
+ )
+ val properties = modules.flatMap { it.packages }.first().properties.first()
+ val annotation = properties.extra[Annotations]?.fileLevelAnnotations?.entries?.first()?.value?.single()
+ assertEquals(emptyMap(), properties.extra[Annotations]?.directAnnotations)
+ assertEquals(expectedAnnotation, annotation)
+ assertEquals(expectedAnnotation.scope, annotation?.scope)
+ assertEquals(expectedAnnotation.mustBeDocumented, annotation?.mustBeDocumented)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt
new file mode 100644
index 00000000..5a2ff93e
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/JavaDeprecatedTest.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.annotations
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.base.transformers.documentables.deprecatedAnnotation
+import org.jetbrains.dokka.base.transformers.documentables.isDeprecated
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.ContentStyle
+import utils.pWrapped
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+class JavaDeprecatedTest : BaseAbstractTest() {
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ @Suppress("UNCHECKED_CAST")
+ fun `should assert util functions for deprecation`() {
+ testInline(
+ """
+ |/src/main/kotlin/deprecated/DeprecatedJavaClass.java
+ |package deprecated
+ |
+ |@Deprecated(forRemoval = true)
+ |public class DeprecatedJavaClass {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ documentablesTransformationStage = { module ->
+ val deprecatedClass = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "DeprecatedJavaClass" }
+
+ val isDeprecated = (deprecatedClass as WithExtraProperties<out Documentable>).isDeprecated()
+ assertTrue(isDeprecated)
+
+ val deprecatedAnnotation = (deprecatedClass as WithExtraProperties<out Documentable>).deprecatedAnnotation
+ assertNotNull(deprecatedAnnotation)
+
+ assertTrue(deprecatedAnnotation.isDeprecated())
+ assertEquals("java.lang", deprecatedAnnotation.dri.packageName)
+ assertEquals("Deprecated", deprecatedAnnotation.dri.classNames)
+ }
+ }
+ }
+
+ @Test
+ fun `should change deprecated header if marked for removal`() {
+ testInline(
+ """
+ |/src/main/kotlin/deprecated/DeprecatedJavaClass.java
+ |package deprecated
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(forRemoval = true)
+ |public class DeprecatedJavaClass {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val deprecatedJavaClass = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "DeprecatedJavaClass" } as ContentPage
+
+ deprecatedJavaClass.content.assertNode {
+ group {
+ header(1) { +"DeprecatedJavaClass" }
+ platformHinted {
+ skipAllNotMatching()
+ group {
+ header(3) {
+ +"Deprecated (for removal)"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should add footnote for 'since' param`() {
+ testInline(
+ """
+ |/src/main/kotlin/deprecated/DeprecatedJavaClass.java
+ |package deprecated
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(since = "11")
+ |public class DeprecatedJavaClass {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val deprecatedJavaClass = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "DeprecatedJavaClass" } as ContentPage
+
+ deprecatedJavaClass.content.assertNode {
+ group {
+ header(1) { +"DeprecatedJavaClass" }
+ platformHinted {
+ skipAllNotMatching()
+ group {
+ header(3) {
+ +"Deprecated"
+ }
+ group {
+ check { assertEquals(ContentStyle.Footnote, this.style.firstOrNull()) }
+ +"Since version 11"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt
new file mode 100644
index 00000000..7612aff8
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/KotlinDeprecatedTest.kt
@@ -0,0 +1,401 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.annotations
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.base.transformers.documentables.deprecatedAnnotation
+import org.jetbrains.dokka.base.transformers.documentables.isDeprecated
+import org.jetbrains.dokka.model.Documentable
+import org.jetbrains.dokka.model.properties.WithExtraProperties
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.ContentStyle
+import utils.ParamAttributes
+import utils.bareSignature
+import utils.pWrapped
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertTrue
+
+
+class KotlinDeprecatedTest : BaseAbstractTest() {
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ @Suppress("UNCHECKED_CAST")
+ fun `should assert util functions for deprecation`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/KotlinFile.kt
+ |package deprecated
+ |
+ |@Deprecated(
+ | message = "Fancy message"
+ |)
+ |fun simpleFunction() {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ documentablesTransformationStage = { module ->
+ val deprecatedFunction = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "simpleFunction" }
+
+ val isDeprecated = (deprecatedFunction as WithExtraProperties<out Documentable>).isDeprecated()
+ assertTrue(isDeprecated)
+
+ val deprecatedAnnotation = (deprecatedFunction as WithExtraProperties<out Documentable>).deprecatedAnnotation
+ assertNotNull(deprecatedAnnotation)
+
+ assertTrue(deprecatedAnnotation.isDeprecated())
+ assertEquals("kotlin", deprecatedAnnotation.dri.packageName)
+ assertEquals("Deprecated", deprecatedAnnotation.dri.classNames)
+ }
+ }
+ }
+
+ @Test
+ fun `should change header if deprecation level is not default`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/DeprecatedKotlin.kt
+ |package deprecated
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(
+ | message = "Reason for deprecation bla bla",
+ | level = DeprecationLevel.ERROR
+ |)
+ |fun oldLegacyFunction(typedParam: SomeOldType, someLiteral: String): String {}
+ |
+ |fun newShinyFunction(typedParam: SomeOldType, someLiteral: String, newTypedParam: SomeNewType): String {}
+ |class SomeOldType {}
+ |class SomeNewType {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionWithDeprecatedFunction = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "oldLegacyFunction" } as ContentPage
+
+ functionWithDeprecatedFunction.content.assertNode {
+ group {
+ header(1) { +"oldLegacyFunction" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "oldLegacyFunction",
+ returnType = "String",
+ params = arrayOf(
+ "typedParam" to ParamAttributes(emptyMap(), emptySet(), "SomeOldType"),
+ "someLiteral" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ )
+ )
+ }
+ after {
+ group {
+ header(3) {
+ +"Deprecated (with error)"
+ }
+ p {
+ +"Reason for deprecation bla bla"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should display repalceWith param with imports as code blocks`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/DeprecatedKotlin.kt
+ |package deprecated
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(
+ | message = "Reason for deprecation bla bla",
+ | replaceWith = ReplaceWith(
+ | "newShinyFunction(typedParam, someLiteral, SomeNewType())",
+ | imports = [
+ | "com.example.dokka.debug.newShinyFunction",
+ | "com.example.dokka.debug.SomeOldType",
+ | "com.example.dokka.debug.SomeNewType",
+ | ]
+ | ),
+ |)
+ |fun oldLegacyFunction(typedParam: SomeOldType, someLiteral: String): String {}
+ |
+ |fun newShinyFunction(typedParam: SomeOldType, someLiteral: String, newTypedParam: SomeNewType): String {}
+ |class SomeOldType {}
+ |class SomeNewType {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionWithDeprecatedFunction = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "oldLegacyFunction" } as ContentPage
+
+ functionWithDeprecatedFunction.content.assertNode {
+ group {
+ header(1) { +"oldLegacyFunction" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "oldLegacyFunction",
+ returnType = "String",
+ params = arrayOf(
+ "typedParam" to ParamAttributes(emptyMap(), emptySet(), "SomeOldType"),
+ "someLiteral" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ )
+ )
+ }
+ after {
+ group {
+ header(3) {
+ +"Deprecated"
+ }
+ p {
+ +"Reason for deprecation bla bla"
+ }
+
+ header(4) {
+ +"Replace with"
+ }
+ codeBlock {
+ +"import com.example.dokka.debug.newShinyFunction"
+ br()
+ +"import com.example.dokka.debug.SomeOldType"
+ br()
+ +"import com.example.dokka.debug.SomeNewType"
+ br()
+ }
+ codeBlock {
+ +"newShinyFunction(typedParam, someLiteral, SomeNewType())"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should add footnote for DeprecatedSinceKotlin annotation`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/DeprecatedKotlin.kt
+ |package deprecated
+ |
+ |/**
+ | * Average function description
+ | */
+ |@DeprecatedSinceKotlin(
+ | warningSince = "1.4",
+ | errorSince = "1.5",
+ | hiddenSince = "1.6"
+ |)
+ |@Deprecated(
+ | message = "Deprecation reason bla bla"
+ |)
+ |fun oldLegacyFunction(typedParam: SomeOldType, someLiteral: String): String {}
+ |
+ |fun newShinyFunction(typedParam: SomeOldType, someLiteral: String, newTypedParam: SomeNewType): String {}
+ |class SomeOldType {}
+ |class SomeNewType {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionWithDeprecatedFunction = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "oldLegacyFunction" } as ContentPage
+
+ functionWithDeprecatedFunction.content.assertNode {
+ group {
+ header(1) { +"oldLegacyFunction" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "oldLegacyFunction",
+ returnType = "String",
+ params = arrayOf(
+ "typedParam" to ParamAttributes(emptyMap(), emptySet(), "SomeOldType"),
+ "someLiteral" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ )
+ )
+ }
+ after {
+ group {
+ header(3) {
+ +"Deprecated"
+ }
+ group {
+ check { assertEquals(ContentStyle.Footnote, this.style.firstOrNull()) }
+ p {
+ +"Warning since 1.4"
+ }
+ p {
+ +"Error since 1.5"
+ }
+ p {
+ +"Hidden since 1.6"
+ }
+ }
+ p {
+ +"Deprecation reason bla bla"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should generate deprecation block with all parameters present and long description`() {
+ testInline(
+ """
+ |/src/main/kotlin/kotlin/DeprecatedKotlin.kt
+ |package deprecated
+ |
+ |/**
+ | * Average function description
+ | */
+ |@Deprecated(
+ | message = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas vel vulputate risus. " +
+ | "Etiam dictum odio vel vulputate auctor.Nulla facilisi. Duis ullamcorper ullamcorper lectus " +
+ | "nec rutrum. Quisque eu risus eu purus bibendum ultricies. Maecenas tincidunt dui in sodales " +
+ | "faucibus. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Proin id sem felis. " +
+ | "Praesent et libero lacinia, egestas libero in, ultrices lectus. Suspendisse eget volutpat " +
+ | "velit. Phasellus laoreet mi eu egestas mattis.",
+ | replaceWith = ReplaceWith(
+ | "newShinyFunction(typedParam, someLiteral, SomeNewType())",
+ | imports = [
+ | "com.example.dokka.debug.newShinyFunction",
+ | "com.example.dokka.debug.SomeOldType",
+ | "com.example.dokka.debug.SomeNewType",
+ | ]
+ | ),
+ | level = DeprecationLevel.ERROR
+ |)
+ |fun oldLegacyFunction(typedParam: SomeOldType, someLiteral: String): String {}
+ |
+ |fun newShinyFunction(typedParam: SomeOldType, someLiteral: String, newTypedParam: SomeNewType): String {}
+ |class SomeOldType {}
+ |class SomeNewType {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionWithDeprecatedFunction = module.children
+ .single { it.name == "deprecated" }.children
+ .single { it.name == "oldLegacyFunction" } as ContentPage
+
+ functionWithDeprecatedFunction.content.assertNode {
+ group {
+ header(1) { +"oldLegacyFunction" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "oldLegacyFunction",
+ returnType = "String",
+ params = arrayOf(
+ "typedParam" to ParamAttributes(emptyMap(), emptySet(), "SomeOldType"),
+ "someLiteral" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ )
+ )
+ }
+ after {
+ group {
+ header(3) {
+ +"Deprecated (with error)"
+ }
+ p {
+ +("Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
+ "Maecenas vel vulputate risus. Etiam dictum odio vel " +
+ "vulputate auctor.Nulla facilisi. Duis ullamcorper " +
+ "ullamcorper lectus nec rutrum. Quisque eu risus eu " +
+ "purus bibendum ultricies. Maecenas tincidunt dui in sodales faucibus. " +
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. " +
+ "Proin id sem felis. Praesent et libero lacinia, egestas " +
+ "libero in, ultrices lectus. Suspendisse eget volutpat velit. " +
+ "Phasellus laoreet mi eu egestas mattis.")
+ }
+ header(4) {
+ +"Replace with"
+ }
+ codeBlock {
+ +"import com.example.dokka.debug.newShinyFunction"
+ br()
+ +"import com.example.dokka.debug.SomeOldType"
+ br()
+ +"import com.example.dokka.debug.SomeNewType"
+ br()
+ }
+ codeBlock {
+ +"newShinyFunction(typedParam, someLiteral, SomeNewType())"
+ }
+ }
+ group { pWrapped("Average function description") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/SinceKotlinTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/SinceKotlinTest.kt
new file mode 100644
index 00000000..6ee95bbd
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/annotations/SinceKotlinTest.kt
@@ -0,0 +1,350 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.annotations
+
+import matchers.content.*
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinTransformer
+import org.jetbrains.dokka.base.transformers.pages.annotations.SinceKotlinVersion
+import org.jetbrains.dokka.model.DFunction
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.model.doc.CustomTagWrapper
+import org.jetbrains.dokka.model.doc.Text
+import org.jetbrains.dokka.pages.ContentPage
+import signatures.AbstractRenderingTest
+import utils.*
+import kotlin.test.*
+
+
+class SinceKotlinTest : AbstractRenderingTest() {
+
+ val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @BeforeTest
+ fun setSystemProperty() {
+ System.setProperty(SinceKotlinTransformer.SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP, "true")
+ }
+ @AfterTest
+ fun clearSystemProperty() {
+ System.clearProperty(SinceKotlinTransformer.SHOULD_DISPLAY_SINCE_KOTLIN_SYS_PROP)
+ }
+
+ @Test
+ fun versionsComparing() {
+ assertTrue(SinceKotlinVersion("1.0").compareTo(SinceKotlinVersion("1.0")) == 0)
+ assertTrue(SinceKotlinVersion("1.0.0").compareTo(SinceKotlinVersion("1")) == 0)
+ assertTrue(SinceKotlinVersion("1.0") >= SinceKotlinVersion("1.0"))
+ assertTrue(SinceKotlinVersion("1.1") > SinceKotlinVersion("1"))
+ assertTrue(SinceKotlinVersion("1.0") < SinceKotlinVersion("2.0"))
+ assertTrue(SinceKotlinVersion("1.0") < SinceKotlinVersion("2.2"))
+ }
+
+ @Test
+ fun `rendered SinceKotlin custom tag for typealias, extensions, functions, properties`() {
+ val writerPlugin = TestOutputWriterPlugin()
+
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@SinceKotlin("1.5")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |@SinceKotlin("1.5")
+ |fun String.extension(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |@SinceKotlin("1.5")
+ |typealias Str = String
+ |@SinceKotlin("1.5")
+ |val str = "str"
+ """.trimIndent(),
+ testConfiguration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ renderingStage = { _, _ ->
+ val content = writerPlugin.renderedContent("root/test/index.html")
+ assertEquals(4, content.getElementsContainingOwnText("Since Kotlin").count())
+ }
+ }
+ }
+
+ @Test
+ fun `should propagate SinceKotlin`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@SinceKotlin("1.5")
+ |class A {
+ | fun ring(abc: String): String {
+ | return "My precious " + abc
+ | }
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ documentablesTransformationStage = { module ->
+ @Suppress("UNCHECKED_CAST") val funcs = module.children.single { it.name == "test" }
+ .children.single { it.name == "A" }
+ .children.filter { it.name == "ring" && it is DFunction } as List<DFunction>
+ with(funcs) {
+ val sinceKotlin = mapOf(
+ Platform.jvm to SinceKotlinVersion("1.5"),
+ )
+
+ for(i in sinceKotlin) {
+ val tag =
+ find { it.sourceSets.first().analysisPlatform == i.key }?.documentation?.values?.first()
+ ?.dfs { it is CustomTagWrapper && it.name == "Since Kotlin" }
+ .assertNotNull("SinceKotlin[${i.key}]")
+ assertEquals((tag.children.first() as Text).body, i.value.toString())
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `mpp fun without SinceKotlin annotation`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/jvm/")
+ analysisPlatform = "jvm"
+ }
+ sourceSet {
+ sourceRoots = listOf("src/native/")
+ analysisPlatform = "native"
+ name = "native"
+ }
+ sourceSet {
+ sourceRoots = listOf("src/common/")
+ analysisPlatform = "common"
+ name = "common"
+ }
+ sourceSet {
+ sourceRoots = listOf("src/js/")
+ analysisPlatform = "js"
+ name = "js"
+ }
+ sourceSet {
+ sourceRoots = listOf("src/wasm/")
+ analysisPlatform = "wasm"
+ name = "wasm"
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/jvm/kotlin/test/source.kt
+ |package test
+ |
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |/src/native/kotlin/test/source.kt
+ |package test
+ |
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |/src/common/kotlin/test/source.kt
+ |package test
+ |
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |/src/js/kotlin/test/source.kt
+ |package test
+ |
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |/src/wasm/kotlin/test/source.kt
+ |package test
+ |
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ """.trimIndent(), configuration
+ ) {
+ documentablesTransformationStage = { module ->
+ @Suppress("UNCHECKED_CAST") val funcs = module.children.single { it.name == "test" }
+ .children.filter { it.name == "ring" && it is DFunction } as List<DFunction>
+ with(funcs) {
+ val sinceKotlin = mapOf(
+ Platform.common to SinceKotlinVersion("1.0"),
+ Platform.jvm to SinceKotlinVersion("1.0"),
+ Platform.js to SinceKotlinVersion("1.1"),
+ Platform.native to SinceKotlinVersion("1.3"),
+ Platform.wasm to SinceKotlinVersion("1.8"),
+ )
+
+ for(i in sinceKotlin) {
+ val tag =
+ find { it.sourceSets.first().analysisPlatform == i.key }?.documentation?.values?.first()
+ ?.dfs { it is CustomTagWrapper && it.name == "Since Kotlin" }
+ .assertNotNull("SinceKotlin[${i.key}]")
+ assertEquals((tag.children.first() as Text).body, i.value.toString())
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `mpp fun with SinceKotlin annotation`() {
+ val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/jvm/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ analysisPlatform = "jvm"
+ }
+ sourceSet {
+ sourceRoots = listOf("src/native/")
+ analysisPlatform = "native"
+ name = "native"
+ }
+ sourceSet {
+ sourceRoots = listOf("src/common/")
+ classpath = listOfNotNull(commonStdlibPath)
+ analysisPlatform = "common"
+ name = "common"
+ }
+ sourceSet {
+ sourceRoots = listOf("src/js/")
+ classpath = listOfNotNull(jsStdlibPath)
+ analysisPlatform = "js"
+ name = "js"
+ }
+ sourceSet {
+ sourceRoots = listOf("src/wasm/")
+ analysisPlatform = "wasm"
+ name = "wasm"
+ }
+ }
+ }
+ testInline(
+ """
+ |/src/jvm/kotlin/test/source.kt
+ |package test
+ |
+ |/** dssdd */
+ |@SinceKotlin("1.3")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |/src/native/kotlin/test/source.kt
+ |package test
+ |
+ |/** dssdd */
+ |@SinceKotlin("1.3")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |/src/common/kotlin/test/source.kt
+ |package test
+ |
+ |/** dssdd */
+ |@SinceKotlin("1.3")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |/src/js/kotlin/test/source.kt
+ |package test
+ |
+ |/** dssdd */
+ |@SinceKotlin("1.3")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ |/src/wasm/kotlin/test/source.kt
+ |package test
+ |
+ |/** dssdd */
+ |@SinceKotlin("1.3")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ """.trimIndent(), configuration
+ ) {
+ documentablesTransformationStage = { module ->
+ @Suppress("UNCHECKED_CAST") val funcs = module.children.single { it.name == "test" }
+ .children.filter { it.name == "ring" && it is DFunction } as List<DFunction>
+ with(funcs) {
+ val sinceKotlin = mapOf(
+ Platform.common to SinceKotlinVersion("1.3"),
+ Platform.jvm to SinceKotlinVersion("1.3"),
+ Platform.js to SinceKotlinVersion("1.3"),
+ Platform.native to SinceKotlinVersion("1.3"),
+ Platform.wasm to SinceKotlinVersion("1.8"),
+ )
+
+ for(i in sinceKotlin) {
+ val tag =
+ find { it.sourceSets.first().analysisPlatform == i.key }?.documentation?.values?.first()
+ ?.dfs { it is CustomTagWrapper && it.name == "Since Kotlin" }
+ .assertNotNull("SinceKotlin[${i.key}]")
+ assertEquals(i.value.toString(), (tag.children.first() as Text).body , "Platform ${i.key}")
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should do not render since kotlin tag when flag is unset`() {
+ clearSystemProperty()
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |@SinceKotlin("1.3")
+ |fun ring(abc: String): String {
+ | return "My precious " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "ring" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"ring" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "ring",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/exceptions/ContentForExceptions.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/exceptions/ContentForExceptions.kt
new file mode 100644
index 00000000..22becb93
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/exceptions/ContentForExceptions.kt
@@ -0,0 +1,439 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.exceptions
+
+import matchers.content.*
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.PluginConfigurationImpl
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.DisplaySourceSet
+import utils.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ContentForExceptions : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ private val mppTestConfiguration = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ classpath = listOfNotNull(commonStdlibPath)
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ classpath = listOfNotNull(jvmStdlibPath)
+ }
+ sourceSet {
+ name = "linuxX64"
+ displayName = "linuxX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
+ }
+ }
+ pluginsConfigurations.add(
+ PluginConfigurationImpl(
+ DokkaBase::class.qualifiedName!!,
+ DokkaConfiguration.SerializationFormat.JSON,
+ """{ "mergeImplicitExpectActualDeclarations": true }""",
+ )
+ )
+ }
+
+ @OnlyDescriptors("Fixed in 1.9.20 (IMPORT STAR)")
+ @Test
+ fun `function with navigatable thrown exception`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |/**
+ |* @throws Exception
+ |*/
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ link { +"Exception" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function with non-navigatable thrown exception`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |/**
+ |* @throws UnavailableException
+ |*/
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"UnavailableException"
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatofrm class with throws`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws CommonException
+ |*/
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws JvmException
+ |*/
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws LinuxException
+ |*/
+ |actual open class Parent
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"CommonException"
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "common",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ group {
+ group {
+ +"JvmException"
+ }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ group {
+ group {
+ +"LinuxException"
+ }
+ check {
+ sourceSets.assertSourceSet("linuxX64")
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatofrm class with throws in few platforms`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws CommonException
+ |*/
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws JvmException
+ |*/
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"CommonException"
+ }
+ check {
+ sourceSets.assertSourceSet("common")
+ }
+ }
+ group {
+ group {
+ +"JvmException"
+ }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ check {
+ assertEquals(2, sourceSets.size)
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `throws in merged functions`() {
+ testInline(
+ """
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws LinuxException
+ |*/
+ |fun function() {
+ | println()
+ |}
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @throws JvmException
+ |*/
+ |fun function() {
+ | println()
+ |}
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ )
+ }
+ after {
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"JvmException"
+ }
+ }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ )
+ }
+ after {
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"LinuxException"
+ }
+ }
+ }
+ }
+ check {
+ sourceSets.assertSourceSet("linuxX64")
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+private fun Set<DisplaySourceSet>.assertSourceSet(expectedName: String) {
+ assertEquals(1, this.size)
+ assertEquals(expectedName, this.first().name)
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/functions/ContentForBriefTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/functions/ContentForBriefTest.kt
new file mode 100644
index 00000000..d93a6c27
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/functions/ContentForBriefTest.kt
@@ -0,0 +1,388 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.functions
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.links.DRI
+import org.jetbrains.dokka.links.TypeConstructor
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.DPackage
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.pages.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+import kotlin.test.assertNull
+
+
+class ContentForBriefTest : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ private val codeWithSecondaryAndPrimaryConstructorsDocumented =
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |/**
+ | * Dummy text.
+ | *
+ | * @constructor constructor docs
+ | * @param exampleParameter dummy parameter.
+ | */
+ |class Example(val exampleParameter: Int) {
+ |
+ | /**
+ | * secondary constructor
+ | * @param param1 param1 docs
+ | */
+ | constructor(param1: String) : this(1)
+ |}
+ """.trimIndent()
+
+ private val codeWithDocumentedParameter =
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |/**
+ | * Dummy text.
+ | *
+ | * @param exampleParameter dummy parameter.
+ | */
+ |class Example(val exampleParameter: Int) {
+ |}
+ """.trimIndent()
+
+
+ @Test
+ fun `primary constructor should not inherit docs from its parameter`() {
+ testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) {
+ pagesTransformationStage = { module ->
+ val classPage = module.findClassPage("Example")
+
+ val constructorsWithBriefs = classPage.findConstructorsWithBriefs()
+ val constructorDocs = constructorsWithBriefs.findConstructorDocs {
+ it.callable?.params?.first() == TypeConstructor("kotlin.Int", emptyList())
+ }
+
+ assertEquals("constructor docs", constructorDocs.text)
+ }
+ }
+ }
+
+ @Test
+ fun `secondary constructor should not inherit docs from its parameter`() {
+ testInline(codeWithSecondaryAndPrimaryConstructorsDocumented, testConfiguration) {
+ pagesTransformationStage = { module ->
+ val classPage = module.findClassPage("Example")
+
+ val constructorsWithBriefs = classPage.findConstructorsWithBriefs()
+ val constructorDocs = constructorsWithBriefs.findConstructorDocs {
+ it.callable?.params?.first() == TypeConstructor("kotlin.String", emptyList())
+ }
+
+ assertEquals("secondary constructor", constructorDocs.text)
+ }
+ }
+ }
+
+ /**
+ * All constructors are merged in one block (like overloaded functions).
+ * That leads to the structure where content block (`constructorsWithBriefs`) consist of plain list
+ * of constructors and briefs. In that list constructor is above, brief is below.
+ */
+ private fun ContentPage.findConstructorsWithBriefs(): List<ContentNode> {
+ val constructorsTable = this.content.dfs {
+ it is ContentTable && it.dci.kind == ContentKind.Constructors
+ } as ContentTable
+
+ val constructorsWithBriefs = constructorsTable.dfs {
+ it is ContentGroup && it.dci.kind == ContentKind.SourceSetDependentHint
+ }?.children
+ assertNotNull(constructorsWithBriefs, "Content node with constructors and briefs is not found")
+
+ return constructorsWithBriefs
+ }
+
+ private fun List<ContentNode>.findConstructorDocs(constructorMatcher: (DRI) -> Boolean): ContentText {
+ val constructorIndex = this.indexOfFirst { constructorMatcher(it.dci.dri.first()) }
+ return this[constructorIndex + 1] // expect that the relevant comment is below the constructor
+ .dfs { it is ContentText && it.dci.kind == ContentKind.Comment } as ContentText
+ }
+
+ @Test
+ fun `primary constructor should not inherit docs from its parameter when no specific docs are provided`() {
+ testInline(codeWithDocumentedParameter, testConfiguration) {
+ pagesTransformationStage = { module ->
+ val classPage = module.findClassPage("Example")
+ val constructorsTable =
+ classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable
+
+ assertEquals(1, constructorsTable.children.size)
+ val primary = constructorsTable.children.first()
+ val primaryConstructorDocs = primary.dfs { it is ContentText && it.dci.kind == ContentKind.Comment }
+
+ assertNull(primaryConstructorDocs, "Expected no primary constructor docs to be present")
+ }
+ }
+ }
+
+ @Test
+ fun `brief should work for typealias`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |/**
+ |* This is an example <!-- not visible --> of html
+ |*
+ |* This is definitely not a brief
+ |*/
+ |typealias A = Int
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionBriefDocs = module.singleTypeAliasesDescription("test")
+
+ assertEquals(
+ "This is an example <!-- not visible --> of html",
+ functionBriefDocs.children.joinToString("") { (it as ContentText).text })
+ }
+ }
+ }
+
+ @Test
+ fun `brief for functions should work with html`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class Example(val exampleParameter: Int) {
+ | /**
+ | * This is an example <!-- not visible --> of html
+ | *
+ | * This is definitely not a brief
+ | */
+ | fun test(): String = "TODO"
+ |}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionBriefDocs = module.singleFunctionDescription("Example")
+
+ assertEquals(
+ "This is an example <!-- not visible --> of html",
+ functionBriefDocs.children.joinToString("") { (it as ContentText).text })
+ }
+ }
+ }
+
+ @Test
+ fun `brief for functions should work with ie`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class Example(val exampleParameter: Int) {
+ | /**
+ | * The user token, i.e. "Bearer xyz". Throw an exception if not available.
+ | *
+ | * This is definitely not a brief
+ | */
+ | fun test(): String = "TODO"
+ |}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionBriefDocs = module.singleFunctionDescription("Example")
+
+ assertEquals(
+ "The user token, i.e. \"Bearer xyz\". Throw an exception if not available.",
+ functionBriefDocs.children.joinToString("") { (it as ContentText).text })
+ }
+ }
+ }
+
+ @Test
+ fun `brief for functions should work with eg`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class Example(val exampleParameter: Int) {
+ | /**
+ | * The user token, e.g. "Bearer xyz". Throw an exception if not available.
+ | *
+ | * This is definitely not a brief
+ | */
+ | fun test(): String = "TODO"
+ |}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionBriefDocs = module.singleFunctionDescription("Example")
+
+ assertEquals(
+ "The user token, e.g. \"Bearer xyz\". Throw an exception if not available.",
+ functionBriefDocs.children.joinToString("") { (it as ContentText).text })
+ }
+ }
+ }
+
+ @Test
+ fun `brief for functions should be first sentence for Java`() {
+ testInline(
+ """
+ |/src/main/java/test/Example.java
+ |package test;
+ |
+ |public class Example {
+ | /**
+ | * The user token, or not. This is definitely not a brief in java
+ | */
+ | public static String test() {
+ | return "TODO";
+ | }
+ |}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionBriefDocs = module.singleFunctionDescription("Example")
+
+ assertEquals(
+ "The user token, or not.",
+ functionBriefDocs.children.joinToString("") { (it as ContentText).text })
+ }
+ }
+ }
+
+ @Test
+ fun `brief for functions should work with ie for Java`() {
+ testInline(
+ """
+ |/src/main/java/test/Example.java
+ |package test;
+ |
+ |public class Example {
+ | /**
+ | * The user token, e.g.&nbsp;"Bearer xyz". This is definitely not a brief in java
+ | */
+ | public static String test() {
+ | return "TODO";
+ | }
+ |}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionBriefDocs = module.singleFunctionDescription("Example")
+
+ assertEquals(
+ "The user token, e.g. \"Bearer xyz\".",
+ functionBriefDocs.children.joinToString("") { (it as ContentText).text })
+ }
+ }
+ }
+
+ //Source: https://www.oracle.com/technical-resources/articles/java/javadoc-tool.html#exampleresult
+ @Test
+ fun `brief for functions should work with html comment for Java`() {
+ testInline(
+ """
+ |/src/main/java/test/Example.java
+ |package test;
+ |
+ |public class Example {
+ | /**
+ | * This is a simulation of Prof.<!-- --> Knuth's MIX computer. This is definitely not a brief in java
+ | */
+ | public static String test() {
+ | return "TODO";
+ | }
+ |}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionBriefDocs = module.singleFunctionDescription("Example")
+
+ assertEquals(
+ "This is a simulation of Prof.<!-- --> Knuth's MIX computer.",
+ functionBriefDocs.children.joinToString("") { (it as ContentText).text })
+ }
+ }
+ }
+
+ @Test
+ fun `brief for functions should work with html comment at the end for Java`() {
+ testInline(
+ """
+ |/src/main/java/test/Example.java
+ |package test;
+ |
+ |public class Example {
+ | /**
+ | * This is a simulation of Prof.<!-- --> Knuth's MIX computer. This is definitely not a brief in java <!-- -->
+ | */
+ | public static String test() {
+ | return "TODO";
+ | }
+ |}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionBriefDocs = module.singleFunctionDescription("Example")
+
+ assertEquals(
+ "This is a simulation of Prof.<!-- --> Knuth's MIX computer.",
+ functionBriefDocs.children.joinToString("") { (it as ContentText).text })
+ }
+ }
+ }
+
+ private fun RootPageNode.findClassPage(className: String): ContentPage {
+ return this.dfs {
+ it.name == className && (it as WithDocumentables).documentables.firstOrNull() is DClass
+ } as ContentPage
+ }
+
+ private fun RootPageNode.singleFunctionDescription(className: String): ContentGroup {
+ val classPage =
+ dfs { it.name == className && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage
+ val functionsTable =
+ classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Functions } as ContentTable
+
+ assertEquals(1, functionsTable.children.size)
+ val function = functionsTable.children.first()
+ return function.dfs { it is ContentGroup && it.dci.kind == ContentKind.Comment && it.children.all { it is ContentText } } as ContentGroup
+ }
+ private fun RootPageNode.singleTypeAliasesDescription(packageName: String): ContentGroup {
+ val packagePage =
+ dfs { it.name == packageName && (it as WithDocumentables).documentables.firstOrNull() is DPackage } as ContentPage
+ val contentTable =
+ packagePage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Classlikes } as ContentTable
+
+ assertEquals(1, contentTable.children.size)
+ val row = contentTable.children.first()
+ return row.dfs { it is ContentGroup && it.dci.kind == ContentKind.Comment && it.children.all { it is ContentText } } as ContentGroup
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/functions/ContentForConstructors.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/functions/ContentForConstructors.kt
new file mode 100644
index 00000000..d1ed93dc
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/functions/ContentForConstructors.kt
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.functions
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.pages.*
+import utils.assertContains
+import utils.assertNotNull
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ContentForConstructors : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `constructor name should have RowTitle style`() {
+ testInline("""
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |/**
+ | * Dummy text.
+ | */
+ |class Example(val exampleParameter: Int) {
+ |}
+ """.trimIndent(), testConfiguration) {
+ pagesTransformationStage = { module ->
+ val classPage =
+ module.dfs { it.name == "Example" && (it as WithDocumentables).documentables.firstOrNull() is DClass } as ContentPage
+ val constructorsTable =
+ classPage.content.dfs { it is ContentTable && it.dci.kind == ContentKind.Constructors } as ContentTable
+
+ assertEquals(1, constructorsTable.children.size)
+ val primary = constructorsTable.children.first()
+ val constructorName =
+ primary.dfs { (it as? ContentText)?.text == "Example" }.assertNotNull("constructorName")
+
+ assertContains(constructorName.style, ContentStyle.RowTitle)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt
new file mode 100644
index 00000000..245592cc
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/inheritors/ContentForInheritorsTest.kt
@@ -0,0 +1,499 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.inheritors
+
+import matchers.content.*
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.PluginConfigurationImpl
+import org.jetbrains.dokka.base.DokkaBase
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import utils.OnlyDescriptors
+import utils.classSignature
+import utils.findTestType
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ContentForInheritorsTest : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ private val mppTestConfiguration = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "linuxX64"
+ displayName = "linuxX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
+ }
+ }
+ pluginsConfigurations.add(
+ PluginConfigurationImpl(
+ DokkaBase::class.qualifiedName!!,
+ DokkaConfiguration.SerializationFormat.JSON,
+ """{ "mergeImplicitExpectActualDeclarations": true }""",
+ )
+ )
+ }
+
+
+ //Case from skiko library
+ private val mppTestConfigurationSharedAsPlatform = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ }
+ val jvm = sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "android"
+ displayName = "android"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(jvm.value.sourceSetID)
+ sourceRoots = listOf("src/androidMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "awt"
+ displayName = "awt"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(jvm.value.sourceSetID)
+ sourceRoots = listOf("src/awtMain/kotlin/pageMerger/Test.kt")
+ }
+
+ }
+ }
+
+ @Test
+ fun `class with one inheritor has table in description`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class Parent
+ |
+ |class Foo : Parent()
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Parent"
+ )
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Foo" }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @OnlyDescriptors("Order of inheritors is different in K2")
+ @Test
+ fun `interface with few inheritors has table in description`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |interface Parent
+ |
+ |class Foo : Parent()
+ |class Bar : Parent()
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"interface "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Foo" }
+ }
+ group {
+ link { +"Bar" }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `inherit from one of multiplatoforms actuals`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |class Child: Parent()
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "linuxX64",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `inherit from class in common code`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |class Child : Parent()
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "common",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `inheritors from merged classes`() {
+ testInline(
+ """
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |class LChild : Parent()
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |class JChild : Parent()
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"JChild" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "jvm",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"LChild" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "linuxX64",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `merged inheritors from merged classes`() {
+ testInline(
+ """
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |class Child : Parent()
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |open class Parent
+ |class Child : Parent()
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "jvm",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ group {
+ +"open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "linuxX64",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `parent in shared source set that analyse as platform`() {
+ testInline(
+ """
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |interface Parent
+ |
+ |/src/androidMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |class Child : Parent
+ |
+ |/src/awtMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |class AwtChild : Parent
+ |class Child : Parent
+ |
+ """.trimMargin(),
+ mppTestConfigurationSharedAsPlatform
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"interface "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Inheritors" }
+ table {
+ group {
+ link { +"Child" }
+ }
+ group {
+ link { +"AwtChild" }
+ }
+ check {
+ assertEquals(1, sourceSets.size)
+ assertEquals(
+ "jvm",
+ this.sourceSets.first().name
+ )
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/params/ContentForParamsTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/params/ContentForParamsTest.kt
new file mode 100644
index 00000000..d0c6ac9d
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/params/ContentForParamsTest.kt
@@ -0,0 +1,1529 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.params
+
+import matchers.content.*
+import org.jetbrains.dokka.Platform
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.DFunction
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.model.doc.DocumentationNode
+import org.jetbrains.dokka.model.doc.Param
+import org.jetbrains.dokka.model.doc.Text
+import org.jetbrains.dokka.pages.*
+import org.jetbrains.dokka.utilities.firstIsInstanceOrNull
+import utils.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ContentForParamsTest : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null, "abc" to ParamAttributes(
+ emptyMap(),
+ emptySet(),
+ "String"
+ )
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented parameter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ group { pWrapped("comment to function") }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented parameter and other tags without function comment`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @author Kordyjan
+ | * @author Woolfy
+ | * @since 0.11
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ unnamedTag("Author") {
+ comment {
+ +"Kordyjan"
+ }
+ comment {
+ +"Woolfy"
+ }
+ }
+ unnamedTag("Since") { comment { +"0.11" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiple authors`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ |/**
+ | * Annotation processor which visits all classes.
+ | *
+ | * @author googler1@google.com (Googler 1)
+ | * @author googler2@google.com (Googler 2)
+ | */
+ | public class DocGenProcessor { }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val classPage = module.findTestType("sample", "DocGenProcessor")
+ classPage.content.assertNode {
+ group {
+ header { +"DocGenProcessor" }
+ platformHinted {
+ group {
+ skipAllNotMatching() //Signature
+ }
+ group {
+ group {
+ group {
+ +"Annotation processor which visits all classes."
+ }
+ }
+ }
+ group {
+ header(4) { +"Author" }
+ comment { +"googler1@google.com (Googler 1)" }
+ comment { +"googler2@google.com (Googler 2)" }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `author delimetered by space`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ |/**
+ | * Annotation processor which visits all classes.
+ | *
+ | * @author Marcin Aman Senior
+ | */
+ | public class DocGenProcessor { }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val classPage = module.findTestType("sample", "DocGenProcessor")
+ classPage.content.assertNode {
+ group {
+ header { +"DocGenProcessor" }
+ platformHinted {
+ group {
+ skipAllNotMatching() //Signature
+ }
+ group {
+ group {
+ group {
+ +"Annotation processor which visits all classes."
+ }
+ }
+ }
+ group {
+ header(4) { +"Author" }
+ comment { +"Marcin Aman Senior" }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `deprecated with multiple links inside`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ |/**
+ | * Return the target fragment set by {@link #setTargetFragment} or {@link
+ | * #setTargetFragment}.
+ | *
+ | * @deprecated Instead of using a target fragment to pass results, the fragment requesting a
+ | * result should use
+ | * {@link java.util.HashMap#containsKey(java.lang.Object) FragmentManager#setFragmentResult(String, Bundle)} to deliver results to
+ | * {@link java.util.HashMap#containsKey(java.lang.Object)
+ | * FragmentResultListener} instances registered by other fragments via
+ | * {@link java.util.HashMap#containsKey(java.lang.Object) FragmentManager#setFragmentResultListener(String, LifecycleOwner,
+ | * FragmentResultListener)}.
+ | */
+ | public class DocGenProcessor {
+ | public String setTargetFragment(){
+ | return "";
+ | }
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val classPage =
+ module.findTestType("sample", "DocGenProcessor")
+ classPage.content.assertNode {
+ group {
+ header { +"DocGenProcessor" }
+ platformHinted {
+ group {
+ skipAllNotMatching() //Signature
+ }
+ group {
+ comment {
+ +"Return the target fragment set by "
+ link { +"setTargetFragment" }
+ +" or "
+ link { +"setTargetFragment" }
+ +"."
+ }
+ }
+ group {
+ header(4) { +"Deprecated" }
+ comment {
+ +"Instead of using a target fragment to pass results, the fragment requesting a result should use "
+ link { +"FragmentManager#setFragmentResult(String, Bundle)" }
+ +" to deliver results to "
+ link { +"FragmentResultListener" }
+ +" instances registered by other fragments via "
+ link { +"FragmentManager#setFragmentResultListener(String, LifecycleOwner, FragmentResultListener)" }
+ +"."
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `deprecated with an html link in multiple lines`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ |/**
+ | * @deprecated Use
+ | * <a href="https://developer.android.com/guide/navigation/navigation-swipe-view ">
+ | * TabLayout and ViewPager</a> instead.
+ | */
+ | public class DocGenProcessor { }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val classPage =
+ module.findTestType("sample", "DocGenProcessor")
+ classPage.content.assertNode {
+ group {
+ header { +"DocGenProcessor" }
+ platformHinted {
+ group {
+ skipAllNotMatching() //Signature
+ }
+ group {
+ header(4) { +"Deprecated" }
+ comment {
+ +"Use "
+ link { +"TabLayout and ViewPager" }
+ +" instead."
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `deprecated with an multiple inline links`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ |/**
+ | * FragmentManagerNonConfig stores the retained instance fragments across
+ | * activity recreation events.
+ | *
+ | * <p>Apps should treat objects of this type as opaque, returned by
+ | * and passed to the state save and restore process for fragments in
+ | * {@link java.util.HashMap#containsKey(java.lang.Object) FragmentController#retainNestedNonConfig()} and
+ | * {@link java.util.HashMap#containsKey(java.lang.Object) FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)}.</p>
+ | *
+ | * @deprecated Have your {@link java.util.HashMap FragmentHostCallback} implement
+ | * {@link java.util.HashMap } to automatically retain the Fragment's
+ | * non configuration state.
+ | */
+ | public class DocGenProcessor { }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val classPage =
+ module.findTestType("sample", "DocGenProcessor")
+ classPage.content.assertNode {
+ group {
+ header { +"DocGenProcessor" }
+ platformHinted {
+ group {
+ skipAllNotMatching() //Signature
+ }
+ group {
+ comment {
+ group {
+ +"FragmentManagerNonConfig stores the retained instance fragments across activity recreation events. "
+ }
+ group {
+ +"Apps should treat objects of this type as opaque, returned by and passed to the state save and restore process for fragments in "
+ link { +"FragmentController#retainNestedNonConfig()" }
+ +" and "
+ link { +"FragmentController#restoreAllState(Parcelable, FragmentManagerNonConfig)" }
+ +"."
+ }
+ }
+ }
+ group {
+ header(4) { +"Deprecated" }
+ comment {
+ +"Have your "
+ link { +"FragmentHostCallback" }
+ +" implement "
+ link { +"java.util.HashMap" }
+ +" to automatically retain the Fragment's non configuration state."
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiline throws with comment`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ | public class DocGenProcessor {
+ | /**
+ | * a normal comment
+ | *
+ | * @throws java.lang.IllegalStateException if the Dialog has not yet been created (before
+ | * onCreateDialog) or has been destroyed (after onDestroyView).
+ | * @throws java.lang.RuntimeException when {@link java.util.HashMap#containsKey(java.lang.Object) Hash
+ | * Map} doesn't contain value.
+ | */
+ | public static void sample(){ }
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionPage =
+ module.findTestType(
+ "sample",
+ "DocGenProcessor"
+ ).children.single { it.name == "sample" } as ContentPage
+ functionPage.content.assertNode {
+ group {
+ header(1) { +"sample" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ skipAllNotMatching() //Signature
+ }
+ after {
+ group { pWrapped("a normal comment") }
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ link { +"IllegalStateException" }
+ }
+ comment { +"if the Dialog has not yet been created (before onCreateDialog) or has been destroyed (after onDestroyView)." }
+ }
+ group {
+ group {
+ link { +"RuntimeException" }
+ }
+ comment {
+ +"when "
+ link { +"Hash Map" }
+ +" doesn't contain value."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @OnlyDescriptors("Fixed in 1.9.20 (IMPORT STAR)")
+ @Test
+ fun `multiline kotlin throws with comment`() {
+ testInline(
+ """
+ |/src/main/kotlin/sample/sample.kt
+ |package sample;
+ | /**
+ | * a normal comment
+ | *
+ | * @throws java.lang.IllegalStateException if the Dialog has not yet been created (before
+ | * onCreateDialog) or has been destroyed (after onDestroyView).
+ | * @exception RuntimeException when [Hash Map][java.util.HashMap.containsKey] doesn't contain value.
+ | */
+ | fun sample(){ }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionPage = module.findTestType("sample", "sample")
+ functionPage.content.assertNode {
+ group {
+ header(1) { +"sample" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ skipAllNotMatching() //Signature
+ }
+ after {
+ group { pWrapped("a normal comment") }
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ link {
+ check {
+ assertEquals(
+ "java.lang/IllegalStateException///PointingToDeclaration/",
+ (this as ContentDRILink).address.toString()
+ )
+ }
+ +"IllegalStateException"
+ }
+ }
+ comment { +"if the Dialog has not yet been created (before onCreateDialog) or has been destroyed (after onDestroyView)." }
+ }
+ group {
+ group {
+ link {
+ check {
+ assertEquals(
+ "kotlin/RuntimeException///PointingToDeclaration/",
+ (this as ContentDRILink).address.toString()
+ )
+ }
+ +"RuntimeException"
+ }
+ }
+ comment {
+ +"when "
+ link { +"Hash Map" }
+ +" doesn't contain value."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should display fully qualified throws name for unresolved class`() {
+ testInline(
+ """
+ |/src/main/kotlin/sample/sample.kt
+ |package sample;
+ | /**
+ | * a normal comment
+ | *
+ | * @throws com.example.UnknownException description for non-resolved
+ | */
+ | fun sample(){ }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionPage =
+ module.findTestType("sample", "sample")
+ functionPage.content.assertNode {
+ group {
+ header(1) { +"sample" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ skipAllNotMatching() //Signature
+ }
+ after {
+ group { pWrapped("a normal comment") }
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ +"com.example.UnknownException"
+ }
+ comment { +"description for non-resolved" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiline throws where exception is not in the same line as description`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ | public class DocGenProcessor {
+ | /**
+ | * a normal comment
+ | *
+ | * @throws java.lang.IllegalStateException if the Dialog has not yet been created (before
+ | * onCreateDialog) or has been destroyed (after onDestroyView).
+ | * @throws java.lang.RuntimeException when
+ | * {@link java.util.HashMap#containsKey(java.lang.Object) Hash
+ | * Map}
+ | * doesn't contain value.
+ | */
+ | public static void sample(){ }
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionPage =
+ module.findTestType(
+ "sample",
+ "DocGenProcessor"
+ ).children.single { it.name == "sample" } as ContentPage
+ functionPage.content.assertNode {
+ group {
+ header(1) { +"sample" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ skipAllNotMatching() //Signature
+ }
+ after {
+ group { pWrapped("a normal comment") }
+ header(4) { +"Throws" }
+ table {
+ group {
+ group {
+ link {
+ check {
+ assertEquals(
+ "java.lang/IllegalStateException///PointingToDeclaration/",
+ (this as ContentDRILink).address.toString()
+ )
+ }
+ +"IllegalStateException"
+ }
+ }
+ comment { +"if the Dialog has not yet been created (before onCreateDialog) or has been destroyed (after onDestroyView)." }
+ }
+ group {
+ group {
+ link {
+ check {
+ assertEquals(
+ "java.lang/RuntimeException///PointingToDeclaration/",
+ (this as ContentDRILink).address.toString()
+ )
+ }
+ +"RuntimeException"
+ }
+ }
+ comment {
+ +"when "
+ link { +"Hash Map" }
+ +" doesn't contain value."
+ }
+ }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+
+
+
+ @Test
+ fun `documentation splitted in 2 using enters`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ |/**
+ | * Listener for handling fragment results.
+ | *
+ | * This object should be passed to
+ | * {@link java.util.HashMap#containsKey(java.lang.Object) FragmentManager#setFragmentResultListener(String, LifecycleOwner, FragmentResultListener)}
+ | * and it will listen for results with the same key that are passed into
+ | * {@link java.util.HashMap#containsKey(java.lang.Object) FragmentManager#setFragmentResult(String, Bundle)}.
+ | *
+ | */
+ | public class DocGenProcessor { }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val classPage =
+ module.findTestType("sample", "DocGenProcessor")
+ classPage.content.assertNode {
+ group {
+ header { +"DocGenProcessor" }
+ platformHinted {
+ group {
+ skipAllNotMatching() //Signature
+ }
+ group {
+ comment {
+ +"Listener for handling fragment results. This object should be passed to "
+ link { +"FragmentManager#setFragmentResultListener(String, LifecycleOwner, FragmentResultListener)" }
+ +" and it will listen for results with the same key that are passed into "
+ link { +"FragmentManager#setFragmentResult(String, Bundle)" }
+ +"."
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiline return tag with param`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ | public class DocGenProcessor {
+ | /**
+ | * a normal comment
+ | *
+ | * @param testParam Sample description for test param that has a type of {@link java.lang.String String}
+ | * @return empty string when
+ | * {@link java.util.HashMap#containsKey(java.lang.Object) Hash
+ | * Map}
+ | * doesn't contain value.
+ | */
+ | public static String sample(String testParam){
+ | return "";
+ | }
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionPage =
+ module.findTestType(
+ "sample",
+ "DocGenProcessor"
+ ).children.single { it.name == "sample" } as ContentPage
+ functionPage.content.assertNode {
+ group {
+ header(1) { +"sample" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ skipAllNotMatching() //Signature
+ }
+ after {
+ group { pWrapped("a normal comment") }
+ group {
+ header(4) { +"Return" }
+ comment {
+ +"empty string when "
+ link { +"Hash Map" }
+ +" doesn't contain value."
+ }
+ }
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"testParam"
+ comment {
+ +"Sample description for test param that has a type of "
+ link { +"String" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `return tag in kotlin`() {
+ testInline(
+ """
+ |/src/main/kotlin/sample/sample.kt
+ |package sample;
+ | /**
+ | * a normal comment
+ | *
+ | * @return empty string when [Hash Map][java.util.HashMap.containsKey] doesn't contain value.
+ | *
+ | */
+ |fun sample(): String {
+ | return ""
+ | }
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val functionPage = module.findTestType("sample", "sample")
+ functionPage.content.assertNode {
+ group {
+ header(1) { +"sample" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ skipAllNotMatching() //Signature
+ }
+ after {
+ group { pWrapped("a normal comment") }
+ group {
+ header(4) { +"Return" }
+ comment {
+ +"empty string when "
+ link { +"Hash Map" }
+ +" doesn't contain value."
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `list with links and description`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ |/**
+ | * Static library support version of the framework's {@link java.lang.String}.
+ | * Used to write apps that run on platforms prior to Android 3.0. When running
+ | * on Android 3.0 or above, this implementation is still used; it does not try
+ | * to switch to the framework's implementation. See the framework {@link java.lang.String}
+ | * documentation for a class overview.
+ | *
+ | * <p>The main differences when using this support version instead of the framework version are:
+ | * <ul>
+ | * <li>Your activity must extend {@link java.lang.String FragmentActivity}
+ | * <li>You must call {@link java.util.HashMap#containsKey(java.lang.Object) FragmentActivity#getSupportFragmentManager} to get the
+ | * {@link java.util.HashMap FragmentManager}
+ | * </ul>
+ | *
+ | */
+ |public class DocGenProcessor { }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val classPage = module.findTestType("sample", "DocGenProcessor")
+ classPage.content.assertNode {
+ group {
+ header { +"DocGenProcessor" }
+ platformHinted {
+ group {
+ skipAllNotMatching() //Signature
+ }
+ group {
+ comment {
+ group {
+ +"Static library support version of the framework's "
+ link { +"java.lang.String" }
+ +". Used to write apps that run on platforms prior to Android 3.0."
+ +" When running on Android 3.0 or above, this implementation is still used; it does not try to switch to the framework's implementation. See the framework "
+ link { +"java.lang.String" }
+ +" documentation for a class overview. " //TODO this probably shouldnt have a space but it is minor
+ }
+ group {
+ +"The main differences when using this support version instead of the framework version are: "
+ }
+ list {
+ group {
+ +"Your activity must extend "
+ link { +"FragmentActivity" }
+ }
+ group {
+ +"You must call "
+ link { +"FragmentActivity#getSupportFragmentManager" }
+ +" to get the "
+ link { +"FragmentManager" }
+ }
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documentation with table`() {
+ testInline(
+ """
+ |/src/main/java/sample/DocGenProcessor.java
+ |package sample;
+ |/**
+ | * <table>
+ | * <caption>List of supported types</caption>
+ | * <tr>
+ | * <td>cell 11</td> <td>cell 21</td>
+ | * </tr>
+ | * <tr>
+ | * <td>cell 12</td> <td>cell 22</td>
+ | * </tr>
+ | * </table>
+ | */
+ | public class DocGenProcessor { }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val classPage =
+ module.findTestType("sample", "DocGenProcessor")
+ classPage.content.assertNode {
+ group {
+ header { +"DocGenProcessor" }
+ platformHinted {
+ group {
+ skipAllNotMatching() //Signature
+ }
+ comment {
+ table {
+ check {
+ caption!!.assertNode {
+ caption {
+ +"List of supported types"
+ }
+ }
+ }
+ group {
+ group {
+ +"cell 11"
+ }
+ group {
+ +"cell 21"
+ }
+ }
+ group {
+ group {
+ +"cell 12"
+ }
+ group {
+ +"cell 22"
+ }
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `undocumented parameter and other tags`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @author Kordyjan
+ | * @since 0.11
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ group { pWrapped("comment to function") }
+ unnamedTag("Author") { comment { +"Kordyjan" } }
+ unnamedTag("Since") { comment { +"0.11" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `single parameter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param abc comment to param
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ group { pWrapped("comment to function") }
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"abc"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
+ }
+ group { group { +"comment to param" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `single parameter in class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to class
+ | * @param abc comment to param
+ | */
+ |class Foo(abc: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ group {
+ pWrapped("comment to class")
+ }
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"abc"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
+ }
+ group { group { +"comment to param" } }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiple parameters`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param first comment to first param
+ | * @param second comment to second param
+ | * @param[third] comment to third param
+ | */
+ |fun function(first: String, second: Int, third: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ after {
+ group { group { group { +"comment to function" } } }
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"first"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
+ }
+ group { group { +"comment to first param" } }
+ }
+ group {
+ +"second"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
+ }
+ group { group { +"comment to second param" } }
+ }
+ group {
+ +"third"
+ check {
+ val textStyles = children.single { it is ContentText }.style
+ assertContains(textStyles, TextStyle.Underlined)
+ }
+ group { group { +"comment to third param" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `multiple parameters with not natural order`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param c comment to c param
+ | * @param b comment to b param
+ | * @param[a] comment to a param
+ | */
+ |fun function(c: String, b: Int, a: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "c" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "b" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "a" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ after {
+ group { group { group { +"comment to function" } } }
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"c"
+ group { group { +"comment to c param" } }
+ }
+ group {
+ +"b"
+ group { group { +"comment to b param" } }
+ }
+ group {
+ +"a"
+ group { group { +"comment to a param" } }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiple parameters without function description`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @param first comment to first param
+ | * @param second comment to second param
+ | * @param[third] comment to third param
+ | */
+ |fun function(first: String, second: Int, third: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ after {
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"first"
+ group { group { +"comment to first param" } }
+ }
+ group {
+ +"second"
+ group { group { +"comment to second param" } }
+ }
+ group {
+ +"third"
+ group { group { +"comment to third param" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function with receiver`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param abc comment to param
+ | * @receiver comment to receiver
+ | */
+ |fun String.function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignatureWithReceiver(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "String",
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ group { pWrapped("comment to function") }
+ group {
+ header(4) { +"Receiver" }
+ pWrapped("comment to receiver")
+ }
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"abc"
+ group { group { +"comment to param" } }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `missing parameter documentation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param first comment to first param
+ | * @param[third] comment to third param
+ | */
+ |fun function(first: String, second: Int, third: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ after {
+ group { group { group { +"comment to function" } } }
+ header(4) { +"Parameters" }
+ table {
+ group {
+ +"first"
+ group { group { +"comment to first param" } }
+ }
+ group {
+ +"third"
+ group { group { +"comment to third param" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `parameters mixed with other tags`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * comment to function
+ | * @param first comment to first param
+ | * @author Kordyjan
+ | * @param second comment to second param
+ | * @since 0.11
+ | * @param[third] comment to third param
+ | */
+ |fun function(first: String, second: Int, third: Double) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(), "", "", emptySet(), "function", null,
+ "first" to ParamAttributes(emptyMap(), emptySet(), "String"),
+ "second" to ParamAttributes(emptyMap(), emptySet(), "Int"),
+ "third" to ParamAttributes(emptyMap(), emptySet(), "Double")
+ )
+ }
+ after {
+ group { pWrapped("comment to function") }
+ unnamedTag("Author") { comment { +"Kordyjan" } }
+ unnamedTag("Since") { comment { +"0.11" } }
+ header(4) { +"Parameters" }
+
+ table {
+ group {
+ +"first"
+ group { group { +"comment to first param" } }
+ }
+ group {
+ +"second"
+ group { group { +"comment to second param" } }
+ }
+ group {
+ +"third"
+ group { group { +"comment to third param" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun javaDocCommentWithDocumentedParameters() {
+ testInline(
+ """
+ |/src/main/java/test/Main.java
+ |package test
+ | public class Main {
+ |
+ | /**
+ | * comment to function
+ | * @param first comment to first param
+ | * @param second comment to second param
+ | */
+ | public void sample(String first, String second) {
+ |
+ | }
+ | }
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val sampleFunction = module.dfs {
+ it is MemberPageNode && it.dri.first()
+ .toString() == "test/Main/sample/#java.lang.String#java.lang.String/PointingToDeclaration/"
+ } as MemberPageNode
+ val forJvm = (sampleFunction.documentables.firstOrNull() as DFunction).parameters.mapNotNull {
+ val jvm = it.documentation.keys.first { it.analysisPlatform == Platform.jvm }
+ it.documentation[jvm]
+ }
+
+ assertEquals(2, forJvm.size)
+ val (first, second) = forJvm.map { it.paramsDescription() }
+ assertEquals("comment to first param", first)
+ assertEquals("comment to second param", second)
+ }
+ }
+ }
+
+ private fun DocumentationNode.paramsDescription(): String =
+ children.firstIsInstanceOrNull<Param>()?.root?.children?.first()?.children?.firstIsInstanceOrNull<Text>()?.body.orEmpty()
+
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/properties/ContentForClassWithParamsAndPropertiesTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/properties/ContentForClassWithParamsAndPropertiesTest.kt
new file mode 100644
index 00000000..d244567f
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/properties/ContentForClassWithParamsAndPropertiesTest.kt
@@ -0,0 +1,272 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.properties
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.DClass
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.model.doc.*
+import org.jetbrains.dokka.pages.ClasslikePageNode
+import org.jetbrains.dokka.pages.RootPageNode
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ContentForClassWithParamsAndPropertiesTest : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `should work for a simple property`() {
+ propertyTest { rootPage ->
+ val node = rootPage.dfs { it.name == "LoadInitialParams" } as ClasslikePageNode
+ val actualDocsForPlaceholdersEnabled =
+ (node.documentables.firstOrNull() as DClass).constructors.first().parameters.find { it.name == "placeholdersEnabled" }
+ ?.documentation?.entries?.first()?.value
+ assertEquals(DocumentationNode(listOf(docsForPlaceholdersEnabled)), actualDocsForPlaceholdersEnabled)
+ }
+ }
+
+ @Test
+ fun `should work for a simple with linebreak`() {
+ propertyTest { rootPage ->
+ val node = rootPage.dfs { it.name == "LoadInitialParams" } as ClasslikePageNode
+ val actualDocsForRequestedLoadSize =
+ (node.documentables.firstOrNull() as DClass).constructors.first().parameters.find { it.name == "requestedLoadSize" }
+ ?.documentation?.entries?.first()?.value
+ assertEquals(DocumentationNode(listOf(docsForRequestedLoadSize)), actualDocsForRequestedLoadSize)
+ }
+ }
+
+ @Test
+ fun `should work with multiline property inline code`() {
+ propertyTest { rootPage ->
+ val node = rootPage.dfs { it.name == "LoadInitialParams" } as ClasslikePageNode
+
+ val actualDocsForRequestedInitialKey =
+ (node.documentables.firstOrNull() as DClass).constructors.first().parameters.find { it.name == "requestedInitialKey" }
+ ?.documentation?.entries?.first()?.value
+ assertEquals(DocumentationNode(listOf(docsForRequestedInitialKey)), actualDocsForRequestedInitialKey)
+ }
+ }
+
+ @Test
+ fun `constructor should only the param and constructor tags`() {
+ propertyTest { rootPage ->
+ val constructorDocs = Description(
+ root = CustomDocTag(
+ children = listOf(
+ P(
+ children = listOf(
+ Text("Creates an empty group.")
+ )
+ )
+ ),
+ emptyMap(), "MARKDOWN_FILE"
+ )
+ )
+ val node = rootPage.dfs { it.name == "LoadInitialParams" } as ClasslikePageNode
+
+ val actualDocs =
+ (node.documentables.firstOrNull() as DClass).constructors.first().documentation.entries.first().value
+ assertEquals(DocumentationNode(listOf(constructorDocs, docsForParam)), actualDocs)
+ }
+ }
+
+ @Test
+ fun `class should have all tags`() {
+ propertyTest { rootPage ->
+ val ownDescription = Description(
+ root = CustomDocTag(
+ children = listOf(
+ P(
+ children = listOf(
+ Text("Holder object for inputs to loadInitial.")
+ )
+ )
+ ),
+ emptyMap(), "MARKDOWN_FILE"
+ )
+ )
+ val node = rootPage.dfs { it.name == "LoadInitialParams" } as ClasslikePageNode
+
+ val actualDocs =
+ (node.documentables.firstOrNull() as DClass).documentation.entries.first().value
+ assertEquals(
+ DocumentationNode(
+ listOf(
+ ownDescription,
+ docsForParam,
+ docsForRequestedInitialKey,
+ docsForRequestedLoadSize,
+ docsForPlaceholdersEnabled,
+ docsForConstructor
+ )
+ ),
+ actualDocs
+ )
+ }
+ }
+
+ @Test
+ fun `property should also work with own docs that override the param tag`() {
+ propertyTest { rootPage ->
+ val ownDescription = Description(
+ root = CustomDocTag(
+ children = listOf(
+ P(
+ children = listOf(
+ Text("Own docs")
+ )
+ )
+ ),
+ emptyMap(), "MARKDOWN_FILE"
+ )
+ )
+ val node = rootPage.dfs { it.name == "ItemKeyedDataSource" } as ClasslikePageNode
+
+ val actualDocs =
+ (node.documentables.firstOrNull() as DClass).properties.first().documentation.entries.first().value
+ assertEquals(
+ DocumentationNode(listOf(ownDescription)),
+ actualDocs
+ )
+ }
+ }
+
+
+ private fun propertyTest(block: (RootPageNode) -> Unit) {
+ testInline(
+ """ |/src/main/kotlin/test/source.kt
+ |package test
+ |/**
+ | * @property tested Docs from class
+ | */
+ |abstract class ItemKeyedDataSource<Key : Any, Value : Any> : DataSource<Key, Value>(ITEM_KEYED) {
+ | /**
+ | * Own docs
+ | */
+ | val tested = ""
+ |
+ | /**
+ | * Holder object for inputs to loadInitial.
+ | *
+ | * @param Key Type of data used to query Value types out of the DataSource.
+ | * @property requestedInitialKey Load items around this key, or at the beginning of the data set
+ | * if `null` is passed.
+ | *
+ | * Note that this key is generally a hint, and may be ignored if you want to always load from
+ | * the beginning.
+ | * @property requestedLoadSize Requested number of items to load.
+ | *
+ | * Note that this may be larger than available data.
+ | * @property placeholdersEnabled Defines whether placeholders are enabled, and whether the
+ | * loaded total count will be ignored.
+ | *
+ | * @constructor Creates an empty group.
+ | */
+ | open class LoadInitialParams<Key : Any>(
+ | @JvmField
+ | val requestedInitialKey: Key?,
+ | @JvmField
+ | val requestedLoadSize: Int,
+ | @JvmField
+ | val placeholdersEnabled: Boolean
+ | )
+ |}""".trimIndent(), testConfiguration
+ ) {
+ pagesGenerationStage = block
+ }
+ }
+
+ private val docsForPlaceholdersEnabled = Property(
+ root = CustomDocTag(
+ listOf(
+ P(
+ children = listOf(
+ Text("Defines whether placeholders are enabled, and whether the loaded total count will be ignored.")
+ )
+ )
+ ), emptyMap(), "MARKDOWN_FILE"
+ ),
+ name = "placeholdersEnabled"
+ )
+
+ private val docsForRequestedInitialKey = Property(
+ root = CustomDocTag(
+ listOf(
+ P(
+ children = listOf(
+ Text("Load items around this key, or at the beginning of the data set if "),
+ CodeInline(
+ listOf(
+ Text("null")
+ )
+ ),
+ Text(" is passed.")
+ ),
+ params = emptyMap()
+ ),
+ P(
+ children = listOf(
+ Text("Note that this key is generally a hint, and may be ignored if you want to always load from the beginning.")
+ )
+ )
+ ), emptyMap(), "MARKDOWN_FILE"
+ ),
+ name = "requestedInitialKey"
+ )
+
+ private val docsForRequestedLoadSize = Property(
+ root = CustomDocTag(
+ listOf(
+ P(
+ children = listOf(
+ Text("Requested number of items to load.")
+ )
+ ),
+ P(
+ children = listOf(
+ Text("Note that this may be larger than available data.")
+ )
+ )
+ ), emptyMap(), "MARKDOWN_FILE"
+ ),
+ name = "requestedLoadSize"
+ )
+
+ private val docsForConstructor = Constructor(
+ root = CustomDocTag(
+ children = listOf(
+ P(
+ children = listOf(
+ Text("Creates an empty group.")
+ )
+ )
+ ),
+ emptyMap(), "MARKDOWN_FILE"
+ )
+ )
+
+ private val docsForParam = Param(
+ root = CustomDocTag(
+ children = listOf(
+ P(
+ children = listOf(
+ Text("Type of data used to query Value types out of the DataSource.")
+ )
+ )
+ ),
+ emptyMap(), "MARKDOWN_FILE"
+ ),
+ name = "Key"
+ )
+}
+
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/receiver/ContentForReceiverTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/receiver/ContentForReceiverTest.kt
new file mode 100644
index 00000000..d94c1106
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/receiver/ContentForReceiverTest.kt
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.receiver
+
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.model.doc.Receiver
+import org.jetbrains.dokka.model.doc.Text
+import org.jetbrains.dokka.pages.ContentHeader
+import org.jetbrains.dokka.pages.ContentText
+import org.jetbrains.dokka.pages.MemberPageNode
+import utils.docs
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotNull
+
+class ContentForReceiverTest: BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `should have docs for receiver`(){
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |/**
+ | * docs
+ | * @receiver docs for string
+ | */
+ |fun String.asd2(): String = this
+ """.trimIndent(),
+ testConfiguration
+ ){
+ documentablesTransformationStage = { module ->
+ with(module.packages.flatMap { it.functions }.first()){
+ val receiver = docs().firstOrNull { it is Receiver }
+ assertNotNull(receiver)
+ val content = receiver.dfs { it is Text } as Text
+ assertEquals("docs for string", content.body)
+ }
+ }
+ pagesTransformationStage = { rootPageNode ->
+ val functionPage = rootPageNode.dfs { it is MemberPageNode } as MemberPageNode
+ val header = functionPage.content.dfs { it is ContentHeader && it.children.firstOrNull() is ContentText }
+ val text = functionPage.content.dfs { it is ContentText && it.text == "docs for string" }
+
+ assertNotNull(header)
+ assertNotNull(text)
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/samples/ContentForSamplesTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/samples/ContentForSamplesTest.kt
new file mode 100644
index 00000000..d166d8f8
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/samples/ContentForSamplesTest.kt
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.samples
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.base.transformers.pages.KOTLIN_PLAYGROUND_SCRIPT
+import org.jetbrains.dokka.model.DisplaySourceSet
+import utils.TestOutputWriterPlugin
+import utils.assertContains
+import utils.classSignature
+import utils.findTestType
+import java.nio.file.Paths
+import kotlin.test.Test
+import kotlin.test.assertEquals
+import kotlin.test.assertNotEquals
+
+class ContentForSamplesTest : BaseAbstractTest() {
+ private val testDataDir = getTestDataDir("content/samples").toAbsolutePath()
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ samples = listOf(
+ Paths.get("$testDataDir/samples.kt").toString(),
+ )
+ }
+ }
+ }
+
+ private val mppTestConfiguration = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ samples = listOf(
+ Paths.get("$testDataDir/samples.kt").toString(),
+ )
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ samples = listOf(
+ Paths.get("$testDataDir/samples.kt").toString(),
+ )
+ }
+ sourceSet {
+ name = "linuxX64"
+ displayName = "linuxX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
+ samples = listOf(
+ Paths.get("$testDataDir/samples.kt").toString(),
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `samples block is rendered in the description`() {
+ val writerPlugin = TestOutputWriterPlugin()
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ | /**
+ | * @sample [test.sampleForClassDescription]
+ | */
+ |class Foo
+ """.trimIndent(), testConfiguration,
+ pluginOverrides = listOf(writerPlugin)
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ assertContains(page.embeddedResources, KOTLIN_PLAYGROUND_SCRIPT)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo"
+ )
+ header(4) { +"Samples" }
+ group {
+ codeBlock {
+ +"""|
+ |fun main() {
+ | //sampleStart
+ | print("Hello")
+ | //sampleEnd
+ |}""".trimMargin()
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ renderingStage = { _, _ ->
+ assertNotEquals(-1, writerPlugin.writer.contents["root/test/-foo/index.html"]?.indexOf(KOTLIN_PLAYGROUND_SCRIPT))
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatofrm class with samples in few platforms`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @sample [test.sampleForClassDescription]
+ |*/
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @sample unresolved
+ |*/
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ assertContains(page.embeddedResources, KOTLIN_PLAYGROUND_SCRIPT)
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) { +"Samples" }
+ group {
+ codeBlock {
+ +"""|
+ |fun main() {
+ | //sampleStart
+ | print("Hello")
+ | //sampleEnd
+ |}""".trimMargin()
+ }
+ check {
+ sourceSets.assertSourceSet("common")
+ }
+ }
+ group {
+ +"unresolved"
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+}
+
+
+private fun Set<DisplaySourceSet>.assertSourceSet(expectedName: String) {
+ assertEquals(1, this.size)
+ assertEquals(expectedName, this.first().name)
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
new file mode 100644
index 00000000..fb72178b
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/seealso/ContentForSeeAlsoTest.kt
@@ -0,0 +1,866 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.seealso
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.DisplaySourceSet
+import org.jetbrains.dokka.pages.ContentDRILink
+import utils.*
+import kotlin.test.Test
+import kotlin.test.assertEquals
+
+class ContentForSeeAlsoTest : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOfNotNull(jvmStdlibPath)
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ private val mppTestConfiguration = dokkaConfiguration {
+ moduleName = "example"
+ sourceSets {
+ val common = sourceSet {
+ name = "common"
+ displayName = "common"
+ analysisPlatform = "common"
+ sourceRoots = listOf("src/commonMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "jvm"
+ displayName = "jvm"
+ analysisPlatform = "jvm"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/jvmMain/kotlin/pageMerger/Test.kt")
+ }
+ sourceSet {
+ name = "linuxX64"
+ displayName = "linuxX64"
+ analysisPlatform = "native"
+ dependentSourceSets = setOf(common.value.sourceSetID)
+ sourceRoots = listOf("src/linuxX64Main/kotlin/pageMerger/Test.kt")
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented seealso`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"See also" }
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented seealso without reference for class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc
+ | */
+ |class Foo()
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo"
+ )
+ header(4) { +"See also" }
+ table {
+ group {
+ +"abc"
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @OnlyDescriptors("No link for `abc` in K1")
+ @Test
+ fun `undocumented seealso with reference to parameter for class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc
+ | */
+ |class Foo(abc: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ header(4) { +"See also" }
+ table {
+ group {
+ +"abc" // link { +"abc" }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @OnlyDescriptors("issue #3179")
+ @Test
+ fun `undocumented seealso with reference to property for class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc
+ | */
+ |class Foo(val abc: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo",
+ "val abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ header(4) { +"See also" }
+ table {
+ group {
+ link { +"Foo.abc" }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented seealso`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc Comment to abc
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"See also" }
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
+ group {
+ group { +"Comment to abc" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @OnlyDescriptors("issue #3179")
+ @Test
+ fun `documented seealso with reference to property for class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc Comment to abc
+ | */
+ |class Foo(val abc: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "Foo")
+ println(page.content)
+ page.content.assertNode {
+ group {
+ header(1) { +"Foo" }
+ platformHinted {
+ classSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "Foo",
+ "val abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ header(4) { +"See also" }
+ table {
+ group {
+ link { +"Foo.abc" }
+ group {
+ group { +"Comment to abc" }
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should use fully qualified name for unresolved link`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see com.example.NonExistingClass description for non-existing
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"See also" }
+ table {
+ group {
+ +"com.example.NonExistingClass"
+ group {
+ group { +"description for non-existing" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `undocumented seealso with stdlib link`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see Collection
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"See also" }
+ table {
+ group {
+ link {
+ check {
+ assertEquals(
+ "kotlin.collections/Collection///PointingToDeclaration/",
+ (this as ContentDRILink).address.toString()
+ )
+ }
+ +"Collection"
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented seealso with stdlib link`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see Collection Comment to stdliblink
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"See also" }
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
+ group {
+ group { +"Comment to stdliblink" }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented seealso with stdlib link with other tags`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * random comment
+ | * @see Collection Comment to stdliblink
+ | * @author pikinier20
+ | * @since 0.11
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ group { comment { +"random comment" } }
+ unnamedTag("Author") { comment { +"pikinier20" } }
+ unnamedTag("Since") { comment { +"0.11" } }
+
+ header(4) { +"See also" }
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
+ group {
+ group { +"Comment to stdliblink" }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented multiple see also`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc Comment to abc1
+ | * @see abc Comment to abc2
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"See also" }
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
+ group {
+ group { +"Comment to abc2" }
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `documented multiple see also mixed source`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ | /**
+ | * @see abc Comment to abc1
+ | * @see[Collection] Comment to collection
+ | */
+ |fun function(abc: String) {
+ | println(abc)
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("test", "function")
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ null,
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ after {
+ header(4) { +"See also" }
+ table {
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"abc" }
+ group {
+ group { +"Comment to abc1" }
+ }
+ }
+ group {
+ //DRI should be "test//abc/#/-1/"
+ link { +"Collection" }
+ group { group { +"Comment to collection" } }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should prefix static function and property links with class name`() {
+ testInline(
+ """
+ |/src/main/kotlin/com/example/package/CollectionExtensions.kt
+ |package com.example.util
+ |
+ |object CollectionExtensions {
+ | val property = "Hi"
+ | fun emptyList() {}
+ |}
+ |
+ |/src/main/kotlin/com/example/foo.kt
+ |package com.example
+ |
+ |import com.example.util.CollectionExtensions.property
+ |import com.example.util.CollectionExtensions.emptyList
+ |
+ |/**
+ | * @see [property] static property
+ | * @see [emptyList] static emptyList
+ | */
+ |fun function() {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("com.example", "function")
+
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "function",
+ returnType = null,
+ )
+ }
+ after {
+ header(4) { +"See also" }
+ table {
+ group {
+ link { +"CollectionExtensions.property" }
+ group {
+ group { +"static property" }
+ }
+ }
+ group {
+ link { +"CollectionExtensions.emptyList" }
+ group {
+ group { +"static emptyList" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `multiplatform class with seealso in few platforms`() {
+ testInline(
+ """
+ |/src/commonMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |/**
+ |* @see Unit
+ |*/
+ |expect open class Parent
+ |
+ |/src/jvmMain/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |val x = 0
+ |/**
+ |* @see x resolved
+ |* @see y unresolved
+ |*/
+ |actual open class Parent
+ |
+ |/src/linuxX64Main/kotlin/pageMerger/Test.kt
+ |package pageMerger
+ |
+ |actual open class Parent
+ |
+ """.trimMargin(),
+ mppTestConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.findTestType("pageMerger", "Parent")
+ page.content.assertNode {
+ group {
+ header(1) { +"Parent" }
+ platformHinted {
+ group {
+ +"expect open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ group {
+ +"actual open class "
+ link {
+ +"Parent"
+ }
+ }
+ header(4) {
+ +"See also"
+ check {
+ assertEquals(2, sourceSets.size)
+ }
+ }
+ table {
+ group {
+ link { +"Unit" }
+ check {
+ sourceSets.assertSourceSet("common")
+ }
+ }
+ group {
+ link { +"Unit" }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ group {
+ link { +"x" }
+ group { group { +"resolved" } }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+ group {
+ +"y"
+ group { group { +"unresolved" } }
+ check {
+ sourceSets.assertSourceSet("jvm")
+ }
+ }
+
+ check {
+ assertEquals(2, sourceSets.size)
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+}
+
+private fun Set<DisplaySourceSet>.assertSourceSet(expectedName: String) {
+ assertEquals(1, this.size)
+ assertEquals(expectedName, this.first().name)
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt
new file mode 100644
index 00000000..9a413e0e
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/signatures/ConstructorsSignaturesTest.kt
@@ -0,0 +1,469 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.signatures
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.pages.BasicTabbedContentType
+import org.jetbrains.dokka.pages.ContentPage
+import kotlin.test.Test
+import utils.OnlyDescriptors
+
+class ConstructorsSignaturesTest : BaseAbstractTest() {
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ }
+ }
+ }
+
+ @Test
+ fun `class name without parenthesis`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass
+ |
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class "
+ link { +"SomeClass" }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class name with empty parenthesis`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass()
+ |
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class "
+ link { +"SomeClass" }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class with a parameter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass(a: String)
+ |
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class "
+ link { +"SomeClass" }
+ +"("
+ group {
+ group {
+ +"a: "
+ group { link { +"String" } }
+ }
+ }
+ +")"
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class with a val parameter`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass(val a: String, var i: Int)
+ |
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class "
+ link { +"SomeClass" }
+ +"("
+ group {
+ group {
+ +"val a: "
+ group { link { +"String" } }
+ +", "
+ }
+ group {
+ +"var i: "
+ group { link { +"Int" } }
+ }
+ }
+ +")"
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+
+ @OnlyDescriptors("Order of constructors is different in K2")
+ @Test
+ fun `class with a parameterless secondary constructor`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |class SomeClass(a: String) {
+ | constructor()
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class "
+ link { +"SomeClass" }
+ +"("
+ group {
+ group {
+ +"a: "
+ group { link { +"String" } }
+ }
+ }
+ +")"
+ }
+ }
+ }
+ tabbedGroup {
+ group {
+ tab(BasicTabbedContentType.CONSTRUCTOR) {
+ header { +"Constructors" }
+ table {
+ group {
+ link { +"SomeClass" }
+ platformHinted {
+ group {
+ +"constructor"
+ +"("
+ +")"
+ }
+ group {
+ +"constructor"
+ +"("
+ group {
+ group {
+ +"a: "
+ group { link { +"String" } }
+ }
+ }
+ +")"
+ }
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+ }
+
+
+ @OnlyDescriptors("Order of constructors is different in K2")
+ @Test
+ fun `class with a few documented constructors`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ | /**
+ | * some comment
+ | * @constructor ctor comment
+ | **/
+ |class SomeClass(a: String){
+ | /**
+ | * ctor one
+ | **/
+ | constructor(): this("")
+ |
+ | /**
+ | * ctor two
+ | **/
+ | constructor(b: Int): this("")
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class "
+ link { +"SomeClass" }
+ +"("
+ group {
+ group {
+ +"a: "
+ group { link { +"String" } }
+ }
+ }
+ +")"
+ }
+ skipAllNotMatching()
+ }
+ }
+ tabbedGroup {
+ group {
+ tab(BasicTabbedContentType.CONSTRUCTOR) {
+ header { +"Constructors" }
+ table {
+ group {
+ link { +"SomeClass" }
+ platformHinted {
+ group {
+ +"constructor"
+ +"("
+ +")"
+ }
+ group {
+ group {
+ group { +"ctor one" }
+ }
+ }
+ group {
+ +"constructor"
+ +"("
+ group {
+ group {
+ +"b: "
+ group {
+ link { +"Int" }
+ }
+ }
+ }
+ +")"
+ }
+ group {
+ group {
+ group { +"ctor two" }
+ }
+ }
+ group {
+ +"constructor"
+ +"("
+ group {
+ group {
+ +"a: "
+ group {
+ link { +"String" }
+ }
+ }
+ }
+ +")"
+ }
+ group {
+ group {
+ group { +"ctor comment" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `class with explicitly documented constructor`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ | /**
+ | * some comment
+ | * @constructor ctor comment
+ | **/
+ |class SomeClass(a: String)
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "SomeClass" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"SomeClass" }
+ platformHinted {
+ group {
+ +"class "
+ link { +"SomeClass" }
+ +"("
+ group {
+ group {
+ +"a: "
+ group { link { +"String" } }
+ }
+ }
+ +")"
+ }
+ skipAllNotMatching()
+ }
+ }
+ tabbedGroup {
+ group {
+ tab(BasicTabbedContentType.CONSTRUCTOR) {
+ header { +"Constructors" }
+ table {
+ group {
+ link { +"SomeClass" }
+ platformHinted {
+ group {
+ +"constructor"
+ +"("
+ group {
+ group {
+ +"a: "
+ group {
+ link { +"String" }
+ }
+ }
+ }
+ +")"
+ }
+ group {
+ group {
+ group { +"ctor comment" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should render primary constructor, but not constructors block for annotation class`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |annotation class MyAnnotation(val param: String) {}
+ """.trimIndent(),
+ testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "MyAnnotation" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"MyAnnotation" }
+ platformHinted {
+ group {
+ +"annotation class "
+ link { +"MyAnnotation" }
+ +"("
+ group {
+ group {
+ +"val param: "
+ group { link { +"String" } }
+ }
+ }
+ +")"
+ }
+ }
+ }
+ group {
+ group {
+ group {
+ header { +"Properties" }
+ table {
+ skipAllNotMatching()
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt
new file mode 100644
index 00000000..8af9e082
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/signatures/ContentForSignaturesTest.kt
@@ -0,0 +1,515 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.signatures
+
+import matchers.content.*
+import org.jetbrains.dokka.DokkaConfiguration
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.pages.ContentPage
+import org.jetbrains.dokka.pages.PackagePageNode
+import utils.ParamAttributes
+import utils.bareSignature
+import utils.propertySignature
+import utils.typealiasSignature
+import kotlin.test.Test
+
+class ContentForSignaturesTest : BaseAbstractTest() {
+
+ private val testConfiguration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ analysisPlatform = "jvm"
+ documentedVisibilities = setOf(
+ DokkaConfiguration.Visibility.PUBLIC,
+ DokkaConfiguration.Visibility.PRIVATE,
+ DokkaConfiguration.Visibility.PROTECTED,
+ DokkaConfiguration.Visibility.INTERNAL,
+ )
+ }
+ }
+ }
+
+ @Test
+ fun `function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `private function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |private fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "private",
+ "",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `open function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |open fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "open",
+ emptySet(),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `function without parameters`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |fun function(): String {
+ | return "Hello"
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = emptySet(),
+ name = "function",
+ returnType = "String",
+ )
+ }
+ }
+ }
+
+ }
+ }
+ }
+ }
+
+
+ @Test
+ fun `suspend function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |suspend fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "",
+ "",
+ setOf("suspend"),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `protected open suspend function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |protected open suspend fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "protected",
+ "open",
+ setOf("suspend"),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `protected open suspend inline function`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |protected open suspend inline fun function(abc: String): String {
+ | return "Hello, " + abc
+ |}
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" }
+ .children.single { it.name == "function" } as ContentPage
+ page.content.assertNode {
+ group {
+ header(1) { +"function" }
+ }
+ divergentGroup {
+ divergentInstance {
+ divergent {
+ bareSignature(
+ emptyMap(),
+ "protected",
+ "open",
+ setOf("inline", "suspend"),
+ "function",
+ "String",
+ "abc" to ParamAttributes(emptyMap(), emptySet(), "String")
+ )
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "", "", emptySet(), "val", "property", "Int", "6")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `const property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |const val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "", "", setOf("const"), "val", "property", "Int", "6")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `protected property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |protected val property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "protected", "", emptySet(), "val", "property", "Int", "6")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `protected lateinit property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |protected lateinit var property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(emptyMap(), "protected", "", setOf("lateinit"), "var", "property", "Int", null)
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `should not display default value for mutable property`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |var property: Int = 6
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ propertySignature(
+ annotations = emptyMap(),
+ visibility = "",
+ modifier = "",
+ keywords = setOf(),
+ preposition = "var",
+ name = "property",
+ type = "Int",
+ value = null
+ )
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to String`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |typealias Alias = String
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "String")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to Int`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |typealias Alias = Int
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "Int")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to type in same package`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |
+ |typealias Alias = X
+ |class X
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "X")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to type in different package`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |import other.X
+ |typealias Alias = X
+ |
+ |/src/main/kotlin/test/source2.kt
+ |package other
+ |class X
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "X")
+ }
+ }
+ }
+ }
+
+ @Test
+ fun `typealias to type in different package with same name`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/source.kt
+ |package test
+ |typealias Alias = other.Alias
+ |
+ |/src/main/kotlin/test/source2.kt
+ |package other
+ |class Alias
+ """.trimIndent(), testConfiguration
+ ) {
+ pagesTransformationStage = { module ->
+ val page = module.children.single { it.name == "test" } as PackagePageNode
+ page.content.assertNode {
+ typealiasSignature("Alias", "other.Alias")
+ }
+ }
+ }
+ }
+}
diff --git a/dokka-subprojects/plugin-base/src/test/kotlin/content/typealiases/TypealiasTest.kt b/dokka-subprojects/plugin-base/src/test/kotlin/content/typealiases/TypealiasTest.kt
new file mode 100644
index 00000000..4015e0f4
--- /dev/null
+++ b/dokka-subprojects/plugin-base/src/test/kotlin/content/typealiases/TypealiasTest.kt
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014-2023 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
+ */
+
+package content.typealiases
+
+import matchers.content.*
+import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest
+import org.jetbrains.dokka.model.dfs
+import org.jetbrains.dokka.pages.ClasslikePageNode
+import org.jetbrains.dokka.pages.PlatformHintedContent
+import utils.assertNotNull
+import kotlin.test.Test
+
+
+class TypealiasTest : BaseAbstractTest() {
+ private val configuration = dokkaConfiguration {
+ sourceSets {
+ sourceSet {
+ sourceRoots = listOf("src/")
+ classpath = listOf(commonStdlibPath!!, jvmStdlibPath!!)
+ externalDocumentationLinks = listOf(stdlibExternalDocumentationLink)
+ }
+ }
+ }
+
+ @Test
+ fun `typealias should have a dedicated page with full documentation`() {
+ testInline(
+ """
+ |/src/main/kotlin/test/Test.kt
+ |package example
+ |
+ | /**
+ | * Brief text
+ | *
+ | * some text
+ | *
+ | * @see String
+ | * @throws Unit
+ | */
+ | typealias A = String
+ """,
+ configuration
+ ) {
+ pagesTransformationStage = { module ->
+ val content = (module.dfs { it.name == "A" } as ClasslikePageNode).content
+ val platformHinted = content.dfs { it is PlatformHintedContent }
+ platformHinted.assertNotNull("platformHinted").assertNode {
+ group {
+ group {
+ group {
+ +"typealias "
+ group { group { link { +"A" } } }
+ +" = "
+ group { link { +"String" } }
+ }
+ }
+
+ group {
+ group {
+ group {
+ group { +"Brief text" }
+ group { +"some text" }
+ }
+ }
+ }
+
+ header { +"See also" }
+ table {
+ group { link { +"String" } }
+ }
+
+ header { +"Throws" }
+ table {
+ group { group { link { +"Unit" } } }
+ }
+ }
+ }
+ }
+ }
+ }
+}