Skip to content

Commit e5d908a

Browse files
committed
Starting to add a new test
Removed the jvmToolchain requirement in build.gradle.kts
1 parent 5a6a42a commit e5d908a

File tree

6 files changed

+198
-111
lines changed

6 files changed

+198
-111
lines changed

.github/workflows/presubmit.yaml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ jobs:
2121
steps:
2222
- name: checkout
2323
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8
24-
# Allows the specification of a specific Java SDK
24+
# Allows the specification of a specific Java SDK, this may be required for the Starter test framework
2525
# - name: Set up JDK 21
2626
# uses: actions/setup-java@v4
2727
# with:
@@ -34,7 +34,7 @@ jobs:
3434
strategy:
3535
fail-fast: true
3636
matrix:
37-
bot: [CHECK_BOT, DART_BOT, UNIT_TEST_BOT, VERIFY_BOT] #, INTEGRATION_BOT]
37+
bot: [CHECK_BOT, DART_BOT, UNIT_TEST_BOT, VERIFY_BOT, INTEGRATION_BOT]
3838
steps:
3939
- name: checkout
4040
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8

build.gradle.kts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -99,11 +99,12 @@ kotlin {
9999
apiVersion.set(KotlinVersion.KOTLIN_2_1)
100100
jvmTarget = jvmVersion
101101
}
102-
jvmToolchain {
103-
languageVersion = JavaLanguageVersion.of(21)
104-
@Suppress("UnstableApiUsage")
105-
vendor = JvmVendorSpec.JETBRAINS
106-
}
102+
// This is how you specify the specific JVM requirements, this may be a requirement for the Starter test framework
103+
// jvmToolchain {
104+
// languageVersion = JavaLanguageVersion.of(21)
105+
// @Suppress("UnstableApiUsage")
106+
// vendor = JvmVendorSpec.JETBRAINS
107+
// }
107108
}
108109

109110
var javaCompatibilityVersion: JavaVersion

gradle.properties

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,3 @@ javaVersion=21
1212
kotlin.stdlib.default.dependency=false
1313
org.gradle.parallel=true
1414
org.gradle.jvmargs=-Xms1024m -Xmx4048m
15-
16-
org.gradle.java.installations.auto-detect=false
17-
org.gradle.java.installations.dirs=/opt/jdk-21 # Replace with the actual path where JDK 21 is installed by the GitHub Actions runner

testSrc/integration/io/flutter/integrationTest/NewProjectUITest.kt

Lines changed: 4 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -6,23 +6,18 @@
66

77
package io.flutter.integrationTest
88

9-
import com.intellij.driver.sdk.ui.Finder
10-
import com.intellij.driver.sdk.ui.components.ComponentData
11-
import com.intellij.driver.sdk.ui.components.UiComponent
129
import com.intellij.driver.sdk.ui.components.UiComponent.Companion.waitFound
1310
import com.intellij.driver.sdk.ui.components.common.ideFrame
1411
import com.intellij.driver.sdk.ui.components.common.toolwindows.projectView
15-
import com.intellij.driver.sdk.ui.components.common.welcomeScreen
16-
import com.intellij.driver.sdk.wait
1712
import com.intellij.driver.sdk.waitForIndicators
1813
import com.intellij.ide.starter.driver.engine.BackgroundRun
1914
import com.intellij.ide.starter.driver.engine.runIdeWithDriver
2015
import com.intellij.ide.starter.junit5.config.UseLatestDownloadedIdeBuild
16+
import io.flutter.integrationTest.utils.newProjectWelcomeScreen
2117
import org.junit.jupiter.api.*
2218
import org.junit.jupiter.api.extension.ExtendWith
2319
import java.nio.file.Paths
2420
import kotlin.time.Duration.Companion.minutes
25-
import kotlin.time.Duration.Companion.seconds
2621

2722
@Tag("ui")
2823
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
@@ -87,7 +82,7 @@ class MyProjectUITest {
8782
println("Test project will be created as: $testProjectName")
8883
run = Setup.setupTestContextIC("MyProjectUITest").runIdeWithDriver()
8984

90-
newProjectWelcomeScreen()
85+
newProjectWelcomeScreen(run, testProjectName)
9186
newProjectInProjectView()
9287
}
9388

@@ -98,7 +93,7 @@ class MyProjectUITest {
9893
println("Test project will be created as: $testProjectName")
9994
run = Setup.setupTestContextUE("MyProjectUITest").runIdeWithDriver()
10095

101-
newProjectWelcomeScreen()
96+
newProjectWelcomeScreen(run, testProjectName)
10297
run.driver.withContext {
10398
ideFrame {
10499
// Wait for the ideFrame to appear before attempting to interact with it.
@@ -115,58 +110,10 @@ class MyProjectUITest {
115110
println("Test project will be created as: $testProjectName")
116111
run = Setup.setupTestContextWS("MyProjectUITest").runIdeWithDriver()
117112

118-
newProjectWelcomeScreen()
113+
newProjectWelcomeScreen(run, testProjectName)
119114
newProjectInProjectView()
120115
}
121116

122-
123-
/**
124-
* Automates the process of creating a new Flutter project from the welcome screen.
125-
* <p>
126-
* This function navigates the "New Project" dialog, selects the Flutter project type,
127-
* and enters a unique project name. It relies on the {@code FLUTTER_SDK} environment
128-
* variable to be set for the test to proceed.
129-
*/
130-
fun newProjectWelcomeScreen() {
131-
run.driver.withContext {
132-
// Assert that the welcome screen is visible before interacting with it.
133-
welcomeScreen {
134-
assert(isVisible())
135-
println("Creating the new project from Welcome Screen")
136-
createNewProjectButton.click()
137-
138-
// The test expects the `FlutterGeneratorPeer` to automatically populate the
139-
// Flutter SDK path. This behavior relies on the FLUTTER_SDK environment
140-
// variable being set.
141-
//
142-
// If the FLUTTER_SDK variable is not present, the UI will not find the SDK,
143-
// and the 'Next' button will remain disabled, causing the test to fail.
144-
// A common reason for this failure is an unconfigured test environment.
145-
146-
newProjectDialog {
147-
// Wait for the dialog to fully load
148-
wait(1.seconds)
149-
150-
// Select project type - adjust based on your needs
151-
chooseProjectType("Flutter")
152-
wait(1.seconds)
153-
154-
// Expect setup to take care of setting the correct Flutter SDK
155-
if (!nextButton.isEnabled()) {
156-
fail { "The FLUTTER_SDK environment variable was not set." }
157-
}
158-
nextButton.click()
159-
wait(1.seconds)
160-
161-
keyboard {
162-
typeText(testProjectName)
163-
}
164-
createButton.click()
165-
}
166-
}
167-
}
168-
}
169-
170117
/**
171118
* Verifies the successful creation of a new project by asserting the presence
172119
* and structure of files in the project view.
@@ -226,47 +173,3 @@ class MyProjectUITest {
226173
}
227174
}
228175
}
229-
230-
//
231-
// TODO(jwren) move this into a utility for project creation tasks
232-
//
233-
234-
// A Kotlin extension function for the `Finder` class.
235-
// This function adds a new method, `newProjectDialog`, that can be
236-
// called on any `Finder` object.
237-
fun Finder.newProjectDialog(action: NewProjectDialogUI.() -> Unit) {
238-
// Locates the "New Project" dialog.
239-
// - `x(...)` creates an XPath-like query to find the UI component.
240-
// - The query targets a `div` with a `title` of "New Project".
241-
// - `NewProjectDialogUI::class.java` specifies that the found component
242-
// should be treated as an instance of the `NewProjectDialogUI` class,
243-
// allowing access to its properties and functions.
244-
//
245-
// The found dialog component is then passed to the `action` lambda,
246-
// which contains the test steps to perform within the dialog.
247-
x("//div[@title='New Project']", NewProjectDialogUI::class.java).action()
248-
}
249-
250-
// A UI component representing the "New Project" dialog.
251-
// This class provides a structured way to interact with the dialog's elements
252-
// using the IntelliJ Driver SDK.
253-
open class NewProjectDialogUI(data: ComponentData) : UiComponent(data) {
254-
255-
// Clicks on the specified project type from the list.
256-
// The function waits until the text is found before performing the click action.
257-
fun chooseProjectType(projectType: String) {
258-
projectTypeList.waitOneText(projectType).click()
259-
}
260-
261-
// Locates the list of project types within the dialog.
262-
// The xQuery targets a component with the specific class "JBList".
263-
private val projectTypeList = x("//div[@class='JBList']")
264-
265-
// Locates the "Next" button in the dialog.
266-
// The xQuery finds a button component with the visible text "Next".
267-
val nextButton = x("//div[@text='Next']")
268-
269-
// Locates the "Create" button in the dialog.
270-
// The xQuery finds a button component with the visible text "Create".
271-
val createButton = x("//div[@text='Create']")
272-
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* Copyright 2025 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
7+
package io.flutter.integrationTest
8+
9+
import com.intellij.ide.starter.driver.engine.BackgroundRun
10+
import com.intellij.ide.starter.driver.engine.runIdeWithDriver
11+
import com.intellij.ide.starter.junit5.config.UseLatestDownloadedIdeBuild
12+
import io.flutter.integrationTest.utils.newProjectWelcomeScreen
13+
import org.junit.jupiter.api.*
14+
import org.junit.jupiter.api.extension.ExtendWith
15+
import java.nio.file.Paths
16+
17+
@Tag("ui")
18+
@TestInstance(TestInstance.Lifecycle.PER_METHOD)
19+
@ExtendWith(UseLatestDownloadedIdeBuild::class)
20+
class PreferencePagesUITest {
21+
22+
companion object {
23+
// Generate a unique folder name for the test project to avoid conflicts
24+
var testProjectName = "my_test_project_${System.currentTimeMillis()}"
25+
26+
/**
27+
* Cleanup method that runs after all tests in this class.
28+
* Removes the test project folder created during testing to keep the system clean.
29+
*/
30+
@JvmStatic
31+
@AfterAll
32+
fun cleanUpTestFolder() {
33+
val projectPath = Paths.get(System.getProperty("user.home"), "IdeaProjects", testProjectName)
34+
val projectFile = projectPath.toFile()
35+
if (projectFile.exists()) {
36+
projectFile.deleteRecursively()
37+
println("Successfully deleted test folder: $projectPath")
38+
} else {
39+
println("Test folder does not exist, skipping cleanup: $projectPath")
40+
}
41+
}
42+
}
43+
44+
/**
45+
* The IDE instance running in the background.
46+
* Initialized in @BeforeEach and closed in @AfterEach.
47+
*/
48+
private lateinit var run: BackgroundRun
49+
50+
/**
51+
* TODO document
52+
*/
53+
@BeforeEach
54+
fun initContext() {
55+
println("Initializing IDE test context")
56+
println("Test project will be created as: $testProjectName")
57+
// TODO consider creating from github as a source instead: GitHubProject.fromGithub()
58+
run = Setup.setupTestContextIC("MyProjectUITest").runIdeWithDriver()
59+
newProjectWelcomeScreen(run, testProjectName)
60+
}
61+
62+
/**
63+
* Tears down the test environment after each test method.
64+
*
65+
* Safely closes the IDE instance if it was successfully started.
66+
* The initialization check prevents errors if the setup failed.
67+
*/
68+
@AfterEach
69+
fun closeIde() {
70+
if (::run.isInitialized) {
71+
println("Closing IDE")
72+
run.closeIdeAndWait()
73+
} else {
74+
println("IDE was not started, skipping close")
75+
}
76+
}
77+
78+
@Test
79+
fun newProjectIC() {
80+
// TODO work in progress here
81+
}
82+
}
83+
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/*
2+
* Copyright 2025 The Chromium Authors. All rights reserved.
3+
* Use of this source code is governed by a BSD-style license that can be
4+
* found in the LICENSE file.
5+
*/
6+
7+
package io.flutter.integrationTest.utils
8+
9+
import com.intellij.driver.sdk.ui.Finder
10+
import com.intellij.driver.sdk.ui.components.ComponentData
11+
import com.intellij.driver.sdk.ui.components.UiComponent
12+
import com.intellij.driver.sdk.ui.components.common.welcomeScreen
13+
import com.intellij.driver.sdk.wait
14+
import com.intellij.ide.starter.driver.engine.BackgroundRun
15+
import org.junit.jupiter.api.fail
16+
import kotlin.time.Duration.Companion.seconds
17+
18+
// A Kotlin extension function for the `Finder` class.
19+
// This function adds a new method, `newProjectDialog`, that can be
20+
// called on any `Finder` object.
21+
fun Finder.newProjectDialog(action: NewProjectDialogUI.() -> Unit) {
22+
// Locates the "New Project" dialog.
23+
// - `x(...)` creates an XPath-like query to find the UI component.
24+
// - The query targets a `div` with a `title` of "New Project".
25+
// - `NewProjectDialogUI::class.java` specifies that the found component
26+
// should be treated as an instance of the `NewProjectDialogUI` class,
27+
// allowing access to its properties and functions.
28+
//
29+
// The found dialog component is then passed to the `action` lambda,
30+
// which contains the test steps to perform within the dialog.
31+
x("//div[@title='New Project']", NewProjectDialogUI::class.java).action()
32+
}
33+
34+
// A UI component representing the "New Project" dialog.
35+
// This class provides a structured way to interact with the dialog's elements
36+
// using the IntelliJ Driver SDK.
37+
open class NewProjectDialogUI(data: ComponentData) : UiComponent(data) {
38+
39+
// Clicks on the specified project type from the list.
40+
// The function waits until the text is found before performing the click action.
41+
fun chooseProjectType(projectType: String) {
42+
projectTypeList.waitOneText(projectType).click()
43+
}
44+
45+
// Locates the list of project types within the dialog.
46+
// The xQuery targets a component with the specific class "JBList".
47+
private val projectTypeList = x("//div[@class='JBList']")
48+
49+
// Locates the "Next" button in the dialog.
50+
// The xQuery finds a button component with the visible text "Next".
51+
val nextButton = x("//div[@text='Next']")
52+
53+
// Locates the "Create" button in the dialog.
54+
// The xQuery finds a button component with the visible text "Create".
55+
val createButton = x("//div[@text='Create']")
56+
}
57+
58+
/**
59+
* Automates the process of creating a new Flutter project from the welcome screen.
60+
* <p>
61+
* This function navigates the "New Project" dialog, selects the Flutter project type,
62+
* and enters a unique project name. It relies on the {@code FLUTTER_SDK} environment
63+
* variable to be set for the test to proceed.
64+
*/
65+
fun newProjectWelcomeScreen(run: BackgroundRun, testProjectName: String) {
66+
run.driver.withContext {
67+
// Assert that the welcome screen is visible before interacting with it.
68+
welcomeScreen {
69+
assert(isVisible())
70+
println("Creating the new project from Welcome Screen")
71+
createNewProjectButton.click()
72+
73+
// The test expects the `FlutterGeneratorPeer` to automatically populate the
74+
// Flutter SDK path. This behavior relies on the FLUTTER_SDK environment
75+
// variable being set.
76+
//
77+
// If the FLUTTER_SDK variable is not present, the UI will not find the SDK,
78+
// and the 'Next' button will remain disabled, causing the test to fail.
79+
// A common reason for this failure is an unconfigured test environment.
80+
81+
newProjectDialog {
82+
// Wait for the dialog to fully load
83+
wait(1.seconds)
84+
85+
// Select project type - adjust based on your needs
86+
chooseProjectType("Flutter")
87+
wait(1.seconds)
88+
89+
// Expect setup to take care of setting the correct Flutter SDK
90+
if (!nextButton.isEnabled()) {
91+
fail { "The FLUTTER_SDK environment variable was not set." }
92+
}
93+
nextButton.click()
94+
wait(1.seconds)
95+
96+
keyboard {
97+
typeText(testProjectName)
98+
}
99+
createButton.click()
100+
}
101+
}
102+
}
103+
}

0 commit comments

Comments
 (0)