diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/driver/ExtendedJTreeDriver.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/driver/ExtendedJTreeDriver.kt index b490ec8bed38..9f2e2372602a 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/driver/ExtendedJTreeDriver.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/driver/ExtendedJTreeDriver.kt @@ -9,9 +9,9 @@ import com.intellij.testGuiFramework.impl.GuiRobotHolder import com.intellij.testGuiFramework.impl.GuiTestUtilKt import org.fest.assertions.Assertions import org.fest.reflect.core.Reflection -import org.fest.swing.cell.JTreeCellReader import org.fest.swing.core.MouseButton import org.fest.swing.core.MouseClickInfo +import org.fest.swing.core.Robot import org.fest.swing.driver.ComponentPreconditions import org.fest.swing.driver.JTreeDriver import org.fest.swing.driver.JTreeLocation @@ -22,10 +22,10 @@ import org.fest.swing.util.Pair import org.fest.swing.util.Triple import java.awt.Point import java.awt.Rectangle -import org.fest.swing.core.Robot import javax.swing.JPopupMenu import javax.swing.JTree import javax.swing.plaf.basic.BasicTreeUI +import javax.swing.tree.DefaultMutableTreeNode import javax.swing.tree.TreePath open class ExtendedJTreeDriver(robot: Robot = GuiRobotHolder.robot) : JTreeDriver(robot) { @@ -33,14 +33,13 @@ open class ExtendedJTreeDriver(robot: Robot = GuiRobotHolder.robot) : JTreeDrive // TODO: check can we remove jTreeLocation and use local variables instead of private val jTreeLocation = JTreeLocation() - private fun JTree.getCellReader(): JTreeCellReader { + init { val resultReader = when (javaClass.name) { "com.intellij.openapi.options.newEditor.SettingsTreeView\$MyTree" -> SettingsTreeCellReader() "com.intellij.ide.projectView.impl.ProjectViewPane\$1" -> ProjectTreeCellReader() else -> ExtendedJTreeCellReader() } - replaceCellReader(resultReader) - return resultReader + this.replaceCellReader(resultReader) } fun clickPath(tree: JTree, treePath: TreePath, mouseClickInfo: MouseClickInfo): Unit = @@ -150,7 +149,8 @@ open class ExtendedJTreeDriver(robot: Robot = GuiRobotHolder.robot) : JTreeDrive /** * node that has as child LoadingNode */ - class LoadingNodeException(val node: Any, var treePath: TreePath?) : Exception("Meet loading node: $node") + class LoadingNodeException(val node: Any, var treePath: TreePath?) : + Exception("Meet loading node: $node (${treePath?.path?.joinToString()}") private fun JTree.childCount(path: TreePath): Int { return GuiTestUtilKt.computeOnEdt { @@ -168,11 +168,15 @@ open class ExtendedJTreeDriver(robot: Robot = GuiRobotHolder.robot) : JTreeDrive } fun expandPath(tree: JTree, treePath: TreePath) { + // do not try to expand leaf + if(tree.model.isLeaf(treePath.lastPathComponent)) return val info = tree.scrollToMatchingPathAndGetToggleInfo(treePath) if (!info.first) tree.toggleCell(info.second!!, info.third) } fun collapsePath(tree: JTree, treePath: TreePath) { + // do not try to collapse leaf + if(tree.model.isLeaf(treePath.lastPathComponent)) return val info = tree.scrollToMatchingPathAndGetToggleInfo(treePath) if (info.first) tree.toggleCell(info.second!!, info.third) } @@ -238,16 +242,45 @@ open class ExtendedJTreeDriver(robot: Robot = GuiRobotHolder.robot) : JTreeDrive drop(tree, tree.scrollToMatchingPath(treePath).second!!) } - fun getPathStrings(tree: JTree, path: TreePath) : List{ - var myPath = path - val result = mutableListOf() - while (myPath.pathCount != 1 || (tree.isRootVisible && myPath.pathCount == 1)) { - val valueAt = tree.getCellReader().valueAt(tree, myPath.lastPathComponent) ?: "null" - result.add(0, valueAt) - if (myPath.pathCount == 1) break - else myPath = myPath.parentPath + fun findPath(tree: JTree, stringPath: List, predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality): TreePath { + fun List.list2tree() = map { subList(0, indexOf(it) + 1) } + lateinit var path: TreePath + stringPath + .list2tree() + .forEach { + path = ExtendedJTreePathFinder(tree) + .findMatchingPathByPredicate(predicate = predicate, pathStrings = *it.toTypedArray()) + expandPath(tree, path) + } + return path + } + + fun findPathToNode(tree: JTree, node: String, predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality): TreePath { + val result: MutableList = mutableListOf() + var currentNode = tree.model.root as DefaultMutableTreeNode + val e = currentNode.preorderEnumeration() + while (e.hasMoreElements()) { + currentNode = e.nextElement() as DefaultMutableTreeNode + if (predicate(currentNode.toString(), node)) { + break + } + } + result.add(0, currentNode.toString()) + while (currentNode.parent != null) { + currentNode = currentNode.parent as DefaultMutableTreeNode + result.add(0, currentNode.toString()) + } + return findPath(tree, result, predicate) + } + + fun exists(tree: JTree, pathStrings: List, predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality): Boolean { + return try { + findPath(tree, pathStrings, predicate) + true + } + catch (e: LocationUnavailableException){ + false } - return result.toList() } } // end of class diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/fixtures/CheckboxTreeFixture.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/fixtures/CheckboxTreeFixture.kt index 1ad9acce37cd..b9d9015916a9 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/fixtures/CheckboxTreeFixture.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/fixtures/CheckboxTreeFixture.kt @@ -2,19 +2,21 @@ package com.intellij.testGuiFramework.fixtures import com.intellij.testGuiFramework.driver.CheckboxTreeDriver +import com.intellij.testGuiFramework.driver.ExtendedJTreePathFinder +import com.intellij.testGuiFramework.driver.FinderPredicate import com.intellij.testGuiFramework.fixtures.extended.ExtendedJTreePathFixture import com.intellij.testGuiFramework.framework.GuiTestUtil import com.intellij.testGuiFramework.impl.GuiRobotHolder import com.intellij.ui.CheckboxTree import org.fest.swing.core.Robot -import javax.swing.tree.TreePath class CheckboxTreeFixture( checkboxTree: CheckboxTree, - path: TreePath, + stringPath: List, + predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality, robot: Robot = GuiRobotHolder.robot, private val myDriver: CheckboxTreeDriver = CheckboxTreeDriver(robot) -) : ExtendedJTreePathFixture(checkboxTree, path, robot) { +) : ExtendedJTreePathFixture(checkboxTree, stringPath, predicate, robot, myDriver) { init { this.replaceDriverWith(myDriver) diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/fixtures/extended/ExtendedJTreePathFixture.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/fixtures/extended/ExtendedJTreePathFixture.kt index 2dbfb8ea9589..add236f98f9e 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/fixtures/extended/ExtendedJTreePathFixture.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/fixtures/extended/ExtendedJTreePathFixture.kt @@ -1,9 +1,10 @@ -// Copyright 2000-2018 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. -// Copyright 2000-2018 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. + // Copyright 2000-2018 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.intellij.testGuiFramework.fixtures.extended import com.intellij.testGuiFramework.driver.ExtendedJTreeDriver import com.intellij.testGuiFramework.driver.ExtendedJTreePathFinder +import com.intellij.testGuiFramework.driver.FinderPredicate +import com.intellij.testGuiFramework.framework.GuiTestUtil import com.intellij.testGuiFramework.impl.GuiRobotHolder import org.fest.swing.core.MouseButton import org.fest.swing.core.MouseClickInfo @@ -12,20 +13,33 @@ import org.fest.swing.exception.LocationUnavailableException import org.fest.swing.fixture.JTreeFixture import javax.swing.JPopupMenu import javax.swing.JTree -import javax.swing.tree.DefaultMutableTreeNode import javax.swing.tree.TreePath open class ExtendedJTreePathFixture( val tree: JTree, - val path: TreePath, + private val stringPath: List, + private val predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality, robot: Robot = GuiRobotHolder.robot, private val myDriver: ExtendedJTreeDriver = ExtendedJTreeDriver(robot) ) : JTreeFixture(robot, tree) { + constructor(tree: JTree, path: TreePath, predicate: FinderPredicate, robot: Robot, driver: ExtendedJTreeDriver) : + this(tree, path.path.map { it.toString() }.toList(), predicate, robot, driver) + init { replaceDriverWith(myDriver) } + private val cachePaths = mutableMapOf, TreePath>() + + protected val path: TreePath + get() { + return if (!cachePaths.containsKey(stringPath)) + expandAndGetPathStepByStep(stringPath) + else + cachePaths.getValue(stringPath) + } + /** * Create a new object of ExtendedJTreePathFixture for a new path * It's supposed the new path in the same tree @@ -33,8 +47,7 @@ open class ExtendedJTreePathFixture( * @throws LocationUnavailableException if path not found * */ fun path(vararg pathStrings: String): ExtendedJTreePathFixture { - val newPath = ExtendedJTreePathFinder(tree).findMatchingPath(*pathStrings) - return ExtendedJTreePathFixture(tree, newPath, robot(), myDriver) + return ExtendedJTreePathFixture(tree, pathStrings.toList(), predicate, robot(), myDriver) } /** @@ -43,13 +56,25 @@ open class ExtendedJTreePathFixture( * It's supposed the new path in the same tree * @param node one node value somewhere whithin the same tree * @throws LocationUnavailableException if node not found + * TODO complete * */ fun pathToNode(node: String): ExtendedJTreePathFixture{ - val newPath = ExtendedJTreePathFinder(tree).findPathToNode(node) - return ExtendedJTreePathFixture(tree, newPath, robot(), myDriver) + val newPath = myDriver.findPathToNode(tree, node, predicate) + return ExtendedJTreePathFixture(tree, newPath, predicate, robot(), myDriver) } - fun hasPath(vararg pathStrings: String):Boolean = ExtendedJTreePathFinder(tree).exists(*pathStrings) + fun hasPath(vararg pathStrings: String): Boolean = + ExtendedJTreePathFixture(tree, pathStrings.toList(), predicate, robot(), myDriver).hasPath() + + fun hasPath(): Boolean { + return try { + path + true + } + catch (e: Exception) { + false + } + } fun clickPath(mouseClickInfo: MouseClickInfo): Unit = myDriver.clickPath(tree, path, mouseClickInfo) @@ -60,7 +85,31 @@ open class ExtendedJTreePathFixture( fun rightClickPath(): Unit = myDriver.rightClickPath(tree, path) - fun expandPath(): Unit = myDriver.expandPath(tree, path) + fun expandPath() { + myDriver.expandPath(tree, path) + } + + protected fun expandAndGetPathStepByStep(stringPath: List): TreePath { + fun List.list2tree() = map { subList(0, indexOf(it) + 1) } + if (!cachePaths.containsKey(stringPath)){ + var partialPath: TreePath? = null + for (partialList in stringPath.list2tree()) { + GuiTestUtil.pause(condition = "wait to find a correct path to click", timeoutSeconds = 2L) { + try { + partialPath = ExtendedJTreePathFinder(tree) + .findMatchingPathByPredicate(predicate = predicate, pathStrings = *partialList.toTypedArray()) + partialPath != null + } + catch (e: Exception) { + false + } + } + cachePaths[partialList] = partialPath!! + myDriver.expandPath(tree, cachePaths.getValue(partialList)) + } + } + return cachePaths.getValue(stringPath) + } fun collapsePath(): Unit = myDriver.collapsePath(tree, path) @@ -73,5 +122,5 @@ open class ExtendedJTreePathFixture( fun drop(): Unit = myDriver.drop(tree, path) - fun getPathStrings(): List = myDriver.getPathStrings(tree, path) + fun getPathStrings(): List = path.path.map { it.toString() } } \ No newline at end of file diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestUtil.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestUtil.kt index d90bb0771745..e30e576e58d4 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestUtil.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/framework/GuiTestUtil.kt @@ -742,10 +742,10 @@ object GuiTestUtil { return JTextComponentFixture(GuiRobotHolder.robot, jTextComponent) } - fun jTreePath(container: Container, - timeout: Long, - vararg pathStrings: String, - predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality): ExtendedJTreePathFixture { + fun jTreeComponent(container: Container, + timeout: Long, + vararg pathStrings: String, + predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality): JTree { val myTree: JTree? try { myTree = if (pathStrings.isEmpty()) { @@ -754,7 +754,7 @@ object GuiTestUtil { else { waitUntilFound(GuiRobotHolder.robot, container, GuiTestUtilKt.typeMatcher(JTree::class.java) { - ExtendedJTreePathFinder(it).existsByPredicate(pathStrings = *pathStrings, predicate = predicate) + ExtendedJTreePathFixture(it, pathStrings.toList(), predicate).hasPath() }, timeout.toFestTimeout()) } @@ -762,8 +762,7 @@ object GuiTestUtil { catch (e: WaitTimedOutError) { throw ComponentLookupException("""JTree "${if (pathStrings.isNotEmpty()) "by path ${pathStrings.joinToString()}" else ""}"""") } - return ExtendedJTreePathFixture(myTree, ExtendedJTreePathFinder(myTree) - .findMatchingPathByPredicate(pathStrings = *pathStrings, predicate = predicate)) + return myTree } //*********COMMON FUNCTIONS WITHOUT CONTEXT diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ComponentFixtureUtils.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ComponentFixtureUtils.kt index 75072a7bf29d..0492d56586e5 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ComponentFixtureUtils.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/ComponentFixtureUtils.kt @@ -263,12 +263,12 @@ fun ComponentFixture.jTree( timeout: Long = defaultTimeout, predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality ): ExtendedJTreePathFixture = - if (target() is Container) GuiTestUtil.jTreePath( + if (target() is Container) ExtendedJTreePathFixture(GuiTestUtil.jTreeComponent( container = target() as Container, timeout = timeout, pathStrings = *pathStrings, predicate = predicate - ) + ), pathStrings.toList(), predicate) else throw unableToFindComponent("""JTree "${if (pathStrings.isNotEmpty()) "by path $pathStrings" else ""}"""") /** @@ -284,14 +284,13 @@ fun ComponentFixture.checkboxTree( predicate: FinderPredicate = ExtendedJTreePathFinder.predicateEquality ): CheckboxTreeFixture = if (target() is Container) { - val extendedJTreePathFixture = GuiTestUtil.jTreePath( + val tree = GuiTestUtil.jTreeComponent( container = target() as Container, timeout = timeout, predicate = predicate, pathStrings = *pathStrings - ) - if (extendedJTreePathFixture.tree !is CheckboxTree) throw ComponentLookupException("Found JTree but not a CheckboxTree") - CheckboxTreeFixture(extendedJTreePathFixture.tree, extendedJTreePathFixture.path, robot()) + ) as? CheckboxTree ?: throw ComponentLookupException("Found JTree but not a CheckboxTree") + CheckboxTreeFixture(tree, pathStrings.toList(), predicate, robot()) } else throw unableToFindComponent("""CheckboxTree "${if (pathStrings.isNotEmpty()) "by path ${pathStrings.joinToString()}" else ""}"""") diff --git a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestCaseExt.kt b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestCaseExt.kt index 26b92ff67151..3e98ba9d40f4 100644 --- a/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestCaseExt.kt +++ b/platform/testGuiFramework/src/com/intellij/testGuiFramework/impl/GuiTestCaseExt.kt @@ -108,6 +108,14 @@ fun GuiTestCase.testTreeItemExist(name: String, vararg expectedItem: String) { } } +/** + * Selects specified [path] in the tree by keyboard searching + * @param path in string form + * @param testCase - test case is required only because of keyboard related functions + * + * TODO: remove [testCase] parameter (so move [shortcut] and [typeText] functions + * out of GuiTestCase) + * */ fun ExtendedJTreePathFixture.selectWithKeyboard(testCase: GuiTestCase, vararg path: String) { fun currentValue(): String { val selectedRow = target().selectionRows.first()