Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main/kotlin/io/kotest/plugin/intellij/Test.kt
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ data class Test(
val specClassName: KtClassOrObject, // the containing class name, which all tests must have
val testType: TestType,
val xdisabled: Boolean, // if true then this test was defined using one of the x methods
val psi: PsiElement // the canonical element that identifies this test
val psi: PsiElement, // the canonical element that identifies this test
val isDataTest: Boolean = false
) {

// true if this test is not xdisabled and not disabled by a bang and not nested inside another disabled test
Expand Down
5 changes: 5 additions & 0 deletions src/main/kotlin/io/kotest/plugin/intellij/psi/utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,8 @@ fun KtValueArgumentList.isSingleStringTemplateArg(): Boolean =
&& children[0] is KtValueArgument
&& children[0].children.size == 1
&& children[0].children[0] is KtStringTemplateExpression

fun LeafPsiElement.isDataTestMethodCall(dataTestMethodNames:Set<String>): KtCallExpression? {
val lambdaCall = ifCallExpressionLambdaOpenBrace()
return lambdaCall.takeIf {lambdaCall?.functionName() in dataTestMethodNames}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@ data class GradleTaskNamesBuilder(
private fun includeArg(): String? {
return when (test) {
null -> "$PROPERTY_INCLUDE='${spec.fqName?.asString()}'"
else -> "$PROPERTY_INCLUDE='${test.descriptorPath()}'"
else ->
when(test.isDataTest){
false -> "$PROPERTY_INCLUDE='${test.descriptorPath()}'"
true -> "$PROPERTY_INCLUDE='${spec.fqName?.asString()}'"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,10 @@ class TestOrSpecGradleRunConfigurationProducer : GradleRunConfigurationProducer(
if (test != null) {
// if we specified a test descriptor before, it needs to match for this configuration to be the same
val descriptorArg = GradleUtils.getIncludeArg(configuration.settings.taskNames) ?: return false
if (test.isDataTest) {
val spec = element.enclosingSpec()
return spec?.fqName?.asString() == descriptorArg
}
if (test.descriptorPath() == descriptorArg) return true
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ class TestPathRunConfigurationProducer : LazyRunConfigurationProducer<KotestRunC

val ktclass = element.enclosingKtClass()
if (ktclass != null) {
configuration.setTestPath(test.testPath())
if (test.isDataTest)
configuration.setTestPath(null)
else
configuration.setTestPath(test.testPath())
configuration.setSpec(ktclass)
configuration.setModule(context.module)
configuration.name = generateName(ktclass, test)
Expand Down
7 changes: 7 additions & 0 deletions src/main/kotlin/io/kotest/plugin/intellij/styles/SpecStyle.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.intellij.psi.PsiElement
import com.intellij.psi.impl.source.tree.LeafPsiElement
import io.kotest.plugin.intellij.Test
import io.kotest.plugin.intellij.TestElement
import io.kotest.plugin.intellij.TestName
import io.kotest.plugin.intellij.TestType
import io.kotest.plugin.intellij.psi.isContainedInSpecificSpec
import org.jetbrains.kotlin.name.FqName
Expand Down Expand Up @@ -47,6 +48,9 @@ interface SpecStyle {
.mapNotNull { it.findAssociatedTest(element) }
.firstOrNull()
}

// in future iterations this could change and be somehow saying running all data tests within the spec
val dataTestDefaultTestName: TestName = TestName(null, "All Spec Tests, including data tests", interpolated = false)
}

/**
Expand Down Expand Up @@ -139,4 +143,7 @@ interface SpecStyle {
* For example, a [FunSpec] would return a string like this: test("given name") { }
*/
fun generateTest(specName: String, name: String): String

// TODO default will be removed if this POC is accepted and all other styles implement it
fun getDataTestMethodNames() : Set<String> = emptySet()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,11 @@ import io.kotest.plugin.intellij.TestType
import io.kotest.plugin.intellij.psi.enclosingKtClassOrObject
import io.kotest.plugin.intellij.psi.extractStringForStringExtensionFunctonWithRhsFinalLambda
import io.kotest.plugin.intellij.psi.extractStringFromStringInvokeWithLambda
import io.kotest.plugin.intellij.psi.hasFunctionName
import io.kotest.plugin.intellij.psi.ifCallExpressionLhsStringOpenQuote
import io.kotest.plugin.intellij.psi.ifDotExpressionSeparator
import io.kotest.plugin.intellij.psi.isDataTestMethodCall
import io.kotest.plugin.intellij.styles.SpecStyle.Companion.dataTestDefaultTestName
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtCallExpression
import org.jetbrains.kotlin.psi.KtDotQualifiedExpression
Expand All @@ -28,6 +31,12 @@ object StringSpecStyle : SpecStyle {
return "\"$name\" { }"
}

override fun getDataTestMethodNames(): Set<String> =
setOf(
"withData",
)


/**
* A test of the form:
*
Expand All @@ -41,6 +50,27 @@ object StringSpecStyle : SpecStyle {
return Test(testName, null, specClass, TestType.Test, xdisabled = false, psi = this)
}

/**
* A test container of the form:
*```
* withData(1, 2, 3) { }
* withData(listOf(1, 2, 3)) { }
* withData(nameFn = { "test $it" }, 1, 2, 3) { }
* ... any other withData permutation
*```
* Note: even tho we build a Test, the runner will only read the `isDataTest` boolean to determine it needs to run the whole spec
*/
private fun KtCallExpression.tryWithData(): Test? {
val specClass = enclosingKtClassOrObject() ?: return null

if (!hasFunctionName(getDataTestMethodNames().toList())) return null


// withData is a container because it generates multiple tests at runtime
return Test(dataTestDefaultTestName, null, specClass, TestType.Container, xdisabled = false, psi = this, isDataTest = true)
}


/**
* Matches tests of the form:
*
Expand All @@ -58,10 +88,11 @@ object StringSpecStyle : SpecStyle {
*
* "test name" { }
* "test name".config(...) {}
* withData(...) { }
*/
override fun test(element: PsiElement): Test? {
return when (element) {
is KtCallExpression -> element.tryTest()
is KtCallExpression -> element.tryTest() ?: element.tryWithData()
is KtDotQualifiedExpression -> element.tryTestWithConfig()
else -> null
}
Expand All @@ -76,6 +107,7 @@ object StringSpecStyle : SpecStyle {
*
* "test name" { }
* "test name".config(...) {}
* withData(...) { }
*/
override fun test(element: LeafPsiElement): Test? {
val ktcall = element.ifCallExpressionLhsStringOpenQuote()
Expand All @@ -84,6 +116,12 @@ object StringSpecStyle : SpecStyle {
val ktdot = element.ifDotExpressionSeparator()
if (ktdot != null) return test(ktdot)

// try to find Data Test Method by finding lambda openings
val dataMethodCall = element.isDataTestMethodCall(getDataTestMethodNames())
if (dataMethodCall != null) {
return test(dataMethodCall)
}

return null
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@ class StringSpecStyleTest : LightJavaCodeInsightFixtureTestCase() {
)

val gutters = myFixture.findAllGutters()
gutters.size shouldBe 3
gutters.size shouldBe 4

val expected = listOf(
Gutter("Run StringSpecExample", 91, AllIcons.RunConfigurations.TestState.Run_run),
Gutter("Run test", 145),
Gutter("Run test with config", 201),
Gutter("Run StringSpecExample", 126, AllIcons.RunConfigurations.TestState.Run_run),
Gutter("Run test", 180),
Gutter("Run test with config", 236),
Gutter("Run All Spec Tests, including data tests", 299),
)

expected.size shouldBe gutters.size
Expand Down
4 changes: 4 additions & 0 deletions src/test/resources/stringspec.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.sksamuel.kotest.specs.stringspec

import io.kotest.core.spec.style.StringSpec
import io.kotest.datatest.withData

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

}
withData(1, 2, 3, 4, 5) { value ->
// test here
}
}
}