diff --git a/ultimate/build.gradle.kts b/ultimate/build.gradle.kts
index bcb5fc03e8c..0b16e411ce1 100644
--- a/ultimate/build.gradle.kts
+++ b/ultimate/build.gradle.kts
@@ -37,7 +37,7 @@ dependencies {
compile(ideaUltimatePreloadedDeps("*.jar", subdir = "nodejs_plugin/NodeJS/lib"))
compile(ideaUltimateSdkCoreDeps("annotations", "trove4j", "intellij-core"))
- compile(ideaUltimateSdkDeps("openapi", "idea", "util"))
+ compile(ideaUltimateSdkDeps("openapi", "idea", "util", "jdom"))
compile(ideaUltimatePluginDeps("*.jar", plugin = "CSS"))
compile(ideaUltimatePluginDeps("*.jar", plugin = "DatabaseTools"))
compile(ideaUltimatePluginDeps("*.jar", plugin = "JavaEE"))
diff --git a/ultimate/resources/META-INF/kotlin-nodejs.xml b/ultimate/resources/META-INF/kotlin-nodejs.xml
index 8771420d1db..4865475e0fe 100644
--- a/ultimate/resources/META-INF/kotlin-nodejs.xml
+++ b/ultimate/resources/META-INF/kotlin-nodejs.xml
@@ -1,5 +1,8 @@
-
+
+
+
+
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/js/jsModuleUtils.kt b/ultimate/src/org/jetbrains/kotlin/idea/js/jsModuleUtils.kt
index 36235d8e754..df617d7747e 100644
--- a/ultimate/src/org/jetbrains/kotlin/idea/js/jsModuleUtils.kt
+++ b/ultimate/src/org/jetbrains/kotlin/idea/js/jsModuleUtils.kt
@@ -35,4 +35,4 @@ fun getJsClasspath(module: Module): List {
true
}
return result
-}
\ No newline at end of file
+}
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/KotlinMochaRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt
similarity index 90%
rename from ultimate/src/org/jetbrains/kotlin/idea/nodejs/KotlinMochaRunConfigurationProducer.kt
rename to ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt
index 919c99926c6..c221c00a862 100644
--- a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/KotlinMochaRunConfigurationProducer.kt
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/mocha/KotlinMochaRunConfigurationProducer.kt
@@ -1,8 +1,23 @@
-package org.jetbrains.kotlin.idea.nodejs
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.nodejs.mocha
import com.intellij.execution.RunManager
import com.intellij.execution.actions.ConfigurationContext
-import com.intellij.execution.configuration.EnvironmentVariablesData
import com.intellij.openapi.module.Module
import com.intellij.openapi.module.ModuleUtilCore
import com.intellij.openapi.project.Project
@@ -13,14 +28,13 @@ 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.impl.file.PsiJavaDirectoryImpl
import com.intellij.psi.util.PsiUtilCore
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.getJsClasspath
import org.jetbrains.kotlin.idea.js.getJsOutputFilePath
+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.js.resolve.JsPlatform
@@ -30,7 +44,6 @@ 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
-import java.io.File
class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() {
private data class TestElementInfo(val runSettings: MochaRunSettings, val enclosingTestElement: PsiElement)
@@ -80,7 +93,7 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() {
}
private fun createSuiteOrTestData(element: PsiElement, module: Module): TestElementPath? {
- if (element is PsiDirectory && module.getModuleDir() == (element as PsiJavaDirectoryImpl).virtualFile.path) {
+ if (element is PsiDirectory && module.getModuleDir() == element.virtualFile.path) {
return TestElementPath.BySingleFile
}
@@ -133,11 +146,7 @@ class KotlinMochaRunConfigurationProducer : MochaRunConfigurationProducer() {
}
}
- val nodeJsClasspath = getJsClasspath(module).joinToString(File.pathSeparator) {
- val basePath = project.basePath ?: return@joinToString it
- FileUtil.getRelativePath(basePath, it, '/') ?: it
- }
- builder.setEnvData(EnvironmentVariablesData.create(mapOf("NODE_PATH" to nodeJsClasspath), true))
+ builder.setEnvData(module.getNodeJsEnvironmentVars())
return TestElementInfo(builder.build(), element)
}
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/nodeJsUtil.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/nodeJsUtil.kt
new file mode 100644
index 00000000000..c352b188078
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/nodeJsUtil.kt
@@ -0,0 +1,33 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.nodejs
+
+import com.intellij.execution.configuration.EnvironmentVariablesData
+import com.intellij.openapi.module.Module
+import com.intellij.openapi.util.io.FileUtil
+import org.jetbrains.kotlin.idea.js.getJsClasspath
+import java.io.File
+
+const val NODE_PATH_VAR = "NODE_PATH"
+
+fun Module.getNodeJsEnvironmentVars(): EnvironmentVariablesData {
+ val nodeJsClasspath = getJsClasspath(this).joinToString(File.pathSeparator) {
+ val basePath = project.basePath ?: return@joinToString it
+ FileUtil.getRelativePath(basePath, it, '/') ?: it
+ }
+ return EnvironmentVariablesData.create(mapOf(NODE_PATH_VAR to nodeJsClasspath), true)
+}
\ No newline at end of file
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorConfigurationType.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorConfigurationType.kt
new file mode 100644
index 00000000000..5653970a61c
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorConfigurationType.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.nodejs.protractor
+
+import com.intellij.execution.configuration.ConfigurationFactoryEx
+import com.intellij.execution.configurations.ConfigurationFactory
+import com.intellij.execution.configurations.ConfigurationTypeBase
+import com.intellij.execution.configurations.ConfigurationTypeUtil
+import com.intellij.openapi.project.Project
+import icons.JavaScriptLanguageIcons
+
+class KotlinProtractorConfigurationType : ConfigurationTypeBase(
+ "KotlinJavaScriptTestRunnerProtractor",
+ "Protractor",
+ NAME,
+ JavaScriptLanguageIcons.Protractor.Protractor
+) {
+ init {
+ addFactory(object : ConfigurationFactoryEx(this) {
+ override fun createTemplateConfiguration(project: Project) = KotlinProtractorRunConfiguration(project, this, NAME)
+
+ override fun isConfigurationSingletonByDefault() = true
+
+ override fun canConfigurationBeSingleton() = false
+
+ override fun onNewConfigurationCreated(configuration: KotlinProtractorRunConfiguration) = configuration.initialize()
+ })
+ }
+
+ companion object {
+ private val NAME = "Protractor"
+
+ fun getInstance() = ConfigurationTypeUtil.findConfigurationType(KotlinProtractorConfigurationType::class.java)
+
+ val factory: ConfigurationFactory
+ get() = getInstance().configurationFactories.first()
+ }
+}
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfiguration.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfiguration.kt
new file mode 100644
index 00000000000..6fd3bb21603
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfiguration.kt
@@ -0,0 +1,103 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.nodejs.protractor
+
+import com.intellij.execution.Executor
+import com.intellij.execution.configurations.ConfigurationFactory
+import com.intellij.execution.configurations.LocatableConfigurationBase
+import com.intellij.execution.configurations.RuntimeConfigurationError
+import com.intellij.execution.runners.ExecutionEnvironment
+import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreter
+import com.intellij.javascript.nodejs.util.NodePackage
+import com.intellij.javascript.protractor.ProtractorUtil
+import com.intellij.openapi.project.Project
+import com.intellij.util.PathUtil
+import org.jdom.Element
+import java.io.File
+
+// Based on com.intellij.javascript.protractor.ProtractorRunConfiguration
+class KotlinProtractorRunConfiguration(
+ project: Project,
+ factory: ConfigurationFactory,
+ name: String
+) : LocatableConfigurationBase(project, factory, name) {
+ var runSettings = KotlinProtractorRunSettings()
+
+ val protractorPackage: NodePackage
+ get() {
+ val project = project
+ val interpreter = runSettings.interpreterRef.resolve(project)
+ return ProtractorUtil.getProtractorPackage(project, null, interpreter, true)
+ }
+
+ override fun getState(executor: Executor, environment: ExecutionEnvironment) =
+ KotlinProtractorRunState(this, environment, protractorPackage)
+
+ override fun getConfigurationEditor() = KotlinProtractorRunConfigurationEditor(project)
+
+ override fun readExternal(element: Element) {
+ super.readExternal(element)
+ runSettings = KotlinProtractorRunSettings.readFromXML(element)
+ }
+
+ override fun writeExternal(element: Element) {
+ super.writeExternal(element)
+ runSettings.writeToXML(element)
+ }
+
+ fun initialize() {
+ setNameChangedByUser(false)
+ }
+
+ override fun checkConfiguration() {
+ val isOptionalConfig = runSettings.testFileSystemDependentPath.isNotBlank() || runSettings.seleniumAddress.isNotBlank()
+ if (isOptionalConfig) {
+ validatePath("test file", runSettings.testFileSystemDependentPath, true, false)
+ if (runSettings.seleniumAddress.isBlank()) {
+ throw RuntimeConfigurationError("Unspecified Selenium address")
+ }
+ }
+ if (!isOptionalConfig || runSettings.configFileSystemDependentPath.isNotBlank()) {
+ validatePath("configuration file", runSettings.configFileSystemDependentPath, true, false)
+ }
+
+ val interpreter = runSettings.interpreterRef.resolve(project)
+ NodeJsLocalInterpreter.checkForRunConfiguration(interpreter)
+
+ validatePath("protractor package", protractorPackage.systemDependentPath, true, true)
+ }
+
+ override fun suggestedName(): String? {
+ val name = PathUtil.getFileName(runSettings.testFileSystemDependentPath)
+ return if (name.isNotBlank()) name else null
+ }
+
+ private fun validatePath(name: String,
+ path: String?,
+ shouldBeAbsolute: Boolean,
+ shouldBeDirectory: Boolean
+ ) {
+ if (path.isNullOrBlank()) throw RuntimeConfigurationError("Unspecified " + name)
+
+ val file = File(path!!)
+
+ if (shouldBeAbsolute && !file.isAbsolute) throw RuntimeConfigurationError("No such " + name)
+
+ val exists = if (shouldBeDirectory) file.isDirectory else file.isFile
+ if (!exists) throw RuntimeConfigurationError("No such " + name)
+ }
+}
\ No newline at end of file
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationEditor.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationEditor.kt
new file mode 100644
index 00000000000..3082aae25c7
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationEditor.kt
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.nodejs.protractor
+
+import com.intellij.execution.configuration.EnvironmentVariablesTextFieldWithBrowseButton
+import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterField
+import com.intellij.javascript.nodejs.util.NodePackageField
+import com.intellij.javascript.protractor.ProtractorUtil
+import com.intellij.openapi.application.ApplicationManager
+import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
+import com.intellij.openapi.options.SettingsEditor
+import com.intellij.openapi.options.ex.SingleConfigurableEditor
+import com.intellij.openapi.project.Project
+import com.intellij.openapi.ui.DialogWrapper
+import com.intellij.openapi.util.io.FileUtil
+import com.intellij.ui.RawCommandLineEditor
+import com.intellij.ui.TextFieldWithHistoryWithBrowseButton
+import com.intellij.util.containers.ContainerUtil
+import com.intellij.util.ui.FormBuilder
+import com.intellij.util.ui.JBUI
+import com.intellij.util.ui.SwingHelper
+
+import javax.swing.*
+import java.util.Collections
+
+// Based com.intellij.javascript.protractor.ProtractorRunConfigurationEditor
+class KotlinProtractorRunConfigurationEditor(private val myProject: Project) : SettingsEditor() {
+ private val testFilePathTextFieldWithBrowseButton: TextFieldWithHistoryWithBrowseButton
+ private val seleniumAddressTextField: JTextField
+ private val extraOptionsEditor: RawCommandLineEditor
+ private val configPathTextFieldWithBrowseButton: TextFieldWithHistoryWithBrowseButton
+ private val interpreterField: NodeJsInterpreterField
+ private val protractorPackageField: NodePackageField
+ private val envVarsComponent: EnvironmentVariablesTextFieldWithBrowseButton
+ private val panel: JPanel
+
+ init {
+ testFilePathTextFieldWithBrowseButton = createTestFileTextField(myProject)
+ seleniumAddressTextField = JTextField()
+ extraOptionsEditor = RawCommandLineEditor().apply { dialogCaption = "Extra Protractor Options" }
+ configPathTextFieldWithBrowseButton = createConfigurationFileTextField(myProject)
+ interpreterField = NodeJsInterpreterField(myProject, false)
+ protractorPackageField = NodePackageField(myProject, ProtractorUtil.PACKAGE_NAME) { interpreterField.interpreter }
+ envVarsComponent = EnvironmentVariablesTextFieldWithBrowseButton()
+ panel = FormBuilder()
+ .setAlignLabelOnRight(false)
+ .addLabeledComponent("&Test file:", testFilePathTextFieldWithBrowseButton)
+ .addLabeledComponent("&Selenium address:", seleniumAddressTextField)
+ .addLabeledComponent("E&xtra Protractor options:", extraOptionsEditor)
+ .addComponent(JSeparator(), JBUI.scale(8))
+ .addLabeledComponent("&Configuration file:", configPathTextFieldWithBrowseButton)
+ .addComponent(JSeparator(), JBUI.scale(8))
+ .addLabeledComponent("Node &interpreter:", interpreterField, JBUI.scale(8))
+ .addLabeledComponent("&Protractor package:", protractorPackageField)
+ .addLabeledComponent("&Environment variables:", envVarsComponent)
+ .panel
+ }
+
+ private fun createConfigurationFileTextField(project: Project) = TextFieldWithHistoryWithBrowseButton().apply {
+ val textFieldWithHistory = childComponent
+ textFieldWithHistory.setHistorySize(-1)
+ textFieldWithHistory.setMinimumAndPreferredWidth(0)
+ SwingHelper.addHistoryOnExpansion(textFieldWithHistory) {
+ val newFiles = ProtractorUtil.listPossibleConfigFilesInProject(project)
+ val newFilePaths = ContainerUtil.map(newFiles) { file -> FileUtil.toSystemDependentName(file.path) }
+ Collections.sort(newFilePaths)
+ newFilePaths
+ }
+
+ SwingHelper.installFileCompletionAndBrowseDialog(
+ project,
+ this,
+ "Select Protractor Configuration File",
+ FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
+ )
+ }
+
+ private fun createTestFileTextField(project: Project) = TextFieldWithHistoryWithBrowseButton().apply {
+ val textFieldWithHistory = childComponent
+ textFieldWithHistory.setHistorySize(-1)
+ textFieldWithHistory.setMinimumAndPreferredWidth(0)
+
+ SwingHelper.installFileCompletionAndBrowseDialog(
+ project,
+ this,
+ "Select Protractor Configuration File",
+ FileChooserDescriptorFactory.createSingleFileNoJarsDescriptor()
+ )
+ }
+
+ override fun resetEditorFrom(runConfiguration: KotlinProtractorRunConfiguration) {
+ with(runConfiguration.runSettings) {
+ testFilePathTextFieldWithBrowseButton.setTextAndAddToHistory(testFileSystemDependentPath)
+ seleniumAddressTextField.text = seleniumAddress
+ extraOptionsEditor.text = extraOptions
+ configPathTextFieldWithBrowseButton.setTextAndAddToHistory(configFileSystemDependentPath)
+ interpreterField.interpreterRef = interpreterRef
+ protractorPackageField.selected = runConfiguration.protractorPackage
+ envVarsComponent.data = envData
+ }
+ updatePreferredWidth()
+ }
+
+ private fun updatePreferredWidth() {
+ val dialogWrapper = DialogWrapper.findInstance(panel)
+ if (dialogWrapper is SingleConfigurableEditor) {
+ // dialog for single run configuration
+ interpreterField.setPreferredWidthToFitText()
+ protractorPackageField.setPreferredWidthToFitText()
+ SwingHelper.setPreferredWidthToFitText(testFilePathTextFieldWithBrowseButton)
+ SwingHelper.setPreferredWidthToFitText(seleniumAddressTextField)
+ SwingHelper.setPreferredWidthToFitText(configPathTextFieldWithBrowseButton)
+ ApplicationManager.getApplication().invokeLater { SwingHelper.adjustDialogSizeToFitPreferredSize(dialogWrapper) }
+ }
+ }
+
+ override fun applyEditorTo(runConfiguration: KotlinProtractorRunConfiguration) {
+ ProtractorUtil.setProtractorPackage(myProject, protractorPackageField.selected)
+ runConfiguration.runSettings = runConfiguration.runSettings.copy(
+ interpreterRef = interpreterField.interpreterRef,
+ configFilePath = configPathTextFieldWithBrowseButton.text,
+ testFilePath = testFilePathTextFieldWithBrowseButton.text,
+ seleniumAddress = seleniumAddressTextField.text,
+ extraOptions = extraOptionsEditor.text,
+ envData = envVarsComponent.data
+ )
+ }
+
+ override fun createEditor() = panel
+}
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt
new file mode 100644
index 00000000000..c34c26e06cf
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunConfigurationProducer.kt
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.nodejs.protractor
+
+import com.intellij.execution.actions.CompatibleRunConfigurationProducer
+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.js.getJsOutputFilePath
+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
+
+class KotlinProtractorRunConfigurationProducer :
+ CompatibleRunConfigurationProducer(KotlinProtractorConfigurationType.getInstance()) {
+ override fun isConfigurationFromCompatibleContext(
+ configuration: KotlinProtractorRunConfiguration,
+ context: ConfigurationContext
+ ): Boolean {
+ val contextPsi = context.psiLocation ?: return false
+ val module = contextPsi.module ?: return false
+ val testFilePath = getJsOutputFilePath(module, isTests = true, isMeta = false) ?: return false
+ return configuration.runSettings.testFileSystemDependentPath == FileUtil.toSystemDependentName(testFilePath)
+ }
+
+ override fun setupConfigurationFromCompatibleContext(
+ configuration: KotlinProtractorRunConfiguration,
+ context: ConfigurationContext,
+ sourceElement: Ref
+ ): 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 (TargetPlatformDetector.getPlatform(module) !is JsPlatform) return false
+ val testFilePath = getJsOutputFilePath(module, true, false) ?: return false
+
+ sourceElement.set(element)
+ configuration.runSettings = configuration.runSettings.copy(
+ testFilePath = testFilePath,
+ envData = module.getNodeJsEnvironmentVars()
+ )
+ configuration.name = configuration.suggestedName()
+ return true
+ }
+}
\ No newline at end of file
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunSettings.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunSettings.kt
new file mode 100644
index 00000000000..f58041b0a91
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunSettings.kt
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.nodejs.protractor
+
+import com.intellij.execution.configuration.EnvironmentVariablesData
+import com.intellij.javascript.nodejs.interpreter.NodeJsInterpreterRef
+import com.intellij.openapi.util.JDOMExternalizerUtil.*
+import com.intellij.openapi.util.io.FileUtil
+import org.jdom.Element
+
+data class KotlinProtractorRunSettings(
+ val interpreterRef: NodeJsInterpreterRef = NodeJsInterpreterRef.createProjectRef(),
+ private val configFilePath: String = "",
+ private val testFilePath: String = "",
+ val seleniumAddress: String = "",
+ val extraOptions: String = "",
+ val envData: EnvironmentVariablesData = EnvironmentVariablesData.DEFAULT
+) {
+ val configFileSystemDependentPath get() = FileUtil.toSystemDependentName(configFilePath)
+ val testFileSystemDependentPath get() = FileUtil.toSystemDependentName(testFilePath)
+
+ fun writeToXML(element: Element) {
+ addElementWithValueAttribute(element, "config-file", FileUtil.toSystemIndependentName(configFileSystemDependentPath))
+ addElementWithValueAttribute(element, "test-file", FileUtil.toSystemIndependentName(testFileSystemDependentPath))
+ addElementWithValueAttribute(element, "selenium-address", seleniumAddress)
+ addElementWithValueAttribute(element, "extra-protractor-options", extraOptions)
+ addElementWithValueAttribute(element, "node-interpreter", interpreterRef.referenceName)
+ envData.writeExternal(element)
+ }
+
+ companion object {
+ fun readFromXML(element: Element): KotlinProtractorRunSettings {
+ val configFilePath = getFirstChildValueAttribute(element, "config-file")
+ val testFilePath = getFirstChildValueAttribute(element, "test-file")
+ val seleniumAddress = getFirstChildValueAttribute(element, "selenium-address")
+ val extraOptions = getFirstChildValueAttribute(element, "extra-protractor-options")
+ val interpreterRefName = getFirstChildValueAttribute(element, "node-interpreter")
+ val envData = EnvironmentVariablesData.readExternal(element)
+ return KotlinProtractorRunSettings(
+ NodeJsInterpreterRef.create(interpreterRefName ?: "project"),
+ configFilePath ?: "",
+ testFilePath ?: "",
+ seleniumAddress ?: "",
+ extraOptions ?: "",
+ envData
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunState.kt b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunState.kt
new file mode 100644
index 00000000000..863e3ae1581
--- /dev/null
+++ b/ultimate/src/org/jetbrains/kotlin/idea/nodejs/protractor/KotlinProtractorRunState.kt
@@ -0,0 +1,178 @@
+/*
+ * Copyright 2010-2017 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jetbrains.kotlin.idea.nodejs.protractor
+
+import com.intellij.execution.DefaultExecutionResult
+import com.intellij.execution.ExecutionException
+import com.intellij.execution.ExecutionResult
+import com.intellij.execution.Executor
+import com.intellij.execution.configurations.GeneralCommandLine
+import com.intellij.execution.configurations.RunProfileState
+import com.intellij.execution.process.ProcessHandler
+import com.intellij.execution.process.ProcessTerminatedListener
+import com.intellij.execution.runners.ExecutionEnvironment
+import com.intellij.execution.testframework.TestConsoleProperties
+import com.intellij.execution.testframework.autotest.ToggleAutoTestAction
+import com.intellij.execution.testframework.sm.SMCustomMessagesParsing
+import com.intellij.execution.testframework.sm.SMTestRunnerConnectionUtil
+import com.intellij.execution.testframework.sm.runner.SMTRunnerConsoleProperties
+import com.intellij.execution.testframework.sm.runner.SMTestLocator
+import com.intellij.execution.ui.ConsoleView
+import com.intellij.javascript.HelperFilesLocator
+import com.intellij.javascript.nodejs.NodeCommandLineUtil
+import com.intellij.javascript.nodejs.debug.NodeLocalDebugRunProfileState
+import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreter
+import com.intellij.javascript.nodejs.util.NodePackage
+import com.intellij.javascript.protractor.ProtractorConsoleFilter
+import com.intellij.javascript.protractor.ProtractorOutputToGeneralTestEventsConverter
+import com.intellij.javascript.protractor.ProtractorUtil
+import com.intellij.javascript.testFramework.navigation.JSTestLocationProvider
+import com.intellij.lang.javascript.buildTools.base.JsbtUtil
+import com.intellij.openapi.util.Disposer
+import com.intellij.util.PathUtil
+import com.intellij.util.execution.ParametersListUtil
+import java.io.File
+import java.io.IOException
+import java.nio.charset.StandardCharsets
+
+class KotlinProtractorRunState(
+ private val runConfiguration: KotlinProtractorRunConfiguration,
+ private val environment: ExecutionEnvironment,
+ private val protractorPackage: NodePackage
+) : RunProfileState, NodeLocalDebugRunProfileState {
+ companion object {
+ val FRAMEWORK_NAME = "KotlinProtractorJavaScriptTestRunner"
+ private val INTELLIJ_CONFIG_FILE_PATH = "protractor-intellij/lib/protractor-intellij-config.js"
+ }
+
+ private val project = runConfiguration.project
+ private val runSettings = runConfiguration.runSettings
+
+ override fun execute(debugPort: Int): ExecutionResult {
+ val interpreter = runSettings.interpreterRef.resolveAsLocal(project)
+ val commandLine = createCommandLine(interpreter, debugPort)
+ val processHandler = NodeCommandLineUtil.createProcessHandler(commandLine, false)
+ val consoleView = createSmtRunnerConsoleView(commandLine.workDirectory)
+ ProcessTerminatedListener.attach(processHandler)
+ consoleView.attachToProcess(processHandler)
+ foldCommandLine(consoleView, processHandler)
+ val executionResult = DefaultExecutionResult(consoleView, processHandler)
+ executionResult.setRestartActions(ToggleAutoTestAction())
+ return executionResult
+ }
+
+ private fun createSmtRunnerConsoleView(workingDirectory: File?): ConsoleView {
+ val testConsoleProperties = ProtractorConsoleProperties(runConfiguration, environment.executor, JSTestLocationProvider())
+ val consoleView = SMTestRunnerConnectionUtil.createConsole(FRAMEWORK_NAME, testConsoleProperties)
+ consoleView.addMessageFilter(ProtractorConsoleFilter(project, workingDirectory))
+ Disposer.register(environment, consoleView)
+ return consoleView
+ }
+
+ override fun foldCommandLine(consoleView: ConsoleView, processHandler: ProcessHandler) {
+ val configFileName = PathUtil.getFileName(runSettings.configFileSystemDependentPath)
+ val testFileName = PathUtil.getFileName(runSettings.testFileSystemDependentPath)
+ val seleniumAddress = runSettings.seleniumAddress
+ val parameters = ArrayList().apply {
+ add("protractor")
+ if (configFileName.isNotBlank()) {
+ add(configFileName)
+ }
+ if (testFileName.isNotBlank()) {
+ add("--specs=$testFileName")
+ }
+ if (seleniumAddress.isNotBlank()) {
+ add("--seleniumAddress=$seleniumAddress")
+ }
+ addAll(ParametersListUtil.parse(runSettings.extraOptions.trim()))
+ }
+ val foldedCommandLine = ParametersListUtil.join(parameters)
+ JsbtUtil.foldCommandLine(consoleView, processHandler, foldedCommandLine)
+ }
+
+ private fun createCommandLine(interpreter: NodeJsLocalInterpreter, debugPort: Int): GeneralCommandLine {
+ val commandLine = GeneralCommandLine().apply { charset = StandardCharsets.UTF_8 }
+
+ runSettings.envData.configureCommandLine(commandLine, true)
+
+ val originalConfigFilePath = runSettings.configFileSystemDependentPath
+ val testFilePath = runSettings.testFileSystemDependentPath
+ val seleniumAddress = runSettings.seleniumAddress
+ val withConfigFile = originalConfigFilePath.isNotBlank()
+ commandLine.setWorkDirectory(project.basePath)
+
+ commandLine.exePath = interpreter.interpreterSystemDependentPath
+ NodeCommandLineUtil.addNodeOptionsForDebugging(commandLine, emptyList(), debugPort, true, interpreter, true)
+ NodeCommandLineUtil.configureUsefulEnvironment(commandLine)
+ commandLine.addParameter(ProtractorUtil.getProtractorMainJsFile(protractorPackage).absolutePath)
+
+ val intellijConfigFile = try {
+ HelperFilesLocator.getFileRelativeToHelpersDir(INTELLIJ_CONFIG_FILE_PATH)
+ }
+ catch (e: IOException) {
+ throw ExecutionException("Cannot locate wrapper config file", e)
+ }
+
+ commandLine.addParameter(intellijConfigFile.absolutePath)
+
+ val actualConfigFilePath = if (withConfigFile) {
+ originalConfigFilePath
+ }
+ else {
+ val tempConfigFile = File.createTempFile("protractor", "conf.js")
+ tempConfigFile.writeText("exports.config = {}")
+ tempConfigFile.absolutePath
+ }
+
+ commandLine.addParameter("--intellijOriginalConfigFile=$actualConfigFilePath")
+
+ if (testFilePath.isNotBlank()) {
+ commandLine.addParameter("--specs=$testFilePath")
+ }
+
+ if (seleniumAddress.isNotBlank()) {
+ commandLine.addParameter("--seleniumAddress=$seleniumAddress")
+ }
+
+ commandLine.addParameters(ParametersListUtil.parse(runSettings.extraOptions.trim()))
+
+ commandLine.addParameter("--disableChecks")
+
+ return commandLine
+ }
+
+ private class ProtractorConsoleProperties(
+ configuration: KotlinProtractorRunConfiguration,
+ executor: Executor,
+ private val myLocator: SMTestLocator
+ ) : SMTRunnerConsoleProperties(configuration, FRAMEWORK_NAME, executor), SMCustomMessagesParsing {
+ init {
+ isUsePredefinedMessageFilter = false
+ setIfUndefined(TestConsoleProperties.HIDE_PASSED_TESTS, false)
+ setIfUndefined(TestConsoleProperties.HIDE_IGNORED_TEST, true)
+ setIfUndefined(TestConsoleProperties.SCROLL_TO_SOURCE, true)
+ setIfUndefined(TestConsoleProperties.SELECT_FIRST_DEFECT, true)
+ isIdBasedTestTree = true
+ isPrintTestingStartedTime = false
+ }
+
+ override fun getTestLocator() = myLocator
+
+ override fun createTestEventsConverter(testFrameworkName: String, consoleProperties: TestConsoleProperties) =
+ ProtractorOutputToGeneralTestEventsConverter(testFrameworkName, consoleProperties.isEditable)
+ }
+}
\ No newline at end of file