blob: e1026a97073418e23d2f2c1a76e7f930cab276c8 (
plain)
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
|
package matchers.content
import assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.isEqualTo
import assertk.assertions.matches
import org.jetbrains.dokka.model.withDescendants
import org.jetbrains.dokka.pages.*
import org.jetbrains.dokka.test.tools.matchers.content.*
import kotlin.reflect.KClass
// entry point:
fun ContentNode.assertNode(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) {
val matcher = ContentMatcherBuilder(ContentComposite::class).apply(block).build()
try {
matcher.tryMatch(this)
} catch (e: MatcherError) {
throw AssertionError(e.message + "\n" + matcher.toDebugString(e.anchor, e.anchorAfter))
}
}
// DSL:
@DslMarker
annotation class ContentMatchersDsl
@ContentMatchersDsl
class ContentMatcherBuilder<T : ContentComposite> @PublishedApi internal constructor(private val kclass: KClass<T>) {
@PublishedApi
internal val children = mutableListOf<MatcherElement>()
internal val assertions = mutableListOf<T.() -> Unit>()
fun build() = CompositeMatcher(kclass, children) { assertions.forEach { it() } }
// part of DSL that cannot be defined as an extension
operator fun String.unaryPlus() {
children += TextMatcher(this)
}
}
fun <T : ContentComposite> ContentMatcherBuilder<T>.check(assertion: T.() -> Unit) {
assertions += assertion
}
private val ContentComposite.extractedText
get() = withDescendants().filterIsInstance<ContentText>().joinToString(separator = "") { it.text }
fun <T : ContentComposite> ContentMatcherBuilder<T>.hasExactText(expected: String) {
assertions += {
assertThat(this::extractedText).isEqualTo(expected)
}
}
fun <T : ContentComposite> ContentMatcherBuilder<T>.textMatches(pattern: Regex) {
assertions += {
assertThat(this::extractedText).matches(pattern)
}
}
inline fun <reified S : ContentComposite> ContentMatcherBuilder<*>.composite(
block: ContentMatcherBuilder<S>.() -> Unit
) {
children += ContentMatcherBuilder(S::class).apply(block).build()
}
inline fun <reified S : ContentNode> ContentMatcherBuilder<*>.node(noinline assertions: S.() -> Unit = {}) {
children += NodeMatcher(S::class, assertions)
}
fun ContentMatcherBuilder<*>.skipAllNotMatching() {
children += Anything
}
// Convenience functions:
fun ContentMatcherBuilder<*>.group(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) = composite(block)
fun ContentMatcherBuilder<*>.header(expectedLevel: Int? = null, block: ContentMatcherBuilder<ContentHeader>.() -> Unit) =
composite<ContentHeader> {
block()
check { if (expectedLevel != null) assertThat(this::level).isEqualTo(expectedLevel) }
}
fun ContentMatcherBuilder<*>.p(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) =
composite<ContentGroup> {
block()
check { assertThat(this::style).contains(TextStyle.Paragraph) }
}
fun ContentMatcherBuilder<*>.link(block: ContentMatcherBuilder<ContentLink>.() -> Unit) = composite(block)
fun ContentMatcherBuilder<*>.table(block: ContentMatcherBuilder<ContentTable>.() -> Unit) = composite(block)
fun ContentMatcherBuilder<*>.platformHinted(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) =
composite<PlatformHintedContent> { group(block) }
fun ContentMatcherBuilder<*>.br() = node<ContentBreakLine>()
fun ContentMatcherBuilder<*>.somewhere(block: ContentMatcherBuilder<*>.() -> Unit) {
skipAllNotMatching()
block()
skipAllNotMatching()
}
fun ContentMatcherBuilder<*>.divergentGroup(block: ContentMatcherBuilder<ContentDivergentGroup>.() -> Unit) =
composite(block)
fun ContentMatcherBuilder<ContentDivergentGroup>.divergentInstance(block: ContentMatcherBuilder<ContentDivergentInstance>.() -> Unit) =
composite(block)
fun ContentMatcherBuilder<ContentDivergentInstance>.before(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) =
composite(block)
fun ContentMatcherBuilder<ContentDivergentInstance>.divergent(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) =
composite(block)
fun ContentMatcherBuilder<ContentDivergentInstance>.after(block: ContentMatcherBuilder<ContentComposite>.() -> Unit) =
composite(block)
|