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