I'm facing a problem in a project (<charleskorn/ka...
# kotest
p
I'm facing a problem in a project (charleskorn/kaml) that was originally for Kotlin/JVM where nested kotest tests are used. Now we're enabling other platforms, and for e.g. JS and Wasm/JS, I'm getting an error that nested tests aren't supported. I'm facing a dilemma: whether to wait for kotest to support nested tests across targets (I see a PR from @Adam S: JS nested tests v2 but not sure if it's being worked on) or flatten the tests. Could you help me decide which path to choose? I see some notes from Adam that there are problems with JS-related platforms, but not sure what kind of effort is required to overcome these
a
tldr is that @sam is working on a completely new and amazing runner for Kotest https://kotlinlang.slack.com/archives/CSYLDDZUY/p1717961475346859 My PR was kind of a hacky workaround that won't be necessary when the new runner is finsihed. So, it depends on how long the new runner takes. 1. It's ready soon. Recommendation: ignore the JS tests for now until Kotest is updated. 2. It won't be ready and released for a while, and Kotest devs decide to proceed with my workaround as a stop-gap. 3. It won't be ready and released for a long time, and my workaround doesn't get merged. My recommendation for 1 & 2: temporarily ignore the JS tests for now until Kotest is updated. And for 3 - Either permanently ignore the JS tests, and cross your fingers they don't break. Or re-write the tests so they're not nested 🙃
👍 1
o
Or you could try if something like this could help in the mean time:
Copy code
import io.kotest.common.ExperimentalKotest
import io.kotest.core.spec.KotestTestScope
import io.kotest.core.spec.style.FunSpec
import io.kotest.core.spec.style.scopes.FunSpecContainerScope
import io.kotest.core.spec.style.scopes.RootTestWithConfigBuilder
import io.kotest.core.test.TestScope
import kotlin.jvm.JvmName

private fun String.toContainerPrefix(): String = "$this -- "

/**
 * Flat version of a Kotest FunSpec for common and JS tests.
 *
 * WORKAROUND <https://github.com/kotest/kotest/issues/3141> (Support for nested tests on JS target)
 *
 * Kotest 5 doesn't support nested tests in Kotlin/JS, so until it is supported
 * this spec can be used, flattening all the nested tests to the root level.
 * Limitation: context() bodies are non-suspendable, only test() bodies accept suspend functions.
 *
 * Posted by @BenWoodworth in Kotest issue #3141:
 * <https://github.com/kotest/kotest/issues/3141#issuecomment-1278433891>
 */
// Using `abstract` causes a compiler error with native, so using `open` instead
open class FlatFunSpec private constructor() : FunSpec() {
    // Using primary constructor causes issue with Kotlin/JS IR:
    // <https://youtrack.jetbrains.com/issue/KT-54450>
    constructor(body: FlatFunSpec.() -> Unit = {}) : this() {
        body()
    }

    // Overload context functions with non-nested versions
    @JvmName("context\$FlatSpec")
    fun context(name: String, test: FlatSpecContainerScope.() -> Unit): Unit =
        test(FlatSpecContainerScope(this, name.toContainerPrefix(), false))

    @JvmName("xcontext\$FlatSpec")
    fun xcontext(name: String, test: FlatSpecContainerScope.() -> Unit): Unit =
        test(FlatSpecContainerScope(this, name.toContainerPrefix(), true))

    // Suppress FunSpec's context functions, so they can't be used
    @Deprecated("Unsupported", level = DeprecationLevel.HIDDEN)
    override fun context(name: String, test: suspend FunSpecContainerScope.() -> Unit): Nothing = error("Unsupported")

    @Deprecated("Unsupported", level = DeprecationLevel.HIDDEN)
    override fun xcontext(name: String, test: suspend FunSpecContainerScope.() -> Unit): Nothing = error("Unsupported")

    @ExperimentalKotest
    @Deprecated("Unsupported", level = DeprecationLevel.HIDDEN)
    override fun context(name: String): Nothing = error("Unsupported")

    @ExperimentalKotest
    @Deprecated("Unsupported", level = DeprecationLevel.HIDDEN)
    override fun xcontext(name: String): Nothing = error("Unsupported")
}

@Suppress("unused")
@KotestTestScope
class FlatSpecContainerScope(
    private val spec: FlatFunSpec,
    private val prefix: String,
    private val ignored: Boolean
) {
    fun test(name: String): RootTestWithConfigBuilder = if (ignored) {
        spec.xtest(prefix + name)
    } else {
        spec.test(prefix + name)
    }

    fun test(name: String, test: suspend TestScope.() -> Unit): Unit = if (ignored) {
        spec.xtest(prefix + name, test)
    } else {
        spec.test(prefix + name, test)
    }

    fun xtest(name: String): RootTestWithConfigBuilder = spec.xtest(prefix + name)

    fun xtest(name: String, test: suspend TestScope.() -> Unit): Unit = spec.xtest(prefix + name, test)

    fun context(name: String, test: FlatSpecContainerScope.() -> Unit): Unit = if (ignored) {
        spec.xcontext(prefix + name, test)
    } else {
        spec.context(prefix + name, test)
    }

    fun xcontext(name: String, test: FlatSpecContainerScope.() -> Unit): Unit = spec.xcontext(prefix + name, test)
}
p
it does the job, thanks! 🙇
👍 1
c
Hey all, thanks for the awesome framework. Any update on support for nested tests in the JS runner, or the new kotest runner (@sam)? Porting a large multiplatform spec set from spek, works great on jvm, ios and android, but js super busted. I'm using
6.0.0.M1
. I tried adapting
FlatFunSpec
above but my specs rely pretty heavily on containers triggering before/after listeners. Which I suppose could be hacked in but yuck.
s
End of 2024
❀ 2