Skip to content

Commit f4c1566

Browse files
committed
New module creation UI test + Refactor and consolidate UI test setup and exclude patterns
Replaced duplicated UI test setup logic with shared methods in `SharedSteps`. Adjusted test exclusion handling for better customization via `excludeTests` property. Introduced UI test improvements, new test cases, and removed deprecated scripts.
1 parent 25eae35 commit f4c1566

File tree

11 files changed

+430
-158
lines changed

11 files changed

+430
-158
lines changed

Diff for: .github/workflows/gradle.yml

+3-3
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ jobs:
3232
- name: Grant execute permission for gradlew
3333
run: chmod +x gradlew
3434
- name: Run automated tests
35-
run: ./gradlew test --no-daemon
35+
run: ./gradlew test -PexcludeTests="**/userInterface/**" --no-daemon
3636

3737
build-windows:
3838
runs-on: windows-latest
@@ -58,7 +58,7 @@ jobs:
5858
- name: Grant execute permission for gradlew
5959
run: chmod +x gradlew
6060
- name: Run automated tests
61-
run: ./gradlew test --no-daemon
61+
run: ./gradlew test -PexcludeTests="**/userInterface/**" --no-daemon
6262

6363
build-macos:
6464
runs-on: macos-latest
@@ -84,7 +84,7 @@ jobs:
8484
- name: Grant execute permission for gradlew
8585
run: chmod +x gradlew
8686
- name: Run automated tests
87-
run: ./gradlew test --no-daemon
87+
run: ./gradlew test -PexcludeTests="**/userInterface/**" --no-daemon
8888

8989
static-tests:
9090
runs-on: ubuntu-latest

Diff for: .github/workflows/uitests.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ jobs:
6666
if: matrix.os == 'ubuntu-latest'
6767
run: |
6868
export DISPLAY=:99.0
69-
./gradlew uiTests
69+
./gradlew test -PexcludeTests="**/reference/**,**/linemarker/**,**/inspections/**,**/completion/**,**/actions/**"
7070
7171
# Uncomment if investigation is needed:
7272

Diff for: build.gradle.kts

+9-9
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,15 @@ tasks {
137137
}
138138

139139
test {
140-
exclude("**/userInterface/**")
140+
val excludePatterns = project.findProperty("excludeTests") as String?
141+
142+
if (!excludePatterns.isNullOrEmpty()) {
143+
// Split the comma-separated string and apply exclusions
144+
excludePatterns.split(",").forEach {
145+
exclude(it.trim())
146+
}
147+
}
148+
141149
useJUnitPlatform()
142150
}
143151

@@ -247,11 +255,3 @@ kover {
247255
}
248256
}
249257
}
250-
251-
tasks.register<Test>("uiTests") {
252-
exclude("**/reference/**")
253-
exclude("**/linemarker/**")
254-
exclude("**/inspections/**")
255-
exclude("**/completion/**")
256-
exclude("**/actions/**") // Deprecated, all actions should be reimplemented in the UI tests and this exclude should be removed
257-
}

Diff for: runTests.sh

-16
This file was deleted.

Diff for: runUiTests.sh

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#!/bin/bash
2+
3+
rm -rf intellij-test-project
4+
./gradlew clean
5+
./gradlew runIdeForUiTests &
6+
RUN_IDE_PID=$!
7+
8+
sleep 10
9+
10+
./gradlew test -PexcludeTests="**/reference/**,**/linemarker/**,**/inspections/**,**/completion/**,**/actions/**" --no-daemon
11+
12+
kill $RUN_IDE_PID
13+
14+
wait $RUN_IDE_PID 2>/dev/null

Diff for: src/main/java/com/magento/idea/magento2plugin/actions/generation/dialog/NewModuleDialog.form

+3-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<grid id="cbd77" binding="contentPane" layout-manager="GridLayoutManager" row-count="4" column-count="2" same-size-horizontally="false" same-size-vertically="false" hgap="-1" vgap="-1">
44
<margin top="10" left="10" bottom="10" right="10"/>
55
<constraints>
6-
<xy x="48" y="54" width="746" height="642"/>
6+
<xy x="48" y="54" width="746" height="646"/>
77
</constraints>
88
<properties>
99
<preferredSize width="455" height="600"/>
@@ -68,6 +68,7 @@
6868
</grid>
6969
</constraints>
7070
<properties>
71+
<name value="Package Name"/>
7172
<text value=""/>
7273
<toolTipText resource-bundle="magento2/validation" key="validator.mustNotBeEmptyShouldContainLettersOrNumbers"/>
7374
</properties>
@@ -92,6 +93,7 @@
9293
</grid>
9394
</constraints>
9495
<properties>
96+
<name value="Module Name"/>
9597
<text value=""/>
9698
<toolTipText resource-bundle="magento2/validation" key="validator.mustNotBeEmptyShouldContainLettersOrNumbers"/>
9799
</properties>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
package com.magento.idea.magento2plugin.pages
7+
8+
import com.intellij.remoterobot.RemoteRobot
9+
import com.intellij.remoterobot.data.RemoteComponent
10+
import com.intellij.remoterobot.fixtures.CommonContainerFixture
11+
import com.intellij.remoterobot.fixtures.DefaultXpath
12+
import com.intellij.remoterobot.fixtures.FixtureName
13+
import com.intellij.remoterobot.fixtures.JTextFieldFixture
14+
import com.intellij.remoterobot.search.locators.byXpath
15+
import java.time.Duration
16+
17+
fun RemoteRobot.createAModuleDialog(function: CreateAModuleDialogFixture.() -> Unit) {
18+
find<CreateAModuleDialogFixture>(timeout = Duration.ofSeconds(10)).apply(function)
19+
}
20+
21+
@FixtureName("CreateAModuleDialog")
22+
@DefaultXpath("CreateAModuleDialog type", "//div[@class='NewModuleDialog']")
23+
class CreateAModuleDialogFixture(
24+
remoteRobot: RemoteRobot,
25+
remoteComponent: RemoteComponent) : CommonContainerFixture(remoteRobot, remoteComponent) {
26+
27+
val packageName
28+
get() = find<JTextFieldFixture>(byXpath("FilteredComboBox", "//div[@name='Package Name']"))
29+
30+
val moduleName
31+
get() = find<JTextFieldFixture>(byXpath("FilteredComboBox", "//div[@name='Module Name']"))
32+
}

Diff for: src/test/kotlin/com/magento/idea/magento2plugin/pages/IdeaFrame.kt

-17
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import com.intellij.remoterobot.RemoteRobot
99
import com.intellij.remoterobot.data.RemoteComponent
1010
import com.intellij.remoterobot.fixtures.*
1111
import com.intellij.remoterobot.search.locators.byXpath
12-
import com.intellij.remoterobot.stepsProcessing.step
13-
import com.intellij.remoterobot.utils.waitFor
1412
import java.time.Duration
1513

1614

@@ -29,21 +27,6 @@ class IdeaFrame(remoteRobot: RemoteRobot, remoteComponent: RemoteComponent) :
2927
val projectViewTree
3028
get() = find<ContainerFixture>(byXpath("//div[@class='ProjectViewTree']"))
3129

32-
@JvmOverloads
33-
fun dumbAware(timeout: Duration = Duration.ofMinutes(5), function: () -> Unit) {
34-
step("Wait for smart mode") {
35-
waitFor(duration = timeout, interval = Duration.ofSeconds(5)) {
36-
runCatching { isDumbMode().not() }.getOrDefault(false)
37-
}
38-
function()
39-
step("..wait for smart mode again") {
40-
waitFor(duration = timeout, interval = Duration.ofSeconds(5)) {
41-
isDumbMode().not()
42-
}
43-
}
44-
}
45-
}
46-
4730
fun isProjectViewVisible(): Boolean {
4831
return try {
4932
with(projectViewTree) {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
1+
/*
2+
* Copyright © Magento, Inc. All rights reserved.
3+
* See COPYING.txt for license details.
4+
*/
5+
6+
package com.magento.idea.magento2plugin.steps
7+
8+
import com.intellij.remoterobot.RemoteRobot
9+
import com.intellij.remoterobot.fixtures.ContainerFixture
10+
import com.intellij.remoterobot.fixtures.JTextFieldFixture
11+
import com.intellij.remoterobot.search.locators.byXpath
12+
import com.intellij.remoterobot.steps.CommonSteps
13+
import com.intellij.remoterobot.stepsProcessing.step
14+
import com.intellij.remoterobot.utils.keyboard
15+
import com.intellij.remoterobot.utils.waitFor
16+
import com.magento.idea.magento2plugin.pages.*
17+
import java.awt.Point
18+
import java.awt.event.KeyEvent.*
19+
import java.io.File
20+
import java.io.IOException
21+
import java.nio.file.Paths
22+
import java.time.Duration.ofMinutes
23+
import java.util.*
24+
25+
class SharedSteps(private val remoteRobot: RemoteRobot) {
26+
private lateinit var tempProjectDir: File
27+
28+
fun createOrOpenTestProject(): File {
29+
setupTemporaryMagentoProject()
30+
31+
step("Create Or Open Test Project", Runnable {
32+
try {
33+
remoteRobot.welcomeFrame {
34+
val newProjectButton = remoteRobot.find(
35+
ContainerFixture::class.java,
36+
byXpath("//div[@visible_text='New Project']")
37+
)
38+
newProjectButton.click()
39+
Thread.sleep(2_000)
40+
41+
val jTextFieldFixture = find<JTextFieldFixture>(byXpath("//div[@class='TextFieldWithBrowseButton']"))
42+
jTextFieldFixture.click()
43+
jTextFieldFixture.keyboard {
44+
hotKey(VK_CONTROL, VK_A)
45+
key(VK_DELETE)
46+
enterText(tempProjectDir.absolutePath.toString().replace("\\", "\\\\"))
47+
}
48+
keyboard { key(VK_ENTER) }
49+
50+
dialog("Directory Is Not Empty") {
51+
button("Create from Existing Sources").click()
52+
}
53+
54+
enableMagentoSupport()
55+
}
56+
} catch (exception: Exception) {
57+
// temporary workaround until we get license for CI
58+
activateIde()
59+
// end temporary workaround
60+
try {
61+
val launchedFromScript = remoteRobot.find(
62+
ContainerFixture::class.java,
63+
byXpath("//div[@class='LinkLabel']")
64+
)
65+
launchedFromScript.click()
66+
} catch (e: Exception) {
67+
// Element does not exist, continue without failing the test
68+
}
69+
70+
createProjectFromExistingFiles()
71+
enableMagentoSupport()
72+
}
73+
})
74+
75+
return tempProjectDir;
76+
}
77+
78+
fun closeProject() {
79+
CommonSteps(remoteRobot).closeProject()
80+
}
81+
82+
private fun setupTemporaryMagentoProject() {
83+
// Create a parent directory and a random child directory inside it
84+
val parentDir = Paths.get("intellij-test-project").toFile()
85+
if (parentDir.exists()) {
86+
parentDir.deleteRecursively()
87+
}
88+
parentDir.mkdirs()
89+
90+
// Create a randomly named child directory inside the parent directory
91+
tempProjectDir = File(parentDir, UUID.randomUUID().toString()).apply {
92+
mkdirs()
93+
}
94+
95+
// Define the source directory for the test data
96+
val sourceDir = File("testData/project/magento2")
97+
98+
// Copy the test data to the temporary directory
99+
sourceDir.copyRecursively(
100+
target = tempProjectDir,
101+
overwrite = true
102+
)
103+
}
104+
105+
private fun activateIde() {
106+
if ("true" == System.getenv("GITHUB_ACTIONS")) {
107+
val startTrial =
108+
remoteRobot.find(ContainerFixture::class.java, byXpath("//div[@visible_text='Start trial']"))
109+
startTrial.click()
110+
111+
val startTrialFree = remoteRobot.find(ContainerFixture::class.java, byXpath("//div[@class='s']"))
112+
startTrialFree.click()
113+
114+
val dialog = remoteRobot.find(
115+
DialogFixture::class.java, byXpath("//div[@class='MyDialog']")
116+
)
117+
dialog.button("Close").click()
118+
119+
try {
120+
Thread.sleep(10000)
121+
} catch (e: InterruptedException) {
122+
Thread.currentThread().interrupt()
123+
throw RuntimeException(e)
124+
}
125+
126+
closeBrowser()
127+
} else {
128+
val dialog = remoteRobot.find(
129+
DialogFixture::class.java, byXpath("//div[@class='MyDialog']")
130+
)
131+
dialog.button("Activate").click()
132+
dialog.button("Close").click()
133+
}
134+
}
135+
136+
private fun enableMagentoSupport() {
137+
remoteRobot.idea {
138+
step("Enable Magento Integration") {
139+
waitFor(ofMinutes(1)) { isDumbMode().not() }
140+
Thread.sleep(5_000)
141+
enableSupportLink.click(Point(1, 1))
142+
waitFor(ofMinutes(1)) { isDumbMode().not() }
143+
144+
if (!isProjectViewVisible()) {
145+
keyboard {
146+
hotKey(VK_ALT, VK_1)
147+
}
148+
}
149+
}
150+
}
151+
}
152+
153+
private fun createProjectFromExistingFiles() {
154+
remoteRobot.welcomeFrame {
155+
try {
156+
val launchedFromScript = find<ContainerFixture>(byXpath("//div[@class='LinkLabel']"))
157+
launchedFromScript.click()
158+
} catch (e: Exception) {
159+
// Element does not exist, continue without failing the test
160+
}
161+
162+
createNewProjectFromExistingFilesLink.click()
163+
selectProjectPath()
164+
}
165+
}
166+
167+
private fun WelcomeFrame.selectProjectPath() {
168+
dialog("Open File or Project") {
169+
// Set the path for the copied test data
170+
val comboBox = find<ContainerFixture>(byXpath("//div[@class='BorderlessTextField']"))
171+
comboBox.click() // Focus on the comboBox
172+
comboBox.keyboard {
173+
hotKey(VK_CONTROL, VK_A) // Select all text
174+
key(VK_DELETE) // Delete selected text
175+
enterText(tempProjectDir.absolutePath.toString().replace("\\", "\\\\"))
176+
}
177+
178+
button("OK").click()
179+
trustProjectLink.click()
180+
}
181+
}
182+
183+
/**
184+
* Closes the browser by terminating its process based on the operating system.
185+
*/
186+
fun closeBrowser() {
187+
val os = System.getProperty("os.name").lowercase(Locale.getDefault())
188+
189+
try {
190+
if (os.contains("win")) {
191+
// For Windows: Close common browsers like Chrome, Firefox, etc.
192+
Runtime.getRuntime().exec("taskkill /F /IM edge.exe")
193+
} else if (os.contains("mac")) {
194+
// For macOS: Kill browsers using `pkill`
195+
Runtime.getRuntime().exec("killall -9 safari")
196+
} else if (os.contains("nix") || os.contains("nux")) {
197+
// For Linux-based systems: Kill typical browser processes
198+
Runtime.getRuntime().exec("killall -9 firefox")
199+
}
200+
} catch (e: IOException) {
201+
e.printStackTrace()
202+
}
203+
}
204+
}

0 commit comments

Comments
 (0)