@@ -2,6 +2,7 @@ package org.utbot.intellij.plugin.language.js
22
33import api.JsTestGenerator
44import com.intellij.codeInsight.CodeInsightUtil
5+ import com.intellij.javascript.nodejs.interpreter.local.NodeJsLocalInterpreterManager
56import com.intellij.lang.ecmascript6.psi.ES6Class
67import com.intellij.lang.javascript.psi.JSFile
78import com.intellij.lang.javascript.refactoring.util.JSMemberInfo
@@ -12,10 +13,13 @@ import com.intellij.openapi.module.Module
1213import com.intellij.openapi.progress.ProgressIndicator
1314import com.intellij.openapi.progress.Task
1415import com.intellij.openapi.project.Project
16+ import com.intellij.openapi.ui.Messages
1517import com.intellij.psi.PsiDocumentManager
1618import com.intellij.psi.PsiFileFactory
1719import com.intellij.psi.impl.file.PsiDirectoryFactory
1820import com.intellij.util.concurrency.AppExecutorUtil
21+ import framework.codegen.Mocha
22+ import mu.KotlinLogging
1923import org.jetbrains.kotlin.idea.util.application.invokeLater
2024import org.jetbrains.kotlin.idea.util.application.runReadAction
2125import org.jetbrains.kotlin.idea.util.application.runWriteAction
@@ -26,6 +30,9 @@ import settings.JsDynamicSettings
2630import settings.JsExportsSettings.endComment
2731import settings.JsExportsSettings.startComment
2832import settings.JsTestGenerationSettings.dummyClassName
33+ import utils.JsCmdExec
34+
35+ private val logger = KotlinLogging .logger {}
2936
3037object JsDialogProcessor {
3138
@@ -38,28 +45,37 @@ object JsDialogProcessor {
3845 editor : Editor ,
3946 file : JSFile
4047 ) {
41- createDialog(project, srcModule, fileMethods, focusedMethod, containingFilePath, file)?.let { dialogProcessor ->
42- if (! dialogProcessor.showAndGet()) return
43- /*
44- Since Tern.js accesses containing file, sync with file system required before test generation.
45- */
46- runWriteAction {
47- with (FileDocumentManager .getInstance()) {
48- saveDocument(editor.document)
48+ val model = createJsTestModel(project, srcModule, fileMethods, focusedMethod, containingFilePath, file)
49+ (object : Task .Backgroundable (
50+ project,
51+ " Check the requirements"
52+ ) {
53+ override fun run (indicator : ProgressIndicator ) {
54+ invokeLater {
55+ getFrameworkLibraryPath(Mocha .displayName.lowercase(), model)
56+ createDialog(model)?.let { dialogProcessor ->
57+ if (! dialogProcessor.showAndGet()) return @invokeLater
58+ // Since Tern.js accesses containing file, sync with file system required before test generation.
59+ runWriteAction {
60+ with (FileDocumentManager .getInstance()) {
61+ saveDocument(editor.document)
62+ }
63+ }
64+ createTests(dialogProcessor.model, containingFilePath, editor)
65+ }
4966 }
5067 }
51- createTests(dialogProcessor.model, containingFilePath, editor)
52- }
68+ }).queue()
5369 }
5470
55- private fun createDialog (
71+ private fun createJsTestModel (
5672 project : Project ,
5773 srcModule : Module ,
5874 fileMethods : Set <JSMemberInfo >,
5975 focusedMethod : JSMemberInfo ? ,
6076 filePath : String ,
6177 file : JSFile
62- ): JsDialogWindow ? {
78+ ): JsTestsModel ? {
6379 val testModules = srcModule.testModules(project)
6480
6581 if (testModules.isEmpty()) {
@@ -70,19 +86,42 @@ object JsDialogProcessor {
7086 showErrorDialogLater(project, errorMessage, " Test source roots not found" )
7187 return null
7288 }
89+ return JsTestsModel (
90+ project = project,
91+ srcModule = srcModule,
92+ potentialTestModules = testModules,
93+ fileMethods = fileMethods,
94+ selectedMethods = if (focusedMethod != null ) setOf (focusedMethod) else emptySet(),
95+ file = file
96+ ).apply {
97+ containingFilePath = filePath
98+ }
7399
74- return JsDialogWindow (
75- JsTestsModel (
76- project = project,
77- srcModule = srcModule,
78- potentialTestModules = testModules,
79- fileMethods = fileMethods,
80- selectedMethods = if (focusedMethod != null ) setOf (focusedMethod) else emptySet(),
81- file = file
82- ).apply {
83- containingFilePath = filePath
84- }
85- )
100+ }
101+
102+ private fun createDialog (
103+ jsTestsModel : JsTestsModel ?
104+ ): JsDialogWindow ? {
105+ try {
106+ jsTestsModel?.pathToNode = NodeJsLocalInterpreterManager .getInstance()
107+ .interpreters.first().interpreterSystemIndependentPath
108+ val (_, error) = JsCmdExec .runCommand(
109+ shouldWait = true ,
110+ cmd = arrayOf(" node" , " -v" )
111+ )
112+ if (error.readText().isNotEmpty()) throw NoSuchElementException ()
113+ } catch (e: NoSuchElementException ) {
114+ Messages .showErrorDialog(
115+ " Node.js interpreter is not found in IDEA settings.\n " +
116+ " Please set it in Settings > Languages & Frameworks > Node.js" ,
117+ " Requirement Error"
118+ )
119+ logger.error { " Node.js interpreter was not found in IDEA settings." }
120+ return null
121+ }
122+ return jsTestsModel?.let {
123+ JsDialogWindow (it)
124+ }
86125 }
87126
88127 private fun unblockDocument (project : Project , document : Document ) {
@@ -207,3 +246,34 @@ object JsDialogProcessor {
207246 }
208247 }
209248}
249+
250+ // TODO(MINOR): Add indicator.text for each installation
251+ fun installMissingRequirement (project : Project , pathToNPM : String , requirement : String ) {
252+ val message = """
253+ Requirement is not installed:
254+ $requirement
255+ Install it?
256+ """ .trimIndent()
257+ val result = Messages .showOkCancelDialog(
258+ project,
259+ message,
260+ " Requirement Missmatch Error" ,
261+ " Install" ,
262+ " Cancel" ,
263+ null
264+ )
265+
266+ if (result == Messages .CANCEL )
267+ return
268+
269+ val (_, errorStream) = installRequirement(pathToNPM, requirement, project.basePath)
270+
271+ val errorText = errorStream.readText()
272+ if (errorText.isNotEmpty()) {
273+ showErrorDialogLater(
274+ project,
275+ " Requirements installing failed with some reason:\n ${errorText} " ,
276+ " Requirements error"
277+ )
278+ }
279+ }
0 commit comments