Skip to content

Commit 4db30ab

Browse files
authored
test(e2e): cover classpath / referenced-libraries commands (#1028)
* test(fixture): add extra-a.jar/extra-b.jar for classpath e2e plan * test(e2e): add classpath / referenced-libraries plan Cover the four java.project.* classpath commands on an invisible (unmanaged-folder) project: - java.project.refreshLibraries -> Refresh inline icon, idempotent - java.project.addLibraries -> + inline icon + simpleFileDialog - java.project.removeLibrary -> include-removal branch - java.project.addLibraryFolders -> command id + Select Library Folders - java.project.removeLibrary (#2) -> exclude-addition branch Reuse the existing est/invisible fixture and the two new �xtraJars/extra-{a,b}.jar files committed earlier so all 33 steps run deterministically against the worktree workspace. Auto-discovered by the existing matrix in .github/workflows/e2eUI.yml, no CI wiring needed. * fix(e2e): use cross-platform workspaceFolderUri for removeLibrary URI The previous template file:///${workspaceFolder}/extraJars/extra-a.jar happened to round-trip through vscode.Uri.parse(...).fsPath on Windows (where ${workspaceFolder} is a C:\... path) but on Linux it produced file:////home/runner/... (FOUR slashes). Uri.parse mapped that to a //home/... path, so workspace.asRelativePath did not match the include entry and removeLibrary silently did nothing -- both verify-extra-a-gone and verify-extra-a-excluded then timed out on the Linux CI matrix. Switch to the new ${workspaceFolderUri} placeholder (autotest 0.7.13), which is backed by Node's url.pathToFileURL and produces a correctly formed file:// URI on both Windows and POSIX.
1 parent 11a0645 commit 4db30ab

3 files changed

Lines changed: 290 additions & 0 deletions

File tree

Lines changed: 290 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,290 @@
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
901 Bytes
Binary file not shown.
901 Bytes
Binary file not shown.

0 commit comments

Comments
 (0)