Test Support: KT-22306

Do not show run markers in JS modules when no relevant run configurations are available

 #KT-22306 Fixed
This commit is contained in:
Alexey Sedunov
2018-03-22 19:59:22 +03:00
parent 3e91346240
commit b52b07ec79
13 changed files with 294 additions and 144 deletions
@@ -49,6 +49,16 @@ public class ForTestCompileRuntime {
return assertExists(new File("dist/kotlinc/lib/kotlin-test.jar")); 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 @NotNull
public static File reflectJarForTests() { public static File reflectJarForTests() {
return assertExists(new File("dist/kotlinc/lib/kotlin-reflect.jar")); return assertExists(new File("dist/kotlinc/lib/kotlin-reflect.jar"));
@@ -16,14 +16,19 @@
package org.jetbrains.kotlin.idea.highlighter package org.jetbrains.kotlin.idea.highlighter
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.execution.lineMarker.ExecutorAction import com.intellij.execution.lineMarker.ExecutorAction
import com.intellij.execution.lineMarker.RunLineMarkerContributor import com.intellij.execution.lineMarker.RunLineMarkerContributor
import com.intellij.icons.AllIcons import com.intellij.icons.AllIcons
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.idea.MainFunctionDetector import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny 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.psi.KtNamedFunction
import org.jetbrains.kotlin.resolve.TargetPlatform
import org.jetbrains.kotlin.resolve.jvm.platform.JvmPlatform
class KotlinRunLineMarkerContributor : RunLineMarkerContributor() { class KotlinRunLineMarkerContributor : RunLineMarkerContributor() {
override fun getInfo(element: PsiElement): Info? { override fun getInfo(element: PsiElement): Info? {
@@ -36,6 +41,22 @@ class KotlinRunLineMarkerContributor : RunLineMarkerContributor() {
} }
if (detector.isMain(function)) { 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<KotlinJSRunConfigurationDataProvider<*>>()
.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)) return RunLineMarkerContributor.Info(AllIcons.RunConfigurations.TestState.Run, null, ExecutorAction.getActions(0))
} }
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.highlighter
import com.intellij.codeInsight.TestFrameworks import com.intellij.codeInsight.TestFrameworks
import com.intellij.execution.TestStateStorage import com.intellij.execution.TestStateStorage
import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.execution.lineMarker.ExecutorAction import com.intellij.execution.lineMarker.ExecutorAction
import com.intellij.execution.lineMarker.RunLineMarkerContributor import com.intellij.execution.lineMarker.RunLineMarkerContributor
import com.intellij.execution.testframework.TestIconMapper 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.ClassDescriptorWithResolutionScopes
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.js.jsOrJsImpl import org.jetbrains.kotlin.idea.js.KotlinJSRunConfigurationDataProvider
import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath
import org.jetbrains.kotlin.idea.project.TargetPlatformDetector 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.idea.util.string.joinWithEscape
import org.jetbrains.kotlin.js.resolve.JsPlatform import org.jetbrains.kotlin.js.resolve.JsPlatform
import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.FqName
@@ -110,12 +109,17 @@ class KotlinTestRunLineMarkerContributor : RunLineMarkerContributor() {
private fun getJavaScriptTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? { private fun getJavaScriptTestIcon(declaration: KtNamedDeclaration, descriptor: DeclarationDescriptor): Icon? {
if (!descriptor.isTest()) return null if (!descriptor.isTest()) return null
val module = declaration.module?.jsOrJsImpl() ?: return null val runConfigData = RunConfigurationProducer
val testFilePath = module.jsTestOutputFilePath ?: return null .getProducers(declaration.project)
.asSequence()
.filterIsInstance<KotlinJSRunConfigurationDataProvider<*>>()
.filter { it.isForTests }
.mapNotNull { it.getConfigurationData(declaration) }
.firstOrNull() ?: return null
val locations = ArrayList<String>() val locations = ArrayList<String>()
locations += FileUtil.toSystemDependentName(testFilePath) locations += FileUtil.toSystemDependentName(runConfigData.jsOutputFilePath)
val klass = when (declaration) { val klass = when (declaration) {
is KtClassOrObject -> declaration is KtClassOrObject -> declaration
@@ -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<out T : KotlinJSRunConfigurationData> {
val isForTests: Boolean
fun getConfigurationData(element: PsiElement): T?
}
@@ -1,6 +1,3 @@
// !CHECK_HIGHLIGHTING // !CHECK_HIGHLIGHTING
package kotlin.test package test
annotation class Test
annotation class Ignore
@@ -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
}
}
@@ -11,8 +11,8 @@ class <lineMarker descr="Run Test">SimpleTest</lineMarker> {
} }
@Ignore @Ignore
class TestTest { class <lineMarker descr="Run Test">TestTest</lineMarker> {
@Test fun emptyTest() { @Test fun <lineMarker descr="Run Test">emptyTest</lineMarker>() {
// Will not run // Will not run
} }
} }
@@ -55,6 +55,10 @@ fun AbstractMultiModuleTest.setupMppProjectFromDirStructure(testRoot: File) {
}) })
} }
is CoroutinesDependency -> module.enableCoroutines() 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("stdlib", ignoreCase = true) -> StdlibDependency
dependencyString.equals("fulljdk", ignoreCase = true) -> FullJdkDependency dependencyString.equals("fulljdk", ignoreCase = true) -> FullJdkDependency
dependencyString.equals("coroutines", ignoreCase = true) -> CoroutinesDependency dependencyString.equals("coroutines", ignoreCase = true) -> CoroutinesDependency
dependencyString.equals("kotlin-test", ignoreCase = true) -> KotlinTestDependency
else -> ModuleDependency(parseModuleId(dependencyString.split("-"))) else -> ModuleDependency(parseModuleId(dependencyString.split("-")))
} }
} }
@@ -191,3 +196,4 @@ private class ModuleDependency(val moduleId: ModuleId) : Dependency()
private object StdlibDependency : Dependency() private object StdlibDependency : Dependency()
private object FullJdkDependency : Dependency() private object FullJdkDependency : Dependency()
private object CoroutinesDependency : Dependency() private object CoroutinesDependency : Dependency()
private object KotlinTestDependency : Dependency()
@@ -55,6 +55,10 @@ fun AbstractMultiModuleTest.setupMppProjectFromDirStructure(testRoot: File) {
}) })
} }
is CoroutinesDependency -> module.enableCoroutines() 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("stdlib", ignoreCase = true) -> StdlibDependency
dependencyString.equals("fulljdk", ignoreCase = true) -> FullJdkDependency dependencyString.equals("fulljdk", ignoreCase = true) -> FullJdkDependency
dependencyString.equals("coroutines", ignoreCase = true) -> CoroutinesDependency dependencyString.equals("coroutines", ignoreCase = true) -> CoroutinesDependency
dependencyString.equals("kotlin-test", ignoreCase = true) -> KotlinTestDependency
else -> ModuleDependency(parseModuleId(dependencyString.split("-"))) else -> ModuleDependency(parseModuleId(dependencyString.split("-")))
} }
} }
@@ -191,3 +196,4 @@ private class ModuleDependency(val moduleId: ModuleId) : Dependency()
private object StdlibDependency : Dependency() private object StdlibDependency : Dependency()
private object FullJdkDependency : Dependency() private object FullJdkDependency : Dependency()
private object CoroutinesDependency : Dependency() private object CoroutinesDependency : Dependency()
private object KotlinTestDependency : Dependency()
@@ -7,14 +7,16 @@ package org.jetbrains.kotlin.idea.nodejs.cli
import com.intellij.execution.actions.ConfigurationContext import com.intellij.execution.actions.ConfigurationContext
import com.intellij.execution.actions.RunConfigurationProducer import com.intellij.execution.actions.RunConfigurationProducer
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.jetbrains.nodejs.run.NodeJsRunConfiguration import com.jetbrains.nodejs.run.NodeJsRunConfiguration
import com.jetbrains.nodejs.run.NodeJsRunConfigurationType import com.jetbrains.nodejs.run.NodeJsRunConfigurationType
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.MainFunctionDetector import org.jetbrains.kotlin.idea.MainFunctionDetector
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny 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.jsOrJsImpl
import org.jetbrains.kotlin.idea.js.jsProductionOutputFilePath import org.jetbrains.kotlin.idea.js.jsProductionOutputFilePath
import org.jetbrains.kotlin.idea.nodejs.TestElementPath 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.KtNamedFunction
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType 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 : private class KotlinNodeJsRunConfigurationProducer :
RunConfigurationProducer<NodeJsRunConfiguration>(NodeJsRunConfigurationType.getInstance()) { RunConfigurationProducer<NodeJsRunConfiguration>(NodeJsRunConfigurationType.getInstance()),
KotlinJSRunConfigurationDataProvider<NodeJsConfigData> {
private val ConfigurationContext?.isAcceptable: Boolean private val ConfigurationContext?.isAcceptable: Boolean
get() { get() {
val original = this?.getOriginalConfiguration(null) val original = this?.getOriginalConfiguration(null)
return original == null || original is NodeJsRunConfiguration 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<KtNamedDeclaration>()
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( override fun setupConfigurationFromContext(
configuration: NodeJsRunConfiguration, configuration: NodeJsRunConfiguration,
context: ConfigurationContext, context: ConfigurationContext,
sourceElement: Ref<PsiElement> sourceElement: Ref<PsiElement>
): Boolean { ): Boolean {
if (!context.isAcceptable) return false
val psiElement = sourceElement.get() ?: return false val psiElement = sourceElement.get() ?: return false
val psiFile = psiElement.containingFile ?: return false val configData = getConfigurationData(psiElement, context) ?: return false
val jsModule = psiFile.module?.jsOrJsImpl() ?: return false
val project = psiFile.project
val declaration = psiElement.getNonStrictParentOfType<KtNamedDeclaration>()
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
if (configuration.workingDirectory.isNullOrBlank()) { if (configuration.workingDirectory.isNullOrBlank()) {
configuration.workingDirectory = FileUtil.toSystemDependentName(project.baseDir.path) configuration.workingDirectory = FileUtil.toSystemDependentName(psiElement.project.baseDir.path)
} }
configuration.inputPath = jsFilePath configuration.inputPath = configData.jsOutputFilePath
configuration.envs = jsModule.getNodeJsEnvironmentVars(false).envs configuration.envs = configData.module.getNodeJsEnvironmentVars(false).envs
configuration.setGeneratedName() configuration.setGeneratedName()
configuration.addBuildTask() configuration.addBuildTask()
@@ -66,11 +79,8 @@ private class KotlinNodeJsRunConfigurationProducer :
} }
override fun isConfigurationFromContext(configuration: NodeJsRunConfiguration, context: ConfigurationContext): Boolean { override fun isConfigurationFromContext(configuration: NodeJsRunConfiguration, context: ConfigurationContext): Boolean {
if (!context.isAcceptable) return false
val contextPsi = context.psiLocation ?: return false val contextPsi = context.psiLocation ?: return false
val jsModule = contextPsi.module?.jsOrJsImpl() ?: return false val configData = getConfigurationData(contextPsi, context) ?: return false
val jsFilePath = jsModule.jsProductionOutputFilePath ?: return false return configuration.inputPath == configData.jsOutputFilePath
return configuration.inputPath == jsFilePath
} }
} }
@@ -10,35 +10,49 @@ import com.intellij.javascript.jest.JestRunConfiguration
import com.intellij.javascript.jest.JestRunConfigurationProducer import com.intellij.javascript.jest.JestRunConfigurationProducer
import com.intellij.javascript.jest.JestRunSettings import com.intellij.javascript.jest.JestRunSettings
import com.intellij.javascript.jest.scope.JestScopeKind import com.intellij.javascript.jest.scope.JestScopeKind
import com.intellij.openapi.module.Module
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.util.PsiUtilCore 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.jsOrJsImpl
import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath 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.TestElementInfo
import org.jetbrains.kotlin.idea.nodejs.TestElementPath import org.jetbrains.kotlin.idea.nodejs.TestElementPath
import org.jetbrains.kotlin.idea.nodejs.getNodeJsEnvironmentVars import org.jetbrains.kotlin.idea.nodejs.getNodeJsEnvironmentVars
import org.jetbrains.kotlin.idea.run.addBuildTask import org.jetbrains.kotlin.idea.run.addBuildTask
import org.jetbrains.kotlin.idea.util.projectStructure.module import org.jetbrains.kotlin.idea.util.projectStructure.module
private typealias JestTestElementInfo = TestElementInfo<JestRunSettings> typealias JestTestElementInfo = TestElementInfo<JestRunSettings>
class JestConfigData(
override val element: PsiElement,
override val module: Module,
override val jsOutputFilePath: String,
val testElementPath: TestElementPath
) : KotlinJSRunConfigurationData
class KotlinJestRunConfigurationProducer :
JestRunConfigurationProducer(),
KotlinJSRunConfigurationDataProvider<JestConfigData> {
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 settings = if (originalSettings.workingDirSystemDependentPath.isBlank()) {
val workingDir = FileUtil.toSystemDependentName(project.baseDir.path) val workingDir = FileUtil.toSystemDependentName(project.baseDir.path)
originalSettings.toBuilder().setWorkingDir(workingDir).build() originalSettings.toBuilder().setWorkingDir(workingDir).build()
} else originalSettings } else originalSettings
val testElementPath = TestElementPath.forElement(element, module) ?: return null
val builder = settings.toBuilder() val builder = settings.toBuilder()
builder.setTestFilePath(testFilePath) builder.setTestFilePath(configData.jsOutputFilePath)
when (testElementPath) { when (configData.testElementPath) {
is TestElementPath.BySuite -> { is TestElementPath.BySuite -> {
val (suiteNames, testName) = testElementPath val (suiteNames, testName) = configData.testElementPath
if (testName == null) { if (testName == null) {
builder.setScopeKind(JestScopeKind.SUITE) builder.setScopeKind(JestScopeKind.SUITE)
builder.setTestNames(suiteNames) builder.setTestNames(suiteNames)
@@ -52,14 +66,38 @@ class KotlinJestRunConfigurationProducer : JestRunConfigurationProducer() {
builder.setScopeKind(JestScopeKind.TEST_FILE) 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 { override fun isConfigurationFromCompatibleContext(configuration: JestRunConfiguration, context: ConfigurationContext): Boolean {
val element = context.psiLocation ?: return false 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 thatRunSettings = configuration.runSettings
if (thisRunSettings.configFileSystemDependentPath != thatRunSettings.configFileSystemDependentPath) return false if (thisRunSettings.configFileSystemDependentPath != thatRunSettings.configFileSystemDependentPath) return false
@@ -80,18 +118,8 @@ class KotlinJestRunConfigurationProducer : JestRunConfigurationProducer() {
sourceElement: Ref<PsiElement> sourceElement: Ref<PsiElement>
): Boolean { ): Boolean {
val element = context.psiLocation ?: return false val element = context.psiLocation ?: return false
val module = element.module val configData = getConfigurationData(element) ?: return false
val jsModule = module?.jsOrJsImpl() ?: return false val (runSettings, enclosingTestElement) = createTestElementRunInfo(configData, configuration.runSettings)
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 configuration.runSettings = runSettings
sourceElement.set(enclosingTestElement) sourceElement.set(enclosingTestElement)
configuration.setGeneratedName() configuration.setGeneratedName()
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.nodejs.mocha
import com.intellij.execution.RunManager import com.intellij.execution.RunManager
import com.intellij.execution.actions.ConfigurationContext import com.intellij.execution.actions.ConfigurationContext
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
@@ -30,6 +31,8 @@ import com.intellij.util.SmartList
import com.intellij.util.containers.SmartHashSet import com.intellij.util.containers.SmartHashSet
import com.jetbrains.nodejs.mocha.MochaUtil import com.jetbrains.nodejs.mocha.MochaUtil
import com.jetbrains.nodejs.mocha.execution.* 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.jsOrJsImpl
import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath
import org.jetbrains.kotlin.idea.nodejs.TestElementInfo import org.jetbrains.kotlin.idea.nodejs.TestElementInfo
@@ -40,79 +43,52 @@ import org.jetbrains.kotlin.idea.util.projectStructure.module
private typealias MochaTestElementInfo = TestElementInfo<MochaRunSettings> private typealias MochaTestElementInfo = TestElementInfo<MochaRunSettings>
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<MochaConfigData> {
// Copied from MochaRunConfigurationProducer.collectMochaTestRoots() // Copied from MochaRunConfigurationProducer.collectMochaTestRoots()
private fun collectMochaTestRoots(project: Project): List<VirtualFile> { private fun collectMochaTestRoots(project: Project): List<VirtualFile> {
return RunManager return RunManager
.getInstance(project) .getInstance(project)
.getConfigurationsList(MochaConfigurationType.getInstance()) .getConfigurationsList(MochaConfigurationType.getInstance())
.filterIsInstance<MochaRunConfiguration>() .filterIsInstance<MochaRunConfiguration>()
.mapNotNullTo(SmartList<VirtualFile>()) { configuration -> .mapNotNullTo(SmartList<VirtualFile>()) { configuration ->
val settings = configuration.runSettings val settings = configuration.runSettings
val path = when (settings.testKind) { val path = when (settings.testKind) {
MochaTestKind.DIRECTORY -> settings.testDirPath MochaTestKind.DIRECTORY -> settings.testDirPath
MochaTestKind.TEST_FILE, MochaTestKind.TEST_FILE,
MochaTestKind.SUITE, MochaTestKind.SUITE,
MochaTestKind.TEST -> settings.testFilePath MochaTestKind.TEST -> settings.testFilePath
else -> null else -> null
}
if (path.isNullOrBlank()) return@mapNotNullTo null
LocalFileSystem.getInstance().findFileByPath(path!!)
} }
} if (path.isNullOrBlank()) return@mapNotNullTo null
LocalFileSystem.getInstance().findFileByPath(path!!)
// 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<VirtualFile>()
for (root in roots) {
if (root.isDirectory) {
dirs.add(root)
} }
else if (root == file) return true
}
return VfsUtilCore.isUnder(file, dirs)
} }
private fun createTestElementRunInfo(element: PsiElement, originalSettings: MochaRunSettings): MochaTestElementInfo? { private fun createTestElementRunInfo(configData: MochaConfigData, originalSettings: MochaRunSettings): MochaTestElementInfo {
val module = element.module?.jsOrJsImpl() ?: return null val project = configData.module.project
val project = module.project
val testFilePath = module.jsTestOutputFilePath ?: return null
val settings = if (originalSettings.workingDir.isBlank()) { val settings = if (originalSettings.workingDir.isBlank()) {
val workingDir = FileUtil.toSystemDependentName(project.baseDir.path) val workingDir = FileUtil.toSystemDependentName(project.baseDir.path)
originalSettings.builder().setWorkingDir(workingDir).build() originalSettings.builder().setWorkingDir(workingDir).build()
} } else originalSettings
else originalSettings
val testElementPath = TestElementPath.forElement(element, module) ?: return null
val builder = settings.builder() val builder = settings.builder()
builder.setTestFilePath(testFilePath) builder.setTestFilePath(configData.jsOutputFilePath)
if (settings.ui.isEmpty()) { if (settings.ui.isEmpty()) {
builder.setUi(MochaUtil.UI_BDD) builder.setUi(MochaUtil.UI_BDD)
} }
when (testElementPath) { when (configData.testElementPath) {
is TestElementPath.BySuite -> { is TestElementPath.BySuite -> {
val (suiteNames, testName) = testElementPath val (suiteNames, testName) = configData.testElementPath
if (testName == null) { if (testName == null) {
builder.setTestKind(MochaTestKind.SUITE) builder.setTestKind(MochaTestKind.SUITE)
builder.setSuiteNames(suiteNames) builder.setSuiteNames(suiteNames)
} } else {
else {
builder.setTestKind(MochaTestKind.TEST) builder.setTestKind(MochaTestKind.TEST)
builder.setTestNames(suiteNames + testName) 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<VirtualFile>()
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 { override fun isConfigurationFromCompatibleContext(configuration: MochaRunConfiguration, context: ConfigurationContext): Boolean {
val element = context.psiLocation ?: return false 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 thatRunSettings = configuration.runSettings
val thisTestKind = thisRunSettings.testKind val thisTestKind = thisRunSettings.testKind
if (thisTestKind != thatRunSettings.testKind) return false if (thisTestKind != thatRunSettings.testKind) return false
@@ -145,13 +160,13 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() {
} }
override fun setupConfigurationFromCompatibleContext( override fun setupConfigurationFromCompatibleContext(
configuration: MochaRunConfiguration, configuration: MochaRunConfiguration,
context: ConfigurationContext, context: ConfigurationContext,
sourceElement: Ref<PsiElement> sourceElement: Ref<PsiElement>
): Boolean { ): Boolean {
val element = context.psiLocation ?: return false val element = context.psiLocation ?: return false
if (!isActiveFor(element, context)) return false val configData = getConfigurationData(element, context) ?: return false
val (runSettings, enclosingTestElement) = createTestElementRunInfo(element, configuration.runSettings) ?: return false val (runSettings, enclosingTestElement) = createTestElementRunInfo(configData, configuration.runSettings)
if (runSettings.testKind == MochaTestKind.DIRECTORY) return false if (runSettings.testKind == MochaTestKind.DIRECTORY) return false
configuration.runSettings = runSettings configuration.runSettings = runSettings
sourceElement.set(enclosingTestElement) sourceElement.set(enclosingTestElement)
@@ -18,44 +18,59 @@ package org.jetbrains.kotlin.idea.nodejs.protractor
import com.intellij.execution.actions.CompatibleRunConfigurationProducer import com.intellij.execution.actions.CompatibleRunConfigurationProducer
import com.intellij.execution.actions.ConfigurationContext import com.intellij.execution.actions.ConfigurationContext
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.util.io.FileUtil
import com.intellij.psi.PsiElement 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.jsOrJsImpl
import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath import org.jetbrains.kotlin.idea.js.jsTestOutputFilePath
import org.jetbrains.kotlin.idea.nodejs.TestElementPath import org.jetbrains.kotlin.idea.nodejs.TestElementPath
import org.jetbrains.kotlin.idea.nodejs.getNodeJsEnvironmentVars import org.jetbrains.kotlin.idea.nodejs.getNodeJsEnvironmentVars
import org.jetbrains.kotlin.idea.run.addBuildTask 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 : class KotlinProtractorRunConfigurationProducer :
CompatibleRunConfigurationProducer<KotlinProtractorRunConfiguration>(KotlinProtractorConfigurationType.getInstance()) { CompatibleRunConfigurationProducer<KotlinProtractorRunConfiguration>(KotlinProtractorConfigurationType.getInstance()),
KotlinJSRunConfigurationDataProvider<ProtractorConfigData> {
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( override fun isConfigurationFromCompatibleContext(
configuration: KotlinProtractorRunConfiguration, configuration: KotlinProtractorRunConfiguration,
context: ConfigurationContext context: ConfigurationContext
): Boolean { ): Boolean {
val contextPsi = context.psiLocation ?: return false val contextPsi = context.psiLocation ?: return false
val jsModule = contextPsi.module?.jsOrJsImpl() ?: return false val configData = getConfigurationData(contextPsi) ?: return false
val testFilePath = jsModule.jsTestOutputFilePath ?: return false return configuration.runSettings.testFileSystemDependentPath == FileUtil.toSystemDependentName(configData.jsOutputFilePath)
return configuration.runSettings.testFileSystemDependentPath == FileUtil.toSystemDependentName(testFilePath)
} }
override fun setupConfigurationFromCompatibleContext( override fun setupConfigurationFromCompatibleContext(
configuration: KotlinProtractorRunConfiguration, configuration: KotlinProtractorRunConfiguration,
context: ConfigurationContext, context: ConfigurationContext,
sourceElement: Ref<PsiElement> sourceElement: Ref<PsiElement>
): Boolean { ): Boolean {
val element = context.psiLocation ?: return false val element = context.psiLocation ?: return false
val module = ModuleUtilCore.findModuleForPsiElement(element) ?: return false val configData = getConfigurationData(element) ?: return false
if (!TestElementPath.isModuleAssociatedDir(element, module)) return false
val jsModule = module.jsOrJsImpl() ?: return false
val testFilePath = jsModule.jsTestOutputFilePath ?: return false
sourceElement.set(element) sourceElement.set(element)
configuration.runSettings = configuration.runSettings.copy( configuration.runSettings = configuration.runSettings.copy(
testFilePath = testFilePath, testFilePath = configData.jsOutputFilePath,
envData = jsModule.getNodeJsEnvironmentVars(true) envData = configData.module.getNodeJsEnvironmentVars(true)
) )
configuration.name = configuration.suggestedName() configuration.name = configuration.suggestedName()
configuration.addBuildTask() configuration.addBuildTask()