mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[IFT] Exclude testGuiFramework dependency from IFT modules
Also make test script to wait editor analyzed IDEA-CR-70569 GitOrigin-RevId: f7b3f69043de4fd064aa1c4c95c25622766825a1
This commit is contained in:
committed by
intellij-monorepo-bot
parent
a088f477b2
commit
4cbe1d21b1
@@ -16,6 +16,6 @@
|
||||
<orderEntry type="module" module-name="intellij.featuresTrainer" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testGuiFramework" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core.ui" />
|
||||
</component>
|
||||
</module>
|
||||
</module>
|
||||
+1
-3
@@ -10,8 +10,6 @@ import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.openapi.wm.ToolWindowId
|
||||
import com.intellij.openapi.wm.ToolWindowManager
|
||||
import com.intellij.openapi.wm.impl.content.BaseLabel
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.ui.InplaceButton
|
||||
import com.intellij.ui.UIBundle
|
||||
import training.dsl.*
|
||||
@@ -43,7 +41,7 @@ class JavaInheritanceHierarchyLesson
|
||||
|
||||
test {
|
||||
Thread.sleep(1000)
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-4
@@ -3,9 +3,6 @@ package com.intellij.java.ift.lesson.navigation
|
||||
|
||||
import com.intellij.find.SearchTextArea
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.impl.actionButton
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.usageView.UsageViewBundle
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonUtil
|
||||
@@ -95,7 +92,7 @@ class JavaOccurrencesLesson
|
||||
stateCheck {
|
||||
editor.headerComponent == null
|
||||
}
|
||||
test { GuiTestUtil.shortcut(Key.ESCAPE) }
|
||||
test { invokeActionViaShortcut("ESCAPE") }
|
||||
}
|
||||
actionTask("FindNext") {
|
||||
JavaLessonsBundle.message("java.find.occurrences.find.next.in.editor", action(it))
|
||||
|
||||
+4
-10
@@ -4,11 +4,9 @@ package com.intellij.java.ift.lesson.refactorings
|
||||
import com.intellij.java.refactoring.JavaRefactoringBundle
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.extractMethod.ExtractMethodHandler
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import com.intellij.ui.UIBundle
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
import training.dsl.TaskTestContext
|
||||
import training.dsl.dropMnemonic
|
||||
import training.dsl.parseLessonSample
|
||||
import training.learn.LessonsBundle
|
||||
@@ -46,10 +44,8 @@ class JavaExtractMethodCocktailSortLesson
|
||||
}
|
||||
|
||||
test {
|
||||
with(TaskTestContext.guiTestCase) {
|
||||
dialog(RefactoringBundle.message("extract.method.title"), needToKeepDialog=true) {
|
||||
button(refactorButtonText).click()
|
||||
}
|
||||
dialog(RefactoringBundle.message("extract.method.title")) {
|
||||
button(refactorButtonText).click()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -63,10 +59,8 @@ class JavaExtractMethodCocktailSortLesson
|
||||
}
|
||||
|
||||
test {
|
||||
with(TaskTestContext.guiTestCase) {
|
||||
dialog(processDuplicatesTitle) {
|
||||
button(replaceButtonText).click()
|
||||
}
|
||||
dialog(processDuplicatesTitle) {
|
||||
button(replaceButtonText).click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-4
@@ -4,8 +4,6 @@ package com.intellij.java.ift.lesson.refactorings
|
||||
import com.intellij.CommonBundle
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonUtil
|
||||
@@ -88,8 +86,8 @@ class JavaRefactoringMenuLesson : RefactoringMenuLessonBase("java.refactoring.me
|
||||
stateCheck {
|
||||
!extractConstantDialogShowing()
|
||||
}
|
||||
test {
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
test(waitEditorToBeReady = false) {
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -6,7 +6,6 @@ import com.intellij.codeInsight.template.impl.TemplateManagerImpl
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import com.intellij.java.refactoring.JavaRefactoringBundle
|
||||
import com.intellij.refactoring.rename.RenameProcessor
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.checkExpectedStateOfEditor
|
||||
|
||||
@@ -4,13 +4,15 @@ package com.intellij.java.ift.lesson.run
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.TaskTestContext
|
||||
import training.dsl.highlightButtonById
|
||||
import training.learn.lesson.general.run.CommonDebugLesson
|
||||
|
||||
class JavaDebugLesson : CommonDebugLesson("java.debug.workflow") {
|
||||
|
||||
override val testScriptProperties = TaskTestContext.TestScriptProperties(duration = 30)
|
||||
|
||||
private val demoClassName = JavaRunLessonsUtils.demoClassName
|
||||
override val configurationName: String = demoClassName
|
||||
override val sample = JavaRunLessonsUtils.demoSample
|
||||
@@ -41,11 +43,9 @@ class JavaDebugLesson : CommonDebugLesson("java.debug.workflow") {
|
||||
!inHotSwapDialog()
|
||||
}
|
||||
proposeModificationRestore(afterFixText)
|
||||
test {
|
||||
with(TaskTestContext.guiTestCase) {
|
||||
dialog(null, needToKeepDialog = true) {
|
||||
button("Yes").click()
|
||||
}
|
||||
test(waitEditorToBeReady = false) {
|
||||
dialog(null) {
|
||||
button("Reload").click()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -64,8 +64,5 @@ class JavaDebugLesson : CommonDebugLesson("java.debug.workflow") {
|
||||
}
|
||||
}
|
||||
|
||||
override val testScriptProperties: TaskTestContext.TestScriptProperties
|
||||
get() = TaskTestContext.TestScriptProperties(duration = 20)
|
||||
|
||||
override val fileName: String = "$demoClassName.java"
|
||||
}
|
||||
|
||||
@@ -12,15 +12,14 @@
|
||||
<orderEntry type="library" name="kotlin-stdlib-jdk8" level="project" />
|
||||
<orderEntry type="library" scope="TEST" name="kotlin-test" level="project" />
|
||||
<orderEntry type="library" name="Guava" level="project" />
|
||||
<orderEntry type="library" name="fest" level="project" />
|
||||
<orderEntry type="library" name="fest-swing" level="project" />
|
||||
<orderEntry type="library" exported="" name="fest" level="project" />
|
||||
<orderEntry type="library" exported="" name="fest-swing" level="project" />
|
||||
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.execution.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.diff.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testGuiFramework" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testFramework" scope="TEST" />
|
||||
<orderEntry type="module" module-name="intellij.platform.statistics" />
|
||||
<orderEntry type="module" module-name="intellij.platform.tasks.impl" />
|
||||
|
||||
@@ -102,7 +102,7 @@ abstract class TaskContext : LearningDslBase {
|
||||
open fun addStep(step: CompletableFuture<Boolean>) = Unit
|
||||
|
||||
/** [action] What should be done to pass the current task */
|
||||
open fun test(action: TaskTestContext.() -> Unit) = Unit
|
||||
open fun test(waitEditorToBeReady: Boolean = true, action: TaskTestContext.() -> Unit) = Unit
|
||||
|
||||
fun triggerByFoundPathAndHighlight(highlightBorder: Boolean = true, highlightInside: Boolean = false, usePulsation: Boolean = false,
|
||||
checkPath: TaskRuntimeContext.(tree: JTree, path: TreePath) -> Boolean) {
|
||||
|
||||
@@ -1,35 +1,58 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package training.dsl
|
||||
|
||||
import com.intellij.ide.util.treeView.NodeRenderer
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.testGuiFramework.cellReader.ExtendedJListCellReader
|
||||
import com.intellij.testGuiFramework.fixtures.ComponentFixture
|
||||
import com.intellij.testGuiFramework.fixtures.IdeFrameFixture
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.framework.Timeouts
|
||||
import com.intellij.testGuiFramework.impl.GuiTestCase
|
||||
import com.intellij.testGuiFramework.impl.findComponentWithTimeout
|
||||
import com.intellij.testGuiFramework.impl.waitUntilFound
|
||||
import com.intellij.testGuiFramework.util.step
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton
|
||||
import com.intellij.openapi.application.invokeAndWaitIfNeeded
|
||||
import com.intellij.openapi.wm.impl.IdeFrameImpl
|
||||
import com.intellij.ui.KeyStrokeAdapter
|
||||
import com.intellij.ui.MultilineTreeCellRenderer
|
||||
import com.intellij.ui.SimpleColoredComponent
|
||||
import org.fest.swing.core.GenericTypeMatcher
|
||||
import org.fest.swing.core.Robot
|
||||
import org.fest.swing.driver.BasicJListCellReader
|
||||
import org.fest.swing.driver.ComponentDriver
|
||||
import org.fest.swing.exception.ComponentLookupException
|
||||
import org.fest.swing.exception.WaitTimedOutError
|
||||
import org.fest.swing.fixture.AbstractComponentFixture
|
||||
import org.fest.swing.fixture.ContainerFixture
|
||||
import org.fest.swing.fixture.JButtonFixture
|
||||
import org.fest.swing.fixture.JListFixture
|
||||
import org.fest.swing.timing.Condition
|
||||
import org.fest.swing.timing.Pause
|
||||
import org.fest.swing.timing.Timeout
|
||||
import training.ui.LearningUiUtil
|
||||
import training.ui.LearningUiUtil.findComponentWithTimeout
|
||||
import training.util.invokeActionForFocusContext
|
||||
import java.awt.Component
|
||||
import java.awt.Container
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.swing.JButton
|
||||
import javax.swing.JDialog
|
||||
import javax.swing.JLabel
|
||||
import javax.swing.JList
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
@LearningDsl
|
||||
class TaskTestContext(rt: TaskRuntimeContext): TaskRuntimeContext(rt) {
|
||||
private val defaultTimeout = Timeout.timeout(3, TimeUnit.SECONDS)
|
||||
|
||||
val robot: Robot get() = LearningUiUtil.robot
|
||||
|
||||
data class TestScriptProperties (
|
||||
val duration: Int = 6, //seconds
|
||||
val duration: Int = 15, //seconds
|
||||
val skipTesting: Boolean = false
|
||||
)
|
||||
|
||||
fun type(text: String) {
|
||||
GuiTestUtil.typeText(text)
|
||||
robot.waitForIdle()
|
||||
for (element in text) {
|
||||
robot.type(element)
|
||||
Pause.pause(10, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
Pause.pause(300, TimeUnit.MILLISECONDS)
|
||||
}
|
||||
|
||||
fun actions(vararg actionIds: String) {
|
||||
@@ -39,50 +62,274 @@ class TaskTestContext(rt: TaskRuntimeContext): TaskRuntimeContext(rt) {
|
||||
}
|
||||
}
|
||||
|
||||
fun ideFrame(action: IdeFrameFixture.() -> Unit) {
|
||||
with(guiTestCase) {
|
||||
ideFrame {
|
||||
// Note: It is not recursive call here. It is GuiTestCase#ideFrame
|
||||
action()
|
||||
}
|
||||
fun ideFrame(action: ContainerFixture<IdeFrameImpl>.() -> Unit) {
|
||||
with(findIdeFrame(robot, defaultTimeout)) {
|
||||
action()
|
||||
}
|
||||
}
|
||||
|
||||
fun <ComponentType : Component> waitComponent(componentClass: Class<ComponentType>, partOfName: String? = null) {
|
||||
waitUntilFound(null, componentClass, Timeouts.seconds02) {
|
||||
(if (partOfName != null) it.javaClass.name.contains(partOfName) else true) && it.isShowing
|
||||
LearningUiUtil.waitUntilFound(robot, null, typeMatcher(componentClass) {
|
||||
(if (partOfName != null) it.javaClass.name.contains(partOfName)
|
||||
else true) && it.isShowing
|
||||
}, defaultTimeout)
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a JList component in hierarchy of context component with a containingItem and returns JListFixture.
|
||||
*
|
||||
* @throws ComponentLookupException if component has not been found or timeout exceeded
|
||||
*/
|
||||
fun <C : Container> ContainerFixture<C>.jList(containingItem: String? = null, timeout: Timeout = defaultTimeout): JListFixture {
|
||||
return generalListFinder(timeout, containingItem) { element, p -> element == p }
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a JButton component in hierarchy of context component with a name and returns ExtendedButtonFixture.
|
||||
*
|
||||
* @throws ComponentLookupException if component has not been found or timeout exceeded
|
||||
*/
|
||||
fun <C : Container> ContainerFixture<C>.button(name: String, timeout: Timeout = defaultTimeout): JButtonFixture {
|
||||
val jButton: JButton = findComponentWithTimeout(timeout) { it.isShowing && it.isVisible && it.text == name }
|
||||
return JButtonFixture(robot(), jButton)
|
||||
}
|
||||
|
||||
fun <C : Container> ContainerFixture<C>.actionButton(actionName: String, timeout: Timeout = defaultTimeout)
|
||||
: AbstractComponentFixture<*, ActionButton, ComponentDriver<*>> {
|
||||
val actionButton: ActionButton = findComponentWithTimeout(timeout) {
|
||||
it.isShowing && it.isEnabled && actionName == it.action.templatePresentation.text
|
||||
}
|
||||
return ActionButtonFixture(robot(), actionButton)
|
||||
}
|
||||
|
||||
// Modified copy-paste
|
||||
fun <C : Container> ContainerFixture<C>.jListContains(partOfItem: String? = null, timeout: Timeout = Timeouts.seconds02): JListFixture {
|
||||
return step("search '$partOfItem' in list") {
|
||||
val extCellReader = ExtendedJListCellReader()
|
||||
val myJList: JList<*> = findComponentWithTimeout(timeout) { jList: JList<*> ->
|
||||
if (partOfItem == null) true //if were searching for any jList()
|
||||
else {
|
||||
val elements = (0 until jList.model.size).map { extCellReader.valueAt(jList, it) }
|
||||
elements.any { it.toString().contains(partOfItem) } && jList.isShowing
|
||||
}
|
||||
}
|
||||
val jListFixture = JListFixture(robot(), myJList)
|
||||
jListFixture.replaceCellReader(extCellReader)
|
||||
return@step jListFixture
|
||||
}
|
||||
fun <C : Container> ContainerFixture<C>.jListContains(partOfItem: String? = null, timeout: Timeout = defaultTimeout): JListFixture {
|
||||
return generalListFinder(timeout, partOfItem) { element, p -> element.contains(p) }
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds JDialog with a specific title (if title is null showing dialog should be only one) and returns created JDialogFixture
|
||||
*/
|
||||
fun dialog(title: String? = null,
|
||||
ignoreCaseTitle: Boolean = false,
|
||||
predicate: (String, String) -> Boolean = { found: String, wanted: String -> found == wanted },
|
||||
timeout: Timeout = defaultTimeout,
|
||||
func: ContainerFixture<JDialog>.() -> Unit = {})
|
||||
: AbstractComponentFixture<*, JDialog, ComponentDriver<*>> {
|
||||
val jDialogFixture = if (title == null) {
|
||||
val jDialog = LearningUiUtil.waitUntilFound(robot, null, typeMatcher(JDialog::class.java) { true }, timeout)
|
||||
JDialogFixture(robot, jDialog)
|
||||
}
|
||||
else {
|
||||
try {
|
||||
val dialog = withPauseWhenNull(timeout = timeout) {
|
||||
val allMatchedDialogs = robot.finder().findAll(typeMatcher(JDialog::class.java) {
|
||||
it.isFocused &&
|
||||
if (ignoreCaseTitle) predicate(it.title.toLowerCase(), title.toLowerCase()) else predicate(it.title, title)
|
||||
}).filter { it.isShowing && it.isEnabled && it.isVisible }
|
||||
if (allMatchedDialogs.size > 1) throw Exception(
|
||||
"Found more than one (${allMatchedDialogs.size}) dialogs matched title \"$title\"")
|
||||
allMatchedDialogs.firstOrNull()
|
||||
}
|
||||
JDialogFixture(robot, dialog)
|
||||
}
|
||||
catch (timeoutError: WaitTimedOutError) {
|
||||
throw ComponentLookupException("Timeout error for finding JDialog by title \"$title\" for ${timeout.duration()}")
|
||||
}
|
||||
}
|
||||
func(jDialogFixture)
|
||||
return jDialogFixture
|
||||
}
|
||||
|
||||
class SimpleComponentFixture(robot: Robot, target: Component): ComponentFixture<SimpleComponentFixture, Component>(
|
||||
SimpleComponentFixture::class.java, robot, target)
|
||||
|
||||
fun IdeFrameFixture.jComponent(target: Component): SimpleComponentFixture {
|
||||
fun ContainerFixture<*>.jComponent(target: Component): AbstractComponentFixture<*, Component, ComponentDriver<*>> {
|
||||
return SimpleComponentFixture(robot(), target)
|
||||
}
|
||||
|
||||
fun invokeActionViaShortcut(shortcut: String) {
|
||||
val keyStroke = KeyStrokeAdapter.getKeyStroke(shortcut)
|
||||
robot.pressAndReleaseKey(keyStroke.keyCode, keyStroke.modifiers)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Volatile
|
||||
var inTestMode: Boolean = false
|
||||
}
|
||||
|
||||
val guiTestCase: GuiTestCase by lazy { GuiTestCase() }
|
||||
////////////------------------------------
|
||||
private fun <C : Container> ContainerFixture<C>.generalListFinder(timeout: Timeout,
|
||||
containingItem: String?,
|
||||
predicate: (String, String) -> Boolean): JListFixture {
|
||||
val extCellReader = ExtendedJListCellReader()
|
||||
val myJList: JList<*> = findComponentWithTimeout(timeout) { jList: JList<*> ->
|
||||
if (containingItem == null) true //if were searching for any jList()
|
||||
else {
|
||||
val elements = (0 until jList.model.size).mapNotNull { extCellReader.valueAt(jList, it) }
|
||||
elements.any { predicate(it, containingItem) } && jList.isShowing
|
||||
}
|
||||
}
|
||||
val jListFixture = JListFixture(robot(), myJList)
|
||||
jListFixture.replaceCellReader(extCellReader)
|
||||
return jListFixture
|
||||
}
|
||||
|
||||
/**
|
||||
* waits for [timeout] when functionProbeToNull() not return null
|
||||
*
|
||||
* @throws WaitTimedOutError with the text: "Timed out waiting for $timeout second(s) until {@code conditionText} will be not null"
|
||||
*/
|
||||
private fun <ReturnType> withPauseWhenNull(conditionText: String = "function to probe",
|
||||
timeout: Timeout = defaultTimeout,
|
||||
functionProbeToNull: () -> ReturnType?): ReturnType {
|
||||
var result: ReturnType? = null
|
||||
waitUntil("$conditionText will be not null", timeout) {
|
||||
result = functionProbeToNull()
|
||||
result != null
|
||||
}
|
||||
return result!!
|
||||
}
|
||||
|
||||
private fun waitUntil(condition: String, timeout: Timeout = defaultTimeout, conditionalFunction: () -> Boolean) {
|
||||
Pause.pause(object : Condition("${timeout.duration()} until $condition") {
|
||||
override fun test() = conditionalFunction()
|
||||
}, timeout)
|
||||
}
|
||||
|
||||
|
||||
private class SimpleComponentFixture(robot: Robot, target: Component) :
|
||||
ComponentFixture<SimpleComponentFixture, Component>(SimpleComponentFixture::class.java, robot, target)
|
||||
|
||||
|
||||
private fun <ComponentType : Component?> typeMatcher(componentTypeClass: Class<ComponentType>,
|
||||
matcher: (ComponentType) -> Boolean): GenericTypeMatcher<ComponentType> {
|
||||
return object : GenericTypeMatcher<ComponentType>(componentTypeClass) {
|
||||
override fun isMatching(component: ComponentType): Boolean = matcher(component)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getValueWithCellRenderer(cellRendererComponent: Component, isExtended: Boolean = true): String? {
|
||||
val result = when (cellRendererComponent) {
|
||||
is JLabel -> cellRendererComponent.text
|
||||
is NodeRenderer -> {
|
||||
if (isExtended) cellRendererComponent.getFullText()
|
||||
else cellRendererComponent.getFirstText()
|
||||
} //should stands before SimpleColoredComponent because it is more specific
|
||||
is SimpleColoredComponent -> cellRendererComponent.getFullText()
|
||||
is MultilineTreeCellRenderer -> cellRendererComponent.text
|
||||
else -> cellRendererComponent.findText()
|
||||
}
|
||||
return result?.trimEnd()
|
||||
}
|
||||
|
||||
|
||||
private class ExtendedJListCellReader : BasicJListCellReader() {
|
||||
override fun valueAt(list: JList<*>, index: Int): String? {
|
||||
val element = list.model.getElementAt(index) ?: return null
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val cellRendererComponent = (list as JList<Any>).cellRenderer
|
||||
.getListCellRendererComponent(list, element, index, true, true)
|
||||
return getValueWithCellRenderer(cellRendererComponent)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SimpleColoredComponent.getFullText(): String {
|
||||
return invokeAndWaitIfNeeded {
|
||||
this.getCharSequence(false).toString()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun SimpleColoredComponent.getFirstText(): String {
|
||||
return invokeAndWaitIfNeeded {
|
||||
this.getCharSequence(true).toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Component.findText(): String? {
|
||||
try {
|
||||
assert(this is Container)
|
||||
val container = this as Container
|
||||
val resultList = ArrayList<String>()
|
||||
resultList.addAll(
|
||||
findAllWithBFS(container, JLabel::class.java)
|
||||
.asSequence()
|
||||
.filter { !it.text.isNullOrEmpty() }
|
||||
.map { it.text }
|
||||
.toList()
|
||||
)
|
||||
resultList.addAll(
|
||||
findAllWithBFS(container, SimpleColoredComponent::class.java)
|
||||
.asSequence()
|
||||
.filter {
|
||||
it.getFullText().isNotEmpty()
|
||||
}
|
||||
.map {
|
||||
it.getFullText()
|
||||
}
|
||||
.toList()
|
||||
)
|
||||
return resultList.firstOrNull { it.isNotEmpty() }
|
||||
}
|
||||
catch (ignored: ComponentLookupException) {
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
private fun <ComponentType : Component> findAllWithBFS(container: Container, clazz: Class<ComponentType>): List<ComponentType> {
|
||||
val result = LinkedList<ComponentType>()
|
||||
val queue: Queue<Component> = LinkedList()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
fun check(container: Component) {
|
||||
if (clazz.isInstance(container)) result.add(container as ComponentType)
|
||||
}
|
||||
|
||||
queue.add(container)
|
||||
while (queue.isNotEmpty()) {
|
||||
val polled = queue.poll()
|
||||
check(polled)
|
||||
if (polled is Container)
|
||||
queue.addAll(polled.components)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
}
|
||||
|
||||
private open class ComponentFixture<S, C : Component>(selfType: Class<S>, robot: Robot, target: C)
|
||||
: AbstractComponentFixture<S, C, ComponentDriver<*>>(selfType, robot, target) {
|
||||
|
||||
override fun createDriver(robot: Robot): ComponentDriver<*> {
|
||||
return ComponentDriver<Component>(robot)
|
||||
}
|
||||
}
|
||||
|
||||
private class ActionButtonFixture(robot: Robot, target: ActionButton):
|
||||
ComponentFixture<ActionButtonFixture, ActionButton>(ActionButtonFixture::class.java, robot, target), ContainerFixture<ActionButton>
|
||||
|
||||
private class IdeFrameFixture(robot: Robot, target: IdeFrameImpl)
|
||||
: ComponentFixture<IdeFrameFixture, IdeFrameImpl>(IdeFrameFixture::class.java, robot, target), ContainerFixture<IdeFrameImpl>
|
||||
|
||||
private class JDialogFixture(robot: Robot, jDialog: JDialog) :
|
||||
ComponentFixture<JDialogFixture, JDialog>(JDialogFixture::class.java, robot, jDialog), ContainerFixture<JDialog>
|
||||
|
||||
private fun findIdeFrame(robot: Robot, timeout: Timeout): IdeFrameFixture {
|
||||
val matcher: GenericTypeMatcher<IdeFrameImpl> = object : GenericTypeMatcher<IdeFrameImpl>(
|
||||
IdeFrameImpl::class.java) {
|
||||
override fun isMatching(frame: IdeFrameImpl): Boolean {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return try {
|
||||
Pause.pause(object : Condition("IdeFrame to show up") {
|
||||
override fun test(): Boolean {
|
||||
return !robot.finder().findAll(matcher).isEmpty()
|
||||
}
|
||||
}, timeout)
|
||||
val ideFrame = robot.finder().find(matcher)
|
||||
IdeFrameFixture(robot, ideFrame)
|
||||
}
|
||||
catch (timedOutError: WaitTimedOutError) {
|
||||
throw ComponentLookupException("Unable to find IdeFrame in " + timeout.duration())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -309,7 +309,7 @@ class LessonExecutor(val lesson: KLesson, val project: Project, initialEditor: E
|
||||
}
|
||||
|
||||
private fun processTestActions(taskContext: TaskContextImpl) {
|
||||
if (TaskTestContext.inTestMode) {
|
||||
if (TaskTestContext.inTestMode && taskContext.testActions.isNotEmpty()) {
|
||||
LessonManager.instance.testActionsExecutor.execute {
|
||||
taskContext.testActions.forEach { it.run() }
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@ import com.intellij.util.Alarm
|
||||
import training.dsl.LearningBalloonConfig
|
||||
import training.dsl.TaskContext
|
||||
import training.dsl.TaskRuntimeContext
|
||||
import training.dsl.TaskTestContext
|
||||
import training.learn.ActionsRecorder
|
||||
import training.learn.LearnBundle
|
||||
import training.learn.lesson.LessonManager
|
||||
@@ -189,8 +188,6 @@ private class ExtractTaskPropertiesContext(override val project: Project) : Task
|
||||
hasDetection = true
|
||||
}
|
||||
|
||||
override fun test(action: TaskTestContext.() -> Unit) = Unit // do nothing
|
||||
|
||||
override fun action(actionId: String): String = "" //Doesn't matter what to return
|
||||
|
||||
override fun code(sourceSample: String): String = "" //Doesn't matter what to return
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package training.dsl.impl
|
||||
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ModalityState
|
||||
import com.intellij.openapi.application.WriteAction
|
||||
import com.intellij.openapi.application.invokeLater
|
||||
import com.intellij.codeInsight.daemon.impl.DaemonCodeAnalyzerEx
|
||||
import com.intellij.openapi.application.*
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
@@ -222,9 +220,21 @@ internal class TaskContextImpl(private val lessonExecutor: LessonExecutor,
|
||||
steps.add(step)
|
||||
}
|
||||
|
||||
override fun test(action: TaskTestContext.() -> Unit) {
|
||||
override fun test(waitEditorToBeReady: Boolean, action: TaskTestContext.() -> Unit) {
|
||||
testActions.add(Runnable {
|
||||
DumbService.getInstance(runtimeContext.project).waitForSmartMode()
|
||||
// This wait implementation is quite ugly, but it works and it is needed in the test mode only. So should be ok for now.
|
||||
if (waitEditorToBeReady) {
|
||||
val psiFile = invokeAndWaitIfNeeded { PsiDocumentManager.getInstance(project).getPsiFile(runtimeContext.editor.document) } ?: return@Runnable
|
||||
var t = 0
|
||||
val step = 100
|
||||
while (!runReadAction { DaemonCodeAnalyzerEx.getInstanceEx(project).isErrorAnalyzingFinished(psiFile) }) {
|
||||
Thread.sleep(step.toLong())
|
||||
t += step
|
||||
if (t > 3000) return@Runnable
|
||||
}
|
||||
}
|
||||
|
||||
TaskTestContext(runtimeContext).action()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -9,14 +9,13 @@ import com.intellij.openapi.editor.actions.ToggleShowLineNumbersGloballyAction
|
||||
import com.intellij.openapi.editor.ex.EditorSettingsExternalizable
|
||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.impl.jList
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.fest.swing.driver.ComponentDriver
|
||||
import training.dsl.*
|
||||
import training.learn.LearnBundle
|
||||
import training.learn.LessonsBundle
|
||||
import training.learn.course.KLesson
|
||||
import java.awt.Component
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.JPanel
|
||||
|
||||
@@ -64,8 +63,8 @@ class GotoActionLesson(private val sample: LessonSample, private val firstLesson
|
||||
// Note 1: it is editor from test IDE fixture
|
||||
// Note 2: In order to pass this task without interference with later task I need to firstly focus lesson
|
||||
// and only then press Escape
|
||||
editor.requestFocus()
|
||||
GuiTestUtil.shortcut(Key.ESCAPE)
|
||||
ComponentDriver<Component>(robot).focusAndWaitForFocusGain(editor.contentComponent)
|
||||
invokeActionViaShortcut("ESCAPE")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-2
@@ -4,7 +4,6 @@ package training.learn.lesson.general.assistance
|
||||
import com.intellij.codeInsight.CodeInsightBundle
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonSample
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
@@ -59,7 +58,7 @@ class CodeFormatLesson(private val sample: LessonSample, private val optimizeImp
|
||||
restoreAfterStateBecomeFalse {
|
||||
focusOwner is EditorComponentImpl
|
||||
}
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
properties.setValue("LayoutCode.optimizeImports", true)
|
||||
ideFrame {
|
||||
button("Run").click()
|
||||
|
||||
+2
-8
@@ -2,11 +2,6 @@
|
||||
package training.learn.lesson.general.assistance
|
||||
|
||||
import com.intellij.ide.IdeBundle
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.impl.jList
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.testGuiFramework.util.Modifier
|
||||
import com.intellij.testGuiFramework.util.Shortcut
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
import training.learn.LessonsBundle
|
||||
@@ -59,9 +54,8 @@ abstract class EditorCodingAssistanceLesson(private val sample: LessonSample) :
|
||||
restoreIfModifiedOrMoved()
|
||||
test {
|
||||
Thread.sleep(500)
|
||||
val errorDescriptionShortcut = Shortcut(hashSetOf(Modifier.CONTROL), Key.F1)
|
||||
GuiTestUtil.shortcut(errorDescriptionShortcut)
|
||||
GuiTestUtil.shortcut(errorDescriptionShortcut)
|
||||
invokeActionViaShortcut("CONTROL F1")
|
||||
invokeActionViaShortcut("CONTROL F1")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -4,8 +4,6 @@ package training.learn.lesson.general.assistance
|
||||
import com.intellij.codeInsight.documentation.DocumentationComponent
|
||||
import com.intellij.codeInsight.documentation.QuickDocUtil
|
||||
import com.intellij.codeInsight.hint.ImplementationViewComponent
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonSample
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
@@ -31,7 +29,7 @@ class QuickPopupsLesson(private val sample: LessonSample) :
|
||||
stateCheck { checkDocComponentClosed() }
|
||||
restoreIfModifiedOrMoved()
|
||||
test {
|
||||
GuiTestUtil.shortcut(Key.ESCAPE)
|
||||
invokeActionViaShortcut("ESCAPE")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
-5
@@ -1,8 +1,6 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package training.learn.lesson.general.completion
|
||||
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonSample
|
||||
import training.dsl.LessonUtil
|
||||
@@ -46,7 +44,7 @@ abstract class BasicCompletionLessonBase : KLesson("Basic completion", LessonsBu
|
||||
}
|
||||
}
|
||||
test {
|
||||
GuiTestUtil.typeText(item1StartToType)
|
||||
type(item1StartToType)
|
||||
}
|
||||
}
|
||||
task("EditorChooseLookupItem") {
|
||||
@@ -59,8 +57,8 @@ abstract class BasicCompletionLessonBase : KLesson("Basic completion", LessonsBu
|
||||
!isTheFirstVariant(ui)
|
||||
} ?: true
|
||||
}
|
||||
test {
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
test(waitEditorToBeReady = false) {
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
waitBeforeContinue(500)
|
||||
|
||||
+1
-3
@@ -8,8 +8,6 @@ import com.intellij.openapi.wm.impl.content.BaseLabel
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiFile
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil.shortcut
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.ui.UIBundle
|
||||
import com.intellij.ui.table.JBTable
|
||||
import training.dsl.LessonContext
|
||||
@@ -56,7 +54,7 @@ abstract class DeclarationAndUsagesLesson
|
||||
actions(it)
|
||||
ideFrame {
|
||||
waitComponent(JBTable::class.java, "ShowUsagesTable")
|
||||
shortcut(Key.ENTER)
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -4,8 +4,6 @@ package training.learn.lesson.general.navigation
|
||||
import com.intellij.ide.dnd.aware.DnDAwareTree
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.ui.speedSearch.SpeedSearchSupply
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonUtil
|
||||
@@ -48,7 +46,7 @@ abstract class FileStructureLesson
|
||||
text(LessonsBundle.message("file.structure.navigate", LessonUtil.rawEnter()))
|
||||
stateCheck { editor.caretModel.logicalPosition == methodToFindPosition }
|
||||
restoreState { !checkWordInSearch(searchSubstring) }
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test { invokeActionViaShortcut("ENTER") }
|
||||
}
|
||||
task("ActivateStructureToolWindow") {
|
||||
text(LessonsBundle.message("file.structure.toolwindow", action(it)))
|
||||
|
||||
+8
-16
@@ -9,20 +9,16 @@ import com.intellij.find.impl.FindInProjectSettingsBase
|
||||
import com.intellij.find.impl.FindPopupPanel
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton
|
||||
import com.intellij.testGuiFramework.fixtures.extended.ExtendedTableFixture
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.impl.actionButton
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import com.intellij.testGuiFramework.impl.findComponentWithTimeout
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.usages.UsagePresentation
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.fest.swing.core.MouseClickInfo
|
||||
import org.fest.swing.data.TableCell
|
||||
import org.fest.swing.fixture.JTableFixture
|
||||
import org.fest.swing.fixture.JTextComponentFixture
|
||||
import training.dsl.*
|
||||
import training.learn.LessonsBundle
|
||||
import training.learn.course.KLesson
|
||||
import training.ui.LearningUiUtil.findComponentWithTimeout
|
||||
import java.awt.event.InputEvent
|
||||
import java.awt.event.KeyEvent
|
||||
import javax.swing.*
|
||||
@@ -42,7 +38,6 @@ class FindInFilesLesson(override val existedFile: String)
|
||||
!popup.helper.isReplaceState
|
||||
}
|
||||
test {
|
||||
Thread.sleep(300)
|
||||
actions(it)
|
||||
}
|
||||
}
|
||||
@@ -61,7 +56,7 @@ class FindInFilesLesson(override val existedFile: String)
|
||||
LessonUtil.rawKeyStroke(KeyStroke.getKeyStroke(KeyEvent.VK_W, InputEvent.ALT_DOWN_MASK))))
|
||||
highlightAndTriggerWhenButtonSelected(wholeWordsButtonText)
|
||||
showWarningIfPopupClosed(false)
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
ideFrame {
|
||||
actionButton(wholeWordsButtonText).click()
|
||||
}
|
||||
@@ -84,9 +79,8 @@ class FindInFilesLesson(override val existedFile: String)
|
||||
restoreByUi(restoreId = showPopupTaskId)
|
||||
test {
|
||||
ideFrame {
|
||||
Thread.sleep(300)
|
||||
val table = findComponentWithTimeout { table: JTable -> table.findLastRowIndexOfItemWithText(it) != -1 }
|
||||
val tableFixture = ExtendedTableFixture(robot(), table)
|
||||
val tableFixture = JTableFixture(robot(), table)
|
||||
val rowIndex = table.findLastRowIndexOfItemWithText(it)
|
||||
tableFixture.click(TableCell.row(rowIndex).column(0), MouseClickInfo.leftButton())
|
||||
}
|
||||
@@ -97,7 +91,7 @@ class FindInFilesLesson(override val existedFile: String)
|
||||
text(LessonsBundle.message("find.in.files.go.to.file", LessonUtil.rawEnter()))
|
||||
stateCheck { virtualFile.name != existedFile.substringAfterLast('/') }
|
||||
restoreByUi(restoreId = showPopupTaskId)
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test { invokeActionViaShortcut("ENTER") }
|
||||
}
|
||||
|
||||
task("ReplaceInPath") {
|
||||
@@ -157,7 +151,6 @@ class FindInFilesLesson(override val existedFile: String)
|
||||
showWarningIfPopupClosed(true)
|
||||
test {
|
||||
ideFrame {
|
||||
Thread.sleep(300)
|
||||
button(replaceAllButtonText).click()
|
||||
}
|
||||
}
|
||||
@@ -168,10 +161,9 @@ class FindInFilesLesson(override val existedFile: String)
|
||||
text(LessonsBundle.message("find.in.files.confirm.replace", strong(replaceButtonText)))
|
||||
stateCheck { editor.document.charsSequence.contains("orange") }
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
test {
|
||||
ideFrame {
|
||||
Thread.sleep(300)
|
||||
findMessageDialog(replaceAllDialogTitle).click(replaceButtonText)
|
||||
test(waitEditorToBeReady = false) {
|
||||
dialog(title = "Replace All") {
|
||||
button(replaceButtonText).click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+7
-9
@@ -7,8 +7,6 @@ import com.intellij.openapi.application.invokeLater
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.wm.IdeFrame
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.ui.SearchTextField
|
||||
import com.intellij.ui.components.JBList
|
||||
import com.intellij.ui.components.fields.ExtendableTextField
|
||||
@@ -89,7 +87,9 @@ abstract class RecentFilesLesson : KLesson("Recent Files and Locations", Lessons
|
||||
restoreState {
|
||||
!checkRecentFilesSearch("rfd") || previous.ui?.isShowing != true
|
||||
}
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test(waitEditorToBeReady = false) {
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
|
||||
actionTask("RecentFiles") {
|
||||
@@ -112,7 +112,7 @@ abstract class RecentFilesLesson : KLesson("Recent Files and Locations", Lessons
|
||||
restoreIfRecentFilesPopupClosed()
|
||||
test {
|
||||
repeat(countOfFilesToDelete) {
|
||||
GuiTestUtil.shortcut(Key.DELETE)
|
||||
invokeActionViaShortcut("DELETE")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -120,7 +120,7 @@ abstract class RecentFilesLesson : KLesson("Recent Files and Locations", Lessons
|
||||
task {
|
||||
text(LessonsBundle.message("recent.files.close.popup", LessonUtil.rawKeyStroke(KeyEvent.VK_ESCAPE)))
|
||||
stateCheck { focusOwner is IdeFrame }
|
||||
test { GuiTestUtil.shortcut(Key.ESCAPE) }
|
||||
test { invokeActionViaShortcut("ESCAPE") }
|
||||
}
|
||||
|
||||
actionTask("RecentLocations") {
|
||||
@@ -146,7 +146,7 @@ abstract class RecentFilesLesson : KLesson("Recent Files and Locations", Lessons
|
||||
restoreState {
|
||||
!checkRecentLocationsSearch(stringForRecentFilesSearch) || previous.ui?.isShowing != true
|
||||
}
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test { invokeActionViaShortcut("ENTER") }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,9 +188,7 @@ abstract class RecentFilesLesson : KLesson("Recent Files and Locations", Lessons
|
||||
private fun checkWordInSearch(expected: String, component: JComponent): Boolean {
|
||||
val supply = SpeedSearchSupply.getSupply(component)
|
||||
val enteredPrefix = supply?.enteredPrefix ?: return false
|
||||
val equals = enteredPrefix.equals(expected, ignoreCase = true)
|
||||
System.err.println("expected = '$expected', enteredPrefix = '$enteredPrefix', equals = $equals")
|
||||
return equals
|
||||
return enteredPrefix.equals(expected, ignoreCase = true)
|
||||
}
|
||||
|
||||
private fun TaskContext.restoreIfRecentFilesPopupClosed() {
|
||||
|
||||
+5
-9
@@ -8,10 +8,6 @@ import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.psi.search.EverythingGlobalScope
|
||||
import com.intellij.psi.search.ProjectScope
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.testGuiFramework.util.Modifier
|
||||
import com.intellij.testGuiFramework.util.Shortcut
|
||||
import com.intellij.ui.components.fields.ExtendableTextField
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import training.dsl.*
|
||||
@@ -52,7 +48,7 @@ abstract class SearchEverywhereLesson : KLesson("Search everywhere", LessonsBund
|
||||
!checkInsideSearchEverywhere()
|
||||
}
|
||||
test {
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,7 +75,7 @@ abstract class SearchEverywhereLesson : KLesson("Search everywhere", LessonsBund
|
||||
!checkInsideSearchEverywhere() && focusOwner !is JList<*>
|
||||
}
|
||||
test {
|
||||
GuiTestUtil.shortcut(Shortcut(HashSet(setOf(Modifier.ALT)), Key.P))
|
||||
invokeActionViaShortcut("ALT P")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,7 +89,7 @@ abstract class SearchEverywhereLesson : KLesson("Search everywhere", LessonsBund
|
||||
task {
|
||||
text(LessonsBundle.message("search.everywhere.close.documentation.popup", LessonUtil.rawKeyStroke(KeyEvent.VK_ESCAPE)))
|
||||
stateCheck { previous.ui?.isShowing != true }
|
||||
test { GuiTestUtil.shortcut(Key.ESCAPE) }
|
||||
test { invokeActionViaShortcut("ENTER") }
|
||||
}
|
||||
|
||||
task {
|
||||
@@ -103,8 +99,8 @@ abstract class SearchEverywhereLesson : KLesson("Search everywhere", LessonsBund
|
||||
if (TaskTestContext.inTestMode) task {
|
||||
stateCheck { focusOwner is EditorComponentImpl }
|
||||
test {
|
||||
GuiTestUtil.shortcut(Key.ESCAPE)
|
||||
GuiTestUtil.shortcut(Key.ESCAPE)
|
||||
invokeActionViaShortcut("ESCAPE")
|
||||
invokeActionViaShortcut("ESCAPE")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+9
-16
@@ -3,7 +3,6 @@ package training.learn.lesson.general.refactorings
|
||||
|
||||
import com.intellij.CommonBundle
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import com.intellij.ui.UIBundle
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
@@ -43,11 +42,9 @@ class ExtractMethodCocktailSortLesson(private val sample: LessonSample)
|
||||
}
|
||||
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
test {
|
||||
with(TaskTestContext.guiTestCase) {
|
||||
dialog(extractMethodDialogTitle, needToKeepDialog = true) {
|
||||
button(okButtonText).click()
|
||||
}
|
||||
test(waitEditorToBeReady = false) {
|
||||
dialog(extractMethodDialogTitle) {
|
||||
button(okButtonText).click()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -61,11 +58,9 @@ class ExtractMethodCocktailSortLesson(private val sample: LessonSample)
|
||||
}
|
||||
|
||||
restoreByUi(restoreId = startTaskId, delayMillis = defaultRestoreDelay)
|
||||
test {
|
||||
with(TaskTestContext.guiTestCase) {
|
||||
dialog(extractMethodDialogTitle) {
|
||||
button(yesButtonText).click()
|
||||
}
|
||||
test(waitEditorToBeReady = false) {
|
||||
dialog(extractMethodDialogTitle) {
|
||||
button(yesButtonText).click()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,11 +71,9 @@ class ExtractMethodCocktailSortLesson(private val sample: LessonSample)
|
||||
previous.ui?.isShowing?.not() ?: true
|
||||
}
|
||||
|
||||
test {
|
||||
with(TaskTestContext.guiTestCase) {
|
||||
dialog(replaceFragmentDialogTitle) {
|
||||
button(UIBundle.message("replace.prompt.replace.button").dropMnemonic()).click()
|
||||
}
|
||||
test(waitEditorToBeReady = false) {
|
||||
dialog(replaceFragmentDialogTitle) {
|
||||
button(UIBundle.message("replace.prompt.replace.button").dropMnemonic()).click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-3
@@ -1,7 +1,6 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package training.learn.lesson.general.refactorings
|
||||
|
||||
import com.intellij.testGuiFramework.impl.jList
|
||||
import com.intellij.ui.components.JBList
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonSample
|
||||
@@ -40,9 +39,13 @@ class ExtractVariableFromBubbleLesson(private val sample: LessonSample)
|
||||
}
|
||||
}
|
||||
|
||||
actionTask("NextTemplateVariable") {
|
||||
task("NextTemplateVariable") {
|
||||
//TODO: fix the shortcut: it should be ${action(it)} but with preference for Enter
|
||||
LessonsBundle.message("extract.variable.choose.name", LessonUtil.rawEnter())
|
||||
text(LessonsBundle.message("extract.variable.choose.name", LessonUtil.rawEnter()))
|
||||
trigger(it)
|
||||
test(waitEditorToBeReady = false) {
|
||||
actions(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -3,8 +3,6 @@ package training.learn.lesson.general.refactorings
|
||||
|
||||
import com.intellij.idea.ActionsBundle
|
||||
import com.intellij.refactoring.rename.inplace.InplaceRefactoring
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.ui.components.JBList
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
@@ -38,7 +36,7 @@ abstract class RefactoringMenuLessonBase(lessonId: String) : KLesson(lessonId, L
|
||||
stateCheck { hasInplaceRename() }
|
||||
restoreState(delayMillis = defaultRestoreDelay, restoreId = showPopupTaskId) { focusOwner !is JBList<*> }
|
||||
test {
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,8 +45,8 @@ abstract class RefactoringMenuLessonBase(lessonId: String) : KLesson(lessonId, L
|
||||
stateCheck {
|
||||
!hasInplaceRename()
|
||||
}
|
||||
test {
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
test(waitEditorToBeReady = false) {
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+11
-7
@@ -19,8 +19,6 @@ import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.openapi.fileEditor.FileDocumentManager
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import com.intellij.tasks.TaskBundle
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.xdebugger.*
|
||||
import com.intellij.xdebugger.impl.XDebugSessionImpl
|
||||
@@ -196,7 +194,7 @@ abstract class CommonDebugLesson(id: String) : KLesson(id, LessonsBundle.message
|
||||
proposeModificationRestore(sample.text)
|
||||
test {
|
||||
Thread.sleep(500)
|
||||
GuiTestUtil.shortcut(Key.ESCAPE)
|
||||
invokeActionViaShortcut("ESCAPE")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,8 +247,8 @@ abstract class CommonDebugLesson(id: String) : KLesson(id, LessonsBundle.message
|
||||
proposeModificationRestore(sample.text)
|
||||
test {
|
||||
Thread.sleep(500)
|
||||
GuiTestUtil.shortcut(if (stepIntoDirection == "→") Key.RIGHT else Key.LEFT)
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
invokeActionViaShortcut(if (stepIntoDirection == "→") "RIGHT" else "LEFT")
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,7 +284,7 @@ abstract class CommonDebugLesson(id: String) : KLesson(id, LessonsBundle.message
|
||||
else checkForBreakpoints()
|
||||
}
|
||||
test {
|
||||
GuiTestUtil.shortcut(Key.ESCAPE)
|
||||
invokeActionViaShortcut("ESCAPE")
|
||||
invokeLater {
|
||||
WriteCommandAction.runWriteCommandAction(project) {
|
||||
val offset = sample.text.indexOf("[0]")
|
||||
@@ -356,6 +354,9 @@ abstract class CommonDebugLesson(id: String) : KLesson(id, LessonsBundle.message
|
||||
}
|
||||
|
||||
task(expressionToBeEvaluated) {
|
||||
before {
|
||||
LearningUiHighlightingManager.clearHighlights()
|
||||
}
|
||||
text(LessonsBundle.message("debug.workflow.type.result", code(it),
|
||||
strong(XDebuggerBundle.message("xdebugger.evaluate.label.expression"))))
|
||||
stateCheck { checkWordInTextField(it) }
|
||||
@@ -377,7 +378,10 @@ abstract class CommonDebugLesson(id: String) : KLesson(id, LessonsBundle.message
|
||||
dialog?.title == XDebuggerBundle.message("xdebugger.evaluate.dialog.title") && root?.children?.size == 1
|
||||
}
|
||||
proposeModificationRestore(afterFixText)
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test(waitEditorToBeReady = false) {
|
||||
invokeActionViaShortcut("ENTER")
|
||||
invokeActionViaShortcut("ESCAPE")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-3
@@ -5,8 +5,6 @@ import com.intellij.execution.ExecutionBundle
|
||||
import com.intellij.execution.RunManager
|
||||
import com.intellij.idea.ActionsBundle
|
||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import com.intellij.testGuiFramework.impl.jList
|
||||
import com.intellij.ui.UIBundle
|
||||
import com.intellij.ui.components.JBCheckBox
|
||||
import training.dsl.*
|
||||
@@ -108,7 +106,7 @@ abstract class CommonRunConfigurationLesson(id: String) : KLesson(id, LessonsBun
|
||||
stateCheck {
|
||||
focusOwner is EditorComponentImpl
|
||||
}
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
ideFrame {
|
||||
button("Cancel").click()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,327 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package training.ui
|
||||
|
||||
import com.intellij.util.ConcurrencyUtil
|
||||
import com.intellij.util.ui.EdtInvocationManager
|
||||
import org.fest.swing.awt.AWT
|
||||
import org.fest.swing.core.*
|
||||
import org.fest.swing.core.Robot
|
||||
import org.fest.swing.edt.GuiActionRunner
|
||||
import org.fest.swing.edt.GuiQuery
|
||||
import org.fest.swing.hierarchy.ComponentHierarchy
|
||||
import org.fest.swing.timing.Pause
|
||||
import java.awt.*
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JPopupMenu
|
||||
import javax.swing.SwingUtilities
|
||||
|
||||
// It is a copy-paster from testGuiFramework (with several changes)
|
||||
internal class IftSmartWaitRobot : Robot {
|
||||
|
||||
override fun moveMouse(component: Component) {
|
||||
var where = AWT.visibleCenterOf(component)
|
||||
if (component is JComponent) {
|
||||
Scrolling.scrollToVisible(this, component)
|
||||
where = AWT.visibleCenterOf(component)
|
||||
}
|
||||
moveMouse(component, where)
|
||||
}
|
||||
|
||||
override fun moveMouse(component: Component, point: Point) {
|
||||
val translatedPoint = performOnEdt { AWT.translate(component, point.x, point.y) }
|
||||
requireNotNull(translatedPoint) { "Translated point should be not null" }
|
||||
moveMouse(translatedPoint.x, translatedPoint.y)
|
||||
}
|
||||
|
||||
override fun moveMouse(point: Point) {
|
||||
moveMouse(point.x, point.y)
|
||||
}
|
||||
|
||||
override fun click(component: Component) {
|
||||
moveMouse(component)
|
||||
basicRobot.click(component)
|
||||
}
|
||||
|
||||
override fun click(component: Component, mouseButton: MouseButton) {
|
||||
moveMouse(component)
|
||||
basicRobot.click(component, mouseButton)
|
||||
}
|
||||
|
||||
override fun click(component: Component, mouseButton: MouseButton, counts: Int) {
|
||||
moveMouse(component)
|
||||
basicRobot.click(component, mouseButton, counts)
|
||||
}
|
||||
|
||||
override fun click(component: Component, point: Point) {
|
||||
moveMouse(component, point)
|
||||
basicRobot.click(component, point)
|
||||
}
|
||||
|
||||
override fun showWindow(window: Window) {
|
||||
basicRobot.showWindow(window)
|
||||
}
|
||||
|
||||
override fun showWindow(window: Window, dimension: Dimension) {
|
||||
basicRobot.showWindow(window, dimension)
|
||||
}
|
||||
|
||||
override fun showWindow(window: Window, dimension: Dimension?, p2: Boolean) {
|
||||
basicRobot.showWindow(window, dimension, p2)
|
||||
}
|
||||
|
||||
override fun isActive(): Boolean = basicRobot.isActive
|
||||
|
||||
|
||||
override fun pressAndReleaseKey(p0: Int, vararg p1: Int) {
|
||||
basicRobot.pressAndReleaseKey(p0, *p1)
|
||||
}
|
||||
|
||||
override fun showPopupMenu(component: Component): JPopupMenu =
|
||||
basicRobot.showPopupMenu(component)
|
||||
|
||||
override fun showPopupMenu(component: Component, point: Point): JPopupMenu =
|
||||
basicRobot.showPopupMenu(component, point)
|
||||
|
||||
override fun jitter(component: Component) {
|
||||
basicRobot.jitter(component)
|
||||
}
|
||||
|
||||
override fun jitter(component: Component, point: Point) {
|
||||
basicRobot.jitter(component, point)
|
||||
}
|
||||
|
||||
override fun pressModifiers(p0: Int) {
|
||||
basicRobot.pressModifiers(p0)
|
||||
}
|
||||
|
||||
override fun pressMouse(mouseButton: MouseButton) {
|
||||
basicRobot.pressMouse(mouseButton)
|
||||
}
|
||||
|
||||
override fun pressMouse(component: Component, point: Point) {
|
||||
basicRobot.pressMouse(component, point)
|
||||
}
|
||||
|
||||
override fun pressMouse(component: Component, point: Point, mouseButton: MouseButton) {
|
||||
basicRobot.pressMouse(component, point, mouseButton)
|
||||
}
|
||||
|
||||
override fun pressMouse(point: Point, mouseButton: MouseButton) {
|
||||
basicRobot.pressMouse(point, mouseButton)
|
||||
}
|
||||
|
||||
override fun hierarchy(): ComponentHierarchy =
|
||||
basicRobot.hierarchy()
|
||||
|
||||
override fun releaseKey(p0: Int) {
|
||||
basicRobot.releaseKey(p0)
|
||||
}
|
||||
|
||||
override fun isDragging(): Boolean = basicRobot.isDragging
|
||||
|
||||
override fun printer(): ComponentPrinter = basicRobot.printer()
|
||||
|
||||
override fun type(char: Char) {
|
||||
basicRobot.type(char)
|
||||
}
|
||||
|
||||
override fun type(char: Char, component: Component) {
|
||||
basicRobot.type(char, component)
|
||||
}
|
||||
|
||||
override fun requireNoJOptionPaneIsShowing() {
|
||||
basicRobot.requireNoJOptionPaneIsShowing()
|
||||
}
|
||||
|
||||
override fun cleanUp() {
|
||||
basicRobot.cleanUp()
|
||||
}
|
||||
|
||||
override fun releaseMouse(mouseButton: MouseButton) {
|
||||
basicRobot.releaseMouse(mouseButton)
|
||||
}
|
||||
|
||||
override fun pressKey(p0: Int) {
|
||||
basicRobot.pressKey(p0)
|
||||
}
|
||||
|
||||
override fun settings(): Settings = basicRobot.settings()
|
||||
|
||||
override fun enterText(text: String) {
|
||||
basicRobot.enterText(text)
|
||||
}
|
||||
|
||||
override fun enterText(text: String, component: Component) {
|
||||
basicRobot.enterText(text, component)
|
||||
}
|
||||
|
||||
override fun releaseMouseButtons() {
|
||||
basicRobot.releaseMouseButtons()
|
||||
}
|
||||
|
||||
override fun rightClick(component: Component) {
|
||||
basicRobot.rightClick(component)
|
||||
}
|
||||
|
||||
override fun focus(component: Component) {
|
||||
basicRobot.focus(component)
|
||||
}
|
||||
|
||||
override fun doubleClick(component: Component) {
|
||||
basicRobot.doubleClick(component)
|
||||
}
|
||||
|
||||
override fun cleanUpWithoutDisposingWindows() {
|
||||
basicRobot.cleanUpWithoutDisposingWindows()
|
||||
}
|
||||
|
||||
override fun isReadyForInput(component: Component): Boolean = basicRobot.isReadyForInput(component)
|
||||
|
||||
override fun focusAndWaitForFocusGain(component: Component) {
|
||||
basicRobot.focusAndWaitForFocusGain(component)
|
||||
}
|
||||
|
||||
override fun releaseModifiers(p0: Int) {
|
||||
basicRobot.releaseModifiers(p0)
|
||||
}
|
||||
|
||||
override fun findActivePopupMenu(): JPopupMenu? {
|
||||
return basicRobot.findActivePopupMenu()
|
||||
}
|
||||
|
||||
override fun rotateMouseWheel(component: Component, p1: Int) {
|
||||
basicRobot.rotateMouseWheel(component, p1)
|
||||
}
|
||||
|
||||
override fun rotateMouseWheel(p0: Int) {
|
||||
basicRobot.rotateMouseWheel(p0)
|
||||
}
|
||||
|
||||
override fun pressAndReleaseKeys(vararg p0: Int) {
|
||||
basicRobot.pressAndReleaseKeys(*p0)
|
||||
}
|
||||
|
||||
override fun finder(): ComponentFinder = basicRobot.finder()
|
||||
|
||||
private val basicRobot: BasicRobot = BasicRobot.robotWithCurrentAwtHierarchyWithoutScreenLock() as BasicRobot
|
||||
|
||||
init {
|
||||
settings().delayBetweenEvents(10)
|
||||
}
|
||||
|
||||
private val waitConst = 30L
|
||||
private var myAwareClick: Boolean = false
|
||||
|
||||
override fun waitForIdle() {
|
||||
if (myAwareClick) {
|
||||
Thread.sleep(50)
|
||||
}
|
||||
else {
|
||||
Pause.pause(waitConst)
|
||||
if (!SwingUtilities.isEventDispatchThread()) EdtInvocationManager.getInstance().invokeAndWait({ })
|
||||
}
|
||||
}
|
||||
|
||||
override fun close(w: Window) {
|
||||
basicRobot.close(w)
|
||||
basicRobot.waitForIdle()
|
||||
}
|
||||
|
||||
//smooth mouse move
|
||||
override fun moveMouse(x: Int, y: Int) {
|
||||
val pauseConstMs = settings().delayBetweenEvents().toLong()
|
||||
val n = 20
|
||||
val start = MouseInfo.getPointerInfo().location
|
||||
val dx = (x - start.x) / n.toDouble()
|
||||
val dy = (y - start.y) / n.toDouble()
|
||||
for (step in 1..n) {
|
||||
try {
|
||||
Pause.pause(pauseConstMs)
|
||||
}
|
||||
catch (e: InterruptedException) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
|
||||
basicRobot.moveMouse(
|
||||
(start.x + dx * ((Math.log(1.0 * step / n) - Math.log(1.0 / n)) * n / (0 - Math.log(1.0 / n)))).toInt(),
|
||||
(start.y + dy * ((Math.log(1.0 * step / n) - Math.log(1.0 / n)) * n / (0 - Math.log(1.0 / n)))).toInt())
|
||||
}
|
||||
basicRobot.moveMouse(x, y)
|
||||
}
|
||||
|
||||
//smooth mouse move to component
|
||||
override fun moveMouse(c: Component, x: Int, y: Int) {
|
||||
moveMouseWithAttempts(c, x, y)
|
||||
}
|
||||
|
||||
//smooth mouse move for find and click actions
|
||||
override fun click(c: Component, where: Point, button: MouseButton, times: Int) {
|
||||
moveMouseAndClick(c, where, button, times)
|
||||
}
|
||||
|
||||
//we are replacing BasicRobot click with our click because the original one cannot handle double click rightly (BasicRobot creates unnecessary move event between click event which breaks clickCount from 2 to 1)
|
||||
override fun click(where: Point, button: MouseButton, times: Int) {
|
||||
moveMouseAndClick(null, where, button, times)
|
||||
}
|
||||
|
||||
private fun moveMouseAndClick(c: Component? = null, where: Point, button: MouseButton, times: Int) {
|
||||
if (c != null) moveMouse(c, where.x, where.y) else moveMouse(where.x, where.y)
|
||||
//pause between moving cursor and performing a click.
|
||||
Pause.pause(waitConst)
|
||||
myEdtAwareClick(button, times, where, c)
|
||||
}
|
||||
|
||||
|
||||
private fun moveMouseWithAttempts(c: Component, x: Int, y: Int, attempts: Int = 3) {
|
||||
if (attempts == 0) return
|
||||
waitFor { c.isShowing }
|
||||
|
||||
val componentLocation: Point = requireNotNull(performOnEdt { AWT.translate(c, x, y) })
|
||||
moveMouse(componentLocation.x, componentLocation.y)
|
||||
|
||||
val componentLocationAfterMove: Point = requireNotNull(performOnEdt { AWT.translate(c, x, y) })
|
||||
val mouseLocation = MouseInfo.getPointerInfo().location
|
||||
if (mouseLocation.x != componentLocationAfterMove.x || mouseLocation.y != componentLocationAfterMove.y)
|
||||
moveMouseWithAttempts(c, x, y, attempts - 1)
|
||||
}
|
||||
|
||||
private fun myInnerClick(button: MouseButton, times: Int, point: Point, component: Component?) {
|
||||
if (component == null)
|
||||
basicRobot.click(point, button, times)
|
||||
else
|
||||
basicRobot.click(component, point, button, times)
|
||||
}
|
||||
|
||||
private fun waitFor(condition: () -> Boolean) {
|
||||
val timeout = 5000 //5 sec
|
||||
val cdl = CountDownLatch(1)
|
||||
val executor = ConcurrencyUtil.newSingleScheduledThreadExecutor("SmartWaitRobot").scheduleWithFixedDelay(Runnable {
|
||||
if (condition()) {
|
||||
cdl.countDown()
|
||||
}
|
||||
}, 0, 100, TimeUnit.MILLISECONDS)
|
||||
cdl.await(timeout.toLong(), TimeUnit.MILLISECONDS)
|
||||
executor.cancel(true)
|
||||
}
|
||||
|
||||
private fun myEdtAwareClick(button: MouseButton, times: Int, point: Point, component: Component?) {
|
||||
awareClick {
|
||||
myInnerClick(button, times, point, component)
|
||||
}
|
||||
waitForIdle()
|
||||
}
|
||||
|
||||
private fun <T> performOnEdt(body: () -> T): T? =
|
||||
GuiActionRunner.execute(object : GuiQuery<T>() {
|
||||
override fun executeInEDT() = body.invoke()
|
||||
})
|
||||
|
||||
private fun awareClick(body: () -> Unit) {
|
||||
myAwareClick = true
|
||||
body.invoke()
|
||||
myAwareClick = false
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,11 +1,11 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package training.ui
|
||||
|
||||
import org.fest.swing.core.BasicRobot
|
||||
import org.fest.swing.core.GenericTypeMatcher
|
||||
import org.fest.swing.core.Robot
|
||||
import org.fest.swing.exception.ComponentLookupException
|
||||
import org.fest.swing.exception.WaitTimedOutError
|
||||
import org.fest.swing.fixture.ContainerFixture
|
||||
import org.fest.swing.timing.Condition
|
||||
import org.fest.swing.timing.Pause
|
||||
import org.fest.swing.timing.Timeout
|
||||
@@ -18,7 +18,8 @@ import java.util.concurrent.atomic.AtomicReference
|
||||
object LearningUiUtil {
|
||||
@Volatile
|
||||
private var myRobot: Robot? = null
|
||||
private val robot: Robot
|
||||
|
||||
val robot: Robot
|
||||
get() {
|
||||
if(myRobot == null)
|
||||
synchronized(this) {
|
||||
@@ -29,7 +30,7 @@ object LearningUiUtil {
|
||||
|
||||
private fun initializeRobot() {
|
||||
if (myRobot != null) releaseRobot()
|
||||
myRobot = BasicRobot.robotWithCurrentAwtHierarchyWithoutScreenLock() // acquires ScreenLock
|
||||
myRobot = IftSmartWaitRobot()
|
||||
}
|
||||
|
||||
private fun releaseRobot() {
|
||||
@@ -119,4 +120,23 @@ object LearningUiUtil {
|
||||
"Unable to find ${componentClass.simpleName} ${if (container != null) "in container $container" else ""} in ${timeout.duration()}(ms)")
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* function to find component of returning type inside a container (gets from receiver).
|
||||
*
|
||||
* @throws ComponentLookupException if desired component haven't been found under the container (gets from receiver) in specified timeout
|
||||
*/
|
||||
inline fun <reified ComponentType : Component, ContainerComponentType : Container> ContainerFixture<ContainerComponentType>?.findComponentWithTimeout(
|
||||
timeout: Timeout = Timeout.timeout(10, TimeUnit.SECONDS),
|
||||
crossinline finderFunction: (ComponentType) -> Boolean = { true }): ComponentType {
|
||||
try {
|
||||
return waitUntilFound(robot, this?.target() as Container?,
|
||||
typeMatcher(ComponentType::class.java) { finderFunction(it) },
|
||||
timeout)
|
||||
}
|
||||
catch (e: WaitTimedOutError) {
|
||||
throw ComponentLookupException(
|
||||
"Unable to find ${ComponentType::class.java.name} ${if (this?.target() != null) "in container ${this.target()}" else ""} in ${timeout.duration()}")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -39,8 +39,7 @@ class LearningLessonsAutoExecutor(val project: Project, private val progress: Pr
|
||||
val lessons = CourseManager.instance.lessonsForModules
|
||||
|
||||
for (lesson in lessons) {
|
||||
if (lesson !is KLesson) continue
|
||||
if (lesson.testScriptProperties.skipTesting) continue
|
||||
if (lesson !is KLesson || lesson.testScriptProperties.skipTesting) continue
|
||||
progress.checkCanceled()
|
||||
runSingleLesson(lesson)
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<orderEntry type="module" module-name="intellij.featuresTrainer" />
|
||||
<orderEntry type="module" module-name="intellij.platform.ide.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger" />
|
||||
<orderEntry type="module" module-name="intellij.platform.testGuiFramework" />
|
||||
<orderEntry type="module" module-name="intellij.platform.lang.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core.ui" />
|
||||
</component>
|
||||
</module>
|
||||
|
||||
+1
-3
@@ -1,8 +1,6 @@
|
||||
// Copyright 2000-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.ift.lesson.completion
|
||||
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.jetbrains.python.ift.PythonLessonsBundle
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonUtil.checkExpectedStateOfEditor
|
||||
@@ -74,7 +72,7 @@ class FStringCompletionLesson
|
||||
stateCheck {
|
||||
editor.document.text == result
|
||||
}
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test(waitEditorToBeReady = false) { invokeActionViaShortcut("ENTER") }
|
||||
}
|
||||
text(PythonLessonsBundle.message("python.f.string.completion.result.message"))
|
||||
}
|
||||
|
||||
+2
-6
@@ -1,8 +1,6 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.ift.lesson.completion
|
||||
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil.typeText
|
||||
import com.intellij.testGuiFramework.impl.jList
|
||||
import com.jetbrains.python.ift.PythonLessonsBundle
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonUtil.checkExpectedStateOfEditor
|
||||
@@ -48,16 +46,14 @@ class PythonPostfixCompletionLesson
|
||||
checkExpectedStateOfEditor(sample) { completionSuffix.startsWith(it) }
|
||||
}
|
||||
test {
|
||||
ideFrame {
|
||||
typeText(completionSuffix)
|
||||
}
|
||||
type(completionSuffix)
|
||||
}
|
||||
}
|
||||
task {
|
||||
text(PythonLessonsBundle.message("python.postfix.completion.select.item", code(completionSuffix)))
|
||||
stateCheck { editor.document.text == result }
|
||||
restoreByUi()
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
ideFrame {
|
||||
jList("ifnn").item(0).doubleClick()
|
||||
}
|
||||
|
||||
+1
-3
@@ -1,8 +1,6 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.jetbrains.python.ift.lesson.completion
|
||||
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.jetbrains.python.ift.PythonLessonsBundle
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.checkExpectedStateOfEditor
|
||||
@@ -67,7 +65,7 @@ class PythonTabCompletionLesson
|
||||
restoreAfterStateBecomeFalse {
|
||||
selectNeededItem()?.not() ?: true
|
||||
}
|
||||
test { GuiTestUtil.shortcut(Key.TAB) }
|
||||
test { invokeActionViaShortcut("TAB") }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-7
@@ -2,8 +2,6 @@
|
||||
package com.jetbrains.python.ift.lesson.refactorings
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.jetbrains.python.ift.PythonLessonsBundle
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.checkExpectedStateOfEditor
|
||||
@@ -70,7 +68,7 @@ class PythonInPlaceRefactoringLesson
|
||||
val expected = template.replace("<caret>", "").replace("<name>", newName)
|
||||
newName != variableName && editor.document.text == expected
|
||||
}
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test { invokeActionViaShortcut("ENTER") }
|
||||
}
|
||||
|
||||
waitBeforeContinue(500)
|
||||
@@ -127,7 +125,7 @@ class PythonInPlaceRefactoringLesson
|
||||
ui.javaClass.name.contains("ChangeSignaturePopup")
|
||||
}
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test { invokeActionViaShortcut("ENTER") }
|
||||
}
|
||||
|
||||
task {
|
||||
@@ -138,7 +136,7 @@ class PythonInPlaceRefactoringLesson
|
||||
restoreAfterStateBecomeFalse(restoreId = showIntentionsTaskId) {
|
||||
previous.ui?.isShowing != true
|
||||
}
|
||||
test { GuiTestUtil.shortcut(Key.ENTER) }
|
||||
test(waitEditorToBeReady = false) { invokeActionViaShortcut("ENTER") }
|
||||
}
|
||||
task {
|
||||
lateinit var beforeSecondRefactoring: String
|
||||
@@ -152,9 +150,9 @@ class PythonInPlaceRefactoringLesson
|
||||
it.className.contains("PySuggestedRefactoringExecution") && it.methodName == "performChangeSignature"
|
||||
}
|
||||
}
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
type("0")
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
text(PythonLessonsBundle.message("python.in.place.refactoring.remark.about.application.scope"))
|
||||
|
||||
+9
-14
@@ -6,9 +6,6 @@ import com.intellij.openapi.application.invokeAndWaitIfNeeded
|
||||
import com.intellij.openapi.editor.impl.EditorComponentImpl
|
||||
import com.intellij.openapi.wm.IdeFocusManager
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.table.JBTableRow
|
||||
import com.jetbrains.python.PyBundle
|
||||
@@ -51,7 +48,7 @@ class PythonQuickFixesRefactoringLesson
|
||||
restoreState {
|
||||
!editor.document.text.contains(it)
|
||||
}
|
||||
test { GuiTestUtil.shortcut(Key.ESCAPE) }
|
||||
test(waitEditorToBeReady = false) { invokeActionViaShortcut("ESCAPE") }
|
||||
}
|
||||
|
||||
prepareRuntimeTask { // restore point
|
||||
@@ -86,7 +83,7 @@ class PythonQuickFixesRefactoringLesson
|
||||
else null
|
||||
}
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
ideFrame {
|
||||
jListContains(quickFixItemText).clickItem(Pattern.compile(".*$quickFixItemText.*"))
|
||||
}
|
||||
@@ -103,12 +100,12 @@ class PythonQuickFixesRefactoringLesson
|
||||
UIUtil.getParentOfType(JDialog::class.java, editor) != null
|
||||
}
|
||||
restoreByUi()
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
invokeAndWaitIfNeeded(ModalityState.any()) {
|
||||
val ui = previous.ui ?: return@invokeAndWaitIfNeeded
|
||||
IdeFocusManager.getInstance(project).requestFocus(ui, true)
|
||||
}
|
||||
GuiTestUtil.shortcut(Key.ENTER)
|
||||
invokeActionViaShortcut("ENTER")
|
||||
}
|
||||
}
|
||||
task {
|
||||
@@ -118,12 +115,12 @@ class PythonQuickFixesRefactoringLesson
|
||||
stateCheck {
|
||||
(previous.ui as? EditorComponentImpl)?.text == "0"
|
||||
}
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
invokeAndWaitIfNeeded(ModalityState.any()) {
|
||||
val ui = previous.ui ?: return@invokeAndWaitIfNeeded
|
||||
IdeFocusManager.getInstance(project).requestFocus(ui, true)
|
||||
}
|
||||
GuiTestUtil.shortcut(Key.BACK_SPACE)
|
||||
invokeActionViaShortcut("BACK_SPACE")
|
||||
type("0")
|
||||
}
|
||||
}
|
||||
@@ -144,11 +141,9 @@ class PythonQuickFixesRefactoringLesson
|
||||
!stackInsideDialogRefactoring()
|
||||
}
|
||||
|
||||
test {
|
||||
with(TaskTestContext.guiTestCase) {
|
||||
dialog("Change Signature") {
|
||||
button("Refactor").click()
|
||||
}
|
||||
test(waitEditorToBeReady = false) {
|
||||
dialog("Change Signature") {
|
||||
button("Refactor").click()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+18
-22
@@ -3,17 +3,12 @@ package com.jetbrains.python.ift.lesson.refactorings
|
||||
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.ide.actions.exclusion.ExclusionHandler
|
||||
import com.intellij.openapi.application.runReadAction
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.testGuiFramework.framework.GuiTestUtil
|
||||
import com.intellij.testGuiFramework.framework.Timeouts
|
||||
import com.intellij.testGuiFramework.impl.button
|
||||
import com.intellij.testGuiFramework.impl.jTree
|
||||
import com.intellij.testGuiFramework.util.Key
|
||||
import com.intellij.ui.tree.TreeVisitor
|
||||
import com.intellij.usageView.UsageViewBundle
|
||||
import com.intellij.util.ui.tree.TreeUtil
|
||||
import com.jetbrains.python.ift.PythonLessonsBundle
|
||||
import org.fest.swing.fixture.JTreeFixture
|
||||
import org.jetbrains.annotations.Nullable
|
||||
import training.dsl.*
|
||||
import training.learn.LessonsBundle
|
||||
@@ -24,6 +19,7 @@ import javax.swing.JTree
|
||||
import javax.swing.tree.TreePath
|
||||
|
||||
class PythonRenameLesson : KLesson("Rename", LessonsBundle.message("rename.lesson.name")) {
|
||||
override val testScriptProperties = TaskTestContext.TestScriptProperties(10)
|
||||
private val template = """
|
||||
class Championship:
|
||||
def __init__(self):
|
||||
@@ -55,9 +51,6 @@ class PythonRenameLesson : KLesson("Rename", LessonsBundle.message("rename.lesso
|
||||
print(c.<name>)
|
||||
""".trimIndent() + '\n'
|
||||
|
||||
/** For test only */
|
||||
private val substringPredicate: (String, String) -> Boolean = { found: String, wanted: String -> found.contains(wanted) }
|
||||
|
||||
private val sample = parseLessonSample(template.replace("<name>", "teams"))
|
||||
|
||||
private val replacePreviewPattern = Pattern.compile(".*Variable to be renamed to (\\w+).*")
|
||||
@@ -66,10 +59,13 @@ class PythonRenameLesson : KLesson("Rename", LessonsBundle.message("rename.lesso
|
||||
prepareSample(sample)
|
||||
val dynamicWord = UsageViewBundle.message("usage.view.results.node.dynamic")
|
||||
var replace: String? = null
|
||||
var dynamicItem: String? = null
|
||||
task("RenameElement") {
|
||||
text(PythonLessonsBundle.message("python.rename.press.rename", action(it), code("teams"), code("teams_number")))
|
||||
triggerByFoundPathAndHighlight { tree: JTree, path: TreePath ->
|
||||
if (path.pathCount == 2 && path.getPathComponent(1).toString().contains(dynamicWord)) {
|
||||
val pathStr = path.getPathComponent(1).toString()
|
||||
if (path.pathCount == 2 && pathStr.contains(dynamicWord)) {
|
||||
dynamicItem = pathStr
|
||||
replace = replacePreviewPattern.matcher(tree.model.root.toString()).takeIf { m -> m.find() }?.group(1)
|
||||
true
|
||||
}
|
||||
@@ -77,11 +73,9 @@ class PythonRenameLesson : KLesson("Rename", LessonsBundle.message("rename.lesso
|
||||
}
|
||||
test {
|
||||
actions(it)
|
||||
with(TaskTestContext.guiTestCase) {
|
||||
dialog {
|
||||
typeText("teams_number")
|
||||
button("Refactor").click()
|
||||
}
|
||||
dialog {
|
||||
type("teams_number")
|
||||
button("Refactor").click()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -102,12 +96,14 @@ class PythonRenameLesson : KLesson("Rename", LessonsBundle.message("rename.lesso
|
||||
}
|
||||
showWarningIfFindToolbarClosed()
|
||||
test {
|
||||
//TODO: Fix tree access
|
||||
val jTree = previous.ui as? JTree ?: return@test
|
||||
val di = dynamicItem ?: return@test
|
||||
ideFrame {
|
||||
val jTree = runReadAction {
|
||||
jTree(dynamicReferencesString, timeout = Timeouts.seconds03, predicate = substringPredicate)
|
||||
}
|
||||
val jTreeFixture = JTreeFixture(robot, jTree)
|
||||
jTreeFixture.replaceSeparator("@@@")
|
||||
jTreeFixture.expandPath(di)
|
||||
// WARNING: several exception will be here because of UsageNode#toString inside info output during this operation
|
||||
jTree.doubleClickPath()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,8 +127,8 @@ class PythonRenameLesson : KLesson("Rename", LessonsBundle.message("rename.lesso
|
||||
showWarningIfFindToolbarClosed()
|
||||
test {
|
||||
ideFrame {
|
||||
type("co_me")
|
||||
GuiTestUtil.shortcut(Key.DELETE)
|
||||
type("come")
|
||||
invokeActionViaShortcut("DELETE")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -149,7 +145,7 @@ class PythonRenameLesson : KLesson("Rename", LessonsBundle.message("rename.lesso
|
||||
text(PythonLessonsBundle.message("python.rename.finish.refactoring", strong(confirmRefactoringButton)))
|
||||
stateCheck { editor.document.text == result }
|
||||
showWarningIfFindToolbarClosed()
|
||||
test {
|
||||
test(waitEditorToBeReady = false) {
|
||||
ideFrame {
|
||||
button(confirmRefactoringButton).click()
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user