Skip to content

Commit 51e868e

Browse files
support for data test gutter icons
1 parent 769bb55 commit 51e868e

File tree

9 files changed

+75
-8
lines changed

9 files changed

+75
-8
lines changed

src/main/kotlin/io/kotest/plugin/intellij/Test.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ data class Test(
5656
val specClassName: KtClassOrObject, // the containing class name, which all tests must have
5757
val testType: TestType,
5858
val xdisabled: Boolean, // if true then this test was defined using one of the x methods
59-
val psi: PsiElement // the canonical element that identifies this test
59+
val psi: PsiElement, // the canonical element that identifies this test
60+
val isDataTest: Boolean = false
6061
) {
6162

6263
// true if this test is not xdisabled and not disabled by a bang and not nested inside another disabled test

src/main/kotlin/io/kotest/plugin/intellij/psi/utils.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,8 @@ fun KtValueArgumentList.isSingleStringTemplateArg(): Boolean =
127127
&& children[0] is KtValueArgument
128128
&& children[0].children.size == 1
129129
&& children[0].children[0] is KtStringTemplateExpression
130+
131+
fun LeafPsiElement.isDataTestMethodCall(dataTestMethodNames:Set<String>): KtCallExpression? {
132+
val lambdaCall = ifCallExpressionLambdaOpenBrace()
133+
return lambdaCall.takeIf {lambdaCall?.functionName() in dataTestMethodNames}
134+
}

src/main/kotlin/io/kotest/plugin/intellij/run/gradle/GradleTaskNamesBuilder.kt

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,11 @@ data class GradleTaskNamesBuilder(
3939
private fun includeArg(): String? {
4040
return when (test) {
4141
null -> "$PROPERTY_INCLUDE='${spec.fqName?.asString()}'"
42-
else -> "$PROPERTY_INCLUDE='${test.descriptorPath()}'"
42+
else ->
43+
when(test.isDataTest){
44+
false -> "$PROPERTY_INCLUDE='${test.descriptorPath()}'"
45+
true -> "$PROPERTY_INCLUDE='${spec.fqName?.asString()}'"
46+
}
4347
}
4448
}
4549
}

src/main/kotlin/io/kotest/plugin/intellij/run/gradle/TestOrSpecGradleRunConfigurationProducer.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,10 @@ class TestOrSpecGradleRunConfigurationProducer : GradleRunConfigurationProducer(
155155
if (test != null) {
156156
// if we specified a test descriptor before, it needs to match for this configuration to be the same
157157
val descriptorArg = GradleUtils.getIncludeArg(configuration.settings.taskNames) ?: return false
158+
if (test.isDataTest) {
159+
val spec = element.enclosingSpec()
160+
return spec?.fqName?.asString() == descriptorArg
161+
}
158162
if (test.descriptorPath() == descriptorArg) return true
159163
}
160164
}

src/main/kotlin/io/kotest/plugin/intellij/run/idea/TestPathRunConfigurationProducer.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,10 @@ class TestPathRunConfigurationProducer : LazyRunConfigurationProducer<KotestRunC
5151

5252
val ktclass = element.enclosingKtClass()
5353
if (ktclass != null) {
54-
configuration.setTestPath(test.testPath())
54+
if (test.isDataTest)
55+
configuration.setTestPath(null)
56+
else
57+
configuration.setTestPath(test.testPath())
5558
configuration.setSpec(ktclass)
5659
configuration.setModule(context.module)
5760
configuration.name = generateName(ktclass, test)

src/main/kotlin/io/kotest/plugin/intellij/styles/SpecStyle.kt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.intellij.psi.PsiElement
44
import com.intellij.psi.impl.source.tree.LeafPsiElement
55
import io.kotest.plugin.intellij.Test
66
import io.kotest.plugin.intellij.TestElement
7+
import io.kotest.plugin.intellij.TestName
78
import io.kotest.plugin.intellij.TestType
89
import io.kotest.plugin.intellij.psi.isContainedInSpecificSpec
910
import org.jetbrains.kotlin.name.FqName
@@ -47,6 +48,9 @@ interface SpecStyle {
4748
.mapNotNull { it.findAssociatedTest(element) }
4849
.firstOrNull()
4950
}
51+
52+
// in future iterations this could change and be somehow saying running all data tests within the spec
53+
val dataTestDefaultTestName: TestName = TestName(null, "All Spec Tests, including data tests", interpolated = false)
5054
}
5155

5256
/**
@@ -139,4 +143,7 @@ interface SpecStyle {
139143
* For example, a [FunSpec] would return a string like this: test("given name") { }
140144
*/
141145
fun generateTest(specName: String, name: String): String
146+
147+
// TODO default will be removed if this POC is accepted and all other styles implement it
148+
fun getDataTestMethodNames() : Set<String> = emptySet()
142149
}

src/main/kotlin/io/kotest/plugin/intellij/styles/StringSpecStyle.kt

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import io.kotest.plugin.intellij.TestType
88
import io.kotest.plugin.intellij.psi.enclosingKtClassOrObject
99
import io.kotest.plugin.intellij.psi.extractStringForStringExtensionFunctonWithRhsFinalLambda
1010
import io.kotest.plugin.intellij.psi.extractStringFromStringInvokeWithLambda
11+
import io.kotest.plugin.intellij.psi.hasFunctionName
1112
import io.kotest.plugin.intellij.psi.ifCallExpressionLhsStringOpenQuote
1213
import io.kotest.plugin.intellij.psi.ifDotExpressionSeparator
14+
import io.kotest.plugin.intellij.psi.isDataTestMethodCall
15+
import io.kotest.plugin.intellij.styles.SpecStyle.Companion.dataTestDefaultTestName
1316
import org.jetbrains.kotlin.name.FqName
1417
import org.jetbrains.kotlin.psi.KtCallExpression
1518
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
@@ -28,6 +31,12 @@ object StringSpecStyle : SpecStyle {
2831
return "\"$name\" { }"
2932
}
3033

34+
override fun getDataTestMethodNames(): Set<String> =
35+
setOf(
36+
"withData",
37+
)
38+
39+
3140
/**
3241
* A test of the form:
3342
*
@@ -41,6 +50,27 @@ object StringSpecStyle : SpecStyle {
4150
return Test(testName, null, specClass, TestType.Test, xdisabled = false, psi = this)
4251
}
4352

53+
/**
54+
* A test container of the form:
55+
*```
56+
* withData(1, 2, 3) { }
57+
* withData(listOf(1, 2, 3)) { }
58+
* withData(nameFn = { "test $it" }, 1, 2, 3) { }
59+
* ... any other withData permutation
60+
*```
61+
* Note: even tho we build a Test, the runner will only read the `isDataTest` boolean to determine it needs to run the whole spec
62+
*/
63+
private fun KtCallExpression.tryWithData(): Test? {
64+
val specClass = enclosingKtClassOrObject() ?: return null
65+
66+
if (!hasFunctionName(getDataTestMethodNames().toList())) return null
67+
68+
69+
// withData is a container because it generates multiple tests at runtime
70+
return Test(dataTestDefaultTestName, null, specClass, TestType.Container, xdisabled = false, psi = this, isDataTest = true)
71+
}
72+
73+
4474
/**
4575
* Matches tests of the form:
4676
*
@@ -58,10 +88,11 @@ object StringSpecStyle : SpecStyle {
5888
*
5989
* "test name" { }
6090
* "test name".config(...) {}
91+
* withData(...) { }
6192
*/
6293
override fun test(element: PsiElement): Test? {
6394
return when (element) {
64-
is KtCallExpression -> element.tryTest()
95+
is KtCallExpression -> element.tryTest() ?: element.tryWithData()
6596
is KtDotQualifiedExpression -> element.tryTestWithConfig()
6697
else -> null
6798
}
@@ -76,6 +107,7 @@ object StringSpecStyle : SpecStyle {
76107
*
77108
* "test name" { }
78109
* "test name".config(...) {}
110+
* withData(...) { }
79111
*/
80112
override fun test(element: LeafPsiElement): Test? {
81113
val ktcall = element.ifCallExpressionLhsStringOpenQuote()
@@ -84,6 +116,12 @@ object StringSpecStyle : SpecStyle {
84116
val ktdot = element.ifDotExpressionSeparator()
85117
if (ktdot != null) return test(ktdot)
86118

119+
// try to find Data Test Method by finding lambda openings
120+
val dataMethodCall = element.isDataTestMethodCall(getDataTestMethodNames())
121+
if (dataMethodCall != null) {
122+
return test(dataMethodCall)
123+
}
124+
87125
return null
88126
}
89127
}

src/test/kotlin/io/kotest/plugin/intellij/styles/StringSpecStyleTest.kt

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,13 @@ class StringSpecStyleTest : LightJavaCodeInsightFixtureTestCase() {
2424
)
2525

2626
val gutters = myFixture.findAllGutters()
27-
gutters.size shouldBe 3
27+
gutters.size shouldBe 4
2828

2929
val expected = listOf(
30-
Gutter("Run StringSpecExample", 91, AllIcons.RunConfigurations.TestState.Run_run),
31-
Gutter("Run test", 145),
32-
Gutter("Run test with config", 201),
30+
Gutter("Run StringSpecExample", 126, AllIcons.RunConfigurations.TestState.Run_run),
31+
Gutter("Run test", 180),
32+
Gutter("Run test with config", 236),
33+
Gutter("Run All Spec Tests, including data tests", 299),
3334
)
3435

3536
expected.size shouldBe gutters.size

src/test/resources/stringspec.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.sksamuel.kotest.specs.stringspec
22

33
import io.kotest.core.spec.style.StringSpec
4+
import io.kotest.datatest.withData
45

56
class StringSpecExample : StringSpec() {
67
init {
@@ -10,5 +11,8 @@ class StringSpecExample : StringSpec() {
1011
"test with config".config(enabled = false) {
1112

1213
}
14+
withData(1, 2, 3, 4, 5) { value ->
15+
// test here
16+
}
1317
}
1418
}

0 commit comments

Comments
 (0)