mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[gui-test] move ProjectViewFixture to use ExtendedCellReader without direct reading nodes value
This commit is contained in:
+67
-34
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user