AT-3003 [remote-driver]: rework SmoothRobot#clickWithRetry

Merge-request: IJ-MR-185988
Merged-by: Vladislav Shishov <vladislav.shishov@jetbrains.com>

GitOrigin-RevId: 6a416d4715be7dd8ad81423d2b6a58956445a9ad
This commit is contained in:
vladislav.shishov
2025-12-17 21:00:39 +00:00
committed by intellij-monorepo-bot
parent d8ed907423
commit cd926ffe35
2 changed files with 36 additions and 29 deletions
@@ -3,6 +3,7 @@ package com.intellij.driver.sdk.ui.components.common
import com.intellij.driver.client.Driver
import com.intellij.driver.client.Remote
import com.intellij.driver.model.OnDispatcher
import com.intellij.driver.model.RemoteMouseButton
import com.intellij.driver.sdk.Project
import com.intellij.driver.sdk.invokeAction
import com.intellij.driver.sdk.step
@@ -17,7 +18,6 @@ import com.intellij.driver.sdk.ui.remote.Window
import com.intellij.driver.sdk.ui.ui
import com.intellij.driver.sdk.waitForIndicators
import java.awt.Frame
import java.awt.Point
import javax.swing.JFrame
import kotlin.time.Duration
import kotlin.time.Duration.Companion.minutes
@@ -93,7 +93,7 @@ open class IdeaFrameUI(data: ComponentData) : WindowUiComponent(data) {
override fun toFront() {
super.toFront()
click(Point(component.width / 2, 0))
robot.click(getLocationOnScreen().apply { translate(component.width / 2, 0) }, RemoteMouseButton.LEFT)
}
fun isMinimized() = ideaFrameComponent.getState() == Frame.ICONIFIED
@@ -19,6 +19,7 @@ import org.assertj.swing.edt.GuiQuery
import org.assertj.swing.timing.Pause.pause
import org.assertj.swing.util.Modifiers
import java.awt.*
import java.awt.event.AWTEventListener
import java.awt.event.KeyEvent
import java.awt.event.MouseAdapter
import java.awt.event.MouseEvent
@@ -242,46 +243,45 @@ internal class SmoothRobot @JvmOverloads constructor(
basicRobot.moveMouse(x, y)
}
private fun clickWithRetry(component: Component, where: Point?, mouseButton: MouseButton, counts: Int) {
private fun clickWithRetry(component: Component, where: Point?, mouseButton: MouseButton, clickCount: Int) {
if (useInputEvents()) {
postClickEvent(component, mouseButton, counts)
return
}
//we don't want to register mouse listener to component that doesn't have mouse listeners
//this will break event propagation to a parent component
if (component.mouseListeners.isEmpty()) {
moveMouseAndClick(component, where, mouseButton, counts)
postClickEvent(component, mouseButton, clickCount)
return
}
var attempt = 0
while (attempt < 3) {
val clickLatch = CountDownLatch(1)
val mouseListener = object : MouseAdapter() {
override fun mouseClicked(e: MouseEvent?) {
clickLatch.countDown()
logger.info("Mouse clicked on $component")
}
override fun mouseReleased(e: MouseEvent?) {
clickLatch.countDown()
logger.info("Mouse released on $component")
}
val listener: AWTEventListener = object : AWTEventListener {
private var pressedAtComponent: Component? = null
//on some components, mouse clicked/released are not registered on click
override fun mousePressed(e: MouseEvent?) {
clickLatch.countDown()
logger.info("Mouse pressed on $component")
override fun eventDispatched(event: AWTEvent) {
if (event !is MouseEvent) return
if (event.id == MouseEvent.MOUSE_PRESSED) {
pressedAtComponent = event.component
}
else if (event.id == MouseEvent.MOUSE_RELEASED || event.id == MouseEvent.MOUSE_CLICKED) {
if (event.clickCount == clickCount && event.button == mouseButton.button && event.component === pressedAtComponent
&& (SwingUtilities.isDescendingFrom(event.component, component) || SwingUtilities.isDescendingFrom(component, event.component))) {
clickLatch.countDown()
}
pressedAtComponent = null
}
}
}
component.addMouseListener(mouseListener)
moveMouseAndClick(component, where, mouseButton, counts)
val clicked = clickLatch.await(3, TimeUnit.SECONDS)
component.removeMouseListener(mouseListener)
Toolkit.getDefaultToolkit().addAWTEventListener(listener, AWTEvent.MOUSE_EVENT_MASK)
try {
moveMouseAndClick(component, where, mouseButton, clickCount)
if (clicked) {
break
val clicked = clickLatch.await(3, TimeUnit.SECONDS)
if (clicked) {
break
}
}
finally {
Toolkit.getDefaultToolkit().removeAWTEventListener(listener)
}
logger.warn("Repeating click. Click was unsuccessful on $component")
@@ -444,5 +444,12 @@ internal class SmoothRobot @JvmOverloads constructor(
execute(object : GuiQuery<T>() {
override fun executeInEDT() = body.invoke()
})
private val MouseButton.button
get() = when (this) {
MouseButton.LEFT_BUTTON -> MouseEvent.BUTTON1
MouseButton.MIDDLE_BUTTON -> MouseEvent.BUTTON2
MouseButton.RIGHT_BUTTON -> MouseEvent.BUTTON3
}
}
}