[gui-test] move ProjectViewFixture to use ExtendedCellReader without direct reading nodes value

This commit is contained in:
Sergey Karashevich
2018-08-20 14:30:18 +03:00
parent a35df5cf24
commit 522e09075d
3 changed files with 112 additions and 39 deletions
@@ -28,32 +28,38 @@ import com.intellij.openapi.projectRoots.JavaSdk
import com.intellij.openapi.roots.JdkOrderEntry
import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.text.StringUtil
import com.intellij.testGuiFramework.cellReader.ExtendedJTreeCellReader
import com.intellij.testGuiFramework.framework.Timeouts
import com.intellij.testGuiFramework.impl.GuiTestUtilKt
import com.intellij.testGuiFramework.impl.GuiTestUtilKt.computeOnEdt
import com.intellij.testGuiFramework.impl.GuiTestUtilKt.runOnEdt
import com.intellij.testGuiFramework.impl.GuiTestUtilKt.tryWithPause
import com.intellij.testGuiFramework.impl.GuiTestUtilKt.waitUntil
import com.intellij.testGuiFramework.impl.GuiTestUtilKt.withPauseWhenNull
import com.intellij.ui.LoadingNode
import com.intellij.util.ui.tree.TreeUtil
import org.fest.assertions.Assertions.assertThat
import org.fest.reflect.core.Reflection.field
import org.fest.swing.cell.JTreeCellReader
import org.fest.swing.core.MouseButton
import org.fest.swing.core.Robot
import org.fest.swing.edt.GuiActionRunner
import org.fest.swing.edt.GuiTask
import org.fest.swing.exception.WaitTimedOutError
import org.fest.swing.exception.ComponentLookupException
import org.junit.Assert.assertNotNull
import java.awt.Point
import java.awt.Rectangle
import java.util.*
import javax.swing.JTree
import javax.swing.tree.DefaultMutableTreeNode
import javax.swing.tree.TreeModel
import javax.swing.tree.TreePath
import kotlin.collections.ArrayList
class ProjectViewFixture internal constructor(project: Project, robot: Robot) : ToolWindowFixture("Project", project, robot) {
val nodeReader: JTreeCellReader = ExtendedJTreeCellReader()
private fun selectProjectPane(): PaneFixture = getPaneById("ProjectPane")
fun selectAndroidPane(): PaneFixture = getPaneById("AndroidView")
@@ -78,41 +84,22 @@ class ProjectViewFixture internal constructor(project: Project, robot: Robot) :
* @return NodeFixture object for a pathTo; may be used for expanding, scrolling and clicking node
*/
fun path(vararg pathTo: String): NodeFixture {
selectProjectPane()
val times = 3
repeat(times) {
val nodeFixture = getNodeFixture(pathTo)
if (nodeFixture != null) return nodeFixture
}
throw Exception("Unable to find path: ${Arrays.toString(pathTo)} for current project structure in $times times.")
}
private fun getNodeFixture(pathTo: Array<out String>): NodeFixture? {
return try {
withPauseWhenNull(timeout = Timeouts.seconds30) {
try {
getNodeFixtureByPath(pathTo as Array<String>)
}
catch (e: Exception) {
LOG.debug("Exception during getting node by path (${Arrays.toString(pathTo)}) : $e")
null
}
}
}
catch (timedOutError: WaitTimedOutError) {
LOG.warn("Unable to find path: ${Arrays.toString(pathTo)} for current project structure.", timedOutError)
null
val projectPane = selectProjectPane()
val canonicalPath = pathTo.toList().expandSlashedPath()
return tryWithPause(exceptionClass = ComponentLookupException::class.java,
condition = "node with path ${Arrays.toString(pathTo)} will appear",
timeout = Timeouts.seconds30) {
activate()
projectPane.getNode(canonicalPath)
}
}
private fun getNodeFixtureByPath(pathTo: Array<String>): NodeFixture? {
if (pathTo.size == 1) {
if (pathTo[0].contains("/")) {
val newPath = pathTo[0].split("/".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
return selectProjectPane().getNode(newPath)
}
private fun List<String>.expandSlashedPath(): List<String> {
return if (this.size == 1 && this[0].contains("/")) {
this[0].split("/".toRegex()).dropLastWhile { it.isEmpty() }
}
return selectProjectPane().getNode(pathTo)
else this
}
@@ -128,9 +115,53 @@ class ProjectViewFixture internal constructor(project: Project, robot: Robot) :
return this
}
fun getNode(path: Array<String>): NodeFixture? {
fun getNode(path: List<String>): NodeFixture {
val tree = myPane.tree
val root = computeOnEdt { myPane.tree.model.root } ?: throw Exception("Unfortunately the root for a tree model in ProjectView is null")
val root = computeOnEdt { myPane.tree.model.root } ?: throw Exception("The root for a tree model in ProjectView is null")
val pathToNode = traverseChildren(tree, TreePath(root), path)
return NodeFixture(pathToNode.lastPathComponent as DefaultMutableTreeNode, pathToNode, myPane)
}
private fun traverseChildren(tree: JTree, treePath: TreePath, path: List<String>): TreePath {
val model = tree.model
val parent = treePath.lastPathComponent
val childCount: Int = computeOnEdt { model.getChildCount(parent) } ?: throw Exception("Unable to calculate children for ${path[0]}")
expandIfLoadingNode(childCount, model, parent, tree, treePath)
for (i in (0 until childCount)){
val child = computeOnEdt { model.getChild(parent, i) }
if (nodeReader.valueAt(tree, child) == path[0]) {
return if (path.size == 1)
treePath.pathByAddingChild(child)
else
traverseChildren(tree, treePath.pathByAddingChild(child), path.drop(1))
}
}
throw ComponentLookupException("Unable to find child with name '${path[0]}'")
}
private fun expandIfLoadingNode(childCount: Int,
model: TreeModel,
parent: Any?,
tree: JTree,
treePath: TreePath) {
if (childCount == 1) {
val singleChild = computeOnEdt { model.getChild(parent, 0) }
if (singleChild is LoadingNode) {
runOnEdt { TreeUtil.selectPath(tree, treePath.pathByAddingChild(singleChild)) }
waitUntil("children will be loaded", Timeouts.seconds30) {
computeOnEdt { model.getChildCount(parent) > 1 || model.getChild(parent, 0) !is LoadingNode }!!
}
}
}
}
//TODO: remove this method
@Deprecated("Because of unreliable logic for reading Nodes", ReplaceWith("getNode1 function"))
fun getNode2(path: Array<String>): NodeFixture? {
val tree = myPane.tree
val root = computeOnEdt { myPane.tree.model.root } ?: throw Exception(
"Unfortunately the root for a tree model in ProjectView is null")
var pivotRoot: Any = root
for (pathItem in path) {
var (childCount, children) = getChildrenAndCountOnEdt(tree, pivotRoot)
@@ -159,6 +190,7 @@ class ProjectViewFixture internal constructor(project: Project, robot: Robot) :
return NodeFixture(pivotRoot as DefaultMutableTreeNode, TreeUtil.getPathFromRoot(pivotRoot), myPane)
}
//TODO: remove this method
private fun getChildrenAndCountOnEdt(tree: JTree,
node: Any): Pair<Int, ArrayList<DefaultMutableTreeNode?>> {
return computeOnEdt {
@@ -213,6 +245,7 @@ class ProjectViewFixture internal constructor(project: Project, robot: Robot) :
fun invokeContextMenu() {
expand()
myRobot.waitForIdle()
myRobot.click(locationOnScreen, MouseButton.RIGHT_BUTTON, 1)
}
@@ -24,3 +24,11 @@ object Timeouts {
}
fun Timeout.toSec(): Long = duration() / 1000
fun Timeout.toPrintable(): String {
return when {
this.duration() > 60000 -> "${this.duration() / 60000}(m)"
this.duration() > 1000 -> "${this.duration() / 1000}(s)"
else -> "${this.duration() / 1000}(ms)"
}
}
@@ -3,8 +3,10 @@ package com.intellij.testGuiFramework.impl
import com.intellij.diagnostic.MessagePool
import com.intellij.openapi.progress.ProgressManager
import com.intellij.openapi.util.Ref
import com.intellij.testGuiFramework.framework.GuiTestUtil
import com.intellij.testGuiFramework.framework.Timeouts
import com.intellij.testGuiFramework.framework.toPrintable
import com.intellij.testGuiFramework.framework.toSec
import com.intellij.ui.EngravedLabel
import org.fest.swing.core.ComponentMatcher
@@ -201,7 +203,9 @@ object GuiTestUtilKt {
*
* @throws WaitTimedOutError with the text: "Timed out waiting for $timeout second(s) until {@code conditionText} will be not null"
*/
fun <ReturnType> withPauseWhenNull(conditionText: String = "function to probe will", timeout: Timeout = Timeouts.defaultTimeout, functionProbeToNull: () -> ReturnType?): ReturnType {
inline fun <ReturnType> withPauseWhenNull(conditionText: String = "function to probe will",
timeout: Timeout = Timeouts.defaultTimeout,
crossinline functionProbeToNull: () -> ReturnType?): ReturnType {
var result: ReturnType? = null
waitUntil("$conditionText will be not null", timeout) {
result = functionProbeToNull()
@@ -210,19 +214,44 @@ object GuiTestUtilKt {
return result!!
}
fun waitUntil(condition: String, timeout: Timeout = Timeouts.defaultTimeout, conditionalFunction: () -> Boolean) {
inline fun waitUntil(condition: String, timeout: Timeout = Timeouts.defaultTimeout, crossinline conditionalFunction: () -> Boolean) {
Pause.pause(object : Condition("${timeout.toSec()} second(s) until $condition") {
override fun test() = conditionalFunction()
}, timeout)
}
inline fun <R> tryWithPause(exceptionClass: Class<out Exception>,
condition: String = "try block will not throw ${exceptionClass.name} exception",
timeout: Timeout,
crossinline tryBlock: () -> R): R {
val exceptionRef: Ref<Exception> = Ref.create()
try {
return withPauseWhenNull (condition, timeout) {
try {
tryBlock()
}
catch (e: Exception) {
if (exceptionClass.isInstance(e)) {
exceptionRef.set(e)
return@withPauseWhenNull null
}
throw e
}
}
}
catch (e: WaitTimedOutError) {
throw Exception("Timeout for $condition exceeded ${timeout.toPrintable()}", exceptionRef.get())
}
}
fun silentWaitUntil(condition: String, timeoutInSeconds: Int = 60, conditionalFunction: () -> Boolean) {
try {
Pause.pause(object : Condition("$timeoutInSeconds second(s) until $condition silently") {
override fun test() = conditionalFunction()
}, Timeout.timeout(timeoutInSeconds.toLong(), TimeUnit.SECONDS))
}
catch (ignore: WaitTimedOutError) { }
catch (ignore: WaitTimedOutError) {
}
}
fun <ComponentType : Component> findAllWithBFS(container: Container, clazz: Class<ComponentType>): List<ComponentType> {
@@ -253,7 +282,9 @@ object GuiTestUtilKt {
return GuiTestUtil.waitUntilGone(root, timeout, matcher)
}
fun GuiTestCase.waitProgressDialogUntilGone(dialogTitle: String, timeoutToAppear: Timeout = Timeouts.seconds05, timeoutToGone: Timeout = Timeouts.defaultTimeout) {
fun GuiTestCase.waitProgressDialogUntilGone(dialogTitle: String,
timeoutToAppear: Timeout = Timeouts.seconds05,
timeoutToGone: Timeout = Timeouts.defaultTimeout) {
waitProgressDialogUntilGone(this.robot(), dialogTitle, timeoutToAppear, timeoutToGone)
}
@@ -311,7 +342,8 @@ object GuiTestUtilKt {
inline fun ignoreComponentLookupException(action: () -> Unit) = try {
action()
}
catch (ignore: ComponentLookupException) { }
catch (ignore: ComponentLookupException) {
}
fun ensureCreateHasDone(guiTestCase: GuiTestCase) {
try {