Skip to content

Commit 1b86778

Browse files
authored
Merge pull request #5 from Umaaz/main
feat(gutter): add gutter actions to run/debug tests
2 parents c1537be + b5c805d commit 1b86778

File tree

3 files changed

+145
-71
lines changed

3 files changed

+145
-71
lines changed
Lines changed: 85 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package no.eirikb.avatest.actions
22

3+
import com.intellij.execution.ExecutorRegistry
34
import com.intellij.execution.RunManager
45
import com.intellij.execution.RunnerAndConfigurationSettings
56
import com.intellij.execution.configurations.ConfigurationFactory
@@ -16,93 +17,107 @@ import com.intellij.openapi.actionSystem.AnActionEvent
1617
import com.intellij.openapi.actionSystem.CommonDataKeys
1718
import com.intellij.openapi.actionSystem.PlatformDataKeys
1819
import com.intellij.openapi.fileEditor.FileDocumentManager
20+
import com.intellij.openapi.wm.ToolWindowId
1921
import com.intellij.psi.PsiElement
2022
import com.jetbrains.nodejs.run.NodeJsRunConfiguration
2123
import java.nio.file.Paths
2224

2325
fun JSCallExpression.isTest() = this.methodExpression?.text.equals("test")
2426

2527
class AvaJavaScriptTestRunnerRunConfigurationGenerator : AnAction() {
26-
private fun writeError(text: String) {
27-
val notification =
28-
Notification(
29-
"no.eirikb.avatest",
30-
"AVA test run configuration generator error",
31-
text,
32-
NotificationType.ERROR
33-
)
34-
Notifications.Bus.notify(notification)
35-
}
28+
companion object {
29+
fun performAction(e: AnActionEvent, debug: Boolean = false, offset: Int? = null) {
30+
val project = e.project
31+
if (project == null) {
32+
writeError("Project not found")
33+
return
34+
}
35+
val editor = e.getRequiredData(CommonDataKeys.EDITOR)
36+
val currentFile = FileDocumentManager.getInstance().getFile(editor.document)
37+
if (currentFile == null) {
38+
writeError("Current file not found")
39+
return
40+
}
41+
var testName: String? = null
42+
val file = e.getData(PlatformDataKeys.PSI_FILE)
43+
if (file != null) {
44+
val element = file.findElementAt(offset ?: editor.caretModel.offset)
45+
testName = getTestName(element)
46+
}
47+
val filePath = currentFile.path
48+
val fileName = Paths.get(filePath).fileName.toString()
49+
val basePath = project.basePath
50+
val relPath = if (basePath == null) fileName else currentFile.path.substring(basePath.length + 1)
51+
val node: NodeJsRunConfiguration? =
52+
NodeJsRunConfiguration.getDefaultRunConfiguration(project)?.clone() as NodeJsRunConfiguration?
53+
if (node == null) {
54+
writeError("NodeJS run configuration type not found")
55+
return
56+
}
57+
val factory: ConfigurationFactory? = node.factory
58+
if (factory == null) {
59+
writeError("Factory not found")
60+
return
61+
}
62+
node.workingDirectory = basePath
63+
node.inputPath = "node_modules/ava/cli.js"
64+
if (testName != null) {
65+
node.name = "ava $fileName $testName"
66+
node.applicationParameters = "-m \"$testName\" -v $relPath"
67+
} else {
68+
node.name = "ava $fileName"
69+
node.applicationParameters = "-v $relPath"
70+
}
71+
val runManager = RunManager.getInstance(project)
72+
val configuration: RunnerAndConfigurationSettings = runManager.createConfiguration(node, factory)
73+
runManager.addConfiguration(configuration)
74+
runManager.selectedConfiguration = configuration
3675

37-
private fun getTestName(element: PsiElement?): String? {
38-
if (element == null || element.parent == null) {
39-
return null
76+
if (debug) {
77+
val executor = ExecutorRegistry.getInstance().getExecutorById(ToolWindowId.DEBUG)
78+
?: DefaultRunExecutor.getRunExecutorInstance()
79+
ExecutionUtil.runConfiguration(configuration, executor)
80+
} else {
81+
ExecutionUtil.runConfiguration(configuration, DefaultRunExecutor.getRunExecutorInstance())
82+
}
4083
}
4184

42-
if (element !is JSCallExpression) {
43-
return getTestName(element.parent)
85+
private fun writeError(text: String) {
86+
val notification =
87+
Notification(
88+
"no.eirikb.avatest",
89+
"AVA test run configuration generator error",
90+
text,
91+
NotificationType.ERROR
92+
)
93+
Notifications.Bus.notify(notification)
4494
}
4595

46-
val jsCallExpression: JSCallExpression = element
47-
if (jsCallExpression.isTest()) {
48-
val arguments: Array<JSExpression> = jsCallExpression.arguments
49-
if (arguments.isNotEmpty()) {
50-
if (arguments[0] is JSLiteralExpression) {
51-
val expression: JSLiteralExpression = arguments[0] as JSLiteralExpression
52-
return expression.stringValue
53-
}
96+
private fun getTestName(element: PsiElement?): String? {
97+
if (element == null || element.parent == null) {
5498
return null
5599
}
100+
101+
if (element !is JSCallExpression) {
102+
return getTestName(element.parent)
103+
}
104+
105+
val jsCallExpression: JSCallExpression = element
106+
if (jsCallExpression.isTest()) {
107+
val arguments: Array<JSExpression> = jsCallExpression.arguments
108+
if (arguments.isNotEmpty()) {
109+
if (arguments[0] is JSLiteralExpression) {
110+
val expression: JSLiteralExpression = arguments[0] as JSLiteralExpression
111+
return expression.stringValue
112+
}
113+
return null
114+
}
115+
}
116+
return getTestName(element.parent)
56117
}
57-
return getTestName(element.parent)
58118
}
59119

60120
override fun actionPerformed(e: AnActionEvent) {
61-
val project = e.project
62-
if (project == null) {
63-
writeError("Project not found")
64-
return
65-
}
66-
val editor = e.getRequiredData(CommonDataKeys.EDITOR)
67-
val currentFile = FileDocumentManager.getInstance().getFile(editor.document)
68-
if (currentFile == null) {
69-
writeError("Current file not found")
70-
return
71-
}
72-
var testName: String? = null
73-
val file = e.getData(PlatformDataKeys.PSI_FILE)
74-
if (file != null) {
75-
val element = file.findElementAt(editor.caretModel.offset)
76-
testName = getTestName(element)
77-
}
78-
val filePath = currentFile.path
79-
val fileName = Paths.get(filePath).fileName.toString()
80-
val basePath = project.basePath
81-
val relPath = if (basePath == null) fileName else currentFile.path.substring(basePath.length + 1)
82-
val node: NodeJsRunConfiguration? =
83-
NodeJsRunConfiguration.getDefaultRunConfiguration(project)?.clone() as NodeJsRunConfiguration?
84-
if (node == null) {
85-
writeError("NodeJS run configuration type not found")
86-
return
87-
}
88-
val factory: ConfigurationFactory? = node.factory
89-
if (factory == null) {
90-
writeError("Factory not found")
91-
return
92-
}
93-
node.workingDirectory = basePath
94-
node.inputPath = "node_modules/ava/cli.js"
95-
if (testName != null) {
96-
node.name = "ava $fileName $testName"
97-
node.applicationParameters = "-m \"$testName\" -v $relPath"
98-
} else {
99-
node.name = "ava $fileName"
100-
node.applicationParameters = "-v $relPath"
101-
}
102-
val runManager = RunManager.getInstance(project)
103-
val configuration: RunnerAndConfigurationSettings = runManager.createConfiguration(node, factory)
104-
runManager.addConfiguration(configuration)
105-
runManager.selectedConfiguration = configuration
106-
ExecutionUtil.runConfiguration(configuration, DefaultRunExecutor.getRunExecutorInstance())
121+
performAction(e)
107122
}
108123
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package no.eirikb.avatest.markers
2+
3+
import com.intellij.execution.lineMarker.RunLineMarkerContributor
4+
import com.intellij.icons.AllIcons
5+
import com.intellij.icons.AllIcons.RunConfigurations.TestState
6+
import com.intellij.lang.javascript.psi.JSCallExpression
7+
import com.intellij.lang.javascript.psi.JSExpression
8+
import com.intellij.lang.javascript.psi.JSLiteralExpression
9+
import com.intellij.openapi.actionSystem.AnAction
10+
import com.intellij.openapi.actionSystem.AnActionEvent
11+
import com.intellij.psi.PsiElement
12+
import no.eirikb.avatest.actions.AvaJavaScriptTestRunnerRunConfigurationGenerator
13+
import no.eirikb.avatest.actions.isTest
14+
import javax.swing.Icon
15+
16+
class AvaRunContributor : RunLineMarkerContributor() {
17+
override fun getInfo(element: PsiElement): Info? {
18+
if (element !is JSCallExpression) {
19+
return null
20+
}
21+
22+
val jsCallExpression: JSCallExpression = element
23+
if (jsCallExpression.isTest()) {
24+
val arguments: Array<JSExpression> = jsCallExpression.arguments
25+
if (arguments.isNotEmpty()) {
26+
if (arguments[0] is JSLiteralExpression) {
27+
val expression: JSLiteralExpression = arguments[0] as JSLiteralExpression
28+
return Info(
29+
TestState.Run,
30+
getActions(element.textOffset, "${expression.stringValue}")
31+
) { "Run Test" }
32+
}
33+
return null
34+
}
35+
}
36+
37+
return null
38+
}
39+
40+
private fun getActions(offset: Int, testName: String): Array<AnAction> {
41+
return listOf(
42+
object : RunAction("Run", testName, TestState.Run) {
43+
override fun actionPerformed(e: AnActionEvent) {
44+
AvaJavaScriptTestRunnerRunConfigurationGenerator.performAction(e, false, offset)
45+
}
46+
},
47+
object : RunAction("Debug", testName, AllIcons.Actions.StartDebugger) {
48+
override fun actionPerformed(e: AnActionEvent) {
49+
AvaJavaScriptTestRunnerRunConfigurationGenerator.performAction(e, true, offset)
50+
}
51+
}
52+
).toTypedArray()
53+
}
54+
}
55+
56+
abstract class RunAction(prefix: String, testName: String, icon: Icon) :
57+
AnAction("$prefix '$testName'", "$prefix '$testName'", icon)

src/main/resources/META-INF/plugin.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@
88
<depends>JavaScript</depends>
99
<depends>NodeJS</depends>
1010

11-
<extensions defaultExtensionNs="com.intellij"/>
11+
<extensions defaultExtensionNs="com.intellij">
12+
<runLineMarkerContributor language="JavaScript" implementationClass="no.eirikb.avatest.markers.AvaRunContributor"/>
13+
</extensions>
1214

1315
<actions>
1416
<action id="AvaJavaScriptTestRunnerRunConfigurationGenerator"

0 commit comments

Comments
 (0)