diff --git a/ijs.tree b/ijs.tree
index e6469161b..5cbc90f2a 100644
--- a/ijs.tree
+++ b/ijs.tree
@@ -241,6 +241,7 @@
+
diff --git a/images/basics/testing_plugins/integration_tests/integration_tests_devtools.png b/images/basics/testing_plugins/integration_tests/integration_tests_devtools.png
new file mode 100644
index 000000000..983e09b1a
Binary files /dev/null and b/images/basics/testing_plugins/integration_tests/integration_tests_devtools.png differ
diff --git a/images/basics/testing_plugins/integration_tests/integration_tests_ui_sample.png b/images/basics/testing_plugins/integration_tests/integration_tests_ui_sample.png
new file mode 100644
index 000000000..ef2d3cf74
Binary files /dev/null and b/images/basics/testing_plugins/integration_tests/integration_tests_ui_sample.png differ
diff --git a/topics/basics/testing_plugins/integration_tests/integration_tests.md b/topics/basics/testing_plugins/integration_tests/integration_tests.md
index 20bfa1b74..4b654138d 100644
--- a/topics/basics/testing_plugins/integration_tests/integration_tests.md
+++ b/topics/basics/testing_plugins/integration_tests/integration_tests.md
@@ -14,5 +14,6 @@ There are several important reasons to create integration tests, including:
1. [](integration_tests_intro.md)
-2. Writing UI Tests (will be available soon)
+2. [](integration_tests_ui.md)
3. API Interaction (will be available soon)
+
diff --git a/topics/basics/testing_plugins/integration_tests/integration_tests_intro.md b/topics/basics/testing_plugins/integration_tests/integration_tests_intro.md
index 138ffe834..cffc47e6c 100644
--- a/topics/basics/testing_plugins/integration_tests/integration_tests_intro.md
+++ b/topics/basics/testing_plugins/integration_tests/integration_tests_intro.md
@@ -6,6 +6,8 @@
Walkthrough how to create the first integration tests.
+> This page is part of [](integration_tests.md) tutorial.
+
## Adding dependencies
Integration testing framework consists of two main components:
diff --git a/topics/basics/testing_plugins/integration_tests/integration_tests_ui.md b/topics/basics/testing_plugins/integration_tests/integration_tests_ui.md
new file mode 100644
index 000000000..db1e70e12
--- /dev/null
+++ b/topics/basics/testing_plugins/integration_tests/integration_tests_ui.md
@@ -0,0 +1,227 @@
+
+
+# Integration Tests: UI Testing
+
+
+
+Walkthrough how to interact with UI in integration tests.
+
+> This page is part of [](integration_tests.md) tutorial.
+
+For introduction and setting up dependencies, refer to [](integration_tests_intro.md).
+
+## UI Hierarchy
+
+IntelliJ-based IDEs primarily use Swing and AWT for their user interface, while [JCEF](embedded_browser_jcef.md) is used in specific cases like Markdown rendering.
+These UI frameworks organize elements in a parent-child hierarchy, similar to HTML's DOM structure:
+
+* Top-level containers (IDE frame and dialogs).
+* Nested containers.
+* Individual components (buttons, text fields, and lists).
+
+Every UI element (except top-level containers) must have a parent container, creating a clear hierarchical structure.
+
+The Driver framework provides a Kotlin DSL that mirrors this hierarchy.
+Here's an example:
+
+```kotlin
+ideFrame { // 1
+ invokeAction("SearchEverywhere") // 2
+ searchEverywherePopup { // 3
+ actionButtonByXpath(xQuery { byAccessibleName("Preview") }) // 4
+ .click()
+ }
+}
+```
+
+This code demonstrates hierarchical navigation:
+
+1. Find the main IDE window.
+2. Trigger the _Search Everywhere_ action.
+3. Locate the Search Everywhere popup.
+4. Find and click the Preview button within the popup.
+
+The code could be more concise:
+
+```kotlin
+ideFrame {
+ actionButtonByXpath(xQuery { byAccessibleName("Preview") }).click()
+}
+```
+
+But the shorter code has two significant drawbacks:
+
+* **Reduced precision**: The code searches for the Preview button throughout the entire IDE frame.
+ It might find unintended matches in the project explorer, tool windows, or other UI elements.
+ This can make tests unreliable and prone to breaking when the UI content changes.
+* **Decreased readability**: While the code is more concise, it doesn't communicate the intended navigation path.
+ The longer version makes it clear exactly where it's expected to find the Preview button, making the code more maintainable and easier to debug.
+
+So, being explicit about the component hierarchy helps create more robust and maintainable UI automation code.
+
+## Searching Components
+
+While the Driver framework provides many pre-built components (like `ideFrame`, `codeEditor`, `button`, `tree`, etc.), sometimes it's required to locate custom elements.
+
+It can be done by pausing the IDE to examine its UI structure:
+
+```kotlin
+Starter.newContext(...).apply { ... }
+ .runIdeWithDriver().useDriverAndCloseIde {
+ Thread.sleep(30.minutes.inWholeMilliseconds) // pause the IDE
+ }
+```
+
+When the test is running, the following line will appear in the logs: `http://localhost:63343/api/remote-driver/`.
+Opening this URL reveals an HTML representation of the IDE's Swing component tree:
+
+{width=706}
+
+Using Developer Tools in the browser, it's possible to inspect detailed component attributes.
+Here's an example component:
+
+```html
+
+
+```
+
+There are other attributes which are omitted for clarity.
+
+The element corresponds to the following button:
+
+{width=706}
+
+Similar to web testing frameworks like Selenium, XPath is used to locate components.
+The Driver framework provides a simple XPath builder — `com.intellij.driver.sdk.ui.QueryBuilder`.
+
+For reliable component identification, prioritize these attributes, which can be found with predefined methods in `QueryBuilder`:
+
+- `accessiblename`: `xQuery { byAccessibleName() }`
+- `visible_text`: `xQuery { byVisibleText() }`
+- `javaclass`: `xQuery { byType() }`
+- `myicon`: `xQuery { byAttribute("myicon", "") }`
+
+A single component can be found in several ways:
+
+```kotlin
+xQuery { byAccessibleName("Current File") }
+xQuery { byVisibleText("Current File") }
+xQuery {
+ byType("com.intellij.execution.ui.RedesignedRunConfigurationSelector\$createCustomComponent$1")
+}
+```
+
+Multiple attributes can be combined for a more precise selection, for example:
+
+```kotlin
+xQuery { and(byAccessibleName("Current File"), byVisibleText("Current File")) }
+xQuery {
+ or(
+ contains(byAccessibleName("Current")),
+ byType("com.intellij.execution.ui.RedesignedRunConfigurationSelector\$createCustomComponent$1")
+ )
+}
+```
+
+## Interaction with Components
+
+Once a component is located, it's possible to interact with it or verify its properties.
+
+To click the Current File button:
+
+```kotlin
+x(xQuery { byVisibleText("Current File") }).click()
+```
+
+The `x()` call creates a lazy reference to the component.
+It means that the XPath query isn't executed immediately and component lookup happens only when an action (like `click()`) is invoked.
+
+Here's a part of test that incorporates UI interaction:
+
+```kotlin
+runIdeWithDriver().useDriverAndCloseIde {
+ waitForIndicators(1.minutes)
+ ideFrame {
+ x(xQuery { byVisibleText("Current File") }).click()
+ }
+}
+```
+
+Beyond mouse clicks, keyboard input and shortcuts can be simulated:
+
+```kotlin
+keyboard {
+ enterText("Sample text")
+ enter()
+ hotKey(if (SystemInfo.isMac) KeyEvent.VK_META else KeyEvent.VK_CONTROL, KeyEvent.VK_A)
+ backspace()
+}
+```
+
+Keyboard methods perform presses using `java.awt.Robot` so to type to some particular component or invoke a shortcut in the appropriate place, you first need to make the component focused.
+The most reliable way to do this is to perform `click` on the component first.
+
+> On macOS, the interaction via `java.awt.Robot` requires special permissions.
+> IntelliJ IDEA should be granted the necessary permissions via the Accessibility page, which can be found under _System Settings | Privacy & Security_.
+>
+{style="note"}
+
+## Asserting Properties
+
+The complete UI test:
+
+```kotlin
+@Test
+fun simpleTestForCustomUIElement() {
+ Starter.newContext(
+ "testExample",
+ TestCase(
+ IdeProductProvider.IC,
+ GitHubProject.fromGithub(
+ branchName = "master",
+ repoRelativeUrl = "JetBrains/ij-perf-report-aggregator"
+ )
+ )
+ .withVersion("2024.3")
+ )
+ .apply {
+ val pathToPlugin = System.getProperty("path.to.build.plugin")
+ PluginConfigurator(this).installPluginFromFolder(File(pathToPlugin))
+ }.runIdeWithDriver().useDriverAndCloseIde {
+ waitForIndicators(1.minutes)
+ ideFrame {
+ x(xQuery { byVisibleText("Current File") }).click() //1
+ val configurations = popup().jBlist( //2
+ xQuery { contains(byVisibleText("Edit Configurations")) } //3
+ )
+ configurations.shouldBe("Configuration list is not present", present) //4
+ Assertions.assertTrue(
+ configurations.rawItems.contains("backup-data"), //5
+ "Configurations list doesn't contain 'backup-data' item: ${configurations.rawItems}"
+ ) //6
+ }
+ }
+}
+```
+
+The test does the following:
+
+1. Opening the popup by clicking the Current File button.
+2. Finding the list by using `popup()` to locate the popup with a configuration list.
+ This works without any XPath because at the moment of the call, there are no other popups shown on the UI.
+3. Finding the list containing the text `Edit Configurations`.
+ XQuery searches for the list component that contains the visible text `Edit Configurations` and verifies the list presence.
+4. Using `shouldBe(, present)` to ensure the list exists.
+ This is important because `popup().jBlist` creates a lazy reference without actually checking the results.
+ The actual check happens when `shouldBe` calls the `present` method.
+ The `shouldBe` method waits 15 seconds until the condition is met and can be used to assert various properties.
+5. Checking list contents by accessing the `rawItems` property to get all list items and asserting `backup-data` exists in the list.
+6. Including the full list content in the error message for debugging.
+
diff --git a/topics/intro/content_updates.md b/topics/intro/content_updates.md
index e5ae413e2..d03c454ff 100644
--- a/topics/intro/content_updates.md
+++ b/topics/intro/content_updates.md
@@ -16,7 +16,7 @@ See [GitHub Changelog](https://github.com/JetBrains/intellij-sdk-docs/commits/ma
{march-25}
Integration Testing
-: Add new section [](integration_tests.md) with the first part [](integration_tests_intro.md).
+: Add new section [](integration_tests.md).
Minor Changes and Additions
: