diff --git a/ultimate/resources/META-INF/kotlin-nodejs.xml b/ultimate/resources/META-INF/kotlin-nodejs.xml
index 4865475e0fe..e8a8cd73eb2 100644
--- a/ultimate/resources/META-INF/kotlin-nodejs.xml
+++ b/ultimate/resources/META-INF/kotlin-nodejs.xml
@@ -4,5 +4,6 @@
+
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jest/KotlinJestRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jest/KotlinJestRunConfigurationProducer.kt
new file mode 100644
index 00000000000..87e03fec9e1
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jest/KotlinJestRunConfigurationProducer.kt
@@ -0,0 +1,99 @@
+/*
+ * Copyright 2000-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.nodejs.jest
+
+import com.intellij.execution.actions.ConfigurationContext
+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.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.jsOrJsImpl
+import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath
+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.util.projectStructure.module
+
+private typealias JestTestElementInfo = TestElementInfo
+
+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) {
+ is TestElementPath.BySuite -> {
+ val (suiteNames, testName) = testElementPath
+ if (testName == null) {
+ builder.setScopeKind(JestScopeKind.SUITE)
+ builder.setTestNames(suiteNames)
+ } else {
+ builder.setScopeKind(JestScopeKind.TEST)
+ builder.setTestNames(suiteNames + testName)
+ }
+ }
+
+ is TestElementPath.BySingleFile -> {
+ builder.setScopeKind(JestScopeKind.TEST_FILE)
+ }
+ }
+ builder.setEnvData(module.getNodeJsEnvironmentVars())
+
+ return JestTestElementInfo(builder.build(), element)
+ }
+
+ override fun isConfigurationFromCompatibleContext(configuration: JestRunConfiguration, context: ConfigurationContext): Boolean {
+ val element = context.psiLocation ?: return false
+ val (thisRunSettings, _) = createTestElementRunInfo(element, configuration.runSettings) ?: return false
+ val thatRunSettings = configuration.runSettings
+
+ if (thisRunSettings.configFileSystemDependentPath != thatRunSettings.configFileSystemDependentPath) return false
+ if (thatRunSettings.scopeKind != thisRunSettings.scopeKind) return false
+ return when (thisRunSettings.scopeKind) {
+ JestScopeKind.ALL -> true
+ JestScopeKind.TEST_FILE -> thisRunSettings.testFileSystemDependentPath == thatRunSettings.testFileSystemDependentPath
+ JestScopeKind.SUITE, JestScopeKind.TEST ->
+ thisRunSettings.testFileSystemDependentPath == thatRunSettings.testFileSystemDependentPath &&
+ thisRunSettings.testNames == thatRunSettings.testNames
+ else -> false
+ }
+ }
+
+ override fun setupConfigurationFromCompatibleContext(
+ configuration: JestRunConfiguration,
+ context: ConfigurationContext,
+ 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
+ configuration.runSettings = runSettings
+ sourceElement.set(enclosingTestElement)
+ configuration.setGeneratedName()
+ return true
+ }
+}
\ No newline at end of file
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jsTestUtils.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jsTestUtils.kt
new file mode 100644
index 00000000000..f05eb81dbe4
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/jsTestUtils.kt
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2000-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.nodejs
+
+import com.intellij.openapi.module.Module
+import com.intellij.psi.PsiDirectory
+import com.intellij.psi.PsiElement
+import org.jetbrains.kotlin.idea.util.projectStructure.getModuleDir
+import org.jetbrains.kotlin.psi.KtClassOrObject
+import org.jetbrains.kotlin.psi.KtNamedDeclaration
+import org.jetbrains.kotlin.psi.KtNamedFunction
+import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
+import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
+import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
+
+data class TestElementInfo(val runSettings: Settings, val enclosingTestElement: PsiElement)
+
+sealed class TestElementPath {
+ data class BySuite(val suiteNames: List, val testName: String?) : TestElementPath()
+ object BySingleFile : TestElementPath()
+
+ companion object {
+ fun isModuleAssociatedDir(element: PsiElement, module: Module): Boolean {
+ return element is PsiDirectory && module.getModuleDir() == element.virtualFile.path
+ }
+
+ fun forElement(element: PsiElement, module: Module): TestElementPath? {
+ if (isModuleAssociatedDir(element, module)) return TestElementPath.BySingleFile
+
+ val declaration = element.getNonStrictParentOfType() ?: return null
+ val klass = when (declaration) {
+ is KtClassOrObject -> declaration
+ is KtNamedFunction -> declaration.containingClassOrObject ?: return null
+ else -> return null
+ }
+ val suiteNames = klass.parentsWithSelf
+ .filterIsInstance()
+ .mapNotNull { it.name }
+ .toList()
+ .asReversed()
+ val testName = (declaration as? KtNamedFunction)?.name
+ return TestElementPath.BySuite(suiteNames, testName)
+ }
+ }
+}
\ No newline at end of file
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 573cc44d6f0..c3cc231e1aa 100644
--- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt
@@ -18,15 +18,12 @@ 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.module.ModuleUtilCore
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.io.FileUtil
import com.intellij.openapi.vfs.LocalFileSystem
import com.intellij.openapi.vfs.VfsUtilCore
import com.intellij.openapi.vfs.VirtualFile
-import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiUtilCore
import com.intellij.util.SmartList
@@ -35,25 +32,14 @@ import com.jetbrains.nodejs.mocha.MochaUtil
import com.jetbrains.nodejs.mocha.execution.*
import org.jetbrains.kotlin.idea.js.jsOrJsImpl
import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath
+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.project.TargetPlatformDetector
-import org.jetbrains.kotlin.idea.util.projectStructure.getModuleDir
import org.jetbrains.kotlin.idea.util.projectStructure.module
-import org.jetbrains.kotlin.js.resolve.JsPlatform
-import org.jetbrains.kotlin.psi.KtClassOrObject
-import org.jetbrains.kotlin.psi.KtNamedDeclaration
-import org.jetbrains.kotlin.psi.KtNamedFunction
-import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
-import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
-import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
+
+private typealias MochaTestElementInfo = TestElementInfo
class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() {
- private data class TestElementInfo(val runSettings: MochaRunSettings, val enclosingTestElement: PsiElement)
- private sealed class TestElementPath {
- data class BySuite(val suiteNames: List, val testName: String?) : TestElementPath()
- object BySingleFile : TestElementPath()
- }
-
// Copied from MochaRunConfigurationProducer.collectMochaTestRoots()
private fun collectMochaTestRoots(project: Project): List {
return RunManager
@@ -103,27 +89,7 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() {
return VfsUtilCore.isUnder(file, dirs)
}
- private fun createSuiteOrTestData(element: PsiElement, module: Module): TestElementPath? {
- if (element is PsiDirectory && module.getModuleDir() == element.virtualFile.path) {
- return TestElementPath.BySingleFile
- }
-
- val declaration = element.getNonStrictParentOfType() ?: return null
- val klass = when (declaration) {
- is KtClassOrObject -> declaration
- is KtNamedFunction -> declaration.containingClassOrObject ?: return null
- else -> return null
- }
- val suiteNames = klass.parentsWithSelf
- .filterIsInstance()
- .mapNotNull { it.name }
- .toList()
- .asReversed()
- val testName = (declaration as? KtNamedFunction)?.name
- return TestElementPath.BySuite(suiteNames, testName)
- }
-
- private fun createTestElementRunInfo(element: PsiElement, originalSettings: MochaRunSettings): TestElementInfo? {
+ 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
@@ -132,7 +98,7 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() {
originalSettings.builder().setWorkingDir(workingDir).build()
}
else originalSettings
- val testElementPath = createSuiteOrTestData(element, module) ?: return null
+ val testElementPath = TestElementPath.forElement(element, module) ?: return null
val builder = settings.builder()
builder.setTestFilePath(testFilePath)
if (settings.ui.isEmpty()) {
@@ -158,7 +124,7 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() {
builder.setEnvData(module.getNodeJsEnvironmentVars())
- return TestElementInfo(builder.build(), element)
+ return MochaTestElementInfo(builder.build(), element)
}
override fun isConfigurationFromCompatibleContext(configuration: MochaRunConfiguration, context: ConfigurationContext): Boolean {
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 006638e182d..0a8fd884eac 100644
--- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt
@@ -21,17 +21,12 @@ import com.intellij.execution.actions.ConfigurationContext
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.io.FileUtil
-import com.intellij.psi.PsiDirectory
import com.intellij.psi.PsiElement
-import org.jetbrains.kotlin.idea.facet.implementingModules
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.project.TargetPlatformDetector
-import org.jetbrains.kotlin.resolve.TargetPlatform.Common
-import org.jetbrains.kotlin.idea.util.projectStructure.getModuleDir
import org.jetbrains.kotlin.idea.util.projectStructure.module
-import org.jetbrains.kotlin.js.resolve.JsPlatform
class KotlinProtractorRunConfigurationProducer :
CompatibleRunConfigurationProducer(KotlinProtractorConfigurationType.getInstance()) {
@@ -52,7 +47,7 @@ class KotlinProtractorRunConfigurationProducer :
): Boolean {
val element = context.psiLocation ?: return false
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return false
- if (element !is PsiDirectory || module.getModuleDir() != element.virtualFile.path) return false
+ if (!TestElementPath.isModuleAssociatedDir(element, module)) return false
val jsModule = module.jsOrJsImpl() ?: return false
val testFilePath = jsModule.jsTestOutputFilePath ?: return false