Skip to content

Commit 6ad9088

Browse files
authored
Add new UI test for SAM local run config (#2432)
- Add a basic UI test for SAM local run config. Covers template and handler based zip lambdas. - Fix bug where typing into the template field did nothing, so you could not copy paste into it or edit it directly
1 parent b4f858f commit 6ad9088

File tree

10 files changed

+272
-36
lines changed

10 files changed

+272
-36
lines changed
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"type" : "bugfix",
3+
"description" : "Fix being unable to type/copy paste into the SAM local run config's template path textbox"
4+
}

gradle.properties

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ telemetryVersion=0.0.82
2121
assertjVersion=3.15.0
2222
junitVersion=4.13.1
2323
junit5Version=5.6.2
24+
apacheCommonsVersion=2.8.0
2425
mockitoKotlinVersion=2.2.0
2526
mockitoVersion=3.4.0
2627

jetbrains-core/src/software/aws/toolkits/jetbrains/services/lambda/execution/local/TemplateSettings.kt

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import com.intellij.openapi.fileChooser.FileChooserDescriptorFactory
77
import com.intellij.openapi.project.Project
88
import com.intellij.openapi.ui.ComboBox
99
import com.intellij.openapi.ui.TextFieldWithBrowseButton
10+
import com.intellij.ui.DocumentAdapter
1011
import com.intellij.ui.SimpleListCellRenderer
1112
import com.intellij.ui.SortedComboBoxModel
1213
import com.intellij.util.PathMappingSettings
@@ -28,6 +29,7 @@ import java.util.Comparator
2829
import javax.swing.DefaultComboBoxModel
2930
import javax.swing.JComboBox
3031
import javax.swing.JPanel
32+
import javax.swing.event.DocumentEvent
3133

3234
class TemplateSettings(val project: Project) {
3335
lateinit var panel: JPanel
@@ -60,9 +62,14 @@ class TemplateSettings(val project: Project) {
6062
project,
6163
FileChooserDescriptorFactory.createSingleFileDescriptor(YAMLFileType.YML)
6264
) {
63-
setTemplateFile(it.canonicalPath)
65+
templateFile.text = it.canonicalPath ?: ""
6466
}
6567
)
68+
templateFile.textField.document.addDocumentListener(object : DocumentAdapter() {
69+
override fun textChanged(e: DocumentEvent) {
70+
updateFunctionModel(templateFile.text)
71+
}
72+
})
6673
function.addActionListener {
6774
val selected = function.selected()
6875
imageSettingsPanel.isVisible = selected is SamFunction && selected.packageType() == PackageType.IMAGE
@@ -93,13 +100,22 @@ class TemplateSettings(val project: Project) {
93100
imageDebugger.renderer = SimpleListCellRenderer.create { label, value, _ -> label.text = value?.displayName() }
94101
}
95102

96-
fun setTemplateFile(file: String?) {
97-
if (file == null) {
103+
fun setTemplateFile(path: String?) {
104+
templateFile.text = path ?: ""
105+
updateFunctionModel(path)
106+
}
107+
108+
private fun updateFunctionModel(path: String?) {
109+
if (path.isNullOrBlank()) {
98110
templateFile.text = ""
99111
updateFunctionModel(emptyList())
112+
return
113+
}
114+
val file = File(path)
115+
if (!file.exists() || !file.isFile) {
116+
updateFunctionModel(emptyList())
100117
} else {
101-
templateFile.text = file
102-
val functions = SamTemplateUtils.findFunctionsFromTemplate(project, File(file))
118+
val functions = SamTemplateUtils.findFunctionsFromTemplate(project, file)
103119
updateFunctionModel(functions)
104120
}
105121
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3+
<modelVersion>4.0.0</modelVersion>
4+
<groupId>helloworld</groupId>
5+
<artifactId>HelloWorld</artifactId>
6+
<version>1.0</version>
7+
<packaging>jar</packaging>
8+
<name>A sample Hello World created for SAM CLI.</name>
9+
<properties>
10+
<maven.compiler.source>1.8</maven.compiler.source>
11+
<maven.compiler.target>1.8</maven.compiler.target>
12+
</properties>
13+
14+
<dependencies>
15+
<dependency>
16+
<groupId>com.amazonaws</groupId>
17+
<artifactId>aws-lambda-java-core</artifactId>
18+
<version>1.2.1</version>
19+
</dependency>
20+
</dependencies>
21+
22+
<build>
23+
<plugins>
24+
<plugin>
25+
<groupId>org.apache.maven.plugins</groupId>
26+
<artifactId>maven-shade-plugin</artifactId>
27+
<version>3.2.4</version>
28+
<configuration>
29+
</configuration>
30+
<executions>
31+
<execution>
32+
<phase>package</phase>
33+
<goals>
34+
<goal>shade</goal>
35+
</goals>
36+
</execution>
37+
</executions>
38+
</plugin>
39+
</plugins>
40+
</build>
41+
</project>
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
public class App {
2+
public String handleRequest(String request) {
3+
return request.toUpperCase();
4+
}
5+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
AWSTemplateFormatVersion: '2010-09-09'
2+
Transform: AWS::Serverless-2016-10-31
3+
4+
Resources:
5+
SomeFunction:
6+
Type: AWS::Serverless::Function
7+
Properties:
8+
CodeUri: HelloWorldFunction
9+
Handler: helloworld.App::handleRequest
10+
Runtime: java11
11+
Timeout: 900
12+
MemorySize: 512
13+
Environment:
14+
Variables:
15+
PARAM1: Value
16+
PARAM2: Value2

ui-tests/build.gradle.kts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ val remoteRobotVersion: String by project
99
val uiTestFixturesVersion: String by project
1010
val awsSdkVersion: String by project
1111
val coroutinesVersion: String by project
12+
val apacheCommonsVersion: String by project
1213

1314
repositories {
1415
maven { url = URI("https://jetbrains.bintray.com/intellij-third-party-dependencies") }
@@ -32,6 +33,7 @@ dependencies {
3233
testImplementation("software.amazon.awssdk:sns:$awsSdkVersion")
3334
testImplementation("software.amazon.awssdk:sqs:$awsSdkVersion")
3435

36+
testImplementation("commons-io:commons-io:$apacheCommonsVersion")
3537
testRuntimeOnly("org.junit.jupiter:junit-jupiter-engine:$junit5Version")
3638
}
3739

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// Copyright 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.uitests.tests
5+
6+
import com.intellij.remoterobot.fixtures.ComboBoxFixture
7+
import com.intellij.remoterobot.fixtures.ContainerFixture
8+
import com.intellij.remoterobot.fixtures.JListFixture
9+
import com.intellij.remoterobot.search.locators.byXpath
10+
import com.intellij.remoterobot.stepsProcessing.step
11+
import com.intellij.remoterobot.utils.keyboard
12+
import org.apache.commons.io.FileUtils
13+
import org.assertj.core.api.Assertions.assertThat
14+
import org.junit.jupiter.api.BeforeAll
15+
import org.junit.jupiter.api.Test
16+
import org.junit.jupiter.api.TestInstance
17+
import org.junit.jupiter.api.io.TempDir
18+
import software.aws.toolkits.jetbrains.uitests.CoreTest
19+
import software.aws.toolkits.jetbrains.uitests.extensions.uiTest
20+
import software.aws.toolkits.jetbrains.uitests.fixtures.DialogFixture
21+
import software.aws.toolkits.jetbrains.uitests.fixtures.JTreeFixture
22+
import software.aws.toolkits.jetbrains.uitests.fixtures.findAndClick
23+
import software.aws.toolkits.jetbrains.uitests.fixtures.idea
24+
import software.aws.toolkits.jetbrains.uitests.fixtures.pressOk
25+
import software.aws.toolkits.jetbrains.uitests.fixtures.welcomeFrame
26+
import software.aws.toolkits.jetbrains.uitests.utils.setupSamCli
27+
import java.nio.file.Path
28+
import java.nio.file.Paths
29+
import java.time.Duration
30+
31+
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
32+
class SamRunConfigTest {
33+
private val dataPath = Paths.get(System.getProperty("testDataPath")).resolve("samProjects/zip/java11")
34+
private val input = "{}"
35+
36+
@TempDir
37+
lateinit var tempDir: Path
38+
39+
@BeforeAll
40+
fun setup() {
41+
setupSamCli()
42+
}
43+
44+
@Test
45+
@CoreTest
46+
fun samRunConfig() {
47+
// copy our test data to the temporary folder so we can edit it
48+
FileUtils.copyDirectory(dataPath.toFile(), tempDir.toFile())
49+
uiTest {
50+
welcomeFrame {
51+
openFolder(tempDir)
52+
}
53+
54+
idea {
55+
waitForBackgroundTasks()
56+
findAndClick("//div[@accessiblename='Add Configuration...']")
57+
step("Create and populate template based run configuration") {
58+
addRunConfig()
59+
step("Populate run configuration") {
60+
step("Set up function from template") {
61+
findAndClick("//div[@text='From template']")
62+
findAndClick("//div[@class='Wrapper']//div[@class='TextFieldWithBrowseButton']")
63+
keyboard { enterText(tempDir.resolve("template.yaml").toAbsolutePath().toString()) }
64+
}
65+
step("Assert validation works by checking the error") {
66+
assertThat(findRunDialog().findAllText()).anySatisfy { assertThat(it.text).contains("Must specify an input") }
67+
}
68+
step("Enter text") {
69+
findAndClick("//div[@class='MyEditorTextField']")
70+
keyboard { enterText(input) }
71+
}
72+
pressOk()
73+
}
74+
}
75+
step("Validate template run configuration was saved and loads properly") {
76+
step("Reopen the run configuration") {
77+
findAndClick("//div[@accessiblename='[Local] SomeFunction']")
78+
find<JListFixture>(byXpath("//div[@class='MyList']")).selectItem("Edit Configurations...")
79+
}
80+
step("Assert the same function is selected") {
81+
assertThat(functionModel().selectedText()).isEqualTo("SomeFunction")
82+
}
83+
step("Assert the run configuration has no errors") {
84+
assertThat(findRunDialog().findAllText()).noneSatisfy { assertThat(it.text).contains("Error") }
85+
}
86+
step("Assert the input is the same") {
87+
// As this is a JTextField we don't have a fixture for it. But, we can extract the data by
88+
// joining all the text it has into a string
89+
val fixture = findRunDialog().find<ContainerFixture>(byXpath("//div[@class='MyEditorTextField']"))
90+
assertThat(fixture.findAllText().joinToString("") { it.text }).isEqualTo(input)
91+
}
92+
pressOk()
93+
}
94+
step("Setup handler based run configuration") {
95+
step("Reopen the run configuration menu") {
96+
findAndClick("//div[@accessiblename='[Local] SomeFunction']")
97+
find<JListFixture>(byXpath("//div[@class='MyList']")).selectItem("Edit Configurations...")
98+
}
99+
addRunConfig()
100+
find<ComboBoxFixture>(byXpath("//div[@text='Runtime:']/following-sibling::div[@class='ComboBox']")).selectItem("java11")
101+
findAndClick("//div[@class='HandlerPanel']")
102+
keyboard { enterText("helloworld.App::handleRequest") }
103+
findAndClick("//div[@class='MyEditorTextField']")
104+
keyboard { enterText(input) }
105+
pressOk()
106+
}
107+
step("Validate handler run configuration was saved and loads properly") {
108+
step("Reopen the run configuration") {
109+
findAndClick("//div[@accessiblename='[Local] App.handleRequest']")
110+
find<JListFixture>(byXpath("//div[@class='MyList']")).selectItem("Edit Configurations...")
111+
}
112+
step("Assert the same handler is selected") {
113+
val fixture = findRunDialog().find<ContainerFixture>(byXpath("//div[@class='HandlerPanel']"))
114+
assertThat(fixture.findAllText().joinToString("") { it.text }).isEqualTo("helloworld.App::handleRequest")
115+
}
116+
// We might want to assert no errors here in the future. However, since we do not import the project, we don't
117+
// index it, so we can't find the handler. We are not testing that here (that is tested in other tests), so
118+
// it would probably not be worth testing in the UI test as well.
119+
}
120+
}
121+
}
122+
}
123+
124+
private fun ContainerFixture.functionModel(): ComboBoxFixture =
125+
find(byXpath("//div[@class='TextFieldWithBrowseButton']/following-sibling::div[@class='ComboBox']"))
126+
127+
private fun ContainerFixture.findRunDialog() = find<DialogFixture>(DialogFixture.byTitleContains("Run"), Duration.ofSeconds(5))
128+
129+
private fun ContainerFixture.addRunConfig() {
130+
step("Add a local run configuration") {
131+
findRunDialog().findAndClick("//div[@accessiblename='Add New Configuration']")
132+
find<JTreeFixture>(byXpath("//div[@accessiblename='WizardTree' and @class='MyTree']")).clickPath("AWS Lambda", "Local")
133+
}
134+
}
135+
}

ui-tests/tst/software/aws/toolkits/jetbrains/uitests/tests/SamTemplateProjectWizardTest.kt

Lines changed: 3 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
package software.aws.toolkits.jetbrains.uitests.tests
55

66
import com.intellij.remoterobot.search.locators.byXpath
7-
import com.intellij.remoterobot.stepsProcessing.log
87
import com.intellij.remoterobot.stepsProcessing.step
98
import org.assertj.core.api.Assertions.assertThat
109
import org.junit.jupiter.api.BeforeAll
@@ -16,9 +15,9 @@ import software.aws.toolkits.jetbrains.uitests.extensions.uiTest
1615
import software.aws.toolkits.jetbrains.uitests.fixtures.editorTab
1716
import software.aws.toolkits.jetbrains.uitests.fixtures.idea
1817
import software.aws.toolkits.jetbrains.uitests.fixtures.newProjectWizard
19-
import software.aws.toolkits.jetbrains.uitests.fixtures.preferencesDialog
2018
import software.aws.toolkits.jetbrains.uitests.fixtures.projectStructureDialog
2119
import software.aws.toolkits.jetbrains.uitests.fixtures.welcomeFrame
20+
import software.aws.toolkits.jetbrains.uitests.utils.setupSamCli
2221
import java.nio.file.Path
2322

2423
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@@ -27,35 +26,8 @@ class SamTemplateProjectWizardTest {
2726
lateinit var tempDir: Path
2827

2928
@BeforeAll
30-
fun setUpSamCli() {
31-
val samPath = System.getenv("SAM_CLI_EXEC")
32-
if (samPath.isNullOrEmpty()) {
33-
log.warn("No custom SAM set, skipping setup")
34-
return
35-
}
36-
37-
uiTest {
38-
welcomeFrame {
39-
step("Open preferences page") {
40-
openPreferences()
41-
42-
preferencesDialog {
43-
// Search for AWS because sometimes it is off the screen
44-
search("AWS")
45-
46-
selectPreferencePage("Tools", "AWS")
47-
48-
step("Set SAM CLI executable path to $samPath") {
49-
textField("SAM CLI executable:").text = samPath
50-
}
51-
52-
pressOk()
53-
}
54-
55-
selectTab("Projects")
56-
}
57-
}
58-
}
29+
fun setup() {
30+
setupSamCli()
5931
}
6032

6133
@Test
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
// Copyright 2021 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.uitests.utils
5+
6+
import com.intellij.remoterobot.stepsProcessing.log
7+
import com.intellij.remoterobot.stepsProcessing.step
8+
import software.aws.toolkits.jetbrains.uitests.extensions.uiTest
9+
import software.aws.toolkits.jetbrains.uitests.fixtures.preferencesDialog
10+
import software.aws.toolkits.jetbrains.uitests.fixtures.welcomeFrame
11+
12+
/**
13+
* Setup SAM cli configuration from the welcome screen
14+
*/
15+
fun setupSamCli() {
16+
val samPath = System.getenv("SAM_CLI_EXEC")
17+
if (samPath.isNullOrEmpty()) {
18+
log.warn("No custom SAM set, skipping setup")
19+
return
20+
}
21+
22+
uiTest {
23+
welcomeFrame {
24+
step("Open preferences page") {
25+
openPreferences()
26+
27+
preferencesDialog {
28+
// Search for AWS because sometimes it is off the screen
29+
search("AWS")
30+
31+
selectPreferencePage("Tools", "AWS")
32+
33+
step("Set SAM CLI executable path to $samPath") {
34+
textField("SAM CLI executable:").text = samPath
35+
}
36+
37+
pressOk()
38+
}
39+
40+
selectTab("Projects")
41+
}
42+
}
43+
}
44+
}

0 commit comments

Comments
 (0)