|
| 1 | +# Test Plan: Java Dependency — Classpath / Referenced Libraries |
| 2 | +# |
| 3 | +# Covers the referenced-library management commands contributed by |
| 4 | +# vscode-java-dependency. These commands are only active for invisible |
| 5 | +# (unmanaged-folder) projects — Maven / Gradle projects manage their |
| 6 | +# classpath through pom.xml / build.gradle and do NOT expose the |
| 7 | +# Referenced Libraries container's inline actions. |
| 8 | +# |
| 9 | +# Commands exercised: |
| 10 | +# - java.project.refreshLibraries (Refresh — inline title icon on Referenced Libraries) |
| 11 | +# - java.project.addLibraries (Add Jar Libraries… — inline `+` icon) |
| 12 | +# - java.project.removeLibrary (Remove from Project Classpath — invoked |
| 13 | +# by command id in both `include`-removal |
| 14 | +# and `exclude`-addition modes) |
| 15 | +# - java.project.addLibraryFolders (Add Library Folders… — Alt-variant of `+` icon; |
| 16 | +# no plain-click UI affordance, invoked via command path) |
| 17 | +# |
| 18 | +# Verification strategy |
| 19 | +# ───────────────────── |
| 20 | +# `referencedLibraries` is a workspace setting (`java.project.referencedLibraries`) |
| 21 | +# whose include/exclude globs are reflected live in the JAVA PROJECTS tree under |
| 22 | +# the "Referenced Libraries" container. Each command we exercise either inserts |
| 23 | +# a new include glob (addLibraries / addLibraryFolders), removes/excludes one |
| 24 | +# (removeLibrary), or re-reads the setting from disk (refreshLibraries). We |
| 25 | +# therefore assert state by name-matching jar leaves in the tree with the |
| 26 | +# deterministic `verifyTreeItem` block — both presence (`visible: true`, the |
| 27 | +# default) and absence (`visible: false`). |
| 28 | +# |
| 29 | +# Substring matching for jar leaves |
| 30 | +# ───────────────────────────────── |
| 31 | +# Each jar leaf is rendered as "<basename>.jar <resolved-path-description>", |
| 32 | +# so the accessible name carries the full resolved path. We deliberately omit |
| 33 | +# `exact: true` on jar `verifyTreeItem` blocks — the driver falls back to |
| 34 | +# substring matching, which is exactly what we need to locate a leaf by its |
| 35 | +# basename. The project root (`invisible`) keeps `exact: true` because no |
| 36 | +# description is appended to project-level rows. |
| 37 | +# |
| 38 | +# Fixture layout (test/invisible) |
| 39 | +# ─────────────────────────────── |
| 40 | +# .vscode/settings.json java.project.referencedLibraries = ["lib/**/*.jar"] |
| 41 | +# lib/simple.jar already attached at startup via the include glob |
| 42 | +# libSource/simple.jar existing companion file (unrelated to this plan) |
| 43 | +# extraJars/extra-a.jar added/removed via the UI in cycles 2 + 3 |
| 44 | +# extraJars/extra-b.jar surfaced by cycle 4's folder-add (extra-a stays excluded) |
| 45 | +# |
| 46 | +# Native file/folder pickers are intercepted by `setup.mockOpenDialog` — the |
| 47 | +# first entry is consumed by `addLibraries`, the second by `addLibraryFolders`. |
| 48 | +# |
| 49 | +# Usage: |
| 50 | +# npx autotest run test/e2e-plans/java-dep-classpath.yaml --vsix <path-to-vsix> |
| 51 | + |
| 52 | +name: "Java Dependency — Classpath / Referenced Libraries" |
| 53 | +description: | |
| 54 | + Tests the four referenced-library commands on an invisible (unmanaged) |
| 55 | + Java project: refreshLibraries, addLibraries, removeLibrary, and |
| 56 | + addLibraryFolders. |
| 57 | +
|
| 58 | +setup: |
| 59 | + extension: "redhat.java" |
| 60 | + vscodeVersion: "stable" |
| 61 | + workspace: "../invisible" |
| 62 | + timeout: 240 |
| 63 | + settings: |
| 64 | + java.configuration.checkProjectSettingsExclusions: false |
| 65 | + workbench.startupEditor: "none" |
| 66 | + # Native file/folder pickers are mocked at the Electron `dialog.showOpenDialog` |
| 67 | + # layer, but VS Code's smoke-test driver suppresses the native dialog and |
| 68 | + # falls back to its internal quick-pick `simpleFileDialog`. The mock therefore |
| 69 | + # never fires — instead we drive the simple dialog with `fillQuickInput`, |
| 70 | + # typing the resolved jar / folder path and pressing Enter. The mockOpenDialog |
| 71 | + # block below is kept as a behavioural reference (and harmless no-op). |
| 72 | + mockOpenDialog: |
| 73 | + - ["~/extraJars/extra-a.jar"] |
| 74 | + - ["~/extraJars"] |
| 75 | + |
| 76 | +steps: |
| 77 | + # ── Setup: activate the Java extension, wait for LS, clear sidebar ── |
| 78 | + # Invisible projects do not auto-activate redhat.java the way Maven / |
| 79 | + # Gradle workspaces do (those activate via the pom.xml / build.gradle |
| 80 | + # workspaceContains contributions). Open `src/App.java` first so the |
| 81 | + # Language Server actually starts — otherwise `waitForLanguageServer` |
| 82 | + # times out and the Java Projects view never registers. |
| 83 | + - id: "open-bootstrap-file" |
| 84 | + action: "open file src/App.java" |
| 85 | + |
| 86 | + - id: "ls-ready" |
| 87 | + action: "waitForLanguageServer" |
| 88 | + # No `verify:` — `waitForLanguageServer` is itself the deterministic |
| 89 | + # readiness check. The AFTER screenshot may transiently show |
| 90 | + # "Java: Building - 0%" which a strict LLM mis-reads as a failure. |
| 91 | + timeout: 180 |
| 92 | + |
| 93 | + - id: "close-aux-bar" |
| 94 | + action: "executeVSCodeCommand workbench.action.closeAuxiliaryBar" |
| 95 | + verify: "Auxiliary bar (Chat) closed" |
| 96 | + |
| 97 | + - id: "collapse-outline" |
| 98 | + action: "collapseSidebarSection OUTLINE" |
| 99 | + |
| 100 | + - id: "collapse-timeline" |
| 101 | + action: "collapseSidebarSection TIMELINE" |
| 102 | + |
| 103 | + # Single-folder workspaces don't have a collapsible aria-level=1 workspace |
| 104 | + # root inside `.explorer-folders-view`, so `collapseWorkspaceRoot` is a |
| 105 | + # no-op here. Instead collapse the whole `invisible` pane in the EXPLORER |
| 106 | + # view container — otherwise its top-level entries (.vscode/extraJars/ |
| 107 | + # lib/libSource/src) push the JAVA PROJECTS pane down so far that the |
| 108 | + # virtualised list does not render the jar leaves and `verifyTreeItem` |
| 109 | + # times out hunting an off-screen node. |
| 110 | + - id: "collapse-explorer-pane" |
| 111 | + action: "collapseSidebarSection invisible" |
| 112 | + |
| 113 | + - id: "focus-java-projects" |
| 114 | + action: "executeVSCodeCommand javaProjectExplorer.focus" |
| 115 | + verify: "Java Projects view is focused" |
| 116 | + |
| 117 | + - id: "wait-tree-load" |
| 118 | + action: "wait 5 seconds" |
| 119 | + |
| 120 | + # Invisible-project root takes the worktree folder name (`invisible`). |
| 121 | + - id: "verify-project-node" |
| 122 | + action: "wait 1 seconds" |
| 123 | + # No `verify:` — state-check step; `verifyTreeItem` is authoritative. |
| 124 | + verifyTreeItem: |
| 125 | + name: "invisible" |
| 126 | + exact: true |
| 127 | + timeout: 15 |
| 128 | + |
| 129 | + - id: "expand-project" |
| 130 | + action: "expandTreeItem invisible" |
| 131 | + waitBefore: 2 |
| 132 | + |
| 133 | + - id: "expand-referenced-libraries" |
| 134 | + action: "expandTreeItem Referenced Libraries" |
| 135 | + waitBefore: 2 |
| 136 | + |
| 137 | + # Baseline: lib/simple.jar matches the default include glob `lib/**/*.jar`. |
| 138 | + # No `exact:` — the jar row's accessible name includes a description with the |
| 139 | + # resolved jar path; substring match on the basename is sufficient and stable. |
| 140 | + - id: "verify-baseline-simple-jar" |
| 141 | + action: "wait 1 seconds" |
| 142 | + verifyTreeItem: |
| 143 | + name: "simple.jar" |
| 144 | + timeout: 15 |
| 145 | + |
| 146 | + # ── Cycle 1: java.project.refreshLibraries ── |
| 147 | + # Click the inline `$(refresh)` icon on the Referenced Libraries container. |
| 148 | + # The aria-label is the localised command title — here "Refresh". The action |
| 149 | + # is idempotent: nothing on disk changed, so simple.jar must remain attached. |
| 150 | + - id: "click-refresh-libraries" |
| 151 | + action: 'clickTreeItemAction "Referenced Libraries" "Refresh"' |
| 152 | + |
| 153 | + - id: "wait-after-refresh" |
| 154 | + action: "wait 3 seconds" |
| 155 | + |
| 156 | + - id: "verify-refresh-stable" |
| 157 | + action: "wait 1 seconds" |
| 158 | + verifyTreeItem: |
| 159 | + name: "simple.jar" |
| 160 | + timeout: 15 |
| 161 | + |
| 162 | + # ── Cycle 2: java.project.addLibraries ── |
| 163 | + # Click the inline `$(add)` icon on the Referenced Libraries container. |
| 164 | + # aria-label = "Add Jar Libraries to Project Classpath...". Partial match |
| 165 | + # on "Add Jar Libraries" is enough — the driver does `aria-label.includes()`. |
| 166 | + # In smoke-test mode VS Code substitutes its quick-pick `simpleFileDialog` |
| 167 | + # for the native picker, so we type the resolved jar path into the input |
| 168 | + # bar and press Enter via `fillQuickInput`. The command then appends the |
| 169 | + # new path to `java.project.referencedLibraries.include`. |
| 170 | + - id: "click-add-libraries" |
| 171 | + action: 'clickTreeItemAction "Referenced Libraries" "Add Jar Libraries"' |
| 172 | + |
| 173 | + - id: "type-add-libraries-path" |
| 174 | + action: "fillQuickInput ${workspaceFolder}/extraJars/extra-a.jar" |
| 175 | + |
| 176 | + - id: "wait-after-add" |
| 177 | + action: "wait 5 seconds" |
| 178 | + |
| 179 | + - id: "verify-extra-a-added" |
| 180 | + action: "wait 1 seconds" |
| 181 | + verifyTreeItem: |
| 182 | + name: "extra-a.jar" |
| 183 | + timeout: 20 |
| 184 | + |
| 185 | + # ── Cycle 3: java.project.removeLibrary ── |
| 186 | + # Invoke the command directly. The inline `$(remove)` icon on the jar leaf |
| 187 | + # is rendered only on row hover and its `<a class="action-label">` hit-target |
| 188 | + # is narrower than the wrapping `<li class="action-item">`, so the centre-of- |
| 189 | + # actionItem click that `clickTreeItemAction` performs lands consistently on |
| 190 | + # extension-contributed view-title icons (`+`, `$(refresh)`) but not on the |
| 191 | + # jar-leaf `–`. Driving the command by id sidesteps the hit-target gap and |
| 192 | + # exercises the same handler — `removeLibrary(Uri.parse(node.uri).fsPath)`. |
| 193 | + # |
| 194 | + # We pass a `{uri}` payload as JSON. JSON.parse runs before |
| 195 | + # resolveWorkspacePlaceholders, so `${workspaceFolderUri}` here is a literal |
| 196 | + # substring inside a valid JSON string. autotest substitutes it with a |
| 197 | + # correctly-formed `file:///...` URI for the current OS (drive-letter form |
| 198 | + # on Windows, plain absolute path on POSIX), so the same template round-trips |
| 199 | + # through `Uri.parse(...).fsPath` and `workspace.asRelativePath(...)` to |
| 200 | + # match the include entry that `java.project.addLibraries` wrote in cycle 2. |
| 201 | + - id: "invoke-remove-extra-a" |
| 202 | + action: 'executeVSCodeCommand java.project.removeLibrary {"uri":"${workspaceFolderUri}/extraJars/extra-a.jar"}' |
| 203 | + |
| 204 | + - id: "wait-after-remove" |
| 205 | + action: "wait 5 seconds" |
| 206 | + |
| 207 | + - id: "verify-extra-a-gone" |
| 208 | + action: "wait 1 seconds" |
| 209 | + verifyTreeItem: |
| 210 | + name: "extra-a.jar" |
| 211 | + visible: false |
| 212 | + timeout: 20 |
| 213 | + |
| 214 | + - id: "verify-simple-still-present" |
| 215 | + action: "wait 1 seconds" |
| 216 | + verifyTreeItem: |
| 217 | + name: "simple.jar" |
| 218 | + timeout: 15 |
| 219 | + |
| 220 | + # ── Cycle 4: java.project.addLibraryFolders ── |
| 221 | + # The `addLibraryFolders` command has no plain-click affordance — it is |
| 222 | + # only bound as the `alt:` variant of the `+` icon (Alt-click). autotest |
| 223 | + # 0.7.x has no Alt-modifier tree-item action, so we invoke the command |
| 224 | + # directly. The same simpleFileDialog appears in folder-pick mode; we |
| 225 | + # type the resolved folder path and press Enter. The command appends |
| 226 | + # `extraJars/**/*.jar` (the folder glob) to the include list. |
| 227 | + # |
| 228 | + # Because extra-a.jar is now in the exclude list, only extra-b.jar |
| 229 | + # surfaces from the folder-add — making this a sharp differentiation |
| 230 | + # test against cycle 3's removeLibrary outcome. |
| 231 | + - id: "invoke-add-library-folders" |
| 232 | + action: "executeVSCodeCommand java.project.addLibraryFolders" |
| 233 | + |
| 234 | + # In folder-pick mode the simpleFileDialog treats Enter on a folder as |
| 235 | + # "navigate into", so `fillQuickInput` types the path AND opens the folder. |
| 236 | + # The "Select Library Folders" button is the explicit confirmation |
| 237 | + # affordance; clicking it is what actually returns the URI to the command. |
| 238 | + - id: "type-add-folder-path" |
| 239 | + action: "fillQuickInput ${workspaceFolder}/extraJars" |
| 240 | + |
| 241 | + - id: "confirm-folder-select" |
| 242 | + action: "tryClickButton Select Library Folders" |
| 243 | + |
| 244 | + - id: "wait-after-add-folder" |
| 245 | + action: "wait 5 seconds" |
| 246 | + |
| 247 | + - id: "verify-extra-b-via-folder" |
| 248 | + action: "wait 1 seconds" |
| 249 | + verifyTreeItem: |
| 250 | + name: "extra-b.jar" |
| 251 | + timeout: 20 |
| 252 | + |
| 253 | + # Sanity-check that extra-a.jar is back too — `removeLibrary` in cycle 3 |
| 254 | + # only stripped the explicit `extraJars/extra-a.jar` include entry; it did |
| 255 | + # NOT add an exclude. The folder glob `extraJars/**/*.jar` therefore |
| 256 | + # re-attaches both jars. This is the exact behavioural contract documented |
| 257 | + # in libraryController.ts (the `if (removedPaths.length === 0)` branch |
| 258 | + # only fires for glob-matched jars). |
| 259 | + - id: "verify-extra-a-reattached-via-glob" |
| 260 | + action: "wait 1 seconds" |
| 261 | + verifyTreeItem: |
| 262 | + name: "extra-a.jar" |
| 263 | + timeout: 15 |
| 264 | + |
| 265 | + # ── Cycle 5: java.project.removeLibrary (exclude code path) ── |
| 266 | + # extra-a.jar is now attached via the folder glob, NOT an explicit include. |
| 267 | + # Removing it now exercises the second branch of removeLibrary: the include |
| 268 | + # list has no exact match for the relative path, so the handler appends |
| 269 | + # `extraJars/extra-a.jar` to `referencedLibraries.exclude`. The folder |
| 270 | + # glob still attaches extra-b.jar, so it must stay visible while extra-a |
| 271 | + # disappears — proving the exclude path works independently of the include |
| 272 | + # removal already covered in cycle 3. |
| 273 | + - id: "invoke-remove-extra-a-glob" |
| 274 | + action: 'executeVSCodeCommand java.project.removeLibrary {"uri":"${workspaceFolderUri}/extraJars/extra-a.jar"}' |
| 275 | + |
| 276 | + - id: "wait-after-glob-remove" |
| 277 | + action: "wait 5 seconds" |
| 278 | + |
| 279 | + - id: "verify-extra-a-excluded" |
| 280 | + action: "wait 1 seconds" |
| 281 | + verifyTreeItem: |
| 282 | + name: "extra-a.jar" |
| 283 | + visible: false |
| 284 | + timeout: 20 |
| 285 | + |
| 286 | + - id: "verify-extra-b-still-via-glob" |
| 287 | + action: "wait 1 seconds" |
| 288 | + verifyTreeItem: |
| 289 | + name: "extra-b.jar" |
| 290 | + timeout: 15 |
0 commit comments