Skip to content

Commit f9f19d5

Browse files
authored
ci: UI test for feature availability in chat panel (aws#5393)
* Using puppeteer for basic tests * test with auth * UI tests with Auth * handle env vars * feedback 1 * Switch test package * Modify import * enable jcef * more settings * install apparmor * install apparmor * install apparmor * install apparmor * install apparmor * Fix test * try this setting * modify 241 config * detekt * assertions * revert to thread sleep * removed print statement
1 parent 21aba02 commit f9f19d5

File tree

10 files changed

+355
-20
lines changed

10 files changed

+355
-20
lines changed

buildspec/linuxUiTests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ phases:
4747
- chmod +x gradlew
4848

4949
- ffmpeg -loglevel quiet -nostdin -f x11grab -video_size ${SCREEN_WIDTH}x${SCREEN_HEIGHT} -i ${DISPLAY} -codec:v libx264 -pix_fmt yuv420p -vf drawtext="fontsize=48:box=1:boxcolor=black@0.75:boxborderw=5:fontcolor=white:x=0:y=h-text_h:text='%{gmtime\:%H\\\\\:%M\\\\\:%S}'" -framerate 12 -g 12 /tmp/screen_recording.mp4 &
50-
- ./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME :ui-tests-starter:test coverageReport --console plain --info
50+
- ./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME :ui-tests-starter:uiTest coverageReport --console plain --info
5151

5252
post_build:
5353
commands:

noop/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33

44
// project that does nothing
55
tasks.register("test")
6+
tasks.register("uiTest")

settings.gradle.kts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ include("sandbox-all")
100100
include("ui-tests-starter")
101101
when (providers.gradleProperty("ideProfileName").get()) {
102102
// FIX_WHEN_MIN_IS_242: `tmp-all` test module no longer needed in 242+
103-
"2023.3", "2024.1" -> {
103+
"2024.1" -> {
104104
include("tmp-all")
105-
106-
// only available 242+
105+
project(":ui-tests-starter").projectDir = file("noop")
106+
}
107+
"2024.2" -> {
108+
// only available 243+
107109
project(":ui-tests-starter").projectDir = file("noop")
108110
}
109111
}

ui-tests-starter/build.gradle.kts

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,26 @@ plugins {
99
id("toolkit-kotlin-conventions")
1010
id("toolkit-intellij-plugin")
1111

12-
id("org.jetbrains.intellij.platform.base")
12+
id("org.jetbrains.intellij.platform")
1313
}
1414

1515
val ideProfile = IdeVersions.ideProfile(project)
16+
val testPlugins by configurations.registering
1617

17-
// Add our source sets per IDE profile version (i.e. src-211)
1818
sourceSets {
1919
test {
20-
java.srcDirs(findFolders(project, "tst", ideProfile))
21-
resources.srcDirs(findFolders(project, "tst-resources", ideProfile))
20+
java.setSrcDirs(findFolders(project, "tst-prep", ideProfile))
21+
resources.setSrcDirs(findFolders(project, "tst-resources", ideProfile))
22+
}
23+
}
24+
25+
val uiTestSource = sourceSets.create("uiTest") {
26+
java.setSrcDirs(findFolders(project, "tst", ideProfile))
27+
}
28+
29+
idea {
30+
module {
31+
testSources.from(uiTestSource.allSource.srcDirs)
2232
}
2333
}
2434

@@ -27,35 +37,57 @@ intellijPlatform {
2737
instrumentCode = false
2838
}
2939

30-
tasks.initializeIntellijPlatformPlugin {
31-
enabled = false
32-
}
40+
val uiTestImplementation by configurations.getting
3341

34-
tasks.verifyPluginProjectConfiguration {
35-
runtimeDirectory.set(null as File?)
36-
enabled = false
42+
configurations.getByName(uiTestSource.compileClasspathConfigurationName) {
43+
extendsFrom(uiTestImplementation)
3744
}
3845

39-
val testPlugins by configurations.registering
46+
configurations.getByName(uiTestSource.runtimeClasspathConfigurationName) {
47+
extendsFrom(uiTestImplementation)
48+
}
4049

4150
dependencies {
4251
// should really be set by the BOM, but too much work to figure out right now
43-
testImplementation("org.kodein.di:kodein-di-jvm:7.20.2")
52+
uiTestImplementation("org.kodein.di:kodein-di-jvm:7.20.2")
53+
uiTestImplementation(platform(libs.junit5.bom))
54+
uiTestImplementation(libs.junit5.jupiter)
55+
4456
intellijPlatform {
45-
// shouldn't be needed? but IsolationException
4657
val version = ideProfile.community.sdkVersion
4758
intellijIdeaCommunity(version, !version.contains("SNAPSHOT"))
48-
testFramework(TestFrameworkType.Starter)
59+
60+
localPlugin(project(":plugin-core"))
61+
testImplementation(project(":plugin-core:core"))
62+
testImplementation(project(":plugin-core:jetbrains-community"))
63+
testImplementation(testFixtures(project(":plugin-core:jetbrains-community")))
64+
65+
testFramework(TestFrameworkType.Bundled)
66+
testFramework(TestFrameworkType.JUnit5)
67+
68+
testFramework(TestFrameworkType.Starter, configurationName = uiTestImplementation.name)
4969
}
5070

5171
testPlugins(project(":plugin-amazonq", "pluginZip"))
5272
testPlugins(project(":plugin-core", "pluginZip"))
5373
}
5474

5575
tasks.test {
56-
dependsOn(testPlugins)
76+
enabled = false
77+
}
5778

58-
useJUnitPlatform()
79+
val prepareAmazonQTest by intellijPlatformTesting.testIde.registering {
80+
task {
81+
useJUnitPlatform()
82+
}
83+
}
84+
85+
tasks.register<Test>("uiTest") {
86+
testClassesDirs = uiTestSource.output.classesDirs
87+
classpath = uiTestSource.runtimeClasspath
88+
89+
dependsOn(prepareAmazonQTest)
90+
dependsOn(testPlugins)
5991

6092
systemProperty("ui.test.plugins", testPlugins.get().asPath)
6193
systemProperty("org.gradle.project.ideProfileName", ideProfile.name)
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.uitests.chatTests
5+
6+
import com.intellij.driver.sdk.waitForProjectOpen
7+
import com.intellij.ide.starter.ci.CIServer
8+
import com.intellij.ide.starter.config.ConfigurationStorage
9+
import com.intellij.ide.starter.di.di
10+
import com.intellij.ide.starter.driver.engine.runIdeWithDriver
11+
import com.intellij.ide.starter.ide.IdeProductProvider
12+
import com.intellij.ide.starter.junit5.hyphenateWithClass
13+
import com.intellij.ide.starter.models.TestCase
14+
import com.intellij.ide.starter.project.LocalProjectInfo
15+
import com.intellij.ide.starter.runner.CurrentTestMethod
16+
import com.intellij.ide.starter.runner.Starter
17+
import org.junit.jupiter.api.AfterAll
18+
import org.junit.jupiter.api.Assertions.assertTrue
19+
import org.junit.jupiter.api.BeforeEach
20+
import org.junit.jupiter.api.Test
21+
import org.kodein.di.DI
22+
import org.kodein.di.bindSingleton
23+
import software.aws.toolkits.jetbrains.uitests.TestCIServer
24+
import software.aws.toolkits.jetbrains.uitests.clearAwsXmlFile
25+
import software.aws.toolkits.jetbrains.uitests.executePuppeteerScript
26+
import software.aws.toolkits.jetbrains.uitests.setupTestEnvironment
27+
import software.aws.toolkits.jetbrains.uitests.useExistingConnectionForTest
28+
import java.io.File
29+
import java.nio.file.Path
30+
import java.nio.file.Paths
31+
32+
class AmazonQChatTest {
33+
34+
init {
35+
di = DI {
36+
extend(di)
37+
bindSingleton<CIServer>(overrides = true) { TestCIServer }
38+
val defaults = ConfigurationStorage.instance().defaults.toMutableMap().apply {
39+
put("LOG_ENVIRONMENT_VARIABLES", (!System.getenv("CI").toBoolean()).toString())
40+
}
41+
42+
bindSingleton<ConfigurationStorage>(overrides = true) {
43+
ConfigurationStorage(this, defaults)
44+
}
45+
}
46+
}
47+
48+
@BeforeEach
49+
fun setUp() {
50+
// Setup test environment
51+
setupTestEnvironment()
52+
}
53+
54+
@Test
55+
fun `Ensure feature availability on slash`() {
56+
val testCase = TestCase(
57+
IdeProductProvider.IC,
58+
LocalProjectInfo(
59+
Paths.get("tstData", "Hello")
60+
)
61+
).useRelease(System.getProperty("org.gradle.project.ideProfileName"))
62+
63+
// inject connection
64+
useExistingConnectionForTest()
65+
66+
Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply {
67+
System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path ->
68+
pluginConfigurator.installPluginFromPath(
69+
Path.of(path)
70+
)
71+
}
72+
73+
copyExistingConfig(Paths.get("tstData", "configAmazonQTests"))
74+
updateGeneralSettings()
75+
}.runIdeWithDriver()
76+
.useDriverAndCloseIde {
77+
waitForProjectOpen()
78+
// required wait time for the system to be fully ready
79+
Thread.sleep(30000)
80+
81+
val result = executePuppeteerScript(testFeatureAvailabilityOnSlash)
82+
assertTrue(result.contains("/doc"))
83+
assertTrue(result.contains("/dev"))
84+
assertTrue(result.contains("/transform"))
85+
assertTrue(result.contains("/help"))
86+
assertTrue(result.contains("/clear"))
87+
assertTrue(result.contains("/review"))
88+
assertTrue(result.contains("/test"))
89+
}
90+
}
91+
92+
companion object {
93+
@JvmStatic
94+
@AfterAll
95+
fun clearAwsXml() {
96+
clearAwsXmlFile()
97+
}
98+
}
99+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.uitests.chatTests
5+
6+
// language=JS
7+
val testFeatureAvailabilityOnSlash = """
8+
const puppeteer = require('puppeteer');
9+
10+
async function testNavigation() {
11+
const browser = await puppeteer.connect({
12+
browserURL: "http://localhost:9222"
13+
})
14+
try {
15+
const pages = await browser.pages()
16+
for(const page of pages) {
17+
const contents = await page.evaluate(el => el.innerHTML, await page.${'$'}(':root'));
18+
const element = await page.$('.mynah-chat-prompt-input')
19+
if(element) {
20+
await page.type('.mynah-chat-prompt-input', '/')
21+
const elements = await page.$$(".mynah-chat-command-selector-command");
22+
const attr = await Promise.all(
23+
elements.map(async element => {
24+
return element.evaluate(el => el.getAttribute("command"));
25+
})
26+
);
27+
console.log(JSON.stringify(attr, null, 2))
28+
}
29+
}
30+
} finally {
31+
await browser.close();
32+
}
33+
}
34+
testNavigation().catch(console.error);
35+
36+
""".trimIndent()
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import com.intellij.openapi.Disposable
5+
import com.intellij.testFramework.ApplicationExtension
6+
import com.intellij.testFramework.junit5.TestDisposable
7+
import org.junit.Rule
8+
import org.junit.jupiter.api.BeforeEach
9+
import org.junit.jupiter.api.Test
10+
import org.junit.jupiter.api.extension.ExtendWith
11+
import software.aws.toolkits.core.rules.SystemPropertyHelper
12+
import software.aws.toolkits.jetbrains.core.credentials.LegacyManagedBearerSsoConnection
13+
import software.aws.toolkits.jetbrains.core.credentials.ManagedBearerSsoConnection
14+
import software.aws.toolkits.jetbrains.core.credentials.pinning.ConnectionPinningManager
15+
import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection
16+
import software.aws.toolkits.jetbrains.core.credentials.sono.Q_SCOPES
17+
import software.aws.toolkits.jetbrains.core.credentials.sso.bearer.BearerTokenProvider
18+
import software.aws.toolkits.jetbrains.utils.extensions.SsoLogin
19+
import software.aws.toolkits.jetbrains.utils.extensions.SsoLoginExtension
20+
21+
@ExtendWith(ApplicationExtension::class, SsoLoginExtension::class)
22+
@SsoLogin("amazonq-test-account")
23+
class PreAmazonQUiTest {
24+
25+
@TestDisposable
26+
lateinit var disposable: Disposable
27+
28+
@Rule
29+
@JvmField
30+
val systemPropertyHelper = SystemPropertyHelper()
31+
32+
private lateinit var connection: ManagedBearerSsoConnection
33+
34+
@BeforeEach
35+
fun setUp() {
36+
System.setProperty("aws.dev.useDAG", "true")
37+
}
38+
39+
@Test
40+
fun `can set up Connection`() {
41+
try {
42+
val startUrl = System.getenv("TEST_START_URL")
43+
val region = System.getenv("TEST_REGION")
44+
connection = LegacyManagedBearerSsoConnection(startUrl, region, Q_SCOPES)
45+
ConnectionPinningManager.getInstance().setPinnedConnection(QConnection.getInstance(), connection)
46+
(connection.getConnectionSettings().tokenProvider.delegate as BearerTokenProvider).reauthenticate()
47+
} catch (e: Exception) {
48+
error("Could not connect to Idc.")
49+
}
50+
}
51+
}

0 commit comments

Comments
 (0)