diff --git a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt index e0bbfe7798a..bdaf6cad180 100644 --- a/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt +++ b/libraries/tools/kotlin-gradle-plugin-integration-tests/src/test/kotlin/org/jetbrains/kotlin/gradle/Kotlin2JsGradlePluginIT.kt @@ -1109,6 +1109,31 @@ abstract class AbstractKotlin2JsGradlePluginIT(protected val irBackend: Boolean) } } + @DisplayName("mocha fails on module not found") + @GradleTest + fun testMochaFailedModuleNotFound(gradleVersion: GradleVersion) { + project("kotlin-js-nodejs-project", gradleVersion) { + projectPath.resolve("src/test/kotlin/Tests.kt").appendText( + "\n" + """ + | + |@JsModule("foo") + |@JsNonModule + |external val foo: dynamic + | + |class Tests2 { + | @Test + | fun testHello() { + | foo + | } + |} + """.trimMargin() + ) + buildAndFail("nodeTest") { + assertTasksFailed(":nodeTest") + } + } + } + @DisplayName("webpack configuration is valid") @GradleTest fun testWebpackConfig(gradleVersion: GradleVersion) { diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesTestExecutor.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesTestExecutor.kt index df753aff258..a6babf15704 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesTestExecutor.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/internal/testing/TCServiceMessagesTestExecutor.kt @@ -20,7 +20,8 @@ open class TCServiceMessagesTestExecutionSpec( val forkOptions: ProcessForkOptions, val args: List, val checkExitCode: Boolean, - val clientSettings: TCServiceMessagesClientSettings + val clientSettings: TCServiceMessagesClientSettings, + val dryRunArgs: List? = null, ) : TestExecutionSpec { internal open fun createClient(testResultProcessor: TestResultProcessor, log: Logger): TCServiceMessagesClient = TCServiceMessagesClient(testResultProcessor, clientSettings, log) @@ -48,6 +49,19 @@ class TCServiceMessagesTestExecutor( val client = spec.createClient(testResultProcessor, log) + if (spec.dryRunArgs != null) { + val exec = execHandleFactory.newExec() + spec.forkOptions.copyTo(exec) + exec.args = spec.args + execHandle = exec.build() + + execHandle.start() + val result: ExecResult = execHandle.waitForFinish() + if (result.exitValue != 0) { + error(client.testFailedMessage(execHandle, result.exitValue)) + } + } + try { val exec = execHandleFactory.newExec() spec.forkOptions.copyTo(exec) diff --git a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt index 13a38f68dce..b730fbcdbc0 100644 --- a/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt +++ b/libraries/tools/kotlin-gradle-plugin/src/main/kotlin/org/jetbrains/kotlin/gradle/targets/js/testing/mocha/KotlinMocha.kt @@ -79,7 +79,7 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv val file = task.inputFileProperty.get().asFile.toString() - val adapter = createAdapterJs(file) + val adapter = createAdapterJs(file, "kotlin-test-nodejs-runner", ADAPTER_NODEJS) val args = mutableListOf( "--require", @@ -102,11 +102,26 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv } } + val dryRunArgs = mutableListOf( + "--require", + npmProject.require("source-map-support/register.js") + ).apply { + if (debug) { + add("--inspect-brk") + } + add(createAdapterJs(file, "kotlin-test-nodejs-empty-runner", ADAPTER_EMPTY_NODEJS).canonicalPath) + addAll(cliArgs.toList()) + if (platformType == KotlinPlatformType.wasm) { + addAll(cliArg("-n", "experimental-wasm-typed-funcref,experimental-wasm-gc,experimental-wasm-eh")) + } + } + return TCServiceMessagesTestExecutionSpec( forkOptions, args, false, - clientSettings + clientSettings, + dryRunArgs ) } @@ -115,14 +130,16 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv } private fun createAdapterJs( - file: String + file: String, + adapter: String, + adapterName: String ): File { - val adapterJs = npmProject.dir.resolve(ADAPTER_NODEJS) + val adapterJs = npmProject.dir.resolve(adapterName) adapterJs.printWriter().use { writer -> - val adapter = npmProject.require("kotlin-test-js-runner/kotlin-test-nodejs-runner.js") + val adapterFile = npmProject.require("kotlin-test-js-runner/$adapter.js") val escapedFile = file.jsQuoted() - writer.println("require(${adapter.jsQuoted()})") + writer.println("require(${adapterFile.jsQuoted()})") writer.println("module.exports = require($escapedFile)") } @@ -132,6 +149,7 @@ class KotlinMocha(@Transient override val compilation: KotlinJsCompilation, priv companion object { const val ADAPTER_NODEJS = "adapter-nodejs.js" + const val ADAPTER_EMPTY_NODEJS = "adapter-empty-nodejs.js" private const val DEFAULT_TIMEOUT = "2s" } diff --git a/libraries/tools/kotlin-test-js-runner/build.gradle.kts b/libraries/tools/kotlin-test-js-runner/build.gradle.kts index 927464882ba..8adb4adf246 100644 --- a/libraries/tools/kotlin-test-js-runner/build.gradle.kts +++ b/libraries/tools/kotlin-test-js-runner/build.gradle.kts @@ -40,6 +40,7 @@ tasks { inputs.dir("src") inputs.files( "nodejs.ts", + "nodejs-empty.ts", "nodejs-idle.ts", "karma.ts", "karma-kotlin-reporter.js", diff --git a/libraries/tools/kotlin-test-js-runner/nodejs-empty.ts b/libraries/tools/kotlin-test-js-runner/nodejs-empty.ts new file mode 100755 index 00000000000..fefda921a33 --- /dev/null +++ b/libraries/tools/kotlin-test-js-runner/nodejs-empty.ts @@ -0,0 +1,5 @@ +import {EmptyKotlinTestRunner} from "./src/EmptyKotlinTestRunner"; + +(globalThis as any).kotlinTest = { + adapterTransformer: () => new EmptyKotlinTestRunner() +} \ No newline at end of file diff --git a/libraries/tools/kotlin-test-js-runner/rollup.config.js b/libraries/tools/kotlin-test-js-runner/rollup.config.js index db42a4881eb..8c78c14f00c 100644 --- a/libraries/tools/kotlin-test-js-runner/rollup.config.js +++ b/libraries/tools/kotlin-test-js-runner/rollup.config.js @@ -21,6 +21,16 @@ export default [ }, plugins: plugins() }, + { + input: './nodejs-empty.ts', + output: { + file: 'lib/kotlin-test-nodejs-empty-runner.js', + format: 'cjs', + banner: '#!/usr/bin/env node', + sourcemap: true + }, + plugins: plugins() + }, { input: './karma.ts', output: { diff --git a/libraries/tools/kotlin-test-js-runner/src/EmptyKotlinTestRunner.ts b/libraries/tools/kotlin-test-js-runner/src/EmptyKotlinTestRunner.ts new file mode 100644 index 00000000000..e3d3f483b09 --- /dev/null +++ b/libraries/tools/kotlin-test-js-runner/src/EmptyKotlinTestRunner.ts @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +import {KotlinTestRunner} from "./KotlinTestRunner"; + +export class EmptyKotlinTestRunner implements KotlinTestRunner { + suite(name: string, isIgnored: boolean, fn: () => void): void { + } + + test(name: string, isIgnored: boolean, fn: () => void): void { + } + +} \ No newline at end of file