mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
WEB-78041 document remote-driver UI-test gotchas in driver-ui-tests skill
Captures lessons from building the Figma connection UI test that the skill did not cover: reading IDE-side state via @Remote (the plugin= classloader field for content-module classes and the declared-class method-dispatch pitfall), matching text-bearing toolbar/title actions that render as ActionButtonWithText rather than ActionButton, seeding a registry flag at startup via a -D system property, and driving a real browser with Playwright from the test JVM. Edited the community source and re-rendered the generated skill copies. (cherry picked from commit b3fd5d34005c7f53b15a72fa22d1de4318bed0c2) GitOrigin-RevId: b4407dc0dc580ada86e64fd9a99296432c0b42ac
This commit is contained in:
committed by
intellij-monorepo-bot
parent
f2c673e845
commit
3096a84009
@@ -108,6 +108,18 @@ val detailPane = ui.x { byClass("PluginDetailsPageComponent") }
|
||||
detailPane.x { byClass("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. Match by visible text across both variants:
|
||||
|
||||
```kotlin
|
||||
// Matches both icon-only and text-bearing action buttons
|
||||
fun Finder.statusButton(text: String) =
|
||||
x("//div[(@class='ActionButtonWithText' or @class='ActionButton') and @visible_text='$text']")
|
||||
```
|
||||
|
||||
The visible text is itself a reliable assertion signal — you usually do not need to read the backing service/state.
|
||||
|
||||
## Keyboard Interactions
|
||||
|
||||
```kotlin
|
||||
@@ -250,6 +262,38 @@ waitFor("text to appear", 10.seconds) {
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
```kotlin
|
||||
@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):
|
||||
|
||||
```kotlin
|
||||
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:
|
||||
|
||||
@@ -109,6 +109,18 @@ val detailPane = ui.x { byClass("PluginDetailsPageComponent") }
|
||||
detailPane.x { byClass("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. Match by visible text across both variants:
|
||||
|
||||
```kotlin
|
||||
// Matches both icon-only and text-bearing action buttons
|
||||
fun Finder.statusButton(text: String) =
|
||||
x("//div[(@class='ActionButtonWithText' or @class='ActionButton') and @visible_text='$text']")
|
||||
```
|
||||
|
||||
The visible text is itself a reliable assertion signal — you usually do not need to read the backing service/state.
|
||||
|
||||
## Keyboard Interactions
|
||||
|
||||
```kotlin
|
||||
@@ -251,6 +263,38 @@ waitFor("text to appear", 10.seconds) {
|
||||
}
|
||||
```
|
||||
|
||||
## 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.
|
||||
|
||||
```kotlin
|
||||
@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):
|
||||
|
||||
```kotlin
|
||||
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:
|
||||
|
||||
Reference in New Issue
Block a user