From 80830530689b161d5cb1e4e467a33a3669ab82c3 Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Thu, 3 Apr 2025 10:58:10 -0700 Subject: [PATCH 1/3] init test files --- .../AmazonQInlineCompletionE2ETest.kt | 564 ++++++++++++++++++ .../inlineCompletionProject/MathClass.java | 9 + .../inlineCompletionProject/nonsense.xyz | 9 + 3 files changed, 582 insertions(+) create mode 100644 ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt create mode 100644 ui-tests-starter/tstData/inlineCompletionProject/MathClass.java create mode 100644 ui-tests-starter/tstData/inlineCompletionProject/nonsense.xyz diff --git a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt new file mode 100644 index 00000000000..4711c9357d4 --- /dev/null +++ b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt @@ -0,0 +1,564 @@ +// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +package software.aws.toolkits.jetbrains.uitests.inlineTests + +import com.intellij.driver.sdk.openFile +import com.intellij.driver.sdk.ui.ui +import org.assertj.core.api.Assertions.assertThat +import com.intellij.driver.sdk.waitForProjectOpen +import com.intellij.ide.starter.ci.CIServer +import com.intellij.ide.starter.config.ConfigurationStorage +import com.intellij.ide.starter.di.di +import com.intellij.ide.starter.driver.engine.runIdeWithDriver +import com.intellij.ide.starter.ide.IdeProductProvider +import com.intellij.ide.starter.junit5.hyphenateWithClass +import com.intellij.ide.starter.models.TestCase +import com.intellij.ide.starter.project.LocalProjectInfo +import com.intellij.ide.starter.runner.CurrentTestMethod +import com.intellij.ide.starter.runner.Starter +import org.junit.jupiter.api.Test +import org.kodein.di.DI +import org.kodein.di.bindSingleton +import software.aws.toolkits.jetbrains.uitests.TestCIServer +import software.aws.toolkits.jetbrains.uitests.useExistingConnectionForTest +import java.io.File +import java.nio.file.Path +import java.nio.file.Paths +import org.junit.jupiter.api.BeforeEach +import java.nio.file.Files +import java.nio.file.StandardOpenOption +import com.intellij.driver.sdk.ui.components.common.editor +import com.intellij.driver.sdk.ui.components.common.ideFrame +import java.awt.event.KeyEvent + +class AmazonQInlineCompletionE2ETest { + init { + di = DI { + extend(di) + bindSingleton(overrides = true) { TestCIServer } + val defaults = ConfigurationStorage.instance().defaults.toMutableMap().apply { + put("LOG_ENVIRONMENT_VARIABLES", (!System.getenv("CI").toBoolean()).toString()) + } + + bindSingleton(overrides = true) { + ConfigurationStorage(this, defaults) + } + } + } + + @BeforeEach + fun resetTestFile() { + val originalContent = """public class MathClass { + public static void main(String[] args) { + int a = 10; + int b = 20; + int c = add(a, b); + System.out.println("The sum of a and b is: " + c); + } + +}""" + + val path = Paths.get("tstData", "inlineCompletionProject", "MathClass.java") + + Files.createDirectories(path.parent) + Files.write( + path, + originalContent.toByteArray(), + StandardOpenOption.CREATE, + StandardOpenOption.TRUNCATE_EXISTING + ) + } + + // Test Case 1: Manual invoke inline completion + @Test + fun `test manual invoke inline completion`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + var originalText: String? = null + var afterSuggestion: String? = null + + ideFrame { + // Open file + openFile("MathClass.java") + // Editor operations + editor { + originalText = text + // Move to position for recommendation + moveCaretToOffset(text.length - 2) + + // Trigger completion and wait for result + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + Thread.sleep(1000) + + // Ensure inlineElement exists + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() + // Accept completion + ui.keyboard { + key(KeyEvent.VK_TAB) + } + afterSuggestion = text + } + } + assertThat(afterSuggestion).isNotEqualTo(originalText) + } + } + + // Test Case 2: Manual trigger + reject + @Test + fun `test manual trigger with rejection`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) + + // Trigger completion + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + Thread.sleep(1000) + + // Verify suggestion appeared + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() + + // Reject with Esc + ui.keyboard { + key(KeyEvent.VK_ESCAPE) + } + + val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() + assertThat(hintGone).isTrue() + } + } + } + } + + // Test Case 3: Manual trigger + discard + @Test + fun `test manual trigger with discard`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + var originalText: String? = null + + ideFrame { + openFile("MathClass.java") + editor { + originalText = text + moveCaretToOffset(text.length - 2) + + // Trigger completion + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + + // Verify suggestion shown + Thread.sleep(1000) + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() + + // Move cursor up to discard + goToLine(getCaretLine() - 1) + + // Verify no suggestion shown + val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() + assertThat(hintGone).isTrue() + } + } + } + } + + // Test Case 4: Auto trigger + accept + @Test + fun `test auto trigger with acceptance`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + var originalText: String? = null + var afterSuggestion: String? = null + + ideFrame { + openFile("MathClass.java") + editor { + originalText = text + moveCaretToOffset(text.length - 2) + + // Auto-trigger with Enter + ui.keyboard { + key(KeyEvent.VK_ENTER) + } + Thread.sleep(1000) + + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() + + ui.keyboard { + key(KeyEvent.VK_TAB) + } + afterSuggestion = text + } + } + assertThat(afterSuggestion).isNotEqualTo(originalText) + } + } + + // Test Case 5: Auto trigger + reject + @Test + fun `test auto trigger with rejections`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + var originalText: String? = null + + ideFrame { + openFile("MathClass.java") + editor { + originalText = text + moveCaretToOffset(text.length - 2) + + // Auto-trigger with Enter + ui.keyboard { + key(KeyEvent.VK_ENTER) + } + Thread.sleep(1000) + + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() + + // Reject with Esc + ui.keyboard { + key(KeyEvent.VK_ESCAPE) + } + + val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() + assertThat(hintGone).isTrue() + } + } + } + } + + // Test Case 6: Auto trigger + reject + @Test + fun `test auto trigger with discards`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + var originalText: String? = null + + ideFrame { + openFile("MathClass.java") + editor { + originalText = text + moveCaretToOffset(text.length - 2) + + // Auto-trigger with Enter + ui.keyboard { + key(KeyEvent.VK_ENTER) + } + Thread.sleep(1000) + + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() + + // Move cursor up to discard + goToLine(getCaretLine() - 1) + + // Verify suggestion disappeared + val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() + assertThat(hintGone).isTrue() + } + } + } + } + // Test Case 7: Suggestion Navigation + @Test + fun `test suggestion navigation`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) + + // Trigger completion + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + Thread.sleep(1000) + + // Get initial suggestion + val initialHints = editor.getInlayModel().getInlineElementsInRange(0, text.length) + assertThat(initialHints).isNotEmpty() + + // Navigate right + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_CLOSE_BRACKET) + } + } + + // Verify suggestion changed + val newHints = editor.getInlayModel().getInlineElementsInRange(0, text.length) + assertThat(newHints).isNotEqualTo(initialHints) + } + } + } + } + + // Test Case 8: Unsupported language + @Test + fun `test completion in unsupported file type`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + + ideFrame { + openFile("nonsense.xyz") + editor { + // Try manual trigger + moveCaretToOffset(text.length - 2) + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + Thread.sleep(1000) + + // Verify no suggestion appeared + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isFalse() + + // Try auto trigger + ui.keyboard { + key(KeyEvent.VK_ENTER) + } + Thread.sleep(1000) + + // Verify still no suggestion + val hintExistsAfterAuto = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExistsAfterAuto).isFalse() + } + } + } + } + + // Test Case 9: Typeahead + @Test + fun `test typeahead behavior`() { + val testCase = TestCase( + IdeProductProvider.IC, + LocalProjectInfo( + Paths.get("tstData", "InlineCompletionProject") + ) + ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + + useExistingConnectionForTest() + + Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { + System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> + pluginConfigurator.installPluginFromPath( + Path.of(path) + ) + } + copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) + updateGeneralSettings() + }.runIdeWithDriver() + .useDriverAndCloseIde { + waitForProjectOpen() + Thread.sleep(2000) + + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) + + // Trigger completion + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + Thread.sleep(1000) + + // Verify suggestion appeared + val initialHintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(initialHintExists).isTrue() + + // Type matching prefix + ui.keyboard { + key(KeyEvent.VK_P) + key(KeyEvent.VK_U) + key(KeyEvent.VK_B) + } + + // Verify suggestion still exists + val hintExistsAfterTyping = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExistsAfterTyping).isTrue() + } + } + } + } +} diff --git a/ui-tests-starter/tstData/inlineCompletionProject/MathClass.java b/ui-tests-starter/tstData/inlineCompletionProject/MathClass.java new file mode 100644 index 00000000000..c06f6d8d030 --- /dev/null +++ b/ui-tests-starter/tstData/inlineCompletionProject/MathClass.java @@ -0,0 +1,9 @@ +public class MathClass { + public static void main(String[] args) { + int a = 10; + int b = 20; + int c = add(a, b); + System.out.println("The sum of a and b is: " + c); + } + +} diff --git a/ui-tests-starter/tstData/inlineCompletionProject/nonsense.xyz b/ui-tests-starter/tstData/inlineCompletionProject/nonsense.xyz new file mode 100644 index 00000000000..c06f6d8d030 --- /dev/null +++ b/ui-tests-starter/tstData/inlineCompletionProject/nonsense.xyz @@ -0,0 +1,9 @@ +public class MathClass { + public static void main(String[] args) { + int a = 10; + int b = 20; + int c = add(a, b); + System.out.println("The sum of a and b is: " + c); + } + +} From a148835e882b113f100a51eb1a7fa207bf87c2a4 Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Fri, 4 Apr 2025 16:59:39 -0700 Subject: [PATCH 2/3] merge tests into one IDE instance --- .../AmazonQInlineCompletionE2ETest.kt | 597 ++++++------------ 1 file changed, 191 insertions(+), 406 deletions(-) diff --git a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt index 4711c9357d4..ba4c69edd00 100644 --- a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt +++ b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt @@ -31,8 +31,21 @@ import java.nio.file.StandardOpenOption import com.intellij.driver.sdk.ui.components.common.editor import com.intellij.driver.sdk.ui.components.common.ideFrame import java.awt.event.KeyEvent +import com.intellij.driver.client.Driver +import com.intellij.driver.sdk.Editor +import com.intellij.driver.sdk.step class AmazonQInlineCompletionE2ETest { + private val originalContent = """public class MathClass { + public static void main(String[] args) { + int a = 10; + int b = 20; + int c = add(a, b); + System.out.println("The sum of a and b is: " + c); + } + +}""" + init { di = DI { extend(di) @@ -45,20 +58,13 @@ class AmazonQInlineCompletionE2ETest { ConfigurationStorage(this, defaults) } } + resetTestFile() } + + @BeforeEach fun resetTestFile() { - val originalContent = """public class MathClass { - public static void main(String[] args) { - int a = 10; - int b = 20; - int c = add(a, b); - System.out.println("The sum of a and b is: " + c); - } - -}""" - val path = Paths.get("tstData", "inlineCompletionProject", "MathClass.java") Files.createDirectories(path.parent) @@ -70,9 +76,8 @@ class AmazonQInlineCompletionE2ETest { ) } - // Test Case 1: Manual invoke inline completion @Test - fun `test manual invoke inline completion`() { + fun `test inline completion functionality`() { val testCase = TestCase( IdeProductProvider.IC, LocalProjectInfo( @@ -94,469 +99,249 @@ class AmazonQInlineCompletionE2ETest { .useDriverAndCloseIde { waitForProjectOpen() Thread.sleep(2000) - var originalText: String? = null - var afterSuggestion: String? = null - - ideFrame { - // Open file - openFile("MathClass.java") - // Editor operations - editor { - originalText = text - // Move to position for recommendation - moveCaretToOffset(text.length - 2) - - // Trigger completion and wait for result - ui.keyboard { - pressing(KeyEvent.VK_ALT) { - key(KeyEvent.VK_C) + + step("Test manual invoke and accept") { + var originalText: String? = null + var afterSuggestion: String? = null + + ideFrame { + openFile("MathClass.java") + editor { + originalText = text + moveCaretToOffset(text.length - 2) + + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } } + Thread.sleep(1000) + + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() + + ui.keyboard { + key(KeyEvent.VK_TAB) + } + afterSuggestion = text + text = originalContent } - Thread.sleep(1000) - - // Ensure inlineElement exists - val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExists).isTrue() - // Accept completion - ui.keyboard { - key(KeyEvent.VK_TAB) - } - afterSuggestion = text } + assertThat(afterSuggestion).isNotEqualTo(originalText) } - assertThat(afterSuggestion).isNotEqualTo(originalText) - } - } - // Test Case 2: Manual trigger + reject - @Test - fun `test manual trigger with rejection`() { - val testCase = TestCase( - IdeProductProvider.IC, - LocalProjectInfo( - Paths.get("tstData", "InlineCompletionProject") - ) - ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) - - useExistingConnectionForTest() + step("Test manual trigger with rejection") { + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) - Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { - System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> - pluginConfigurator.installPluginFromPath( - Path.of(path) - ) - } - copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) - updateGeneralSettings() - }.runIdeWithDriver() - .useDriverAndCloseIde { - waitForProjectOpen() - Thread.sleep(2000) + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + Thread.sleep(1000) - ideFrame { - openFile("MathClass.java") - editor { - moveCaretToOffset(text.length - 2) + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() - // Trigger completion - ui.keyboard { - pressing(KeyEvent.VK_ALT) { - key(KeyEvent.VK_C) + ui.keyboard { + key(KeyEvent.VK_ESCAPE) } - } - Thread.sleep(1000) - // Verify suggestion appeared - val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExists).isTrue() - - // Reject with Esc - ui.keyboard { - key(KeyEvent.VK_ESCAPE) + val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() + assertThat(hintGone).isTrue() + text = originalContent } - - val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() - assertThat(hintGone).isTrue() } } - } - } - // Test Case 3: Manual trigger + discard - @Test - fun `test manual trigger with discard`() { - val testCase = TestCase( - IdeProductProvider.IC, - LocalProjectInfo( - Paths.get("tstData", "InlineCompletionProject") - ) - ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) - - useExistingConnectionForTest() + step("Test manual trigger with discard") { + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) - Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { - System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> - pluginConfigurator.installPluginFromPath( - Path.of(path) - ) - } - copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) - updateGeneralSettings() - }.runIdeWithDriver() - .useDriverAndCloseIde { - waitForProjectOpen() - Thread.sleep(2000) - var originalText: String? = null - - ideFrame { - openFile("MathClass.java") - editor { - originalText = text - moveCaretToOffset(text.length - 2) - - // Trigger completion - ui.keyboard { - pressing(KeyEvent.VK_ALT) { - key(KeyEvent.VK_C) + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } } - } + Thread.sleep(1000) - // Verify suggestion shown - Thread.sleep(1000) - val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExists).isTrue() + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() - // Move cursor up to discard - goToLine(getCaretLine() - 1) + goToLine(getCaretLine() - 1) - // Verify no suggestion shown - val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() - assertThat(hintGone).isTrue() + val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() + assertThat(hintGone).isTrue() + text = originalContent + } } + } - } - } - // Test Case 4: Auto trigger + accept - @Test - fun `test auto trigger with acceptance`() { - val testCase = TestCase( - IdeProductProvider.IC, - LocalProjectInfo( - Paths.get("tstData", "InlineCompletionProject") - ) - ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + step("Test auto trigger with acceptance") { + var originalText: String? = null + var afterSuggestion: String? = null - useExistingConnectionForTest() + ideFrame { + openFile("MathClass.java") + editor { + originalText = text + moveCaretToOffset(text.length - 2) - Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { - System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> - pluginConfigurator.installPluginFromPath( - Path.of(path) - ) - } - copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) - updateGeneralSettings() - }.runIdeWithDriver() - .useDriverAndCloseIde { - waitForProjectOpen() - Thread.sleep(2000) - var originalText: String? = null - var afterSuggestion: String? = null - - ideFrame { - openFile("MathClass.java") - editor { - originalText = text - moveCaretToOffset(text.length - 2) - - // Auto-trigger with Enter - ui.keyboard { - key(KeyEvent.VK_ENTER) - } - Thread.sleep(1000) + ui.keyboard { + key(KeyEvent.VK_ENTER) + } + Thread.sleep(1000) - val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExists).isTrue() + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() - ui.keyboard { - key(KeyEvent.VK_TAB) + ui.keyboard { + key(KeyEvent.VK_TAB) + } + afterSuggestion = text + text = originalContent } - afterSuggestion = text } - } - assertThat(afterSuggestion).isNotEqualTo(originalText) - } - } + assertThat(afterSuggestion).isNotEqualTo(originalText) - // Test Case 5: Auto trigger + reject - @Test - fun `test auto trigger with rejections`() { - val testCase = TestCase( - IdeProductProvider.IC, - LocalProjectInfo( - Paths.get("tstData", "InlineCompletionProject") - ) - ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + } - useExistingConnectionForTest() + step("Test auto trigger with rejection") { + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) - Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { - System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> - pluginConfigurator.installPluginFromPath( - Path.of(path) - ) - } - copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) - updateGeneralSettings() - }.runIdeWithDriver() - .useDriverAndCloseIde { - waitForProjectOpen() - Thread.sleep(2000) - var originalText: String? = null - - ideFrame { - openFile("MathClass.java") - editor { - originalText = text - moveCaretToOffset(text.length - 2) + ui.keyboard { + key(KeyEvent.VK_ENTER) + } + Thread.sleep(1000) - // Auto-trigger with Enter - ui.keyboard { - key(KeyEvent.VK_ENTER) - } - Thread.sleep(1000) + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() - val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExists).isTrue() + ui.keyboard { + key(KeyEvent.VK_ESCAPE) + } - // Reject with Esc - ui.keyboard { - key(KeyEvent.VK_ESCAPE) + val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() + assertThat(hintGone).isTrue() + text = originalContent } - - val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() - assertThat(hintGone).isTrue() } } - } - } - // Test Case 6: Auto trigger + reject - @Test - fun `test auto trigger with discards`() { - val testCase = TestCase( - IdeProductProvider.IC, - LocalProjectInfo( - Paths.get("tstData", "InlineCompletionProject") - ) - ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + step("Test auto trigger with discard") { + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) - useExistingConnectionForTest() + ui.keyboard { + key(KeyEvent.VK_ENTER) + } + Thread.sleep(1000) - Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { - System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> - pluginConfigurator.installPluginFromPath( - Path.of(path) - ) - } - copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) - updateGeneralSettings() - }.runIdeWithDriver() - .useDriverAndCloseIde { - waitForProjectOpen() - Thread.sleep(2000) - var originalText: String? = null + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isTrue() - ideFrame { - openFile("MathClass.java") - editor { - originalText = text - moveCaretToOffset(text.length - 2) + goToLine(getCaretLine() - 1) - // Auto-trigger with Enter - ui.keyboard { - key(KeyEvent.VK_ENTER) + val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() + assertThat(hintGone).isTrue() + text = originalContent } - Thread.sleep(1000) - - val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExists).isTrue() - - // Move cursor up to discard - goToLine(getCaretLine() - 1) - - // Verify suggestion disappeared - val hintGone = editor.getInlayModel().getInlineElementsInRange(0, text.length).isEmpty() - assertThat(hintGone).isTrue() } } - } - } - // Test Case 7: Suggestion Navigation - @Test - fun `test suggestion navigation`() { - val testCase = TestCase( - IdeProductProvider.IC, - LocalProjectInfo( - Paths.get("tstData", "InlineCompletionProject") - ) - ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) - - useExistingConnectionForTest() - - Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { - System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> - pluginConfigurator.installPluginFromPath( - Path.of(path) - ) - } - copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) - updateGeneralSettings() - }.runIdeWithDriver() - .useDriverAndCloseIde { - waitForProjectOpen() - Thread.sleep(2000) - ideFrame { - openFile("MathClass.java") - editor { - moveCaretToOffset(text.length - 2) + step("Test suggestion navigation") { + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) - // Trigger completion - ui.keyboard { - pressing(KeyEvent.VK_ALT) { - key(KeyEvent.VK_C) + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } } - } - Thread.sleep(1000) + Thread.sleep(1000) - // Get initial suggestion - val initialHints = editor.getInlayModel().getInlineElementsInRange(0, text.length) - assertThat(initialHints).isNotEmpty() + val initialHints = editor.getInlayModel().getInlineElementsInRange(0, text.length) + assertThat(initialHints).isNotEmpty() - // Navigate right - ui.keyboard { - pressing(KeyEvent.VK_ALT) { - key(KeyEvent.VK_CLOSE_BRACKET) + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_CLOSE_BRACKET) + } } - } - // Verify suggestion changed - val newHints = editor.getInlayModel().getInlineElementsInRange(0, text.length) - assertThat(newHints).isNotEqualTo(initialHints) + val newHints = editor.getInlayModel().getInlineElementsInRange(0, text.length) + assertThat(newHints).isNotEqualTo(initialHints) + text = originalContent + } } } - } - } - - // Test Case 8: Unsupported language - @Test - fun `test completion in unsupported file type`() { - val testCase = TestCase( - IdeProductProvider.IC, - LocalProjectInfo( - Paths.get("tstData", "InlineCompletionProject") - ) - ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) - useExistingConnectionForTest() + step("Test completion in unsupported file type") { + ideFrame { + openFile("nonsense.xyz") + editor { + moveCaretToOffset(text.length - 2) + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + Thread.sleep(1000) - Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { - System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> - pluginConfigurator.installPluginFromPath( - Path.of(path) - ) - } - copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) - updateGeneralSettings() - }.runIdeWithDriver() - .useDriverAndCloseIde { - waitForProjectOpen() - Thread.sleep(2000) + val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExists).isFalse() - ideFrame { - openFile("nonsense.xyz") - editor { - // Try manual trigger - moveCaretToOffset(text.length - 2) - ui.keyboard { - pressing(KeyEvent.VK_ALT) { - key(KeyEvent.VK_C) + ui.keyboard { + key(KeyEvent.VK_ENTER) } - } - Thread.sleep(1000) - - // Verify no suggestion appeared - val hintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExists).isFalse() + Thread.sleep(1000) - // Try auto trigger - ui.keyboard { - key(KeyEvent.VK_ENTER) + val hintExistsAfterAuto = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExistsAfterAuto).isFalse() + text = originalContent } - Thread.sleep(1000) - - // Verify still no suggestion - val hintExistsAfterAuto = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExistsAfterAuto).isFalse() } } - } - } - // Test Case 9: Typeahead - @Test - fun `test typeahead behavior`() { - val testCase = TestCase( - IdeProductProvider.IC, - LocalProjectInfo( - Paths.get("tstData", "InlineCompletionProject") - ) - ).withVersion(System.getProperty("org.gradle.project.ideProfileName")) + step("Test typeahead behavior") { + ideFrame { + openFile("MathClass.java") + editor { + moveCaretToOffset(text.length - 2) - useExistingConnectionForTest() - - Starter.newContext(CurrentTestMethod.hyphenateWithClass(), testCase).apply { - System.getProperty("ui.test.plugins").split(File.pathSeparator).forEach { path -> - pluginConfigurator.installPluginFromPath( - Path.of(path) - ) - } - copyExistingConfig(Paths.get("tstData", "configAmazonQTests")) - updateGeneralSettings() - }.runIdeWithDriver() - .useDriverAndCloseIde { - waitForProjectOpen() - Thread.sleep(2000) + ui.keyboard { + pressing(KeyEvent.VK_ALT) { + key(KeyEvent.VK_C) + } + } + Thread.sleep(1000) - ideFrame { - openFile("MathClass.java") - editor { - moveCaretToOffset(text.length - 2) + val initialHintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(initialHintExists).isTrue() - // Trigger completion - ui.keyboard { - pressing(KeyEvent.VK_ALT) { - key(KeyEvent.VK_C) + ui.keyboard { + key(KeyEvent.VK_P) + key(KeyEvent.VK_U) + key(KeyEvent.VK_B) } - } - Thread.sleep(1000) - - // Verify suggestion appeared - val initialHintExists = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(initialHintExists).isTrue() - // Type matching prefix - ui.keyboard { - key(KeyEvent.VK_P) - key(KeyEvent.VK_U) - key(KeyEvent.VK_B) + val hintExistsAfterTyping = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() + assertThat(hintExistsAfterTyping).isTrue() + text = originalContent } - - // Verify suggestion still exists - val hintExistsAfterTyping = editor.getInlayModel().getInlineElementsInRange(0, text.length).isNotEmpty() - assertThat(hintExistsAfterTyping).isTrue() } } } From 2226c33348d388fc7c77e05a504a06e648682a96 Mon Sep 17 00:00:00 2001 From: samgst-amazon Date: Mon, 7 Apr 2025 11:38:03 -0700 Subject: [PATCH 3/3] detekt --- .../AmazonQInlineCompletionE2ETest.kt | 20 +++++++------------ 1 file changed, 7 insertions(+), 13 deletions(-) diff --git a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt index ba4c69edd00..d8b84e713ed 100644 --- a/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt +++ b/ui-tests-starter/tst-243+/software/aws/toolkits/jetbrains/uitests/inlineTests/AmazonQInlineCompletionE2ETest.kt @@ -4,8 +4,10 @@ package software.aws.toolkits.jetbrains.uitests.inlineTests import com.intellij.driver.sdk.openFile +import com.intellij.driver.sdk.step +import com.intellij.driver.sdk.ui.components.common.editor +import com.intellij.driver.sdk.ui.components.common.ideFrame import com.intellij.driver.sdk.ui.ui -import org.assertj.core.api.Assertions.assertThat import com.intellij.driver.sdk.waitForProjectOpen import com.intellij.ide.starter.ci.CIServer import com.intellij.ide.starter.config.ConfigurationStorage @@ -17,23 +19,19 @@ import com.intellij.ide.starter.models.TestCase import com.intellij.ide.starter.project.LocalProjectInfo import com.intellij.ide.starter.runner.CurrentTestMethod import com.intellij.ide.starter.runner.Starter +import org.assertj.core.api.Assertions.assertThat +import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.Test import org.kodein.di.DI import org.kodein.di.bindSingleton import software.aws.toolkits.jetbrains.uitests.TestCIServer import software.aws.toolkits.jetbrains.uitests.useExistingConnectionForTest +import java.awt.event.KeyEvent import java.io.File +import java.nio.file.Files import java.nio.file.Path import java.nio.file.Paths -import org.junit.jupiter.api.BeforeEach -import java.nio.file.Files import java.nio.file.StandardOpenOption -import com.intellij.driver.sdk.ui.components.common.editor -import com.intellij.driver.sdk.ui.components.common.ideFrame -import java.awt.event.KeyEvent -import com.intellij.driver.client.Driver -import com.intellij.driver.sdk.Editor -import com.intellij.driver.sdk.step class AmazonQInlineCompletionE2ETest { private val originalContent = """public class MathClass { @@ -61,8 +59,6 @@ class AmazonQInlineCompletionE2ETest { resetTestFile() } - - @BeforeEach fun resetTestFile() { val path = Paths.get("tstData", "inlineCompletionProject", "MathClass.java") @@ -180,7 +176,6 @@ class AmazonQInlineCompletionE2ETest { text = originalContent } } - } step("Test auto trigger with acceptance") { @@ -209,7 +204,6 @@ class AmazonQInlineCompletionE2ETest { } } assertThat(afterSuggestion).isNotEqualTo(originalText) - } step("Test auto trigger with rejection") {