-
-
Notifications
You must be signed in to change notification settings - Fork 757
/
HtmlOutputReportSpec.kt
277 lines (233 loc) · 11.4 KB
/
HtmlOutputReportSpec.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
package io.github.detekt.report.html
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.github.detekt.test.utils.createTempFileForTest
import io.github.detekt.test.utils.internal.FakeKtElement
import io.github.detekt.test.utils.internal.FakePsiFile
import io.github.detekt.test.utils.readResourceContent
import io.gitlab.arturbosch.detekt.api.Detektion
import io.gitlab.arturbosch.detekt.api.Issue
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.createIssue
import io.gitlab.arturbosch.detekt.test.createLocation
import io.gitlab.arturbosch.detekt.test.createRuleInfo
import org.assertj.core.api.Assertions.assertThat
import org.jetbrains.kotlin.psi.KtElement
import org.junit.jupiter.api.Test
import java.nio.file.Path
import kotlin.io.path.Path
import kotlin.io.path.absolute
import kotlin.io.path.invariantSeparatorsPathString
import kotlin.io.path.writeText
class HtmlOutputReportSpec {
private val htmlReport = HtmlOutputReport()
@Test
fun `renders the HTML headers correctly`() {
val result = htmlReport.render(TestDetektion())
assertThat(result).startsWith("<!DOCTYPE html>\n<html lang=\"en\">")
assertThat(result).endsWith("</html>\n")
assertThat(result).contains("<h2>Metrics</h2>")
assertThat(result).contains("<h2>Complexity Report</h2>")
assertThat(result).contains("<h2>Issues</h2>")
}
@Test
fun `renders the 'generated with' text correctly`() {
val version = whichDetekt()
val header =
"""generated with <a href="https://detekt.dev/">detekt version $version</a> on """
val result = htmlReport.render(TestDetektion())
assertThat(result).contains(header)
assertThat(result).doesNotContain("@@@date@@@")
}
@Test
fun `contains the total number of issues`() {
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
assertThat(result).contains("Total: 3")
}
@Test
fun `contains no issues`() {
val detektion = TestDetektion()
val result = htmlReport.render(detektion)
assertThat(result).contains("Total: 0")
}
@Test
fun `renders the right file locations`() {
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
val root = Path("/").absolute().invariantSeparatorsPathString
assertThat(result).contains("<span class=\"location\">${root}src/main/com/sample/Sample1.kt:11:1</span>")
assertThat(result).contains("<span class=\"location\">${root}src/main/com/sample/Sample2.kt:22:2</span>")
assertThat(result).contains("<span class=\"location\">${root}src/main/com/sample/Sample3.kt:33:3</span>")
}
@Test
fun `renders the right file locations for relative paths`() {
val htmlReport = HtmlOutputReport()
htmlReport.basePath = Path("Users/tester/detekt/").absolute()
val result = htmlReport.render(createTestDetektionFromRelativePath())
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample1.kt:11:1</span>")
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample2.kt:22:2</span>")
assertThat(result).contains("<span class=\"location\">src/main/com/sample/Sample3.kt:33:3</span>")
}
@Test
fun `renders the right number of issues per rule`() {
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
assertThat(result).contains("<span class=\"rule\">id_a: 2 </span>")
assertThat(result).contains("<span class=\"rule\">id_b: 1 </span>")
}
@Test
fun `renders the right violation messages for the rules`() {
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
assertThat(result).contains("<span class=\"message\">Issue message 1</span>")
assertThat(result).contains("<span class=\"message\">Issue message 2</span>")
assertThat(result).doesNotContain("<span class=\"message\"></span>")
}
@Test
fun `renders the right violation description for the rules`() {
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
assertThat(result).contains("<span class=\"description\">Description id_a</span>")
assertThat(result).contains("<span class=\"description\">Description id_b</span>")
}
@Test
fun `renders the right documentation links for the rules`() {
val detektion = TestDetektion(
createIssue(createRuleInfo("ValCouldBeVar", "Style")),
createIssue(createRuleInfo("EmptyBody", "empty")),
createIssue(createRuleInfo("EmptyIf", "empty")),
)
val result = htmlReport.render(detektion)
assertThat(result).contains("<a href=\"https://detekt.dev/docs/rules/style#valcouldbevar\">Documentation</a>")
assertThat(result).contains("<a href=\"https://detekt.dev/docs/rules/empty#emptybody\">Documentation</a>")
assertThat(result).contains("<a href=\"https://detekt.dev/docs/rules/empty#emptyif\">Documentation</a>")
}
@Test
fun `renders a metric report correctly`() {
val detektion = TestDetektion(
metrics = listOf(ProjectMetric("M1", 10_000), ProjectMetric("M2", 2))
)
val result = htmlReport.render(detektion)
assertThat(result).contains("<li>10,000 M1</li>")
assertThat(result).contains("<li>2 M2</li>")
}
@Test
fun `renders the complexity report correctly`() {
val detektion = TestDetektion()
detektion.putUserData(complexityKey, 10)
detektion.putUserData(CognitiveComplexity.KEY, 10)
detektion.putUserData(sourceLinesKey, 20)
detektion.putUserData(logicalLinesKey, 10)
detektion.putUserData(commentLinesKey, 2)
detektion.putUserData(linesKey, 2222)
val result = htmlReport.render(detektion)
assertThat(result).contains("<li>2,222 lines of code (loc)</li>")
assertThat(result).contains("<li>20 source lines of code (sloc)</li>")
assertThat(result).contains("<li>10 logical lines of code (lloc)</li>")
}
@Test
fun `renders a blank complexity report correctly`() {
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
assertThat(result).contains("<h2>Complexity Report</h2>\n\n<div>\n <ul></ul>\n</div>")
}
@Test
fun `asserts that the generated HTML is the same as expected`() {
val expectedString = readResourceContent("HtmlOutputFormatTest.html")
.replace("<PREFIX>", Path("/").absolute().invariantSeparatorsPathString)
val expected = createTempFileForTest("expected-report", ".html").apply { writeText(expectedString) }
val result = htmlReport.render(createTestDetektionWithMultipleSmells())
.replace(generatedRegex, REPLACEMENT)
val actual = createTempFileForTest("actual-report", ".html").apply { writeText(result) }
assertThat(actual).hasSameTextualContentAs(expected)
}
@Test
fun `asserts that the generated HTML is the same even if we change the order of the issues`() {
val issues = issues()
val reversedIssues = issues.reversedArray()
val firstReport = createReportWithIssues(*issues)
val secondReport = createReportWithIssues(*reversedIssues)
assertThat(firstReport).hasSameTextualContentAs(secondReport)
}
}
private fun fakeKtElement(): KtElement {
val code = "\n\n\n\n\n\n\n\n\n\nabcdef\nhi\n"
val fakePsiFile = FakePsiFile(code)
val fakeKtElement = FakeKtElement(fakePsiFile)
return fakeKtElement
}
private fun createTestDetektionWithMultipleSmells(): Detektion {
val entity1 = createEntity(
location = createLocation("src/main/com/sample/Sample1.kt", position = 11 to 1, text = 10..14),
ktElement = fakeKtElement()
)
val entity2 = createEntity(location = createLocation("src/main/com/sample/Sample2.kt", position = 22 to 2))
val entity3 = createEntity(location = createLocation("src/main/com/sample/Sample3.kt", position = 33 to 3))
return TestDetektion(
createIssue(createRuleInfo("id_a", "RuleSet1"), entity1, "Issue message 1"),
createIssue(createRuleInfo("id_a", "RuleSet1"), entity2, "Issue message 2"),
createIssue(createRuleInfo("id_b", "RuleSet2"), entity3, "Issue message 3"),
)
}
private fun createTestDetektionFromRelativePath(): Detektion {
val basePath = "${System.getProperty("user.dir")}/Users/tester/detekt/"
val entity1 = createEntity(
location = createLocation(
path = "src/main/com/sample/Sample1.kt",
basePath = basePath,
position = 11 to 1,
text = 10..14,
),
ktElement = fakeKtElement(),
)
val entity2 = createEntity(
location = createLocation(
path = "src/main/com/sample/Sample2.kt",
basePath = basePath,
position = 22 to 2,
)
)
val entity3 = createEntity(
location = createLocation(
path = "src/main/com/sample/Sample3.kt",
basePath = basePath,
position = 33 to 3,
)
)
return TestDetektion(
createIssue(createRuleInfo("id_a", "RuleSet1"), entity1, "Issue message 1"),
createIssue(createRuleInfo("id_a", "RuleSet1"), entity2, "Issue message 2"),
createIssue(createRuleInfo("id_b", "RuleSet2"), entity3, "Issue message 3"),
)
}
private fun issues(): Array<Issue> {
val entity1 = createEntity(location = createLocation("src/main/com/sample/Sample1.kt", position = 11 to 5))
val entity2 = createEntity(location = createLocation("src/main/com/sample/Sample1.kt", position = 22 to 2))
val entity3 = createEntity(location = createLocation("src/main/com/sample/Sample1.kt", position = 11 to 2))
val entity4 = createEntity(location = createLocation("src/main/com/sample/Sample2.kt", position = 1 to 1))
return arrayOf(
createIssue(createRuleInfo("id_a", "RuleSet1"), entity1),
createIssue(createRuleInfo("id_a", "RuleSet1"), entity2),
createIssue(createRuleInfo("id_a", "RuleSet1"), entity3),
createIssue(createRuleInfo("id_a", "RuleSet1"), entity4),
createIssue(createRuleInfo("id_b", "RuleSet1"), entity2),
createIssue(createRuleInfo("id_b", "RuleSet1"), entity1),
createIssue(createRuleInfo("id_b", "RuleSet1"), entity4),
createIssue(createRuleInfo("id_b", "RuleSet2"), entity3),
createIssue(createRuleInfo("id_c", "RuleSet2"), entity1),
createIssue(createRuleInfo("id_c", "RuleSet2"), entity2),
)
}
private val generatedRegex = """^generated\swith.*$""".toRegex(RegexOption.MULTILINE)
private const val REPLACEMENT = "generated with..."
private fun createReportWithIssues(vararg issues: Issue): Path {
val htmlReport = HtmlOutputReport()
val detektion = TestDetektion(*issues)
var result = htmlReport.render(detektion)
result = generatedRegex.replace(result, REPLACEMENT)
val reportPath = createTempFileForTest("report", ".html")
reportPath.writeText(result)
return reportPath
}