From b52b07ec7999c6797862bd63cd645ebf99140cb9 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Thu, 22 Mar 2018 19:59:22 +0300 Subject: [PATCH] Test Support: KT-22306 Do not show run markers in JS modules when no relevant run configurations are available #KT-22306 Fixed --- .../forTestCompile/ForTestCompileRuntime.java | 10 ++ .../KotlinRunLineMarkerContributor.kt | 23 ++- .../KotlinTestRunLineMarkerContributor.kt | 16 +- .../KotlinJSRunConfigurationDataProvider.kt | 20 +++ .../a_common_test/common.kt | 5 +- .../a_js_test_dep(kotlin-test)/test.kt | 18 +++ .../test.kt | 4 +- .../idea/multiplatform/multiPlatformSetup.kt | 8 +- .../multiplatform/multiPlatformSetup.kt.182 | 8 +- .../KotlinNodeJsRunConfigurationProducer.kt | 58 ++++--- .../KotlinJestRunConfigurationProducer.kt | 78 ++++++---- .../KotlinMochaRunConfigurationProducer.kt | 141 ++++++++++-------- ...otlinProtractorRunConfigurationProducer.kt | 49 +++--- 13 files changed, 294 insertions(+), 144 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/js/KotlinJSRunConfigurationDataProvider.kt create mode 100644 idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_js_test_dep(kotlin-test)/test.kt rename idea/testData/multiModuleLineMarker/kotlinTestAnnotations/{a_js_test => a_jvm_test_dep(kotlin-test)}/test.kt (68%) diff --git a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/forTestCompile/ForTestCompileRuntime.java b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/forTestCompile/ForTestCompileRuntime.java index e8c73b64734..ddd35f1cb0a 100644 --- a/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/forTestCompile/ForTestCompileRuntime.java +++ b/compiler/tests-common/tests/org/jetbrains/kotlin/codegen/forTestCompile/ForTestCompileRuntime.java @@ -49,6 +49,16 @@ public class ForTestCompileRuntime { return assertExists(new File("dist/kotlinc/lib/kotlin-test.jar")); } + @NotNull + public static File kotlinTestJUnitJarForTests() { + return assertExists(new File("dist/kotlinc/lib/kotlin-test-junit.jar")); + } + + @NotNull + public static File kotlinTestJsJarForTests() { + return assertExists(new File("dist/kotlinc/lib/kotlin-test-js.jar")); + } + @NotNull public static File reflectJarForTests() { return assertExists(new File("dist/kotlinc/lib/kotlin-reflect.jar")); diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRunLineMarkerContributor.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRunLineMarkerContributor.kt index c5511b9af98..68375950bb0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRunLineMarkerContributor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinRunLineMarkerContributor.kt @@ -16,14 +16,19 @@ package org.jetbrains.kotlin.idea.highlighter +import com.intellij.execution.actions.RunConfigurationProducer import com.intellij.execution.lineMarker.ExecutorAction import com.intellij.execution.lineMarker.RunLineMarkerContributor import com.intellij.icons.AllIcons import com.intellij.psi.PsiElement import org.jetbrains.kotlin.idea.MainFunctionDetector import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider +import org.jetbrains.kotlin.idea.project.TargetPlatformDetector +import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.psi.KtNamedFunction - +import org.jetbrains.kotlin.resolve.TargetPlatform +import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform class KotlinRunLineMarkerContributor : RunLineMarkerContributor() { override fun getInfo(element: PsiElement): Info? { @@ -36,6 +41,22 @@ class KotlinRunLineMarkerContributor : RunLineMarkerContributor() { } if (detector.isMain(function)) { + val platform = TargetPlatformDetector.getPlatform(function.containingKtFile) + val isAvailable = when (platform) { + is JvmPlatform -> true + is JsPlatform, is TargetPlatform.Common -> { + RunConfigurationProducer + .getProducers(function.project) + .asSequence() + .filterIsInstance>() + .filter { !it.isForTests } + .mapNotNull { it.getConfigurationData(function) } + .firstOrNull() != null + } + else -> false + } + if (!isAvailable) return null + return RunLineMarkerContributor.Info(AllIcons.RunConfigurations.TestState.Run, null, ExecutorAction.getActions(0)) } diff --git a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt index 51d54c037eb..f526337497a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/highlighter/KotlinTestRunLineMarkerContributor.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.highlighter import com.intellij.codeInsight.TestFrameworks import com.intellij.execution.TestStateStorage +import com.intellij.execution.actions.RunConfigurationProducer import com.intellij.execution.lineMarker.ExecutorAction import com.intellij.execution.lineMarker.RunLineMarkerContributor import com.intellij.execution.testframework.TestIconMapper @@ -33,10 +34,8 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny -import org.jetbrains.kotlin.idea.js.jsOrJsImpl -import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider import org.jetbrains.kotlin.idea.project.TargetPlatformDetector -import org.jetbrains.kotlin.idea.util.module import org.jetbrains.kotlin.idea.util.string.joinWithEscape import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.name.FqName @@ -110,12 +109,17 @@ class KotlinTestRunLineMarkerContributor : RunLineMarkerContributor() { private fun getJavaScriptTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? { if (!descriptor.isTest()) return null - val module = declaration.module?.jsOrJsImpl() ?: return null - val testFilePath = module.jsTestOutputFilePath ?: return null + val runConfigData = RunConfigurationProducer + .getProducers(declaration.project) + .asSequence() + .filterIsInstance>() + .filter { it.isForTests } + .mapNotNull { it.getConfigurationData(declaration) } + .firstOrNull() ?: return null val locations = ArrayList() - locations += FileUtil.toSystemDependentName(testFilePath) + locations += FileUtil.toSystemDependentName(runConfigData.jsOutputFilePath) val klass = when (declaration) { is KtClassOrObject -> declaration diff --git a/idea/src/org/jetbrains/kotlin/idea/js/KotlinJSRunConfigurationDataProvider.kt b/idea/src/org/jetbrains/kotlin/idea/js/KotlinJSRunConfigurationDataProvider.kt new file mode 100644 index 00000000000..68302b19da3 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/js/KotlinJSRunConfigurationDataProvider.kt @@ -0,0 +1,20 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.js + +import com.intellij.openapi.module.Module +import com.intellij.psi.PsiElement + +interface KotlinJSRunConfigurationData { + val element: PsiElement + val module: Module + val jsOutputFilePath: String +} + +interface KotlinJSRunConfigurationDataProvider { + val isForTests: Boolean + fun getConfigurationData(element: PsiElement): T? +} \ No newline at end of file diff --git a/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_common_test/common.kt b/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_common_test/common.kt index 3550e004b80..8a098c74351 100644 --- a/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_common_test/common.kt +++ b/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_common_test/common.kt @@ -1,6 +1,3 @@ // !CHECK_HIGHLIGHTING -package kotlin.test - -annotation class Test -annotation class Ignore \ No newline at end of file +package test \ No newline at end of file diff --git a/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_js_test_dep(kotlin-test)/test.kt b/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_js_test_dep(kotlin-test)/test.kt new file mode 100644 index 00000000000..74ef7dd477d --- /dev/null +++ b/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_js_test_dep(kotlin-test)/test.kt @@ -0,0 +1,18 @@ +import kotlin.test.* + +class SimpleTest { + @Test fun testFoo() { + // Will run + } + + @Ignore fun testFooWrong() { + // Will not run + } +} + +@Ignore +class TestTest { + @Test fun emptyTest() { + // Will not run + } +} diff --git a/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_js_test/test.kt b/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_jvm_test_dep(kotlin-test)/test.kt similarity index 68% rename from idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_js_test/test.kt rename to idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_jvm_test_dep(kotlin-test)/test.kt index 9ef8f426ebd..9dbfa854bb5 100644 --- a/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_js_test/test.kt +++ b/idea/testData/multiModuleLineMarker/kotlinTestAnnotations/a_jvm_test_dep(kotlin-test)/test.kt @@ -11,8 +11,8 @@ class SimpleTest { } @Ignore -class TestTest { - @Test fun emptyTest() { +class TestTest { + @Test fun emptyTest() { // Will not run } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/multiplatform/multiPlatformSetup.kt b/idea/tests/org/jetbrains/kotlin/idea/multiplatform/multiPlatformSetup.kt index 2363e49c8ed..3bac7699ec1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/multiplatform/multiPlatformSetup.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/multiplatform/multiPlatformSetup.kt @@ -55,6 +55,10 @@ fun AbstractMultiModuleTest.setupMppProjectFromDirStructure(testRoot: File) { }) } is CoroutinesDependency -> module.enableCoroutines() + is KotlinTestDependency -> when (id.platform) { + is TargetPlatformKind.Jvm -> module.addLibrary(ForTestCompileRuntime.kotlinTestJUnitJarForTests()) + is TargetPlatformKind.JavaScript -> module.addLibrary(ForTestCompileRuntime.kotlinTestJsJarForTests(), kind = JSLibraryKind) + } } } } @@ -142,6 +146,7 @@ private fun parseDependency(it: String): Dependency { dependencyString.equals("stdlib", ignoreCase = true) -> StdlibDependency dependencyString.equals("fulljdk", ignoreCase = true) -> FullJdkDependency dependencyString.equals("coroutines", ignoreCase = true) -> CoroutinesDependency + dependencyString.equals("kotlin-test", ignoreCase = true) -> KotlinTestDependency else -> ModuleDependency(parseModuleId(dependencyString.split("-"))) } } @@ -190,4 +195,5 @@ private sealed class Dependency private class ModuleDependency(val moduleId: ModuleId) : Dependency() private object StdlibDependency : Dependency() private object FullJdkDependency : Dependency() -private object CoroutinesDependency : Dependency() \ No newline at end of file +private object CoroutinesDependency : Dependency() +private object KotlinTestDependency : Dependency() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/multiplatform/multiPlatformSetup.kt.182 b/idea/tests/org/jetbrains/kotlin/idea/multiplatform/multiPlatformSetup.kt.182 index 2614c46574c..2b5053783e4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/multiplatform/multiPlatformSetup.kt.182 +++ b/idea/tests/org/jetbrains/kotlin/idea/multiplatform/multiPlatformSetup.kt.182 @@ -55,6 +55,10 @@ fun AbstractMultiModuleTest.setupMppProjectFromDirStructure(testRoot: File) { }) } is CoroutinesDependency -> module.enableCoroutines() + is KotlinTestDependency -> when (id.platform) { + is TargetPlatformKind.Jvm -> module.addLibrary(ForTestCompileRuntime.kotlinTestJUnitJarForTests()) + is TargetPlatformKind.JavaScript -> module.addLibrary(ForTestCompileRuntime.kotlinTestJsJarForTests(), kind = JSLibraryKind) + } } } } @@ -142,6 +146,7 @@ private fun parseDependency(it: String): Dependency { dependencyString.equals("stdlib", ignoreCase = true) -> StdlibDependency dependencyString.equals("fulljdk", ignoreCase = true) -> FullJdkDependency dependencyString.equals("coroutines", ignoreCase = true) -> CoroutinesDependency + dependencyString.equals("kotlin-test", ignoreCase = true) -> KotlinTestDependency else -> ModuleDependency(parseModuleId(dependencyString.split("-"))) } } @@ -190,4 +195,5 @@ private sealed class Dependency private class ModuleDependency(val moduleId: ModuleId) : Dependency() private object StdlibDependency : Dependency() private object FullJdkDependency : Dependency() -private object CoroutinesDependency : Dependency() \ No newline at end of file +private object CoroutinesDependency : Dependency() +private object KotlinTestDependency : Dependency() \ No newline at end of file diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/cli/KotlinNodeJsRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/cli/KotlinNodeJsRunConfigurationProducer.kt index 5fcdfb994a4..1d60fe91e24 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/cli/KotlinNodeJsRunConfigurationProducer.kt +++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/cli/KotlinNodeJsRunConfigurationProducer.kt @@ -7,14 +7,16 @@ package org.jetbrains.kotlin.idea.nodejs.cli import com.intellij.execution.actions.ConfigurationContext import com.intellij.execution.actions.RunConfigurationProducer +import com.intellij.openapi.module.Module import com.intellij.openapi.util.Ref import com.intellij.openapi.util.io.FileUtil import com.intellij.psi.PsiElement import com.jetbrains.nodejs.run.NodeJsRunConfiguration import com.jetbrains.nodejs.run.NodeJsRunConfigurationType -import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.idea.MainFunctionDetector import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationData +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider import org.jetbrains.kotlin.idea.js.jsOrJsImpl import org.jetbrains.kotlin.idea.js.jsProductionOutputFilePath import org.jetbrains.kotlin.idea.nodejs.TestElementPath @@ -25,40 +27,51 @@ import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.psi.KtNamedFunction import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +class NodeJsConfigData( + override val element: PsiElement, + override val module: Module, + override val jsOutputFilePath: String +) : KotlinJSRunConfigurationData + private class KotlinNodeJsRunConfigurationProducer : - RunConfigurationProducer(NodeJsRunConfigurationType.getInstance()) { + RunConfigurationProducer(NodeJsRunConfigurationType.getInstance()), + KotlinJSRunConfigurationDataProvider { private val ConfigurationContext?.isAcceptable: Boolean get() { val original = this?.getOriginalConfiguration(null) return original == null || original is NodeJsRunConfiguration } + override val isForTests: Boolean + get() = false + + private fun getConfigurationData(element: PsiElement, context: ConfigurationContext?): NodeJsConfigData? { + if (context != null && !context.isAcceptable) return null + val jsModule = element.module?.jsOrJsImpl() ?: return null + val jsFilePath = jsModule.jsProductionOutputFilePath ?: return null + val declaration = element.getNonStrictParentOfType() + if (declaration is KtNamedFunction) { + val detector = MainFunctionDetector { it.resolveToDescriptorIfAny() } + if (!detector.isMain(declaration, false)) return null + } else if (!TestElementPath.isModuleAssociatedDir(element, jsModule)) return null + return NodeJsConfigData(element, jsModule, jsFilePath) + } + + override fun getConfigurationData(element: PsiElement) = getConfigurationData(element, null) + override fun setupConfigurationFromContext( configuration: NodeJsRunConfiguration, context: ConfigurationContext, sourceElement: Ref ): Boolean { - if (!context.isAcceptable) return false - val psiElement = sourceElement.get() ?: return false - val psiFile = psiElement.containingFile ?: return false - val jsModule = psiFile.module?.jsOrJsImpl() ?: return false - val project = psiFile.project - - val declaration = psiElement.getNonStrictParentOfType() - if (declaration is KtNamedFunction) { - val detector = MainFunctionDetector { it.resolveToDescriptorIfAny() } - if (!detector.isMain(declaration, false)) return false - } - else if (!TestElementPath.isModuleAssociatedDir(psiElement, jsModule)) return false - - val jsFilePath = jsModule.jsProductionOutputFilePath ?: return false + val configData = getConfigurationData(psiElement, context) ?: return false if (configuration.workingDirectory.isNullOrBlank()) { - configuration.workingDirectory = FileUtil.toSystemDependentName(project.baseDir.path) + configuration.workingDirectory = FileUtil.toSystemDependentName(psiElement.project.baseDir.path) } - configuration.inputPath = jsFilePath - configuration.envs = jsModule.getNodeJsEnvironmentVars(false).envs + configuration.inputPath = configData.jsOutputFilePath + configuration.envs = configData.module.getNodeJsEnvironmentVars(false).envs configuration.setGeneratedName() configuration.addBuildTask() @@ -66,11 +79,8 @@ private class KotlinNodeJsRunConfigurationProducer : } override fun isConfigurationFromContext(configuration: NodeJsRunConfiguration, context: ConfigurationContext): Boolean { - if (!context.isAcceptable) return false - val contextPsi = context.psiLocation ?: return false - val jsModule = contextPsi.module?.jsOrJsImpl() ?: return false - val jsFilePath = jsModule.jsProductionOutputFilePath ?: return false - return configuration.inputPath == jsFilePath + val configData = getConfigurationData(contextPsi, context) ?: return false + return configuration.inputPath == configData.jsOutputFilePath } } \ No newline at end of file diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jest/KotlinJestRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jest/KotlinJestRunConfigurationProducer.kt index fe907a3d2d7..466a2086295 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jest/KotlinJestRunConfigurationProducer.kt +++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jest/KotlinJestRunConfigurationProducer.kt @@ -10,35 +10,49 @@ import com.intellij.javascript.jest.JestRunConfiguration import com.intellij.javascript.jest.JestRunConfigurationProducer import com.intellij.javascript.jest.JestRunSettings import com.intellij.javascript.jest.scope.JestScopeKind +import com.intellij.openapi.module.Module import com.intellij.openapi.util.Ref import com.intellij.openapi.util.io.FileUtil import com.intellij.psi.PsiElement import com.intellij.psi.util.PsiUtilCore +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationData import org.jetbrains.kotlin.idea.js.jsOrJsImpl import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider import org.jetbrains.kotlin.idea.nodejs.TestElementInfo import org.jetbrains.kotlin.idea.nodejs.TestElementPath import org.jetbrains.kotlin.idea.nodejs.getNodeJsEnvironmentVars import org.jetbrains.kotlin.idea.run.addBuildTask import org.jetbrains.kotlin.idea.util.projectStructure.module -private typealias JestTestElementInfo = TestElementInfo +typealias JestTestElementInfo = TestElementInfo + +class JestConfigData( + override val element: PsiElement, + override val module: Module, + override val jsOutputFilePath: String, + val testElementPath: TestElementPath +) : KotlinJSRunConfigurationData + +class KotlinJestRunConfigurationProducer : + JestRunConfigurationProducer(), + KotlinJSRunConfigurationDataProvider { + private fun createTestElementRunInfo( + configData: JestConfigData, + originalSettings: JestRunSettings + ): JestTestElementInfo { + val project = configData.module.project -class KotlinJestRunConfigurationProducer : JestRunConfigurationProducer() { - private fun createTestElementRunInfo(element: PsiElement, originalSettings: JestRunSettings): JestTestElementInfo? { - val module = element.module?.jsOrJsImpl() ?: return null - val project = module.project - val testFilePath = module.jsTestOutputFilePath ?: return null val settings = if (originalSettings.workingDirSystemDependentPath.isBlank()) { val workingDir = FileUtil.toSystemDependentName(project.baseDir.path) originalSettings.toBuilder().setWorkingDir(workingDir).build() } else originalSettings - val testElementPath = TestElementPath.forElement(element, module) ?: return null + val builder = settings.toBuilder() - builder.setTestFilePath(testFilePath) - when (testElementPath) { + builder.setTestFilePath(configData.jsOutputFilePath) + when (configData.testElementPath) { is TestElementPath.BySuite -> { - val (suiteNames, testName) = testElementPath + val (suiteNames, testName) = configData.testElementPath if (testName == null) { builder.setScopeKind(JestScopeKind.SUITE) builder.setTestNames(suiteNames) @@ -52,14 +66,38 @@ class KotlinJestRunConfigurationProducer : JestRunConfigurationProducer() { builder.setScopeKind(JestScopeKind.TEST_FILE) } } - builder.setEnvData(module.getNodeJsEnvironmentVars(true)) + builder.setEnvData(configData.module.getNodeJsEnvironmentVars(true)) - return JestTestElementInfo(builder.build(), element) + return JestTestElementInfo(builder.build(), configData.element) + } + + override val isForTests: Boolean + get() = true + + override fun getConfigurationData(element: PsiElement): JestConfigData? { + val module = element.module + val jsModule = module?.jsOrJsImpl() ?: return null + + val file = if (jsModule != module) { + jsModule.moduleFile + } else { + PsiUtilCore.getVirtualFile(element) + } ?: return null + + val project = module.project + + if (!isTestRunnerPackageAvailableFor(project, file)) return null + + val testFilePath = module.jsTestOutputFilePath ?: return null + val testElementPath = TestElementPath.forElement(element, module) ?: return null + + return JestConfigData(element, jsModule, testFilePath, testElementPath) } override fun isConfigurationFromCompatibleContext(configuration: JestRunConfiguration, context: ConfigurationContext): Boolean { val element = context.psiLocation ?: return false - val (thisRunSettings, _) = createTestElementRunInfo(element, configuration.runSettings) ?: return false + val configData = getConfigurationData(element) ?: return false + val (thisRunSettings, _) = createTestElementRunInfo(configData, configuration.runSettings) val thatRunSettings = configuration.runSettings if (thisRunSettings.configFileSystemDependentPath != thatRunSettings.configFileSystemDependentPath) return false @@ -80,18 +118,8 @@ class KotlinJestRunConfigurationProducer : JestRunConfigurationProducer() { sourceElement: Ref ): Boolean { val element = context.psiLocation ?: return false - val module = element.module - val jsModule = module?.jsOrJsImpl() ?: return false - val file = if (jsModule != module) { - jsModule.moduleFile - } else { - PsiUtilCore.getVirtualFile(element) - } ?: return false - val project = module.project - - if (!isTestRunnerPackageAvailableFor(project, file)) return false - - val (runSettings, enclosingTestElement) = createTestElementRunInfo(element, configuration.runSettings) ?: return false + val configData = getConfigurationData(element) ?: return false + val (runSettings, enclosingTestElement) = createTestElementRunInfo(configData, configuration.runSettings) configuration.runSettings = runSettings sourceElement.set(enclosingTestElement) configuration.setGeneratedName() diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt index bbd98374f85..56ccc10db7d 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt +++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt @@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.nodejs.mocha import com.intellij.execution.RunManager import com.intellij.execution.actions.ConfigurationContext +import com.intellij.openapi.module.Module import com.intellij.openapi.project.Project import com.intellij.openapi.util.Ref import com.intellij.openapi.util.io.FileUtil @@ -30,6 +31,8 @@ import com.intellij.util.SmartList import com.intellij.util.containers.SmartHashSet import com.jetbrains.nodejs.mocha.MochaUtil import com.jetbrains.nodejs.mocha.execution.* +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationData +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider import org.jetbrains.kotlin.idea.js.jsOrJsImpl import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath import org.jetbrains.kotlin.idea.nodejs.TestElementInfo @@ -40,79 +43,52 @@ import org.jetbrains.kotlin.idea.util.projectStructure.module private typealias MochaTestElementInfo = TestElementInfo -class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() { +class MochaConfigData( + override val element: PsiElement, + override val module: Module, + override val jsOutputFilePath: String, + val testElementPath: TestElementPath +) : KotlinJSRunConfigurationData + +class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer(), KotlinJSRunConfigurationDataProvider { // Copied from MochaRunConfigurationProducer.collectMochaTestRoots() private fun collectMochaTestRoots(project: Project): List { return RunManager - .getInstance(project) - .getConfigurationsList(MochaConfigurationType.getInstance()) - .filterIsInstance() - .mapNotNullTo(SmartList()) { configuration -> - val settings = configuration.runSettings - val path = when (settings.testKind) { - MochaTestKind.DIRECTORY -> settings.testDirPath - MochaTestKind.TEST_FILE, - MochaTestKind.SUITE, - MochaTestKind.TEST -> settings.testFilePath - else -> null - } - if (path.isNullOrBlank()) return@mapNotNullTo null - LocalFileSystem.getInstance().findFileByPath(path!!) + .getInstance(project) + .getConfigurationsList(MochaConfigurationType.getInstance()) + .filterIsInstance() + .mapNotNullTo(SmartList()) { configuration -> + val settings = configuration.runSettings + val path = when (settings.testKind) { + MochaTestKind.DIRECTORY -> settings.testDirPath + MochaTestKind.TEST_FILE, + MochaTestKind.SUITE, + MochaTestKind.TEST -> settings.testFilePath + else -> null } - } - - // Copied from MochaRunConfigurationProducer.isActiveFor() - private fun isActiveFor(element: PsiElement, context: ConfigurationContext): Boolean { - val module = element.module - val jsModule = module?.jsOrJsImpl() ?: return false - val file = if (jsModule != module) { - jsModule.moduleFile - } - else { - PsiUtilCore.getVirtualFile(element) - } ?: return false - val project = module.project - - if (isTestRunnerPackageAvailableFor(project, file)) return true - - if (context.getOriginalConfiguration(MochaConfigurationType.getInstance()) is MochaRunConfiguration) return true - - val roots = collectMochaTestRoots(project) - if (roots.isEmpty()) return false - - val dirs = SmartHashSet() - for (root in roots) { - if (root.isDirectory) { - dirs.add(root) + if (path.isNullOrBlank()) return@mapNotNullTo null + LocalFileSystem.getInstance().findFileByPath(path!!) } - else if (root == file) return true - } - return VfsUtilCore.isUnder(file, dirs) } - private fun createTestElementRunInfo(element: PsiElement, originalSettings: MochaRunSettings): MochaTestElementInfo? { - val module = element.module?.jsOrJsImpl() ?: return null - val project = module.project - val testFilePath = module.jsTestOutputFilePath ?: return null + private fun createTestElementRunInfo(configData: MochaConfigData, originalSettings: MochaRunSettings): MochaTestElementInfo { + val project = configData.module.project val settings = if (originalSettings.workingDir.isBlank()) { val workingDir = FileUtil.toSystemDependentName(project.baseDir.path) originalSettings.builder().setWorkingDir(workingDir).build() - } - else originalSettings - val testElementPath = TestElementPath.forElement(element, module) ?: return null + } else originalSettings val builder = settings.builder() - builder.setTestFilePath(testFilePath) + builder.setTestFilePath(configData.jsOutputFilePath) if (settings.ui.isEmpty()) { builder.setUi(MochaUtil.UI_BDD) } - when (testElementPath) { + when (configData.testElementPath) { is TestElementPath.BySuite -> { - val (suiteNames, testName) = testElementPath + val (suiteNames, testName) = configData.testElementPath if (testName == null) { builder.setTestKind(MochaTestKind.SUITE) builder.setSuiteNames(suiteNames) - } - else { + } else { builder.setTestKind(MochaTestKind.TEST) builder.setTestNames(suiteNames + testName) } @@ -123,14 +99,53 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() { } } - builder.setEnvData(module.getNodeJsEnvironmentVars(true)) + builder.setEnvData(configData.module.getNodeJsEnvironmentVars(true)) - return MochaTestElementInfo(builder.build(), element) + return MochaTestElementInfo(builder.build(), configData.element) + } + + private fun getConfigurationData(element: PsiElement, context: ConfigurationContext?): MochaConfigData? { + val module = element.module + val jsModule = module?.jsOrJsImpl() ?: return null + val file = if (jsModule != module) { + jsModule.moduleFile + } else { + PsiUtilCore.getVirtualFile(element) + } ?: return null + val project = module.project + + if (isTestRunnerPackageAvailableFor(project, file)) return null + + val testFilePath = module.jsTestOutputFilePath ?: return null + val testElementPath = TestElementPath.forElement(element, module) ?: return null + + val configData = MochaConfigData(element, jsModule, testFilePath, testElementPath) + + if (context?.getOriginalConfiguration(MochaConfigurationType.getInstance()) is MochaRunConfiguration) return configData + + val roots = collectMochaTestRoots(project) + if (roots.isEmpty()) return null + + val dirs = SmartHashSet() + for (root in roots) { + if (root.isDirectory) { + dirs.add(root) + } else if (root == file) return configData + } + return if (VfsUtilCore.isUnder(file, dirs)) configData else configData + } + + override val isForTests: Boolean + get() = true + + override fun getConfigurationData(element: PsiElement): MochaConfigData? { + return getConfigurationData(element) } override fun isConfigurationFromCompatibleContext(configuration: MochaRunConfiguration, context: ConfigurationContext): Boolean { val element = context.psiLocation ?: return false - val (thisRunSettings, _) = createTestElementRunInfo(element, configuration.runSettings) ?: return false + val configData = getConfigurationData(element) ?: return false + val (thisRunSettings, _) = createTestElementRunInfo(configData, configuration.runSettings) val thatRunSettings = configuration.runSettings val thisTestKind = thisRunSettings.testKind if (thisTestKind != thatRunSettings.testKind) return false @@ -145,13 +160,13 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() { } override fun setupConfigurationFromCompatibleContext( - configuration: MochaRunConfiguration, - context: ConfigurationContext, - sourceElement: Ref + configuration: MochaRunConfiguration, + context: ConfigurationContext, + sourceElement: Ref ): Boolean { val element = context.psiLocation ?: return false - if (!isActiveFor(element, context)) return false - val (runSettings, enclosingTestElement) = createTestElementRunInfo(element, configuration.runSettings) ?: return false + val configData = getConfigurationData(element, context) ?: return false + val (runSettings, enclosingTestElement) = createTestElementRunInfo(configData, configuration.runSettings) if (runSettings.testKind == MochaTestKind.DIRECTORY) return false configuration.runSettings = runSettings sourceElement.set(enclosingTestElement) diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt index 370134cfa9a..a76b268c571 100644 --- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt +++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt @@ -18,44 +18,59 @@ package org.jetbrains.kotlin.idea.nodejs.protractor import com.intellij.execution.actions.CompatibleRunConfigurationProducer import com.intellij.execution.actions.ConfigurationContext +import com.intellij.openapi.module.Module import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.util.Ref import com.intellij.openapi.util.io.FileUtil import com.intellij.psi.PsiElement +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationData +import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider import org.jetbrains.kotlin.idea.js.jsOrJsImpl import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath import org.jetbrains.kotlin.idea.nodejs.TestElementPath import org.jetbrains.kotlin.idea.nodejs.getNodeJsEnvironmentVars import org.jetbrains.kotlin.idea.run.addBuildTask -import org.jetbrains.kotlin.idea.util.projectStructure.module + +class ProtractorConfigData( + override val element: PsiElement, + override val module: Module, + override val jsOutputFilePath: String +) : KotlinJSRunConfigurationData class KotlinProtractorRunConfigurationProducer : - CompatibleRunConfigurationProducer(KotlinProtractorConfigurationType.getInstance()) { + CompatibleRunConfigurationProducer(KotlinProtractorConfigurationType.getInstance()), + KotlinJSRunConfigurationDataProvider { + override val isForTests: Boolean + get() = true + + override fun getConfigurationData(element: PsiElement): ProtractorConfigData? { + val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return null + if (!TestElementPath.isModuleAssociatedDir(element, module)) return null + val jsModule = module.jsOrJsImpl() ?: return null + val testFilePath = jsModule.jsTestOutputFilePath ?: return null + return ProtractorConfigData(element, module, testFilePath) + } + override fun isConfigurationFromCompatibleContext( - configuration: KotlinProtractorRunConfiguration, - context: ConfigurationContext + configuration: KotlinProtractorRunConfiguration, + context: ConfigurationContext ): Boolean { val contextPsi = context.psiLocation ?: return false - val jsModule = contextPsi.module?.jsOrJsImpl() ?: return false - val testFilePath = jsModule.jsTestOutputFilePath ?: return false - return configuration.runSettings.testFileSystemDependentPath == FileUtil.toSystemDependentName(testFilePath) + val configData = getConfigurationData(contextPsi) ?: return false + return configuration.runSettings.testFileSystemDependentPath == FileUtil.toSystemDependentName(configData.jsOutputFilePath) } override fun setupConfigurationFromCompatibleContext( - configuration: KotlinProtractorRunConfiguration, - context: ConfigurationContext, - sourceElement: Ref + configuration: KotlinProtractorRunConfiguration, + context: ConfigurationContext, + sourceElement: Ref ): Boolean { val element = context.psiLocation ?: return false - val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return false - if (!TestElementPath.isModuleAssociatedDir(element, module)) return false - val jsModule = module.jsOrJsImpl() ?: return false - val testFilePath = jsModule.jsTestOutputFilePath ?: return false - + val configData = getConfigurationData(element) ?: return false sourceElement.set(element) configuration.runSettings = configuration.runSettings.copy( - testFilePath = testFilePath, - envData = jsModule.getNodeJsEnvironmentVars(true) + testFilePath = configData.jsOutputFilePath, + envData = configData.module.getNodeJsEnvironmentVars(true) ) configuration.name = configuration.suggestedName() configuration.addBuildTask()