本文へ移動
cccskills
無料GitHub で公開日本語紹介

driver-ui-tests

IntelliJの画面操作を伴うテストを、IDE StarterやUI Driverで作成するための手順や要素の探し方、失敗時の調べ方を案内します。

原文原文の説明を見る

Write IntelliJ UI tests with IDE Starter or UI Driver.

JetBrains/intellij-communityスター 20,664(GitHub 20,664 + cccskills 0)
連携ツールFigmaPlaywright

実行スコア 未検証

インストール方法を見る

こんなときに便利

  • IntelliJの画面操作テストを作りたいとき
  • ダイアログの要素を安定して特定したいとき
  • UIテスト失敗の原因を調べたいとき

日本語での紹介

できること

IntelliJ IDEの画面を操作するテストを、IDE StarterとUI Driverを使って作成するための指針をまとめています。JUnit 5でのテスト構成、テスト対象IDEの指定、画面要素を見つけるセレクター、親要素に範囲を絞った検索方法を確認できます。プロジェクトの読み込みやインデックス作成を待つ方法、ファイルを直接開く方法、アクション実行後の待ち方、キーボード入力、IDE内の状態を読む方法も扱います。テストの分類に使うアノテーションや、起動時に設定するVMオプション、実ブラウザーを操作する例も含まれています。失敗時にはUI階層やログ、スクリーンショットを確認し、要素が見つからない原因を調べる手順を参照できます。

こんなときに便利

IntelliJの設定画面やツールバーなど、実際のUIを通した動作確認を追加したいときに役立ちます。画面要素の特定方法や待機の入れ方を揃えたい場合、テスト失敗時の手掛かりを探したい場合にも利用できます。

使い方の例

  • 「この設定画面を開いて、項目を変更するUIテストを書いて」
  • 「このツールバー操作に使える要素の検索方法を教えて」
  • 「UIテストで要素が見つからない原因を調べて」

注意点

テストにはビルド済みのIDEが必要です。記載された実行例ではtests.cmdを使います。

この紹介文は、公開されている SKILL.md をもとに AI(OpenAI Codex)が作成しました。正確な仕様は下の原文を確認してください。

含まれるファイル(1)

  • SKILL.md14.9 KB

SKILL.md(原文)

インストールする前に、エージェントに与えられる指示の中身を確認できます。

<!-- Generated by community/.ai/render-guides.mjs; edit community/.agents/skills/driver-ui-tests/SKILL.md -->

Driver UI Tests Guide

Guidelines for writing UI tests using IDE Starter and UI Driver frameworks.

Common Imports

// Driver core
import com.intellij.driver.client.Driver
import com.intellij.driver.sdk.waitForProjectOpen
import com.intellij.driver.sdk.advancedSettings

// Test utilities
import com.intellij.driver.tests.utils.waitForIndicators
import com.intellij.driver.tests.utils.Plugin
import com.intellij.driver.tests.utils.PluginInstaller
import com.intellij.driver.tests.utils.Plugins

// IDE Starter framework
import com.intellij.ide.starter.driver.runIdeTest
import com.intellij.ide.starter.ide.IDETestContext
import com.intellij.ide.starter.models.IDEStartResult
import com.intellij.ide.starter.models.VMOptions
import com.intellij.ide.starter.runner.IDERunContext
import com.intellij.ide.starter.runner.Starter
import com.intellij.ide.starter.utils.catchAll

// Extended test infrastructure
import com.intellij.ide.starter.extended.allure.AllureHelperExtended.step
import com.intellij.ide.starter.extended.allure.Subsystems
import com.intellij.ide.starter.extended.engine.newTestContainerExtended
import com.intellij.ide.starter.extended.engine.TestContainerExtended
import com.intellij.ide.starter.extended.license.StagingLicenseGenerator.licenseProductCode
import com.intellij.ide.starter.extended.loadMetadataFromServer
import com.intellij.ide.starter.extended.setupTestMetadataSchemeWithGroupsFromCode

// Test framework
import com.intellij.testFramework.TestApplicationManager

Test Structure

  • Tests use JUnit 5 with an IDE Starter framework and UI Driver framework (community/platform/remote-driver)
  • Test case projects are represented by the com.intellij.ide.starter.models.TestCase see src/com/intellij/ide/starter/project
  • Tests run against specific IDE (community/tools/intellij.tools.ide.starter/src/com/intellij/ide/starter/ide/IdeProductProvider.kt)

Test project examples

See tests/intellij.ide.starter.extended/src/com/intellij/ide/starter/extended/data/cases

Page Object Pattern or UiComponent

Page objects extend UiComponent with ComponentData constructor:

class MyPageObject(data: ComponentData) : UiComponent(data) {
    val myButton = x { byAccessibleName("Button Name") }
    val myPanel = x { byType("com.example.MyPanel") }

    fun clickMyButton() {
        myButton.click()
    }
}

// Extension function on Finder to create the page object
fun Finder.myPageObject(): MyPageObject = x(
    xQuery { byAccessibleName("Root Element Name") }, MyPageObject::class.java
)

// Use specific ui component to specify the context of the search
fun AnotherPageObject.myPageObject(): MyPageObject = x(
  xQuery { byAccessibleName("Root Element Name") }, MyPageObject::class.java
)

Element Selectors

SelectorUsage
byAccessibleName("name")Find by accessible name attribute
byJavaClass("com.example.MyPanel")Find by the full class name
byType("javax.swing.JTree")Find by the full class name or a supertype
byVisibleText("text")Find by visible text content

Use byJavaClass or byType. Do not use byClass, which is deprecated. A full class name has @Language("jvm-class-name"), so the IDE resolves it and updates it on rename.

Write the class name inside the query. Do not put it, or a full XPath, into a separate constant: the IDE cannot resolve or rename a class name in a plain string.

// BAD - a short class name in a separate constant
private const val POPUP_LIST_XPATH = "//div[@class='HeavyWeightWindow']//div[@class='MyList']"

// GOOD - a private helper with a resolvable class name
private fun Finder.optionPopup() = x { byType("com.intellij.ui.components.JBList") }

UI test examples

See directory tests/remote-driver-tests

Common UI Components

See directory community/platform/remote-driver/test-sdk/src/com/intellij/driver/sdk/ui

Scoping Element Searches

When multiple elements match a selector, scope to a parent element:

// BAD - will fail if multiple InstallButtons exist
ui.x { byJavaClass("com.intellij.ide.plugins.newui.InstallButton") }.click()

// GOOD - scope to a parent element first
val detailPane = ui.x { byJavaClass("com.intellij.ide.plugins.newui.PluginDetailsPageComponent") }
detailPane.x { byJavaClass("com.intellij.ide.plugins.newui.InstallButton") }.click()

Finding toolbar / title-bar actions

Toolbar and tool-window title actions that show their text (presentation.putClientProperty(ActionUtil.SHOW_TEXT_IN_TOOLBAR, true)) render as ActionButtonWithText, not ActionButton. The SDK actionButton(text) helper searches @class='ActionButton' only, so it silently never matches them. ActionButtonWithText extends ActionButton, so byType matches both variants:

// Matches both icon-only and text-bearing action buttons
fun Finder.statusButton(text: String) =
  x { byType("com.intellij.openapi.actionSystem.impl.ActionButton") and byVisibleText(text) }

The visible text is itself a reliable assertion signal — you usually do not need to read the backing service/state.

Keyboard Interactions

element.keyboard { typeText("search text") }
ui.keyboard { key(KeyEvent.VK_ENTER) }
ui.keyboard { hotKey(KeyEvent.VK_META, KeyEvent.VK_COMMA) }  // Cmd+,

Writing Tests

Required Annotations

Every UI test must have the following annotations at the class level:

AnnotationPurposeTestOps Custom Field
@Subsystems.*Categorizes the test by subsystemSubsystem
@Features.*Specifies the feature being testedFeature
@Components.*Identifies the component under testComponent

For tests linked to TestOps test cases, also add:

  • @AllureId("test_case_id") - Links the test to the TestOps test case ID

The annotation values should match the corresponding TestOps custom fields (Subsystem, Feature, Component).

Available annotations: See tests/intellij.ide.starter.extended.allure/src/com/intellij/ide/starter/extended/allure/Annotations.kt

Example with TestOps test case:

@Subsystems.Java
@Features.Completion
@Components.Editor
class MyTestFromTestOps {

    @Test
    @AllureId("318541")  // Required when test case exists in TestOps
    fun `my test from testops`(testInfo: TestInfo) {
        // ...
    }
}

Example for new test (not yet in TestOps):

@Subsystems.UI
@Features.PluginManager
@Components.Miscellaneous
class MyNewTest {

    @Test
    fun `my new test`(testInfo: TestInfo) {
        // ...
    }
}

Basic Test Structure

@Subsystems.Java
@Features.Completion
@Components.Editor
class MyTest {
    val testCase = TestCase(IdeProductProvider.IU, myProject)

    @Test
    @AllureId("123456")  // Required if test case exists in TestOps
    fun `my test name`(testInfo: TestInfo) {
        val context = Starter.newContext(testName = "TestName", testCase = testCase)

        context.applyVMOptionsPatch {
            addSystemProperty("ide.ui.non.modal.settings.window", "true")
        }

        context.runIdeTest(testName = testInfo.displayName) {
            waitForIndicators(5.minutes)  // Wait for indexing to complete

            step("Step description") {
                // Test actions here
            }
        }
    }
}

Waiting for Project Import and Indexing

Always wait for indicators at the start of your test:

waitForIndicators()

This ensures the project is fully imported and indexed before interacting with the IDE.

Opening Files

Use openFile instead of UI-based file navigation:

// GOOD - Direct and reliable
openFile(relativePath = "src/Main.java")

// AVOID - UI-based approach is slower and more fragile
invokeAction("GotoFile", now = false)
ui.keyboard { typeText("Main.java") }
ui.keyboard { key(KeyEvent.VK_ENTER) }

invokeAction: now Parameter

The now parameter controls whether the action completes before continuing:

// now = true: Waits for action to complete (use when keyboard input follows)
invokeAction("ToggleBookmarkWithMnemonic", now = true)
ui.keyboard { key(KeyEvent.VK_1) }  // This input goes to the bookmark dialog

// now = false: Returns immediately (use when waiting for UI to appear)
invokeAction("ShowSettings", now = false)
ui.x { byJavaClass("com.intellij.openapi.options.newEditor.SettingsEditor") }.shouldBe { present() }

Rule: Use now = true when the next step is keyboard input to prevent input going to the wrong component. Rule: Use now = false when you expect to the UI dialog to appear.

Custom Wait Conditions

Use waitFor to wait for specific conditions:

waitFor("description of what we're waiting for", 30.seconds) {
    ui.x { byJavaClass("com.example.MyComponent") }.present()
}

waitFor("text to appear", 10.seconds) {
    ui.x { byType("javax.swing.JTree") }.hasText("expected text")
}

To show the actual value on failure, use the waitFor overload with getter and checker. It adds the last value to the error, so do not build errorMessage yourself:

// BAD - errorMessage repeats the getter
waitFor("the filter field holds '$query'", errorMessage = { "the filter field holds '${filterField.text}'" }) {
    filterField.text == query
}

// GOOD - the error shows the last filterField.text
waitFor("the filter field holds '$query'", getter = { filterField.text }, checker = { it == query })

Reading IDE state via @Remote

To read state from a service or model in the IDE under test, declare a @Remote interface and call it via driver.service(...) / driver.utility(...).

  • Plugin classes need the plugin field. Without it the class resolves against the platform/core classloader → DriverIllegalStateException: No such class '<fqn>' in plugin null.
    • Class in a plugin content module: @Remote("<fqn>", plugin = "<plugin.id>/<content.module>") (e.g. com.intellij.figma/intellij.figma.core).
    • Class in the main / embedded plugin module: @Remote("<fqn>", plugin = "<plugin.id>").
  • Method dispatch resolves against the DECLARED @Remote class, not the runtime object. A method declared on a sealed/abstract supertype ref is "not found" at runtime — declare it on the concrete subtype, or expose it via a top-level type. (The jvm-class-name injection also cannot resolve a nested Foo$Bar name → a cosmetic "Cannot resolve class" inspection error; prefer top-level types.)
  • Add the plugin module as a TEST dependency so the FQNs resolve for code-insight.
@Remote("com.example.MyAppService", plugin = "com.example.myplugin/com.example.myplugin.core")
interface MyAppServiceRef {
  fun getConfig(): MyConfigRef
}
// driver.service(MyAppServiceRef::class).getConfig()...

Enabling a registry flag at startup

Seed a registry key before the IDE starts with a -D VM option — RegistryValue falls back to System.getProperty. Required when a startup ProjectActivity or ToolWindowFactory.shouldBeAvailable reads the flag (setting it via the driver after start is too late):

context.applyVMOptionsPatch {
  addSystemProperty("my.feature.enabled", "true")
}

Driving a real browser (Playwright)

Playwright runs in the test JVM, alongside the driver-driven IDE (both on localhost) — useful when the IDE's client is a web app/plugin. page.onConsoleMessage { ... } captures the page and its iframes (a strong diagnostic). Put custom screenshots/files under context.paths.testHome.resolve("log") so they are collected as test artifacts. See plugins/figma/integrationTests for a full example.

Running Tests from Terminal

Driver tests require a fully built IDE. There are several ways to run them:

Option 1: Using tests.cmd (Recommended)

The tests.cmd script builds the IDE from sources and runs tests. Recommended for dev server mode.

Example:

./tests.cmd \
  --module intellij.driver.tests \
  --test com.intellij.driver.tests.idea.java.FindAndGoToTest

Key parameters:

  • --test - fully qualified test class name (or pattern)
  • --module intellij.driver.tests - required for driver tests

Example with specific test:

./tests.cmd \
  --module intellij.driver.tests \
  --test com.intellij.driver.tests.idea.ultimate.httpclient.BuiltInHttpClientBrotliCompressionUiTest

Debugging Test Failures

Output Locations

After test failure, check:

  • UI hierarchy: out/ide-tests/tests/{IDE-version}/{TestName}/{test-method}/log/ui-hierarchy/ui.html
  • IDE log: out/ide-tests/tests/{IDE-version}/{TestName}/{test-method}/log/idea.log
  • Screenshots: out/ide-tests/tests/{IDE-version}/{TestName}/{test-method}/log/screenshots/
  • Exceptions: out/ide-tests/tests/{IDE-version}/{TestName}/{test-method}/error/

Inspect the LIVE UI hierarchy, not just the post-mortem file

The ui-hierarchy/ui.html and full-screen.png written on failure are captured after useDriverAndCloseIde tears the IDE down — by then the session has ended and panels often revert to an empty/welcome state, so they can be misleading. Two better sources:

  • Heartbeat screenshot log/screenshots/001_heartbeat/ — captured mid-run, shows the real state during the wait.
  • Live UI hierarchy server — while the IDE is up, the component tree is browsable at http://localhost:<port>/api/remote-driver/ (the harness sets -Dexpose.ui.hierarchy.url=true; the port is logged at startup as UI Hierarchy: http://localhost:<port>/api/remote-driver/). To inspect interactively, park the test at the point of interest — temporarily raise a waitFor timeout (e.g. to 20.minutes) — and curl/open that URL while the IDE stays alive. Each node exposes class (simple), javaclass (FQN, incl. Outer$Inner for inner classes), visible_text, and accessiblename; read these to build a reliable matcher instead of guessing from source.

Common Issues

  1. Element Not Found: Check UI hierarchy HTML for the correct accessible name or class
  2. Multiple Elements Match: Scope search to parent element

Critical Rules

  • Never use Thread.sleep() or delay() - Driver framework automatically waits for UI elements
  • Wrap test logic in step("description") { } for better logs
  • Verify assertions actually fail – Comment out the action being tested and confirm the test fails. If it still passes, your assertion is too weak.
  • Use common UI components – create new if necessary
  • Always check UI hierarchy to understand the UI state when a test fails
  • Keep a helper for a specific test scenario private – keep it in its test class. Put a reusable helper into a common UI component
  • Do not add retries to test-sdk – a retry hides the cause of a failure. Find the cause and wait for the correct UI state. If a test really needs withRetries, keep it in the test module

レビュー

まだレビューはありません。使ってみた感想をお寄せください。

コメント

ログインなしで書き込めます。使ってみた感想、質問、動かなかった環境などを気軽にどうぞ。

まだコメントはありません。最初のコメントを書いてみませんか。

送信するとすぐに公開されます。メールアドレスや電話番号などの個人情報は書かないでください。URL は 2 つまでです。

0 / 1000 文字

このフォームは reCAPTCHA で保護されており、Google のプライバシーポリシーと利用規約が適用されます。

同じリポジトリのスキル

actions

無料概要あり

IntelliJプラグインのAnActionと登録設定を実装・変更し、表示文言やアイコンの定義、非同期処理の起動方法を整えます。

  • IDEに新しいアクションを追加したいとき
  • アクションの登録やアイコンを変更したいとき
  • 表示名と説明文の定義を整理したいとき
JetBrains/intellij-community2.1万2026年10月26日 更新

bazel-test-migration

無料概要あり

IntelliJのJPSテストモジュールをBazelへ移行し、Bazelだけで起きる失敗を依存関係や実行環境から切り分ける手順を案内します。

  • JPSテストをBazelへ移行したいとき
  • Bazelだけで起きるテスト失敗を調べたいとき
  • テストの依存関係やデータ設定を確認したいとき
JetBrains/intellij-community2.1万2026年10月26日 更新

code-style

無料概要あり

IntelliJのKotlin・Javaコードを新規作成やレビューする際に、言語の選び方、書式、型の扱い、例外処理などのプロジェクト規約に沿って整えるスキルです。

  • IntelliJ規約に沿ってKotlinを書きたいとき
  • Javaコードをレビューしたいとき
  • 例外処理やsealed型の分岐を確認したいとき
JetBrains/intellij-community2.1万2026年10月26日 更新

commits

無料概要あり

IntelliJ向けの変更を適切な単位に分け、チケットの有無に応じた形式で、理由や判断も伝わるコミットメッセージを作成・修正するスキルです。

  • チケット付き変更のメッセージを作りたいとき
  • テストやドキュメントのコミットを整理したいとき
  • 既存のコミットメッセージを修正したいとき
JetBrains/intellij-community2.1万2026年10月26日 更新

compare-python-typecheckers

無料概要あり

Pythonコードの型推論や型エラーを複数の型チェッカーで比較し、各ツールの判定と診断内容をまとめたレポートを作成します。

  • 型推論の違いを比較したいとき
  • 型エラーになるか確かめたいとき
  • 複数チェッカーの診断を見比べたいとき
JetBrains/intellij-community2.1万2026年10月26日 更新

compose-ui-architecture

無料概要あり

Compose Desktopの画面で状態や副作用をどこに持たせるか整理し、表示と判断の境界、再利用部品やリストの設計を整えます。

  • 画面とプレゼンターの責務を分けたいとき
  • UI状態の置き場所を整理したいとき
  • Effectやコルーチンの使い方を見直したいとき
JetBrains/intellij-community2.1万2026年10月26日 更新

JetBrains のスキルをすべて見る

このスキルの問題を報告する