diff --git a/docs/src/doc/docs/user_guide/cli/usage.md b/docs/src/doc/docs/user_guide/cli/usage.md index 65c4f930077..eca68c7d274 100644 --- a/docs/src/doc/docs/user_guide/cli/usage.md +++ b/docs/src/doc/docs/user_guide/cli/usage.md @@ -30,7 +30,7 @@ Dokka supports the following command line arguments: * `-samples` - list of directories containing sample code (documentation for those directories is not generated but declarations from them can be referenced using the `@sample` tag) separated by `;` * `-includes` - list of files containing the documentation for the module and individual packages separated by `;` * `-includeNonPublic` - **Deprecated**, prefer using `documentedVisibilities`. Include protected and private code - * `-documentedVisibilities` - a list of visibility modifiers (separated by `;`) that should be documented. Possible values: `PUBLIC`, `PRIVATE`, `PROTECTED`, `INTERNAL` (Kotlin-specific), `PACKAGE` (Java-specific package-private) + * `-documentedVisibilities` - a list of visibility modifiers (separated by `;`) that should be documented. Overrides `includeNonPublic`. Default is `PUBLIC`. Possible values: `PUBLIC`, `PRIVATE`, `PROTECTED`, `INTERNAL` (Kotlin-specific), `PACKAGE` (Java-specific package-private) * `-skipDeprecated` - if set, deprecated elements are not included in the generated documentation * `-reportUndocumented` - warn about undocumented members * `-noSkipEmptyPackages` - create index pages for empty packages diff --git a/docs/src/doc/docs/user_guide/gradle/usage.md b/docs/src/doc/docs/user_guide/gradle/usage.md index 5f810cb79ac..0b09b4303b8 100644 --- a/docs/src/doc/docs/user_guide/gradle/usage.md +++ b/docs/src/doc/docs/user_guide/gradle/usage.md @@ -104,13 +104,14 @@ val dokkaHtml by getting(DokkaTask::class) { includeNonPublic.set(false) // A set of visibility modifiers that should be documented + // If set by user, overrides includeNonPublic. Default is PUBLIC documentedVisibilities.set( setOf( - Visibility.PUBLIC, // Same for both Kotlin and Java - Visibility.PRIVATE, // Same for both Kotlin and Java - Visibility.PROTECTED, // Same for both Kotlin and Java - Visibility.INTERNAL, // Kotlin-specific internal modifier - Visibility.PACKAGE, // Java-specific package-private visibility + DokkaConfiguration.Visibility.PUBLIC, // Same for both Kotlin and Java + DokkaConfiguration.Visibility.PRIVATE, // Same for both Kotlin and Java + DokkaConfiguration.Visibility.PROTECTED, // Same for both Kotlin and Java + DokkaConfiguration.Visibility.INTERNAL, // Kotlin-specific internal modifier + DokkaConfiguration.Visibility.PACKAGE, // Java-specific package-private visibility ) ) @@ -191,13 +192,16 @@ val dokkaHtml by getting(DokkaTask::class) { skipDeprecated.set(false) reportUndocumented.set(true) // Emit warnings about not documented members includeNonPublic.set(false) // Deprecated, prefer using documentedVisibilities - documentedVisibilities.set( // Visibilities that should be included in the documentation + + // Visibilities that should be included in the documentation + // If set by user, overrides includeNonPublic. Default is PUBLIC + documentedVisibilities.set( setOf( - Visibility.PUBLIC, // Same for both Kotlin and Java - Visibility.PRIVATE, // Same for both Kotlin and Java - Visibility.PROTECTED, // Same for both Kotlin and Java - Visibility.INTERNAL, // Kotlin-specific internal modifier - Visibility.PACKAGE, // Java-specific package-private visibility + DokkaConfiguration.Visibility.PUBLIC, // Same for both Kotlin and Java + DokkaConfiguration.Visibility.PRIVATE, // Same for both Kotlin and Java + DokkaConfiguration.Visibility.PROTECTED, // Same for both Kotlin and Java + DokkaConfiguration.Visibility.INTERNAL, // Kotlin-specific internal modifier + DokkaConfiguration.Visibility.PACKAGE, // Java-specific package-private visibility ) ) } diff --git a/docs/src/doc/docs/user_guide/maven/usage.md b/docs/src/doc/docs/user_guide/maven/usage.md index cd8d890b565..d799396ab1b 100644 --- a/docs/src/doc/docs/user_guide/maven/usage.md +++ b/docs/src/doc/docs/user_guide/maven/usage.md @@ -75,6 +75,7 @@ The available configuration options are shown below: + PUBLIC PRIVATE @@ -165,6 +166,7 @@ The available configuration options are shown below: false + PUBLIC PRIVATE diff --git a/integration-tests/cli/projects/it-cli/src/main/kotlin/it/basic/PublicClass.kt b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/basic/PublicClass.kt index 71bc7e6394e..d7a72392785 100644 --- a/integration-tests/cli/projects/it-cli/src/main/kotlin/it/basic/PublicClass.kt +++ b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/basic/PublicClass.kt @@ -2,6 +2,9 @@ package it.basic +/** + * §PUBLIC§ (marker for asserts) + */ class PublicClass { /** * This function is public and documented @@ -24,6 +27,12 @@ class PublicClass { private fun privateUndocumentedFunction(): String = "" + /** + * This function is protected and documented + */ + protected fun protectedDocumentedFunction(): String = "" + + protected fun protectedUndocumentedFunction(): String = "" /** * This property is public and documented diff --git a/integration-tests/cli/projects/it-cli/src/main/kotlin/it/internal/InternalClass.kt b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/internal/InternalClass.kt new file mode 100644 index 00000000000..f5be5406eb3 --- /dev/null +++ b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/internal/InternalClass.kt @@ -0,0 +1,7 @@ +package it.internal + +/** + * §INTERNAL§ (marker for asserts) + * This class is internal and should not be rendered + */ +internal class InternalClass \ No newline at end of file diff --git a/integration-tests/cli/projects/it-cli/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt new file mode 100644 index 00000000000..230f5e0b812 --- /dev/null +++ b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt @@ -0,0 +1,12 @@ +package it.overriddenVisibility + +/** + * Private classes and methods generally should not be visible, but [documentedVisibilities] + * are overriden for this specific package to include private code + * + * §PRIVATE§ (marker for asserts) + */ +private class VisiblePrivateClass { + private val privateVal: Int = 0 + private fun privateMethod() {} +} \ No newline at end of file diff --git a/integration-tests/cli/projects/it-cli/src/main/kotlin/it/protected/ProtectedClass.kt b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/protected/ProtectedClass.kt new file mode 100644 index 00000000000..ad19f1a1a54 --- /dev/null +++ b/integration-tests/cli/projects/it-cli/src/main/kotlin/it/protected/ProtectedClass.kt @@ -0,0 +1,10 @@ +package it.protected + +/** + * Protected class should be visible because it's included in documentedVisibilities + * + * §PROTECTED§ (marker for asserts) + */ +protected class ProtectedClass { + protected fun protectedFun(): String = "protected" +} diff --git a/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt b/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt index 8935f8f5310..b87badd71ca 100644 --- a/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt +++ b/integration-tests/cli/src/integrationTest/kotlin/org/jetbrains/dokka/it/cli/CliIntegrationTest.kt @@ -84,6 +84,14 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { assertNoEmptySpans(file) } + assertContentVisibility( + contentFiles = projectDir.allHtmlFiles().toList(), + documentPublic = true, + documentInternal = false, + documentProtected = false, + documentPrivate = false + ) + assertFalse( projectDir.resolve("output").resolve("index.html").readText().contains("emptypackagetest"), "Expected not to render empty packages" @@ -205,4 +213,50 @@ class CliIntegrationTest : AbstractCliIntegrationTest() { assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") assertFalse(result.output.contains("Loaded plugins: "), "Expected output to not contain info logs") } + + @Test + fun `custom documented visibility`() { + val dokkaOutputDir = File(projectDir, "output") + assertTrue(dokkaOutputDir.mkdirs()) + val process = ProcessBuilder( + "java", "-jar", cliJarFile.path, + "-outputDir", dokkaOutputDir.path, + "-pluginsClasspath", basePluginJarFile.path, + "-moduleName", "Basic Project", + "-sourceSet", + buildString { + append(" -sourceSetName cliMain") + append(" -src ${File(projectDir, "src").path}") + append(" -jdkVersion 8") + append(" -analysisPlatform jvm") + append(" -documentedVisibilities PUBLIC;PROTECTED") + append(" -perPackageOptions it.overriddenVisibility.*,+visibility:PRIVATE") + } + ) + .redirectErrorStream(true) + .start() + + val result = process.awaitProcessResult() + assertEquals(0, result.exitCode, "Expected exitCode 0 (Success)") + + val allHtmlFiles = projectDir.allHtmlFiles().toList() + + assertContentVisibility( + contentFiles = allHtmlFiles, + documentPublic = true, + documentProtected = true, // sourceSet documentedVisibilities + documentInternal = false, + documentPrivate = true // for overriddenVisibility package + ) + + assertContainsFilePaths( + outputFiles = allHtmlFiles, + expectedFilePaths = listOf( + // documentedVisibilities is overridden for package `overriddenVisibility` specifically + // to include private code, so html pages for it are expected to have been created + Regex("it\\.overriddenVisibility/-visible-private-class/private-method\\.html"), + Regex("it\\.overriddenVisibility/-visible-private-class/private-val\\.html"), + ) + ) + } } diff --git a/integration-tests/gradle/projects/it-basic/build.gradle.kts b/integration-tests/gradle/projects/it-basic/build.gradle.kts index e5abd7e1b0c..c9302653ae4 100644 --- a/integration-tests/gradle/projects/it-basic/build.gradle.kts +++ b/integration-tests/gradle/projects/it-basic/build.gradle.kts @@ -2,6 +2,7 @@ import org.jetbrains.dokka.gradle.DokkaTask import org.jetbrains.dokka.gradle.kotlinSourceSet import org.jetbrains.dokka.base.DokkaBase import org.jetbrains.dokka.base.DokkaBaseConfiguration +import org.jetbrains.dokka.DokkaConfiguration import java.net.URL plugins { @@ -28,11 +29,20 @@ tasks.withType { moduleName.set("Basic Project") dokkaSourceSets { configureEach { + documentedVisibilities.set( + setOf(DokkaConfiguration.Visibility.PUBLIC, DokkaConfiguration.Visibility.PROTECTED) + ) suppressedFiles.from(file("src/main/kotlin/it/suppressedByPath")) perPackageOption { matchingRegex.set("it.suppressedByPackage.*") suppress.set(true) } + perPackageOption { + matchingRegex.set("it.overriddenVisibility.*") + documentedVisibilities.set( + setOf(DokkaConfiguration.Visibility.PRIVATE) + ) + } sourceLink { localDirectory.set(file("src/main")) remoteUrl.set( diff --git a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/basic/PublicClass.kt b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/basic/PublicClass.kt index fc4b36bd837..2958948cd40 100644 --- a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/basic/PublicClass.kt +++ b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/basic/PublicClass.kt @@ -6,6 +6,8 @@ import RootPackageClass /** * This class, unlike [RootPackageClass] is located in a sub-package + * + * §PUBLIC§ (marker for asserts) */ class PublicClass { /** @@ -22,6 +24,13 @@ class PublicClass { internal fun internalUndocumentedFunction(): String = "" + /** + * This function is protected and documented + */ + protected fun protectedDocumentedFunction(): String = "" + + protected fun protectedUndocumentedFunction(): String = "" + /** * This function is private and documented */ @@ -44,6 +53,13 @@ class PublicClass { val internalUndocumentedProperty: Int = 0 + /** + * This property is protected and documented + */ + val protectedDocumentedProperty: Int = 0 + + val protectedUndocumentedProperty: Int = 0 + /** * This property private and documented */ diff --git a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/internal/InternalClass.kt b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/internal/InternalClass.kt index 7d42b978f7e..6173d239197 100644 --- a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/internal/InternalClass.kt +++ b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/internal/InternalClass.kt @@ -1,7 +1,7 @@ package it.internal /** - * §INTERNAL§ + * §INTERNAL§ (marker for asserts) * This class is internal and should not be rendered */ internal class InternalClass diff --git a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt new file mode 100644 index 00000000000..230f5e0b812 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt @@ -0,0 +1,12 @@ +package it.overriddenVisibility + +/** + * Private classes and methods generally should not be visible, but [documentedVisibilities] + * are overriden for this specific package to include private code + * + * §PRIVATE§ (marker for asserts) + */ +private class VisiblePrivateClass { + private val privateVal: Int = 0 + private fun privateMethod() {} +} \ No newline at end of file diff --git a/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/protected/ProtectedClass.kt b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/protected/ProtectedClass.kt new file mode 100644 index 00000000000..ad19f1a1a54 --- /dev/null +++ b/integration-tests/gradle/projects/it-basic/src/main/kotlin/it/protected/ProtectedClass.kt @@ -0,0 +1,10 @@ +package it.protected + +/** + * Protected class should be visible because it's included in documentedVisibilities + * + * §PROTECTED§ (marker for asserts) + */ +protected class ProtectedClass { + protected fun protectedFun(): String = "protected" +} diff --git a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt index d0ca69c15fb..b468c8b2fc4 100644 --- a/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt +++ b/integration-tests/gradle/src/integrationTest/kotlin/org/jetbrains/dokka/it/gradle/BasicGradleIntegrationTest.kt @@ -152,6 +152,8 @@ class BasicGradleIntegrationTest(override val versions: BuildVersions) : Abstrac ) } assertTrue(imagesDir.resolve("custom-resource.svg").isFile) + + assertConfiguredVisibility(this) } private fun File.assertJavadocOutputDir() { @@ -178,4 +180,26 @@ class BasicGradleIntegrationTest(override val versions: BuildVersions) : Abstrac private fun File.assertJekyllOutputDir() { assertTrue(isDirectory, "Missing dokka jekyll output directory") } + + private fun assertConfiguredVisibility(outputDir: File) { + val allHtmlFiles = outputDir.allHtmlFiles().toList() + + assertContentVisibility( + contentFiles = allHtmlFiles, + documentPublic = true, + documentProtected = true, // sourceSet documentedVisibilities + documentInternal = false, + documentPrivate = true // for overriddenVisibility package + ) + + assertContainsFilePaths( + outputFiles = allHtmlFiles, + expectedFilePaths = listOf( + // documentedVisibilities is overridden for package `overriddenVisibility` specifically + // to include private code, so html pages for it are expected to have been created + Regex("it\\.overriddenVisibility/-visible-private-class/private-method\\.html"), + Regex("it\\.overriddenVisibility/-visible-private-class/private-val\\.html"), + ) + ) + } } diff --git a/integration-tests/maven/projects/it-maven/pom.xml b/integration-tests/maven/projects/it-maven/pom.xml index cfc8ba6690f..23f6548ffc9 100644 --- a/integration-tests/maven/projects/it-maven/pom.xml +++ b/integration-tests/maven/projects/it-maven/pom.xml @@ -117,6 +117,10 @@ ${project.basedir}/src/main/java + + PUBLIC + PROTECTED + false @@ -138,6 +142,13 @@ true false + + + it.overriddenVisibility.* + + PRIVATE + + diff --git a/integration-tests/maven/projects/it-maven/src/main/kotlin/it/basic/PublicClass.kt b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/basic/PublicClass.kt index 71bc7e6394e..d7a72392785 100644 --- a/integration-tests/maven/projects/it-maven/src/main/kotlin/it/basic/PublicClass.kt +++ b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/basic/PublicClass.kt @@ -2,6 +2,9 @@ package it.basic +/** + * §PUBLIC§ (marker for asserts) + */ class PublicClass { /** * This function is public and documented @@ -24,6 +27,12 @@ class PublicClass { private fun privateUndocumentedFunction(): String = "" + /** + * This function is protected and documented + */ + protected fun protectedDocumentedFunction(): String = "" + + protected fun protectedUndocumentedFunction(): String = "" /** * This property is public and documented diff --git a/integration-tests/maven/projects/it-maven/src/main/kotlin/it/internal/InternalClass.kt b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/internal/InternalClass.kt new file mode 100644 index 00000000000..6173d239197 --- /dev/null +++ b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/internal/InternalClass.kt @@ -0,0 +1,7 @@ +package it.internal + +/** + * §INTERNAL§ (marker for asserts) + * This class is internal and should not be rendered + */ +internal class InternalClass diff --git a/integration-tests/maven/projects/it-maven/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt new file mode 100644 index 00000000000..230f5e0b812 --- /dev/null +++ b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/overriddenVisibility/VisiblePrivateClass.kt @@ -0,0 +1,12 @@ +package it.overriddenVisibility + +/** + * Private classes and methods generally should not be visible, but [documentedVisibilities] + * are overriden for this specific package to include private code + * + * §PRIVATE§ (marker for asserts) + */ +private class VisiblePrivateClass { + private val privateVal: Int = 0 + private fun privateMethod() {} +} \ No newline at end of file diff --git a/integration-tests/maven/projects/it-maven/src/main/kotlin/it/protected/ProtectedClass.kt b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/protected/ProtectedClass.kt new file mode 100644 index 00000000000..ad19f1a1a54 --- /dev/null +++ b/integration-tests/maven/projects/it-maven/src/main/kotlin/it/protected/ProtectedClass.kt @@ -0,0 +1,10 @@ +package it.protected + +/** + * Protected class should be visible because it's included in documentedVisibilities + * + * §PROTECTED§ (marker for asserts) + */ +protected class ProtectedClass { + protected fun protectedFun(): String = "protected" +} diff --git a/integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt b/integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt index be70fdb4695..0b532057eb0 100644 --- a/integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt +++ b/integration-tests/maven/src/integrationTest/kotlin/org/jetbrains/dokka/it/maven/MavenIntegrationTest.kt @@ -4,10 +4,7 @@ import org.jetbrains.dokka.it.AbstractIntegrationTest import org.jetbrains.dokka.it.awaitProcessResult import org.jetbrains.dokka.it.ProcessResult import java.io.File -import kotlin.test.BeforeTest -import kotlin.test.Test -import kotlin.test.assertEquals -import kotlin.test.assertTrue +import kotlin.test.* class MavenIntegrationTest : AbstractIntegrationTest() { @@ -69,6 +66,8 @@ class MavenIntegrationTest : AbstractIntegrationTest() { } assertEquals("""/* custom stylesheet */""", stylesDir.resolve("custom-style-to-add.css").readText()) assertTrue(imagesDir.resolve("custom-resource.svg").isFile) + + assertConfiguredVisibility(projectDir) } @Test @@ -146,4 +145,26 @@ class MavenIntegrationTest : AbstractIntegrationTest() { "Expected at least one report of undocumented java code (found $amountOfUndocumentedJavaReports)" ) } + + private fun assertConfiguredVisibility(projectDir: File) { + val projectHtmlFiles = projectDir.allHtmlFiles().toList() + + assertContentVisibility( + contentFiles = projectHtmlFiles, + documentPublic = true, + documentProtected = true, // sourceSet documentedVisibilities + documentInternal = false, + documentPrivate = true // for overriddenVisibility package + ) + + assertContainsFilePaths( + outputFiles = projectHtmlFiles, + expectedFilePaths = listOf( + // documentedVisibilities is overridden for package `overriddenVisibility` specifically + // to include private code, so html pages for it are expected to have been created + Regex("it\\.overriddenVisibility/-visible-private-class/private-method\\.html"), + Regex("it\\.overriddenVisibility/-visible-private-class/private-val\\.html"), + ) + ) + } } diff --git a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt index a47adbc4683..fcf6b9ddb52 100644 --- a/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt +++ b/integration-tests/src/main/kotlin/org/jetbrains/dokka/it/AbstractIntegrationTest.kt @@ -7,7 +7,9 @@ import org.junit.runner.RunWith import org.junit.runners.JUnit4 import java.io.File import java.net.URL +import kotlin.test.assertEquals import kotlin.test.assertFalse +import kotlin.test.assertNotNull import kotlin.test.assertTrue @RunWith(JUnit4::class) @@ -110,4 +112,57 @@ abstract class AbstractIntegrationTest { "Expected all templates to be substituted" ) } + + /** + * Asserts that [contentFiles] have no pages where content contains special visibility markers, + * such as §INTERNAL§ for `internal`, §PROTECTED§ for `protected` and §PRIVATE§ for `private` modifiers + * + * This can be used to check whether actual documented code corresponds to configured documented visibility + * + * @param contentFiles any readable content file such as html/md/rst/etc + */ + protected fun assertContentVisibility( + contentFiles: List, + documentPublic: Boolean, + documentProtected: Boolean, + documentInternal: Boolean, + documentPrivate: Boolean + ) { + val hasPublic = contentFiles.any { file -> "§PUBLIC§" in file.readText() } + assertEquals(documentPublic, hasPublic, "Expected content visibility and file content do not match for public") + + val hasInternal = contentFiles.any { file -> "§INTERNAL§" in file.readText() } + assertEquals( + documentInternal, + hasInternal, + "Expected content visibility and file content do not match for internal" + ) + + val hasProtected = contentFiles.any { file -> "§PROTECTED§" in file.readText() } + assertEquals( + documentProtected, + hasProtected, + "Expected content visibility and file content do not match for protected" + ) + + val hasPrivate = contentFiles.any { file -> "§PRIVATE§" in file.readText() } + assertEquals( + documentPrivate, + hasPrivate, + "Expected content visibility and file content do not match for private" + ) + } + + /** + * Check that [outputFiles] contain specific file paths provided in [expectedFilePaths]. + * Can be used for checking whether expected folders/pages have been created. + */ + protected fun assertContainsFilePaths(outputFiles: List, expectedFilePaths: List) { + expectedFilePaths.forEach { pathRegex -> + assertNotNull( + outputFiles.any { it.absolutePath.contains(pathRegex) }, + "Expected to find a file with path regex $pathRegex, but found nothing" + ) + } + } }