mirror of
https://github.com/jlengrand/detekt.git
synced 2026-03-10 08:11:23 +00:00
Add Markdown report (#4858)
Co-authored-by: Brais Gabín <braisgabin@gmail.com>
This commit is contained in:
committed by
GitHub
parent
43ea42ca51
commit
5d49409814
@@ -10,6 +10,7 @@ dependencies {
|
||||
implementation(projects.detektMetrics)
|
||||
implementation(projects.detektPsiUtils)
|
||||
implementation(projects.detektReportHtml)
|
||||
implementation(projects.detektReportMd)
|
||||
implementation(projects.detektReportTxt)
|
||||
implementation(projects.detektReportXml)
|
||||
implementation(projects.detektReportSarif)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.gitlab.arturbosch.detekt.core.reporting
|
||||
|
||||
import io.github.detekt.report.html.HtmlOutputReport
|
||||
import io.github.detekt.report.md.MdOutputReport
|
||||
import io.github.detekt.report.sarif.SarifOutputReport
|
||||
import io.github.detekt.report.txt.TxtOutputReport
|
||||
import io.github.detekt.report.xml.XmlOutputReport
|
||||
@@ -18,6 +19,7 @@ internal fun defaultReportMapping(reportId: String) = when (reportId) {
|
||||
XmlOutputReport::class.java.simpleName -> "xml"
|
||||
HtmlOutputReport::class.java.simpleName -> "html"
|
||||
SarifOutputReport::class.java.simpleName -> "sarif"
|
||||
MdOutputReport::class.java.simpleName -> "md"
|
||||
else -> reportId
|
||||
}
|
||||
|
||||
|
||||
@@ -46,6 +46,7 @@ output-reports:
|
||||
# - 'TxtOutputReport'
|
||||
# - 'XmlOutputReport'
|
||||
# - 'HtmlOutputReport'
|
||||
# - 'MdOutputReport'
|
||||
|
||||
comments:
|
||||
active: true
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.gitlab.arturbosch.detekt.core.reporting
|
||||
|
||||
import io.github.detekt.report.html.HtmlOutputReport
|
||||
import io.github.detekt.report.md.MdOutputReport
|
||||
import io.github.detekt.report.txt.TxtOutputReport
|
||||
import io.github.detekt.report.xml.XmlOutputReport
|
||||
import io.github.detekt.test.utils.StringPrintStream
|
||||
@@ -24,6 +25,7 @@ class OutputFacadeSpec {
|
||||
val plainOutputPath = createTempFileForTest("detekt", ".txt")
|
||||
val htmlOutputPath = createTempFileForTest("detekt", ".html")
|
||||
val xmlOutputPath = createTempFileForTest("detekt", ".xml")
|
||||
val mdOutputPath = createTempFileForTest("detekt", ".md")
|
||||
|
||||
val spec = createNullLoggingSpec {
|
||||
project {
|
||||
@@ -33,6 +35,7 @@ class OutputFacadeSpec {
|
||||
report { "html" to htmlOutputPath }
|
||||
report { "txt" to plainOutputPath }
|
||||
report { "xml" to xmlOutputPath }
|
||||
report { "md" to mdOutputPath }
|
||||
}
|
||||
logging {
|
||||
outputChannel = printStream
|
||||
@@ -44,7 +47,8 @@ class OutputFacadeSpec {
|
||||
assertThat(printStream.toString()).contains(
|
||||
"Successfully generated ${TxtOutputReport().name} at $plainOutputPath",
|
||||
"Successfully generated ${XmlOutputReport().name} at $xmlOutputPath",
|
||||
"Successfully generated ${HtmlOutputReport().name} at $htmlOutputPath"
|
||||
"Successfully generated ${HtmlOutputReport().name} at $htmlOutputPath",
|
||||
"Successfully generated ${MdOutputReport().name} at $mdOutputPath"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package io.gitlab.arturbosch.detekt.core.reporting
|
||||
|
||||
import io.github.detekt.report.html.HtmlOutputReport
|
||||
import io.github.detekt.report.md.MdOutputReport
|
||||
import io.github.detekt.report.txt.TxtOutputReport
|
||||
import io.github.detekt.report.xml.XmlOutputReport
|
||||
import io.github.detekt.test.utils.resourceAsPath
|
||||
@@ -28,11 +29,12 @@ class OutputReportsSpec {
|
||||
report { "txt" to Paths.get("/tmp/path2") }
|
||||
report { reportUnderTest to Paths.get("/tmp/path3") }
|
||||
report { "html" to Paths.get("D:_Gradle\\xxx\\xxx\\build\\reports\\detekt\\detekt.html") }
|
||||
report { "md" to Paths.get("/tmp/path4") }
|
||||
}.build().reports.toList()
|
||||
|
||||
@Test
|
||||
fun `should parse multiple report entries`() {
|
||||
assertThat(reports).hasSize(4)
|
||||
assertThat(reports).hasSize(5)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -66,6 +68,13 @@ class OutputReportsSpec {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `it should properly parse MD report entry`() {
|
||||
val mdRepot = reports[4]
|
||||
assertThat(mdRepot.type).isEqualTo(defaultReportMapping(MdOutputReport::class.java.simpleName))
|
||||
assertThat(mdRepot.path).isEqualTo(Paths.get("/tmp/path4"))
|
||||
}
|
||||
|
||||
@Nested
|
||||
inner class `default report ids` {
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ dependencies {
|
||||
implementation(projects.detektRulesEmpty)
|
||||
implementation(projects.detektFormatting)
|
||||
implementation(projects.detektCli)
|
||||
implementation(projects.detektUtils)
|
||||
implementation(libs.jcommander)
|
||||
|
||||
testImplementation(projects.detektCore)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package io.gitlab.arturbosch.detekt.generator
|
||||
|
||||
import io.github.detekt.utils.yaml
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.RuleSetPage
|
||||
import io.gitlab.arturbosch.detekt.generator.out.MarkdownWriter
|
||||
import io.gitlab.arturbosch.detekt.generator.out.PropertiesWriter
|
||||
import io.gitlab.arturbosch.detekt.generator.out.YamlWriter
|
||||
import io.gitlab.arturbosch.detekt.generator.out.yaml
|
||||
import io.gitlab.arturbosch.detekt.generator.printer.DeprecatedPrinter
|
||||
import io.gitlab.arturbosch.detekt.generator.printer.RuleSetPagePrinter
|
||||
import io.gitlab.arturbosch.detekt.generator.printer.defaultconfig.ConfigPrinter
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.collection
|
||||
|
||||
import io.github.detekt.utils.YamlNode
|
||||
import io.github.detekt.utils.keyValue
|
||||
import io.github.detekt.utils.list
|
||||
import io.github.detekt.utils.listOfMaps
|
||||
import io.gitlab.arturbosch.detekt.api.ValuesWithReason
|
||||
import io.gitlab.arturbosch.detekt.generator.out.YamlNode
|
||||
import io.gitlab.arturbosch.detekt.generator.out.keyValue
|
||||
import io.gitlab.arturbosch.detekt.generator.out.list
|
||||
import io.gitlab.arturbosch.detekt.generator.out.listOfMaps
|
||||
|
||||
sealed interface DefaultValue {
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.printer
|
||||
|
||||
import io.github.detekt.utils.bold
|
||||
import io.github.detekt.utils.code
|
||||
import io.github.detekt.utils.crossOut
|
||||
import io.github.detekt.utils.description
|
||||
import io.github.detekt.utils.h4
|
||||
import io.github.detekt.utils.item
|
||||
import io.github.detekt.utils.list
|
||||
import io.github.detekt.utils.markdown
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.Configuration
|
||||
import io.gitlab.arturbosch.detekt.generator.out.bold
|
||||
import io.gitlab.arturbosch.detekt.generator.out.code
|
||||
import io.gitlab.arturbosch.detekt.generator.out.crossOut
|
||||
import io.gitlab.arturbosch.detekt.generator.out.description
|
||||
import io.gitlab.arturbosch.detekt.generator.out.h4
|
||||
import io.gitlab.arturbosch.detekt.generator.out.item
|
||||
import io.gitlab.arturbosch.detekt.generator.out.list
|
||||
import io.gitlab.arturbosch.detekt.generator.out.markdown
|
||||
|
||||
internal object RuleConfigurationPrinter : DocumentationPrinter<List<Configuration>> {
|
||||
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.printer
|
||||
|
||||
import io.github.detekt.utils.MarkdownContent
|
||||
import io.github.detekt.utils.bold
|
||||
import io.github.detekt.utils.codeBlock
|
||||
import io.github.detekt.utils.h3
|
||||
import io.github.detekt.utils.h4
|
||||
import io.github.detekt.utils.markdown
|
||||
import io.github.detekt.utils.paragraph
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.Active
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.Rule
|
||||
import io.gitlab.arturbosch.detekt.generator.out.MarkdownContent
|
||||
import io.gitlab.arturbosch.detekt.generator.out.bold
|
||||
import io.gitlab.arturbosch.detekt.generator.out.codeBlock
|
||||
import io.gitlab.arturbosch.detekt.generator.out.h3
|
||||
import io.gitlab.arturbosch.detekt.generator.out.h4
|
||||
import io.gitlab.arturbosch.detekt.generator.out.markdown
|
||||
import io.gitlab.arturbosch.detekt.generator.out.paragraph
|
||||
|
||||
internal object RulePrinter : DocumentationPrinter<Rule> {
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.printer
|
||||
|
||||
import io.github.detekt.utils.markdown
|
||||
import io.github.detekt.utils.paragraph
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.RuleSetPage
|
||||
import io.gitlab.arturbosch.detekt.generator.out.markdown
|
||||
import io.gitlab.arturbosch.detekt.generator.out.paragraph
|
||||
|
||||
object RuleSetPagePrinter : DocumentationPrinter<RuleSetPage> {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.printer.defaultconfig
|
||||
|
||||
import io.github.detekt.utils.yaml
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.RuleSetPage
|
||||
import io.gitlab.arturbosch.detekt.generator.out.yaml
|
||||
import io.gitlab.arturbosch.detekt.generator.printer.DocumentationPrinter
|
||||
|
||||
object ConfigPrinter : DocumentationPrinter<List<RuleSetPage>> {
|
||||
@@ -81,5 +81,6 @@ object ConfigPrinter : DocumentationPrinter<List<RuleSetPage>> {
|
||||
# - 'TxtOutputReport'
|
||||
# - 'XmlOutputReport'
|
||||
# - 'HtmlOutputReport'
|
||||
# - 'MdOutputReport'
|
||||
""".trimIndent()
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.printer.defaultconfig
|
||||
|
||||
import io.github.detekt.utils.YamlNode
|
||||
import io.github.detekt.utils.keyValue
|
||||
import io.github.detekt.utils.node
|
||||
import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.Configuration
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.Rule
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.RuleSetPage
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.RuleSetProvider
|
||||
import io.gitlab.arturbosch.detekt.generator.out.YamlNode
|
||||
import io.gitlab.arturbosch.detekt.generator.out.keyValue
|
||||
import io.gitlab.arturbosch.detekt.generator.out.node
|
||||
|
||||
internal fun YamlNode.printRuleSetPage(ruleSetPage: RuleSetPage) {
|
||||
printRuleSet(ruleSetPage.ruleSet, ruleSetPage.rules)
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.printer.defaultconfig
|
||||
|
||||
import io.github.detekt.utils.yaml
|
||||
import io.gitlab.arturbosch.detekt.api.Config
|
||||
import io.gitlab.arturbosch.detekt.api.valuesWithReason
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.Active
|
||||
@@ -8,7 +9,6 @@ import io.gitlab.arturbosch.detekt.generator.collection.DefaultValue
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.Inactive
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.Rule
|
||||
import io.gitlab.arturbosch.detekt.generator.collection.RuleSetProvider
|
||||
import io.gitlab.arturbosch.detekt.generator.out.yaml
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
13
detekt-report-md/build.gradle.kts
Normal file
13
detekt-report-md/build.gradle.kts
Normal file
@@ -0,0 +1,13 @@
|
||||
plugins {
|
||||
id("module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(projects.detektMetrics)
|
||||
implementation(projects.detektApi)
|
||||
implementation(projects.detektUtils)
|
||||
|
||||
testImplementation(testFixtures(projects.detektApi))
|
||||
testImplementation(libs.mockk)
|
||||
testImplementation(libs.assertj)
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package io.github.detekt.report.md
|
||||
|
||||
import io.github.detekt.metrics.ComplexityReportGenerator
|
||||
import io.github.detekt.psi.toUnifiedString
|
||||
import io.github.detekt.utils.MarkdownContent
|
||||
import io.github.detekt.utils.codeBlock
|
||||
import io.github.detekt.utils.emptyLine
|
||||
import io.github.detekt.utils.h1
|
||||
import io.github.detekt.utils.h2
|
||||
import io.github.detekt.utils.h3
|
||||
import io.github.detekt.utils.item
|
||||
import io.github.detekt.utils.list
|
||||
import io.github.detekt.utils.markdown
|
||||
import io.github.detekt.utils.paragraph
|
||||
import io.gitlab.arturbosch.detekt.api.Detektion
|
||||
import io.gitlab.arturbosch.detekt.api.Finding
|
||||
import io.gitlab.arturbosch.detekt.api.OutputReport
|
||||
import io.gitlab.arturbosch.detekt.api.ProjectMetric
|
||||
import io.gitlab.arturbosch.detekt.api.SourceLocation
|
||||
import io.gitlab.arturbosch.detekt.api.internal.whichDetekt
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneOffset
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Locale
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
private const val DETEKT_WEBSITE_BASE_URL = "https://detekt.dev"
|
||||
|
||||
private const val EXTRA_LINES_IN_SNIPPET = 3
|
||||
|
||||
/**
|
||||
* Contains rule violations in Markdown format report.
|
||||
* [See](https://detekt.dev/docs/introduction/configurations/#output-reports)
|
||||
*/
|
||||
class MdOutputReport : OutputReport() {
|
||||
override val ending: String = "md"
|
||||
|
||||
override val name = "Markdown report"
|
||||
|
||||
override fun render(detektion: Detektion) = markdown {
|
||||
h1 { "detekt" }
|
||||
|
||||
h2 { "Metrics" }
|
||||
renderMetrics(detektion.metrics)
|
||||
|
||||
h2 { "Complexity Report" }
|
||||
renderComplexity(getComplexityMetrics(detektion))
|
||||
|
||||
renderFindings(detektion.findings)
|
||||
emptyLine()
|
||||
|
||||
paragraph {
|
||||
val detektLink = link("detekt version ${renderVersion()}", "$DETEKT_WEBSITE_BASE_URL/")
|
||||
"generated with $detektLink on ${renderDate()}"
|
||||
}
|
||||
}
|
||||
|
||||
private fun renderVersion(): String = whichDetekt() ?: "unknown"
|
||||
|
||||
private fun renderDate(): String {
|
||||
val formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
||||
return "${OffsetDateTime.now(ZoneOffset.UTC).format(formatter)} UTC"
|
||||
}
|
||||
|
||||
private fun getComplexityMetrics(detektion: Detektion): List<String> {
|
||||
return ComplexityReportGenerator.create(detektion).generate().orEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarkdownContent.renderMetrics(metrics: Collection<ProjectMetric>) {
|
||||
list {
|
||||
metrics.forEach { item { "%,d ${it.type}".format(Locale.US, it.value) } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarkdownContent.renderComplexity(complexityReport: List<String>) {
|
||||
list {
|
||||
complexityReport.forEach { item { it.trim() } }
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarkdownContent.renderGroup(group: String, findings: List<Finding>) {
|
||||
findings
|
||||
.groupBy { it.id }
|
||||
.toList()
|
||||
.sortedBy { (rule, _) -> rule }
|
||||
.forEach { (rule, ruleFindings) ->
|
||||
renderRule(rule, group, ruleFindings)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarkdownContent.renderRule(rule: String, group: String, findings: List<Finding>) {
|
||||
h3 { "$group, $rule (%,d)".format(Locale.US, findings.size) }
|
||||
paragraph { (findings.first().issue.description) }
|
||||
|
||||
paragraph {
|
||||
link(
|
||||
"Documentation",
|
||||
"$DETEKT_WEBSITE_BASE_URL/docs/rules/${group.toLowerCase(Locale.US)}#${rule.toLowerCase(Locale.US)}"
|
||||
)
|
||||
}
|
||||
|
||||
list {
|
||||
findings
|
||||
.sortedWith(compareBy({ it.file }, { it.location.source.line }, { it.location.source.column }))
|
||||
.forEach {
|
||||
item { renderFinding(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarkdownContent.renderFindings(findings: Map<String, List<Finding>>) {
|
||||
val total = findings.values
|
||||
.asSequence()
|
||||
.map { it.size }
|
||||
.fold(0) { a, b -> a + b }
|
||||
|
||||
h2 { "Findings (%,d)".format(Locale.US, total) }
|
||||
|
||||
findings
|
||||
.filter { it.value.isNotEmpty() }
|
||||
.toList()
|
||||
.sortedBy { (group, _) -> group }
|
||||
.forEach { (group, groupFindings) ->
|
||||
renderGroup(group, groupFindings)
|
||||
}
|
||||
}
|
||||
|
||||
private fun MarkdownContent.renderFinding(finding: Finding): String {
|
||||
val filePath = finding.location.filePath.relativePath ?: finding.location.filePath.absolutePath
|
||||
val location = "${filePath.toUnifiedString()}:${finding.location.source.line}:${finding.location.source.column}"
|
||||
|
||||
val message = if (finding.message.isNotEmpty()) {
|
||||
codeBlock("") { finding.message }
|
||||
} else { "" }
|
||||
|
||||
val psiFile = finding.entity.ktElement?.containingFile
|
||||
val snippet = if (psiFile != null) {
|
||||
val lineSequence = psiFile.text.splitToSequence('\n')
|
||||
snippetCode(lineSequence, finding.startPosition)
|
||||
} else { "" }
|
||||
|
||||
return "$location\n$message\n$snippet"
|
||||
}
|
||||
|
||||
private fun MarkdownContent.snippetCode(lines: Sequence<String>, location: SourceLocation): String {
|
||||
val dropLineCount = max(location.line - 1 - EXTRA_LINES_IN_SNIPPET, 0)
|
||||
val takeLineCount = EXTRA_LINES_IN_SNIPPET + 1 + min(location.line - 1, EXTRA_LINES_IN_SNIPPET)
|
||||
var currentLineNumber = dropLineCount + 1
|
||||
var text = ""
|
||||
|
||||
val lineNoSpace = (currentLineNumber + takeLineCount).toString().length
|
||||
|
||||
lines
|
||||
.drop(dropLineCount)
|
||||
.take(takeLineCount)
|
||||
.forEach { line ->
|
||||
val lineNo = ("$currentLineNumber ").take(lineNoSpace)
|
||||
text += "$lineNo $line\n"
|
||||
|
||||
if (currentLineNumber == location.line) {
|
||||
val positions = currentLineNumber.toString().length
|
||||
val lineErr = "!".repeat(positions) + " ".repeat(location.column + lineNoSpace - positions)
|
||||
text += "$lineErr^ error\n"
|
||||
}
|
||||
currentLineNumber++
|
||||
}
|
||||
|
||||
return codeBlock("kotlin") { text }
|
||||
}
|
||||
|
||||
internal fun MarkdownContent.link(text: String, url: String) = "[$text]($url)"
|
||||
@@ -0,0 +1 @@
|
||||
io.github.detekt.report.md.MdOutputReport
|
||||
@@ -0,0 +1,224 @@
|
||||
package io.github.detekt.report.md
|
||||
|
||||
import io.github.detekt.metrics.CognitiveComplexity
|
||||
import io.github.detekt.metrics.processors.commentLinesKey
|
||||
import io.github.detekt.metrics.processors.complexityKey
|
||||
import io.github.detekt.metrics.processors.linesKey
|
||||
import io.github.detekt.metrics.processors.logicalLinesKey
|
||||
import io.github.detekt.metrics.processors.sourceLinesKey
|
||||
import io.gitlab.arturbosch.detekt.api.Detektion
|
||||
import io.gitlab.arturbosch.detekt.api.Finding
|
||||
import io.gitlab.arturbosch.detekt.api.ProjectMetric
|
||||
import io.gitlab.arturbosch.detekt.api.internal.whichDetekt
|
||||
import io.gitlab.arturbosch.detekt.test.TestDetektion
|
||||
import io.gitlab.arturbosch.detekt.test.createEntity
|
||||
import io.gitlab.arturbosch.detekt.test.createFinding
|
||||
import io.gitlab.arturbosch.detekt.test.createIssue
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.jetbrains.kotlin.com.intellij.psi.PsiFile
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.junit.jupiter.api.Test
|
||||
|
||||
class MdOutputReportSpec {
|
||||
private val mdReport = MdOutputReport()
|
||||
private val detektion = createTestDetektionWithMultipleSmells()
|
||||
private val result = mdReport.render(detektion)
|
||||
|
||||
@Test
|
||||
fun `renders Markdown structure correctly`() {
|
||||
assertThat(result).contains("Metrics")
|
||||
assertThat(result).contains("Complexity Report")
|
||||
assertThat(result).contains("Findings")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `contains zero findings`() {
|
||||
val result = mdReport.render(TestDetektion())
|
||||
|
||||
assertThat(result).contains("Findings (0)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `contains the total number of findings`() {
|
||||
assertThat(result).contains("Findings (3)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the 'generated with' text correctly`() {
|
||||
val header = "generated with [detekt version ${whichDetekt()}](https://detekt.dev/) on "
|
||||
|
||||
assertThat(result).contains(header)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right file locations`() {
|
||||
assertThat(result).contains("src/main/com/sample/Sample1.kt:9:17")
|
||||
assertThat(result).contains("src/main/com/sample/Sample2.kt:13:17")
|
||||
assertThat(result).contains("src/main/com/sample/Sample3.kt:14:16")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right number of issues per rule`() {
|
||||
assertThat(result).contains("id_a (2)")
|
||||
assertThat(result).contains("id_b (1)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right violation messages for the rules`() {
|
||||
assertThat(result).contains("Message finding 1")
|
||||
assertThat(result).contains("Message finding 2")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right violation description for the rules`() {
|
||||
assertThat(result).contains("Description id_a")
|
||||
assertThat(result).contains("Description id_b")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `renders the right documentation links for the rules`() {
|
||||
val detektion = object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = mapOf(
|
||||
"Style" to listOf(
|
||||
createFinding(createIssue("ValCouldBeVar"), createEntity(""))
|
||||
),
|
||||
"empty" to listOf(
|
||||
createFinding(createIssue("EmptyBody"), createEntity("")),
|
||||
createFinding(createIssue("EmptyIf"), createEntity(""))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val result = mdReport.render(detektion)
|
||||
assertThat(result).contains("[Documentation](https://detekt.dev/docs/rules/style#valcouldbevar)")
|
||||
assertThat(result).contains("[Documentation](https://detekt.dev/docs/rules/empty#emptybody)")
|
||||
assertThat(result).contains("[Documentation](https://detekt.dev/docs/rules/empty#emptyif)")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `asserts that the generated HTML is the same even if we change the order of the findings`() {
|
||||
val findings = findings()
|
||||
val reversedFindings = findings
|
||||
.reversedArray()
|
||||
.map { (section, findings) -> section to findings.asReversed() }
|
||||
.toTypedArray()
|
||||
|
||||
val firstDetektion = createMdDetektion(*findings)
|
||||
val secondDetektion = createMdDetektion(*reversedFindings)
|
||||
|
||||
val firstReport = mdReport.render(firstDetektion)
|
||||
val secondReport = mdReport.render(secondDetektion)
|
||||
|
||||
assertThat(firstReport).isEqualTo(secondReport)
|
||||
}
|
||||
}
|
||||
|
||||
private fun mockKtElement(): KtElement {
|
||||
val ktElementMock = mockk<KtElement>()
|
||||
val psiFileMock = mockk<PsiFile>()
|
||||
val code = """
|
||||
package com.example.test
|
||||
|
||||
import io.github.*
|
||||
|
||||
class Test() {
|
||||
val greeting: String = "Hello, World!"
|
||||
|
||||
init {
|
||||
println(greetings)
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
println(greetings)
|
||||
return this
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
|
||||
every { psiFileMock.text } returns code
|
||||
every { ktElementMock.containingFile } returns psiFileMock
|
||||
return ktElementMock
|
||||
}
|
||||
|
||||
private fun createTestDetektionWithMultipleSmells(): Detektion {
|
||||
val entity1 = createEntity(
|
||||
path = "src/main/com/sample/Sample1.kt",
|
||||
position = 9 to 17,
|
||||
text = 17..20,
|
||||
ktElement = mockKtElement(),
|
||||
basePath = "/Users/tester/detekt/"
|
||||
)
|
||||
val entity2 = createEntity(
|
||||
path = "src/main/com/sample/Sample2.kt",
|
||||
ktElement = mockKtElement(),
|
||||
position = 13 to 17,
|
||||
basePath = "/Users/tester/detekt/"
|
||||
)
|
||||
val entity3 = createEntity(
|
||||
path = "src/main/com/sample/Sample3.kt",
|
||||
position = 14 to 16,
|
||||
ktElement = mockKtElement(),
|
||||
basePath = "/Users/tester/detekt/"
|
||||
)
|
||||
|
||||
val issueA = createIssue("id_a")
|
||||
val issueB = createIssue("id_b")
|
||||
|
||||
return createMdDetektion(
|
||||
"Section-1" to listOf(
|
||||
createFinding(issueA, entity1, "Message finding 1"),
|
||||
createFinding(issueA, entity2, "Message finding 2")
|
||||
),
|
||||
"Section-2" to listOf(
|
||||
createFinding(issueB, entity3, "")
|
||||
)
|
||||
).also {
|
||||
it.addData(complexityKey, 10)
|
||||
it.addData(CognitiveComplexity.KEY, 10)
|
||||
it.addData(sourceLinesKey, 20)
|
||||
it.addData(logicalLinesKey, 10)
|
||||
it.addData(commentLinesKey, 2)
|
||||
it.addData(linesKey, 2222)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMdDetektion(vararg findingPairs: Pair<String, List<Finding>>): Detektion {
|
||||
return object : TestDetektion() {
|
||||
override val findings: Map<String, List<Finding>> = findingPairs.toMap()
|
||||
|
||||
override val metrics: Collection<ProjectMetric> = listOf(
|
||||
ProjectMetric("M1", 10_000),
|
||||
ProjectMetric("M2", 2)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun findings(): Array<Pair<String, List<Finding>>> {
|
||||
val issueA = createIssue("id_a")
|
||||
val issueB = createIssue("id_b")
|
||||
val issueC = createIssue("id_c")
|
||||
|
||||
val entity1 = createEntity("src/main/com/sample/Sample1.kt", 11 to 5)
|
||||
val entity2 = createEntity("src/main/com/sample/Sample1.kt", 22 to 2)
|
||||
val entity3 = createEntity("src/main/com/sample/Sample1.kt", 11 to 2)
|
||||
val entity4 = createEntity("src/main/com/sample/Sample2.kt", 1 to 1)
|
||||
|
||||
return arrayOf(
|
||||
"Section 1" to listOf(
|
||||
createFinding(issueA, entity1),
|
||||
createFinding(issueA, entity2),
|
||||
createFinding(issueA, entity3),
|
||||
createFinding(issueA, entity4),
|
||||
createFinding(issueB, entity2),
|
||||
createFinding(issueB, entity1),
|
||||
createFinding(issueB, entity4)
|
||||
),
|
||||
"Section 2" to listOf(
|
||||
createFinding(issueB, entity3),
|
||||
createFinding(issueC, entity1),
|
||||
createFinding(issueC, entity2)
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -1,3 +1,7 @@
|
||||
plugins {
|
||||
id("module")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation(libs.assertj)
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
@file:Suppress("detekt.TooManyFunctions")
|
||||
|
||||
package io.gitlab.arturbosch.detekt.generator.out
|
||||
package io.github.detekt.utils
|
||||
|
||||
sealed class Markdown(open var content: String = "") {
|
||||
fun append(value: String) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.out
|
||||
package io.github.detekt.utils
|
||||
|
||||
sealed class YML(open val indent: Int = 0, open var content: String = "") {
|
||||
fun append(value: String) {
|
||||
@@ -1,4 +1,4 @@
|
||||
package io.gitlab.arturbosch.detekt.generator.out
|
||||
package io.github.detekt.utils
|
||||
|
||||
import org.assertj.core.api.Assertions.assertThat
|
||||
import org.junit.jupiter.api.Nested
|
||||
@@ -17,6 +17,7 @@ include("detekt-metrics")
|
||||
include("detekt-parser")
|
||||
include("detekt-psi-utils")
|
||||
include("detekt-report-html")
|
||||
include("detekt-report-md")
|
||||
include("detekt-report-sarif")
|
||||
include("detekt-report-txt")
|
||||
include("detekt-report-xml")
|
||||
|
||||
Reference in New Issue
Block a user