/
ContentMatchersDsl.kt
124 lines (92 loc) · 4.59 KB
/
ContentMatchersDsl.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
package matchers.content
import assertk.assertThat
import assertk.assertions.contains
import assertk.assertions.isEqualTo
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, childrenOrSkip()) { assertions.forEach { it() } }
// part of DSL that cannot be defined as an extension
operator fun String.unaryPlus() {
children += TextMatcher(this)
}
private fun childrenOrSkip() = if (children.isEmpty() && assertions.isNotEmpty()) listOf(Anything) else children
}
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)
}
}
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<*>.list(block: ContentMatcherBuilder<ContentList>.() -> Unit) = composite(block)
fun ContentMatcherBuilder<*>.codeBlock(block: ContentMatcherBuilder<ContentCodeBlock>.() -> Unit) = composite(block)
fun ContentMatcherBuilder<*>.codeInline(block: ContentMatcherBuilder<ContentCodeInline>.() -> Unit) = composite(block)
fun ContentMatcherBuilder<*>.caption(block: ContentMatcherBuilder<ContentGroup>.() -> Unit) = composite<ContentGroup> {
block()
check { assertThat(this::style).contains(ContentStyle.Caption) }
}
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)