diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCompletionFixture.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCompletionFixture.kt index ef56dd768670..10578b14e246 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCompletionFixture.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCompletionFixture.kt @@ -12,20 +12,26 @@ import com.intellij.openapi.actionSystem.impl.SimpleDataContext import com.intellij.openapi.project.Project import com.intellij.openapi.util.Disposer import com.intellij.openapi.util.registry.Registry +import com.intellij.terminal.completion.spec.ShellCommandSpec import com.intellij.terminal.frontend.ReworkedTerminalView import com.intellij.terminal.frontend.TimedKeyEvent import com.intellij.terminal.session.TerminalBlocksModelState import com.intellij.terminal.session.TerminalOutputBlock import com.intellij.terminal.session.TerminalSession +import com.intellij.terminal.tests.block.util.TestCommandSpecsProvider +import com.intellij.testFramework.ExtensionTestUtil import org.jetbrains.plugins.terminal.JBTerminalSystemSettingsProvider import org.jetbrains.plugins.terminal.LocalBlockTerminalRunner.Companion.REWORKED_TERMINAL_COMPLETION_POPUP +import org.jetbrains.plugins.terminal.block.completion.spec.ShellCommandSpecConflictStrategy +import org.jetbrains.plugins.terminal.block.completion.spec.ShellCommandSpecInfo +import org.jetbrains.plugins.terminal.block.completion.spec.ShellCommandSpecsProvider import org.jetbrains.plugins.terminal.block.reworked.TerminalOutputModel import java.awt.event.KeyEvent import java.awt.event.KeyEvent.VK_UNDEFINED import java.util.concurrent.CompletableFuture import kotlin.time.TimeSource -class TerminalCompletionFixture(val project: Project, testRootDisposable: Disposable) { +class TerminalCompletionFixture(val project: Project, val testRootDisposable: Disposable) { private val view: ReworkedTerminalView @@ -84,12 +90,8 @@ class TerminalCompletionFixture(val project: Project, testRootDisposable: Dispos /** * Simulates a key press in the active completion popup. - * @param keyCode (VK_LEFT or VK_ENTER) */ fun pressKey(keycode: Int) { - if (keycode != KeyEvent.VK_LEFT && keycode != KeyEvent.VK_RIGHT) { - throw IllegalArgumentException("keycode must be VK_LEFT or VK_RIGHT") - } val keyPressEvent = KeyEvent( view.outputEditor.component, KeyEvent.KEY_PRESSED, @@ -100,8 +102,14 @@ class TerminalCompletionFixture(val project: Project, testRootDisposable: Dispos KeyEvent.KEY_LOCATION_STANDARD ) view.outputEditorEventsHandler.keyPressed(TimedKeyEvent(keyPressEvent, TimeSource.Monotonic.markNow())) - val commonOffset = view.outputEditor.getCaretModel().offset - view.outputModel.updateCursorPosition(view.outputModel.relativeOffset(commonOffset)) + val offset = view.outputModel.cursorOffsetState.value + val newOffset = when (keycode) { + KeyEvent.VK_LEFT -> offset - 1 + KeyEvent.VK_RIGHT -> offset + 1 + KeyEvent.VK_BACK_SPACE -> offset - 1 + else -> offset + } + view.outputModel.updateCursorPosition(view.outputModel.relativeOffset(newOffset)) } private fun runActionById(actionId: String) { @@ -118,4 +126,11 @@ class TerminalCompletionFixture(val project: Project, testRootDisposable: Dispos ActionUtil.performAction(action, event) } } + + fun mockTestShellCommand(testCommandSpec: ShellCommandSpec) { + val specsProvider: ShellCommandSpecsProvider = TestCommandSpecsProvider( + ShellCommandSpecInfo.create(testCommandSpec, ShellCommandSpecConflictStrategy.DEFAULT) + ) + ExtensionTestUtil.maskExtensions(ShellCommandSpecsProvider.EP_NAME, listOf(specsProvider), testRootDisposable) + } } \ No newline at end of file diff --git a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCompletionTest.kt b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCompletionTest.kt index 34105f47764b..a2ccfebfd2b4 100644 --- a/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCompletionTest.kt +++ b/plugins/terminal/tests/src/com/intellij/terminal/tests/reworked/frontend/TerminalCompletionTest.kt @@ -1,39 +1,62 @@ package com.intellij.terminal.tests.reworked.frontend import com.intellij.testFramework.fixtures.BasePlatformTestCase +import org.jetbrains.plugins.terminal.block.completion.spec.ShellCommandSpec import org.junit.Assert.assertNotEquals import org.junit.Test import org.junit.runner.RunWith import org.junit.runners.JUnit4 -import java.awt.event.KeyEvent.VK_LEFT -import java.awt.event.KeyEvent.VK_RIGHT +import java.awt.event.KeyEvent.* @RunWith(JUnit4::class) class TerminalCompletionTest : BasePlatformTestCase() { + val testCommandSpec = ShellCommandSpec("test_cmd") { + subcommands { + subcommand("status") + subcommand("start") + subcommand("stop") + subcommand("set") + subcommand("sync") + subcommand("show") + } + + subcommands { + subcommand("build") + subcommand("bind") + subcommand("branch") + } + } + @Test fun `test completions list filtered on typing`() { val terminalCompletionFixture = TerminalCompletionFixture(project, testRootDisposable) - terminalCompletionFixture.type("git s") + terminalCompletionFixture.mockTestShellCommand(testCommandSpec) + terminalCompletionFixture.type("test_cmd ") terminalCompletionFixture.callCompletionPopup() assertEquals(true, terminalCompletionFixture.isLookupActive()) val beforeResult = terminalCompletionFixture.getLookupElements() val beforeResultStrings = beforeResult.map { it.lookupString } - assertEquals(listOf("show", "stage", "stash", "status", "submodule", "switch"), beforeResultStrings) + assertSameElements(listOf("bind", "branch", "build", "set", "show", "start", "status", "stop", "sync"), + beforeResultStrings) - terminalCompletionFixture.type("t") + terminalCompletionFixture.type("s") val result = terminalCompletionFixture.getLookupElements() val resultStrings = result.map { it.lookupString } - assertEquals(listOf("stage", "stash", "status"), resultStrings) + assertSameElements(listOf("set", "show", "start", "status", "stop", "sync"), resultStrings) + terminalCompletionFixture.type("t") + assertSameElements(listOf("start", "status", "stop"), terminalCompletionFixture.getLookupElements().map { it.lookupString }) + } @Test - fun `test terminal completion up and down`() { + fun `test selection returns to original item after down and up actions`() { val terminalCompletionFixture = TerminalCompletionFixture(project, testRootDisposable) - terminalCompletionFixture.type("git b") + terminalCompletionFixture.mockTestShellCommand(testCommandSpec) + terminalCompletionFixture.type("test_cmd ") terminalCompletionFixture.callCompletionPopup() - val startResult = terminalCompletionFixture.getLookupElements() - assertEquals(3, startResult.size) + assertSameElements(listOf("bind", "branch", "build", "set", "show", "start", "status", "stop", "sync"), + terminalCompletionFixture.getLookupElements().map { it.lookupString }) val firstElement = terminalCompletionFixture.getCurrentItem() terminalCompletionFixture.downCompletionPopup() @@ -46,41 +69,75 @@ class TerminalCompletionTest : BasePlatformTestCase() { } @Test - fun `test terminal completion with left right`() { + fun `test completion list is re-filtered when caret moves over prefix to the left, right`() { val terminalCompletionFixture = TerminalCompletionFixture(project, testRootDisposable) - terminalCompletionFixture.type("git ") + terminalCompletionFixture.mockTestShellCommand(testCommandSpec) + terminalCompletionFixture.type("test_cmd ") terminalCompletionFixture.callCompletionPopup() terminalCompletionFixture.type("st") val startResult = terminalCompletionFixture.getLookupElements() + assertSameElements(listOf("start", "status", "stop"), startResult.map { it.lookupString }) terminalCompletionFixture.pressKey(VK_LEFT) val afterFirstLeftResult = terminalCompletionFixture.getLookupElements() + assertSameElements(listOf("set", "show", "start", "status", "stop", "sync"), + afterFirstLeftResult.map { it.lookupString }) terminalCompletionFixture.pressKey(VK_LEFT) - val afterSecondLeftResult = terminalCompletionFixture.getLookupElements() - assertNotEquals(afterFirstLeftResult.size, afterSecondLeftResult.size) + assertSameElements(listOf("bind", "branch", "build", "set", "show", "start", "status", "stop", "sync"), + afterSecondLeftResult.map { it.lookupString }) + terminalCompletionFixture.pressKey(VK_RIGHT) val afterRightResult = terminalCompletionFixture.getLookupElements() - assertEquals(afterFirstLeftResult.size, afterRightResult.size) + assertEquals(afterFirstLeftResult, afterRightResult) terminalCompletionFixture.pressKey(VK_RIGHT) val afterSecondRightResult = terminalCompletionFixture.getLookupElements() - assertEquals(startResult.size, afterSecondRightResult.size) + assertEquals(startResult, afterSecondRightResult) } @Test - fun `test terminal completion with left right reopening`() { + fun `test lookup remains active when caret moves into text typed before call popup`() { val terminalCompletionFixture = TerminalCompletionFixture(project, testRootDisposable) - terminalCompletionFixture.type("git st") + terminalCompletionFixture.mockTestShellCommand(testCommandSpec) + terminalCompletionFixture.type("test_cmd st") terminalCompletionFixture.callCompletionPopup() val startResult = terminalCompletionFixture.getLookupElements() - assertEquals(3, startResult.size) + assertSameElements(listOf("start", "status", "stop"), + startResult.map { it.lookupString }) terminalCompletionFixture.pressKey(VK_LEFT) - val afterLeftResult = terminalCompletionFixture.getLookupElements() - assertEquals(6, afterLeftResult.size) + assertSameElements(listOf("set", "show", "start", "status", "stop", "sync"), + terminalCompletionFixture.getLookupElements().map { it.lookupString }) + terminalCompletionFixture.pressKey(VK_RIGHT) + assertSameElements(startResult.map { it.lookupString }, + terminalCompletionFixture.getLookupElements().map { it.lookupString }) + } + + @Test + fun `test completion list is correctly re-filtered after pressing backspace`() { + val terminalCompletionFixture = TerminalCompletionFixture(project, testRootDisposable) + terminalCompletionFixture.mockTestShellCommand(testCommandSpec) + terminalCompletionFixture.type("test_cmd st") + terminalCompletionFixture.callCompletionPopup() + + val startResult = terminalCompletionFixture.getLookupElements() + assertSameElements(listOf("start", "status", "stop"), + startResult.map { it.lookupString }) + + terminalCompletionFixture.type("a") + assertSameElements(listOf("start", "status"), + terminalCompletionFixture.getLookupElements().map { it.lookupString }) + + terminalCompletionFixture.pressKey(VK_BACK_SPACE) + assertSameElements(listOf("start", "status", "stop"), + terminalCompletionFixture.getLookupElements().map { it.lookupString }) + + terminalCompletionFixture.pressKey(VK_BACK_SPACE) + assertSameElements(listOf("set", "show", "start", "status", "stop", "sync"), + terminalCompletionFixture.getLookupElements().map { it.lookupString }) } } \ No newline at end of file