package content.params import matchers.content.* import org.jetbrains.dokka.Platform 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.ContentDRILink import org.jetbrains.dokka.pages.ContentPage import org.jetbrains.dokka.pages.MemberPageNode import org.jetbrains.dokka.base.testApi.testRunner.BaseAbstractTest import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull import org.junit.jupiter.api.Test import utils.* import kotlin.test.assertEquals class ContentForParamsTest : BaseAbstractTest() { private val testConfiguration = dokkaConfiguration { sourceSets { sourceSet { sourceRoots = listOf("src/") 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.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", 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.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", 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.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", 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.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage 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.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage 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}. | * | * @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.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage classPage.content.assertNode { group { header { +"DocGenProcessor" } platformHinted { group { skipAllNotMatching() //Signature } group { comment { +"Return the target fragment set by " 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 | * | * TabLayout and ViewPager instead. | */ | public class DocGenProcessor { } """.trimIndent(), testConfiguration ) { pagesTransformationStage = { module -> val classPage = module.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage 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. | * | *
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)}.
| * | * @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.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage 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.children.single { it.name == "sample" }.children.single { it.name == "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" } platformHinted { table { group { group { link { +"java.lang.IllegalStateException" } } comment { +"if the Dialog has not yet been created (before onCreateDialog) or has been destroyed (after onDestroyView)." } } group { group { link { +"java.lang.RuntimeException" } } comment { +"when " link { +"Hash Map" } +" doesn't contain value." } } } } } } } } } } } @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.children.single { it.name == "sample" }.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" } platformHinted { table { group { group { link { check { assertEquals( "java.lang/IllegalStateException///PointingToDeclaration/", (this as ContentDRILink).address.toString() ) } +"java.lang.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() ) } +"java.lang.RuntimeException" } } comment { +"when " link { +"Hash Map" } +" doesn't contain value." } } } } } } } } } } } @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.children.single { it.name == "sample" }.children.single { it.name == "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" } platformHinted { table { group { group { link { check { assertEquals( "java.lang/IllegalStateException///PointingToDeclaration/", (this as ContentDRILink).address.toString() ) } +"java.lang.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() ) } +"java.lang.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.children.single { it.name == "sample" }.children.single { it.name == "DocGenProcessor" } as ContentPage 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.children.single { it.name == "sample" }.children.single { it.name == "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(2) { +"Parameters" } group { platformHinted { 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.children.single { it.name == "sample" }.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." } } } } } } } } } @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. | * | *The main differences when using this support version instead of the framework version are: | *
cell 11 | cell 21 | | *
cell 12 | cell 22 | | *