This library allows you to write and execute UI tests in IntelliJ IDEA. You can use it to test your plugin.
If you have any questions, feel free to post them in our Slack Channel.
The first thing we need to do is to launch the IDE. Because the runIdeForUiTests task is blocking, we can run it as an asynchronous
process:
./gradlew ui-test-example:clean ui-test-example:runIdeForUiTests &
Next, we can start the tests. Since they run locally, you need to make sure that the Welcome Frame is visible on the screen:
./gradlew ui-test-example:test
Alternatively, you can run all tasks at once with the following command:
./gradlew ui-test-example:clean ui-test-example:runIdeForUiTests & ./gradlew ui-test-example:test
The Remote-Robot library is inspired by Selenium WebDriver. It supports IntelliJ IDEA since version 2019.2.
It consists of a remote-robot client and a robot-server plugin:
remote-robot- is a client (test) side library that is used to send commands to therobot-serverpluginrobot-server- is an IDEA plugin that must be run with the plugin you are developing
The easiest way to start the test system is to execute the runIdeForUiTests task (refer to the Quick Start section above).
When IDEA is initialized, the robot-server plugin starts listening for commands from the UI test client.
The remote-robot library communicates with the robot-server plugin via an HTTP protocol. This connection means you can
launch IDEA on remote machines or in Docker containers to check your plugin within different test environments.
The last version of the Remote-Robot is 0.11.21.
In the test project:
repositories {
maven { url = "https://packages.jetbrains.team/maven/p/ij/intellij-dependencies" }
}
dependencies {
testImplementation("com.intellij.remoterobot:remote-robot:REMOTE-ROBOT_VERSION")
}In the plugin project:
runIdeForUiTests {
systemProperty "robot-server.port", "8082" // default port 8580
}
downloadRobotServerPlugin {
version = REMOTE-ROBOT_VERSION
}By default, the port is set to local, so it cannot be reached from another host. In case you need to make it public, you can
add the robot-server.host.public system property to the runIdeForUiTests task:
runIdeForUiTests {
// ......
systemProperty "robot-server.host.public", "true" // port is public
}Of course, you can write UI tests in the plugin project.
There are two ways of launching IDEA and UI tests:
Using IntelliJ gradle plugin
First, we need to launch the IDE. Because the runIdeForUiTests task is blocking, we can run it as an asynchronous
process:
./gradlew ui-test-example:clean ui-test-example:runIdeForUiTests &
Next, we can start the tests. Since they run locally, you need to make sure that the Welcome Frame is visible on the screen:
./gradlew ui-test-example:test
Alternatively, you can run all tasks at once with the following command:
./gradlew ui-test-example:clean ui-test-example:runIdeForUiTests & ./gradlew ui-test-example:test
Check this project as an example.
The ide-launcher library allows us to launch IDEA directly from the test. To use it, we need to add a dependency to our
project:
dependencies {
testImplementation("com.intellij.remoterobot:ide-launcher:REMOTE-ROBOT_VERSION")
}Next, we can use IdeLauncher to start IDEA:
final OkHttpClient client=new OkHttpClient();
final IdeDownloader ideDownloader=new IdeDownloader(client);
ideaProcess = IdeLauncher.INSTANCE.launchIde(
ideDownloader.downloadAndExtract(Ide.IDEA_COMMUNITY, tmpDir),
Map.of("robot-server.port",8082),
List.of(),
List.of(ideDownloader.downloadRobotPlugin(tmpDir), pathToOurPlugin),
tmpDir
);Check Java and Kotlin examples.
| Property | Value | Description |
|---|---|---|
| jb.consents.confirmation.enabled | false | Disable the consent dialog. |
| eap.require.license | true | The EAP version requires license the same way as the Release version. This property helps to avoid EAP login on CI. |
| ide.mac.message.dialogs.as.sheets | false | Disable the Sheet dialogs on Mac, they are not recognized by Java Robot. |
| ide.mac.file.chooser.native | false | Disable the Mac native file chooser, it is not recognized by Java Robot. |
| jbScreenMenuBar.enabled + apple.laf.useScreenMenuBar | false + false | Disable the Mac native menu, it is not recognized by Java Robot. |
| idea.trust.all.projects | true | Disable the Trust Project dialog that appears when the project is opened. |
| ide.show.tips.on.startup.default.value | false | Disable the Tips Of the Day dialog on startup. |
In the UI test project:
RemoteRobot remoteRobot = new RemoteRobot("http://127.0.0.1:8082");We use the XPath query language to find components. Once IDEA with robot-server
has started, you can open the http://ROBOT-SERVER:PORT link. The page shows the IDEA UI
components hierarchy in HTML format. You can find the component of interest and write an XPath to it, similar to
Selenium WebDriver. There is also a simple XPath generator that can help you write and test your XPaths.
For example:
- Define a locator
Locator loginToGitHubLocator = byXpath("//div[@class='MainButton' and @text='Log in to GitHub...']");- Find one component
ComponentFixture loginToGitHub = remoteRobot.find(ComponentFixture.class,loginToGitHubLocator);- Find many components
List<ContainterFixture> dialogs = remoteRobot.findAll(
ComponentFixture.class,
byXpath("//div[@class='MyDialog']")
);Fixtures support the PageObject pattern. There are two basic fixtures:
ComponentFixtureis the simplest representation of any real component with basic methodsContainerFixtureextendsComponentFixtureand allows searching other components within it
You can create your own fixtures:
@DefaultXpath(by = "FlatWelcomeFrame type", xpath = "//div[@class='FlatWelcomeFrame']")
@FixtureName(name = "Welcome Frame")
public class WelcomeFrameFixture extends ContainerFixture {
public WelcomeFrameFixture(@NotNull RemoteRobot remoteRobot, @NotNull RemoteComponent remoteComponent) {
super(remoteRobot, remoteComponent);
}
// Create New Project
public ComponentFixture createNewProjectLink() {
return find(ComponentFixture.class, byXpath("//div[@text='Create New Project' and @class='ActionLink']"));
}
// Import Project
public ComponentFixture importProjectLink() {
return find(ComponentFixture.class, byXpath("//div[@text='Import Project' and @class='ActionLink']"));
}
}// find the custom fixture by its default XPath
WelcomeFrameFixture welcomeFrame=remoteRobot.find(WelcomeFrameFixture.class);
welcomeFrame.createNewProjectLink().click();We have prepared some basic fixtures:
dependencies {
testImplementation("com.intellij.remoterobot:remote-fixtures:REMOTE-ROBOT_VERSION")
}The library contains fixtures for most basic UI components. Please check this package to learn more. In case you want to add missing basic fixtures, you are welcome to PR or create an issue.
We use the JavaScript rhino engine to work with components on the IDEA side.
For example, retrieving text from the ActionLink component:
public class ActionLinkFixture extends ComponentFixture {
public ActionLinkFixture(@NotNull RemoteRobot remoteRobot, @NotNull RemoteComponent remoteComponent) {
super(remoteRobot, remoteComponent);
}
public String text() {
return callJs("component.getText();");
}
}We can retrieve data using RemoteRobot with the callJs method. In this case, there is a robot var in the context
of JavaScript execution. The robot is an instance of extending
the org.assertj.swing.core.Robot
class.
When you use the callJs() method of a fixture object, the component argument represents the actual UI component
found (see Searching Components) and used to initialize the ComponentFixture.
The runJs method works the same way without any return value:
public void click() {
runJs("const offset = component.getHeight()/2;"+
"robot.click("+
"component, "+
"new Point(offset, offset), "+
"MouseButton.LEFT_BUTTON, 1);"
);
}We import some packages to the context before the script is executed:
java.awt
org.assertj.swing.core
org.assertj.swing.fixtureYou can add other packages or classes with js methods:
importClass(java.io.File);
importPackage(java.io);Alternatively, you can just use the full path:
Boolean isDumbMode=ideaFtame.callJs(
"com.intellij.openapi.project.DumbService.isDumb(component.project);"
);There are global and local Map<String, Object> variables available in the js context:
globalis a single map for the whole IDElocalmap is defined on a per-fixture basis
Please check GlobalAndLocalMapExamples for additional information.
In case you made robot-server-plugin port public, you may want to enable encryption for JavaScript code:
runIdeForUiTests {
systemProperty "robot.encryption.enabled", "true"
systemProperty "robot.encryption.password", "secret"
}
test {
systemProperty "robot.encryption.password", "secret"
}Sometimes, you may not want to dig through the whole component to determine which field contains the text you need to
reach. If you need to check whether some text is present on the component or you need to click the text, you can
use fixture methods:
welcomeFrame.findText("Create New Project").click();
assert(welcomeFrame.hasText(startsWith("Version 20")));
List<String> renderedText=welcomeFrame.findAllText()
.stream()
.map(RemoteText::getText)
.collect(Collectors.toList());Instead of looking for the text inside the component structure, we render it on a fake Graphics to collect text data and
its points.
There are two ways to get a screenshot.
- Get a screenshot of the whole screen (method of
RemoteRobotobject).
remoteRobot.getScreenshot()- Get component screenshot (method of
Fixtureobject).
TheisPaintingModeparameter allows you to return a new render of the component (set tofalseby default).
This might be helpful when you don't have a complete set of desktop environments or when any other component covers the component of your interest.
someFixture.getScreenshot();
someFixture.getScreenshot(true);In both cases, you will get the BufferedImage object specified as .png.
If you are familiar with Kotlin, please take a look at the kotlin example. You may find it easier to read and use.
We use the step wrapper method to make test logs easy to read. The StepLogger example shows how useful it can be.
For instance, by implementing your own StepProcessor, you can extend the steps workflow and connect to
the allure report framework.

