From 2911ddfbd5cf8be928289d01961d653c960558dc Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 2 Jun 2026 17:10:40 +0800 Subject: [PATCH 1/5] Fix #914: refresh now surfaces externally-generated files in new packages The source package-root load only did a shallow (DEPTH_ONE) refresh, so files written to disk by external generators into brand-new packages never appeared in the Java Projects view, even after Refresh. Manual Refresh now deep-refreshes (DEPTH_INFINITE) and closes the package root so the Java Model re-enumerates its package-fragment list. Auto-refresh passes the changed resource URI to the server via PackageParams.syncPaths, which scopes the refresh to the nearest existing ancestor subtree instead of the whole source root, falling back to a full deep refresh when a path cannot be resolved. - PackageParams: add syncPaths field + accessors - PackageCommand: targeted refresh branch + findNearestExistingResource helper - packageRootNode: stash pending sync paths, snapshot/clear in loadData - syncHandler: onDidCreate stashes the created URI (cycle-safe NodeKind check) - add E2E plans for manual deep refresh and targeted auto-refresh --- .../jdtls/ext/core/PackageCommand.java | 100 ++++++++++- .../jdtls/ext/core/PackageParams.java | 18 ++ src/syncHandler.ts | 13 +- src/views/packageRootNode.ts | 16 ++ .../java-dep-autorefresh-targeted.yaml | 130 ++++++++++++++ .../java-dep-refresh-generated-files.yaml | 158 ++++++++++++++++++ 6 files changed, 431 insertions(+), 4 deletions(-) create mode 100644 test/e2e-plans/java-dep-autorefresh-targeted.yaml create mode 100644 test/e2e-plans/java-dep-refresh-generated-files.yaml diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java index cada8754..025d014b 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java @@ -371,7 +371,7 @@ private static List getPackageRootChildren(PackageParams query, IPr throw new CoreException( new Status(IStatus.ERROR, JdtlsExtActivator.PLUGIN_ID, String.format("No package root found for %s", query.getPath()))); } - List result = getPackageFragmentRootContent(packageRoot, query.isHierarchicalView(), pm); + List result = getPackageFragmentRootContent(packageRoot, query.isHierarchicalView(), query.getSyncPaths(), pm); ResourceSet resourceSet = new ResourceSet(result, query.isHierarchicalView()); ResourceVisitor visitor = new JavaResourceVisitor(packageRoot.getJavaProject()); resourceSet.accept(visitor); @@ -508,8 +508,59 @@ private static List getFolderChildren(PackageParams query, IProgres * @param pm the progress monitor */ public static List getPackageFragmentRootContent(IPackageFragmentRoot root, boolean isHierarchicalView, IProgressMonitor pm) throws CoreException { + return getPackageFragmentRootContent(root, isHierarchicalView, null, pm); + } + + public static List getPackageFragmentRootContent(IPackageFragmentRoot root, boolean isHierarchicalView, List syncPaths, IProgressMonitor pm) throws CoreException { ArrayList result = new ArrayList<>(); - refreshLocal(root.getResource(), pm); + IResource rootResource = root.getResource(); + if (rootResource instanceof IContainer && rootResource.exists() + && root.getKind() == IPackageFragmentRoot.K_SOURCE) { + // Packages created out-of-band (e.g. by code generators or + // refactor-moves that write straight to disk) are otherwise never + // surfaced. A shallow DEPTH_ONE refresh only syncs the source root's + // immediate children and never discovers brand-new nested package + // folders. Even a DEPTH_INFINITE resource refresh is not enough on + // its own: the Java Model keeps a cached list of package fragments + // for the root, so closing it forces getChildren() below to rebuild + // that list from the freshly refreshed resource tree. + // + // On auto-refresh the client passes the changed resource URIs in + // syncPaths so we only deep-refresh those subtrees instead of the + // whole source tree. If any path cannot be resolved to an existing + // resource inside this root we conservatively fall back to a full + // DEPTH_INFINITE refresh so no package is ever missed. + // See https://github.com/microsoft/vscode-java-dependency/issues/914 + boolean refreshedTargets = false; + if (syncPaths != null && !syncPaths.isEmpty()) { + List targets = new ArrayList<>(); + boolean allResolved = true; + for (String syncPath : syncPaths) { + IResource target = findNearestExistingResource(syncPath, (IContainer) rootResource); + if (target == null) { + allResolved = false; + break; + } + targets.add(target); + } + if (allResolved && !targets.isEmpty()) { + for (IResource target : targets) { + refreshLocal(target, IResource.DEPTH_INFINITE, pm); + } + refreshedTargets = true; + } + } + if (!refreshedTargets) { + refreshLocal(rootResource, IResource.DEPTH_INFINITE, pm); + } + try { + root.close(); + } catch (JavaModelException e) { + JdtlsExtActivator.log(e); + } + } else { + refreshLocal(rootResource, IResource.DEPTH_ONE, pm); + } if (isHierarchicalView) { Map map = new HashMap<>(); for (IJavaElement child : root.getChildren()) { @@ -599,13 +650,56 @@ public static IJavaProject getJavaProject(String projectUri) { } private static void refreshLocal(IResource resource, IProgressMonitor monitor) { + refreshLocal(resource, IResource.DEPTH_ONE, monitor); + } + + private static void refreshLocal(IResource resource, int depth, IProgressMonitor monitor) { if (resource == null || !resource.exists()) { return; } try { - resource.refreshLocal(IResource.DEPTH_ONE, monitor); + resource.refreshLocal(depth, monitor); } catch (CoreException e) { JdtlsExtActivator.log(e); } } + + /** + * Resolve a changed resource URI to the nearest ancestor that already exists + * in the workspace resource tree and lives inside the given source root. + * Used to scope an auto-refresh to only the affected subtree. Returns null + * when the URI cannot be mapped to a resource within the root, in which case + * the caller falls back to a full refresh so no package is missed. + */ + private static IResource findNearestExistingResource(String uriStr, IContainer root) { + if (StringUtils.isBlank(uriStr) || root == null) { + return null; + } + try { + URI uri = JDTUtils.toURI(uriStr); + if (uri == null) { + return null; + } + IWorkspaceRoot wsRoot = ResourcesPlugin.getWorkspace().getRoot(); + IResource resource = null; + IFile[] files = wsRoot.findFilesForLocationURI(uri); + if (files.length > 0) { + resource = files[0]; + } else { + IContainer[] containers = wsRoot.findContainersForLocationURI(uri); + if (containers.length > 0) { + resource = containers[0]; + } + } + while (resource != null && !resource.exists()) { + resource = resource.getParent(); + } + if (resource != null && root.getFullPath().isPrefixOf(resource.getFullPath())) { + return resource; + } + } catch (Exception e) { + JdtlsExtActivator.logException("Failed to resolve sync path " + uriStr, e); + } + return null; + } } diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageParams.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageParams.java index 4b6468ed..576563c5 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageParams.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageParams.java @@ -11,6 +11,8 @@ package com.microsoft.jdtls.ext.core; +import java.util.List; + import com.microsoft.jdtls.ext.core.model.NodeKind; /** @@ -31,6 +33,14 @@ public class PackageParams { private boolean isHierarchicalView; + /** + * Optional list of resource URIs (sent by the client on auto-refresh) that + * have just changed on disk. When present, the server only refreshes the + * affected subtrees instead of deeply refreshing the whole source root. + * See https://github.com/microsoft/vscode-java-dependency/issues/914 + */ + private List syncPaths; + public PackageParams() { } @@ -97,4 +107,12 @@ public void setRootPath(String rootPath) { this.rootPath = rootPath; } + public List getSyncPaths() { + return syncPaths; + } + + public void setSyncPaths(List syncPaths) { + this.syncPaths = syncPaths; + } + } diff --git a/src/syncHandler.ts b/src/syncHandler.ts index cbdd05eb..57dd97f9 100644 --- a/src/syncHandler.ts +++ b/src/syncHandler.ts @@ -90,7 +90,18 @@ class SyncHandler implements Disposable { })); this.disposables.push(watcher.onDidCreate((uri: Uri) => { - this.refresh(this.getParentNodeInExplorer(uri)); + const node: ExplorerNode | undefined = this.getParentNodeInExplorer(uri); + // When the created resource lands in a package that is not currently + // rendered, getParentNodeInExplorer resolves to the source root. Tell + // that root which path changed so the server can refresh only that + // subtree instead of deeply refreshing the whole source tree. Gate on + // the node kind (not instanceof) to avoid importing PackageRootNode + // here, which would create a module cycle and break activation. + // See https://github.com/microsoft/vscode-java-dependency/issues/914 + if (node instanceof DataNode && node.nodeData?.kind === NodeKind.PackageRoot) { + (node as unknown as { pendingSyncPaths: Set }).pendingSyncPaths.add(uri.toString()); + } + this.refresh(node); })); this.disposables.push(watcher.onDidDelete((uri: Uri) => { diff --git a/src/views/packageRootNode.ts b/src/views/packageRootNode.ts index 63de2c65..46f18d30 100644 --- a/src/views/packageRootNode.ts +++ b/src/views/packageRootNode.ts @@ -16,6 +16,14 @@ import { NodeFactory } from "./nodeFactory"; export class PackageRootNode extends DataNode { + /** + * Resource URIs reported by the file watcher as newly created under this + * source root since the last load. Consumed on the next loadData() so the + * server can scope its filesystem refresh to only the changed subtrees. + * See https://github.com/microsoft/vscode-java-dependency/issues/914 + */ + public pendingSyncPaths: Set = new Set(); + constructor(nodeData: INodeData, parent: DataNode, protected _project: ProjectNode) { super(nodeData, parent); } @@ -25,12 +33,20 @@ export class PackageRootNode extends DataNode { } protected async loadData(): Promise { + let syncPaths: string[] | undefined; + if (this.pendingSyncPaths.size) { + // Snapshot and clear synchronously before the async server call so + // watcher events arriving during the await are not lost. + syncPaths = Array.from(this.pendingSyncPaths); + this.pendingSyncPaths.clear(); + } return Jdtls.getPackageData({ kind: NodeKind.PackageRoot, projectUri: this._project.nodeData.uri, rootPath: this.nodeData.path, handlerIdentifier: this.nodeData.handlerIdentifier, isHierarchicalView: Settings.isHierarchicalView(), + syncPaths, }); } diff --git a/test/e2e-plans/java-dep-autorefresh-targeted.yaml b/test/e2e-plans/java-dep-autorefresh-targeted.yaml new file mode 100644 index 00000000..f4037f38 --- /dev/null +++ b/test/e2e-plans/java-dep-autorefresh-targeted.yaml @@ -0,0 +1,130 @@ +# Test Plan: Java Dependency — Auto-refresh surfaces a new package via the +# targeted (scoped) server refresh +# +# Companion to java-dep-refresh-generated-files.yaml. That plan validates the +# MANUAL Refresh command (which deep-refreshes the whole source root). This plan +# validates the performance optimization for issue #914 (方案A / option A): +# +# On AUTO-refresh, the extension passes the changed resource URI to the server +# (PackageParams.syncPaths). The server then refreshes ONLY the affected +# subtree (the nearest existing ancestor of the changed path) instead of doing +# a DEPTH_INFINITE refresh of the entire source tree, and closes the package +# root so its cached package-fragment list is rebuilt. +# +# Flow under test: +# syncHandler FileSystemWatcher.onDidCreate(uri) +# -> getParentNodeInExplorer(uri) resolves to the (rendered) source root, +# because the new package is not yet rendered +# -> stashes uri in PackageRootNode.pendingSyncPaths +# -> refresh(rootNode) +# -> PackageRootNode.loadData() sends syncPaths to the server +# -> PackageCommand targeted refresh + IPackageFragmentRoot.close() +# -> the brand-new package appears WITHOUT a manual Refresh. +# +# This deliberately does NOT call java.view.package.refresh — it relies solely on +# the file watcher's auto-refresh, so it exercises the syncPaths code path. +# Auto-refresh must be enabled (java.dependency.autoRefresh defaults to true). +# +# Note on tree layout: the Java Projects tree is virtualized, so off-screen rows +# are not in the DOM. The plan keeps the tree compact — the Explorer file tree is +# collapsed and explorer.autoReveal is disabled — and asserts the new package +# while every package node is collapsed. +# +# Expected: PASSES after the 方案A change (targeted auto-refresh surfaces the +# new package). Pre-fix (DEPTH_ONE) it never appears. +# +# Usage: +# npx autotest run test/e2e-plans/java-dep-autorefresh-targeted.yaml \ +# --override extensionPath= + +name: "Java Dependency — Auto-refresh surfaces a new package (targeted, #914)" +description: | + Validates the targeted (scoped) auto-refresh optimization for issue #914: a + .java file written by an external generator into a brand-new sub-package must + appear in the Java Projects view via the file-watcher auto-refresh alone (no + manual Refresh), which routes the changed URI through PackageParams.syncPaths. + +setup: + extension: "redhat.java" + vscodeVersion: "stable" + workspace: "../maven" + timeout: 180 + settings: + java.configuration.checkProjectSettingsExclusions: false + workbench.startupEditor: "none" + explorer.autoReveal: false + +steps: + - id: "ls-ready" + action: "waitForLanguageServer" + timeout: 180 + + # ── Free vertical space so the Java Projects tree is not occluded ── + - id: "close-aux-bar" + action: "executeVSCodeCommand workbench.action.closeAuxiliaryBar" + + - id: "collapse-outline" + action: "collapseSidebarSection OUTLINE" + + - id: "collapse-timeline" + action: "collapseSidebarSection TIMELINE" + + - id: "collapse-explorer-folders" + action: "collapseSidebarSection MAVEN" + + - id: "focus-java-projects" + action: "executeVSCodeCommand javaProjectExplorer.focus" + verify: "Java Projects view is focused" + + - id: "wait-tree-load" + action: "wait 3 seconds" + + # ── Establish a well-defined baseline: the source root must be EXPANDED so a + # PackageRootNode exists for the watcher to target. ── + - id: "expand-project" + action: "expandTreeItem my-app" + verify: "my-app project expanded" + + - id: "expand-source-root" + action: "expandTreeItem src/main/java" + verify: "source root src/main/java expanded; package nodes visible" + + - id: "baseline-existing-pkg" + action: "wait 1 seconds" + verify: "Existing package com.mycompany.app visible (sanity check of tree state)" + verifyTreeItem: + name: "com.mycompany.app" + visible: true + + # ── Simulate a code generator writing a file straight to disk into a + # BRAND-NEW sub-package (com.mycompany.app.autogen). insertLineInFile uses + # fs.writeFileSync, bypassing VS Code's file service — exactly an external + # generator — which the FileSystemWatcher then observes. ── + - id: "gen-file-new-pkg" + action: "insertLineInFile src/main/java/com/mycompany/app/autogen/Gen914AutoNewPkg.java 1 package com.mycompany.app.autogen;\n\npublic class Gen914AutoNewPkg {\n}\n" + verify: "Gen914AutoNewPkg.java written to disk under brand-new package com.mycompany.app.autogen" + + # Dismiss any transient editor/quick-open overlay left by opening the file. + - id: "dismiss-overlay" + action: "pressKey Escape" + + # Give the file watcher + debounced auto-refresh time to fire. NO manual + # Refresh is issued — surfacing the package here proves the auto-refresh path. + - id: "wait-for-auto-refresh" + action: "wait 6 seconds" + + # Make sure the source root is expanded so its (collapsed) package nodes show. + - id: "reexpand-source-root" + action: "expandTreeItem src/main/java" + + # ── Assertion (the targeted auto-refresh) ───────────────────────────── + # Every package node is collapsed here, so the tree is compact and the new + # sibling package node is within the viewport. waitForTreeItem polls until the + # item is visible or the timeout elapses. + - id: "check-new-pkg-autorefresh" + action: "wait 1 seconds" + verify: "Brand-new package com.mycompany.app.autogen appears via auto-refresh alone (no manual Refresh)" + verifyTreeItem: + name: "com.mycompany.app.autogen" + visible: true + timeout: 20 diff --git a/test/e2e-plans/java-dep-refresh-generated-files.yaml b/test/e2e-plans/java-dep-refresh-generated-files.yaml new file mode 100644 index 00000000..81047cba --- /dev/null +++ b/test/e2e-plans/java-dep-refresh-generated-files.yaml @@ -0,0 +1,158 @@ +# Test Plan: Java Dependency — Refresh surfaces externally generated files +# +# Regression test for https://github.com/microsoft/vscode-java-dependency/issues/914 +# "Refresh option doesn't work, classes not visible" +# +# Root cause: jdtls.ext PackageCommand.getPackageFragmentRootContent() called +# refreshLocal(root.getResource(), IResource.DEPTH_ONE), a shallow refresh that +# only syncs the source root's immediate children. A file written into a +# BRAND-NEW nested package folder (e.g. by a code generator or a refactor-move) +# was therefore never discovered: JDT created no IPackageFragment for it, so the +# Java Projects view stayed empty even after a manual Refresh — while the Folder +# (Explorer) view, which reads the filesystem directly, showed the file. +# +# The fix deep-refreshes the source root (DEPTH_INFINITE) and closes the +# IPackageFragmentRoot so its cached child list is rebuilt from the refreshed +# resource tree. +# +# This test writes .java files straight to disk (insertLineInFile uses +# fs.writeFileSync, bypassing VS Code's file service — i.e. exactly an external +# generator), then invokes the explicit Refresh command and asserts that it +# surfaces BOTH a brand-new sub-package AND a new class in an existing package. +# The brand-new package is the decisive case: pre-fix it never appears because +# DEPTH_ONE never descends into the source root to discover the new folder. +# +# Note on tree layout: the Java Projects tree is virtualized, so an off-screen +# row is not present in the DOM. The plan therefore keeps the tree compact — +# the Explorer file tree is collapsed (and explorer.autoReveal is disabled so +# opening generated files does not re-expand it), and the brand-new package is +# asserted while every package node is collapsed, before the existing package is +# expanded for its class-level check. +# +# Expected: FAILS before the fix (the brand-new package never appears), +# PASSES after the PackageCommand.java fix. +# +# Usage: +# npx autotest run test/e2e-plans/java-dep-refresh-generated-files.yaml \ +# --override extensionPath= +# # or in CI with the built VSIX: +# npx autotest run test/e2e-plans/java-dep-refresh-generated-files.yaml --vsix + +name: "Java Dependency — Refresh surfaces externally generated files (#914)" +description: | + Regression for issue #914. Externally written .java files — one in a brand-new + sub-package, one in an existing package — must both appear in the Java Projects + view after an explicit Refresh. + +setup: + extension: "redhat.java" + vscodeVersion: "stable" + workspace: "../maven" + timeout: 180 + settings: + java.configuration.checkProjectSettingsExclusions: false + workbench.startupEditor: "none" + # Keep the Explorer file tree collapsed when generated files are opened, so + # the Java Projects view keeps its full height and rows are not virtualized + # out of the DOM. + explorer.autoReveal: false + +steps: + - id: "ls-ready" + action: "waitForLanguageServer" + timeout: 180 + + # ── Free vertical space so the Java Projects tree is not occluded ── + - id: "close-aux-bar" + action: "executeVSCodeCommand workbench.action.closeAuxiliaryBar" + + - id: "collapse-outline" + action: "collapseSidebarSection OUTLINE" + + - id: "collapse-timeline" + action: "collapseSidebarSection TIMELINE" + + # Collapse the Explorer "folders" (file tree) section entirely so the Java + # Projects view section gets the full sidebar height — otherwise its rows are + # virtualized out of the DOM and cannot be asserted on. + - id: "collapse-explorer-folders" + action: "collapseSidebarSection MAVEN" + + - id: "focus-java-projects" + action: "executeVSCodeCommand javaProjectExplorer.focus" + verify: "Java Projects view is focused" + + - id: "wait-tree-load" + action: "wait 3 seconds" + + # ── Establish a well-defined baseline tree state ── + - id: "expand-project" + action: "expandTreeItem my-app" + verify: "my-app project expanded" + + - id: "expand-source-root" + action: "expandTreeItem src/main/java" + verify: "source root src/main/java expanded; package nodes visible" + + - id: "baseline-existing-pkg" + action: "wait 1 seconds" + verify: "Existing package com.mycompany.app visible (sanity check of tree state)" + verifyTreeItem: + name: "com.mycompany.app" + visible: true + + # ── Simulate a code generator writing files directly to disk ────────── + # A) new class inside a BRAND-NEW sub-package (com.mycompany.app.gen) + - id: "gen-file-new-pkg" + action: "insertLineInFile src/main/java/com/mycompany/app/gen/Gen914InNewPkg.java 1 package com.mycompany.app.gen;\n\npublic class Gen914InNewPkg {\n}\n" + verify: "Gen914InNewPkg.java written to disk under brand-new package com.mycompany.app.gen" + + # B) new class inside an EXISTING package (com.mycompany.app) + - id: "gen-file-existing-pkg" + action: "insertLineInFile src/main/java/com/mycompany/app/Gen914InExisting.java 1 package com.mycompany.app;\n\npublic class Gen914InExisting {\n}\n" + verify: "Gen914InExisting.java written to disk under existing package" + + # Dismiss any transient editor/quick-open overlay left by opening the files. + - id: "dismiss-overlay" + action: "pressKey Escape" + + - id: "settle" + action: "wait 3 seconds" + + # ── The behaviour under test: an explicit manual Refresh ────────────── + - id: "manual-refresh" + action: "executeVSCodeCommand java.view.package.refresh" + verify: "Java Projects view refreshed via the Refresh command" + + - id: "wait-after-refresh" + action: "wait 4 seconds" + + # Make sure the source root is expanded so its (collapsed) package nodes show. + - id: "reexpand-source-root" + action: "expandTreeItem src/main/java" + + # ── Assertion 1 (the core regression) ───────────────────────────────── + # Every package node is still collapsed here, so the tree is compact and the + # brand-new sibling package node is within the viewport. Pre-fix this fails + # (DEPTH_ONE never discovers the new folder); post-fix it passes. + - id: "check-new-pkg" + action: "wait 1 seconds" + verify: "Brand-new package com.mycompany.app.gen appears after manual refresh" + verifyTreeItem: + name: "com.mycompany.app.gen" + visible: true + timeout: 15 + + # ── Assertion 2 (sanity) ────────────────────────────────────────────── + # A new class added to an already-known package also surfaces after Refresh. + - id: "expand-existing-pkg" + action: "expandTreeItem com.mycompany.app" + verify: "existing package com.mycompany.app expanded" + + - id: "check-existing-pkg-class" + action: "wait 1 seconds" + verify: "Gen914InExisting appears under com.mycompany.app after manual refresh" + verifyTreeItem: + name: "Gen914InExisting" + visible: true + timeout: 15 From 24157ed6fb6cfae0c704edf09a1b4a6177b647f0 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Wed, 3 Jun 2026 15:57:33 +0800 Subject: [PATCH 2/5] test: make #914 E2E assertions deterministic to fix CI false negatives CI runs the autotest harness with the LLM screenshot judge enabled, which the local --no-llm runs skipped. Four state-check steps sat on a wait action while asserting a state that was already true (the package/class had appeared in an earlier refresh step). The LLM judge diffed the before/after screenshots, saw no visual delta, and failed the steps -- even though the deterministic verifyTreeItem DOM assertion passed. This produced a spurious 18/20 in CI vs 20/20 locally. Remove the natural-language erify: field from those pure state-check steps and rely solely on the deterministic erifyTreeItem assertion, which is the actual behaviour under test. Steps where a visible change genuinely occurs within their own window keep their erify:. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- test/e2e-plans/java-dep-autorefresh-targeted.yaml | 5 +++-- test/e2e-plans/java-dep-refresh-generated-files.yaml | 9 ++++++--- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/test/e2e-plans/java-dep-autorefresh-targeted.yaml b/test/e2e-plans/java-dep-autorefresh-targeted.yaml index f4037f38..954176a7 100644 --- a/test/e2e-plans/java-dep-autorefresh-targeted.yaml +++ b/test/e2e-plans/java-dep-autorefresh-targeted.yaml @@ -91,7 +91,6 @@ steps: - id: "baseline-existing-pkg" action: "wait 1 seconds" - verify: "Existing package com.mycompany.app visible (sanity check of tree state)" verifyTreeItem: name: "com.mycompany.app" visible: true @@ -121,9 +120,11 @@ steps: # Every package node is collapsed here, so the tree is compact and the new # sibling package node is within the viewport. waitForTreeItem polls until the # item is visible or the timeout elapses. + # No `verify:` — verifyTreeItem is the authoritative deterministic assertion. + # An LLM screenshot diff here is timing-dependent (the package may appear just + # before or during this wait), so we rely solely on the DOM check. - id: "check-new-pkg-autorefresh" action: "wait 1 seconds" - verify: "Brand-new package com.mycompany.app.autogen appears via auto-refresh alone (no manual Refresh)" verifyTreeItem: name: "com.mycompany.app.autogen" visible: true diff --git a/test/e2e-plans/java-dep-refresh-generated-files.yaml b/test/e2e-plans/java-dep-refresh-generated-files.yaml index 81047cba..a0603573 100644 --- a/test/e2e-plans/java-dep-refresh-generated-files.yaml +++ b/test/e2e-plans/java-dep-refresh-generated-files.yaml @@ -96,7 +96,6 @@ steps: - id: "baseline-existing-pkg" action: "wait 1 seconds" - verify: "Existing package com.mycompany.app visible (sanity check of tree state)" verifyTreeItem: name: "com.mycompany.app" visible: true @@ -135,9 +134,12 @@ steps: # Every package node is still collapsed here, so the tree is compact and the # brand-new sibling package node is within the viewport. Pre-fix this fails # (DEPTH_ONE never discovers the new folder); post-fix it passes. + # No `verify:` — verifyTreeItem is the authoritative deterministic assertion. + # The new package becomes visible during the earlier wait-after-refresh step, + # so an LLM screenshot before/after diff on this pure wait would see no delta + # and yield a false negative. The DOM check below is what actually matters. - id: "check-new-pkg" action: "wait 1 seconds" - verify: "Brand-new package com.mycompany.app.gen appears after manual refresh" verifyTreeItem: name: "com.mycompany.app.gen" visible: true @@ -149,9 +151,10 @@ steps: action: "expandTreeItem com.mycompany.app" verify: "existing package com.mycompany.app expanded" + # No `verify:` — verifyTreeItem is the authoritative deterministic assertion + # (see check-new-pkg). The class is already visible by the time this wait runs. - id: "check-existing-pkg-class" action: "wait 1 seconds" - verify: "Gen914InExisting appears under com.mycompany.app after manual refresh" verifyTreeItem: name: "Gen914InExisting" visible: true From cf41b6dbf4938ad293b23c67f34dd16183a0f467 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Wed, 3 Jun 2026 16:47:38 +0800 Subject: [PATCH 3/5] test: drop LLM verify on file-write/refresh steps and trim comments (#914) The Windows-UI run still failed (19/20): the LLM screenshot judge could not confirm the insertLineInFile step because the written file is not visible in the collapsed Explorer (autoReveal off), a flaky judgment that passed on Linux but failed on Windows. Remove erify: from all steps with no reliable visual signal (file writes, manual refresh, view focus); keep it only on expandTreeItem steps, which produce a clear visual change. Deterministic verifyTreeItem remains the real assertion. Also trim the verbose header/inline comments. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../java-dep-autorefresh-targeted.yaml | 70 ++++------------ .../java-dep-refresh-generated-files.yaml | 81 ++++--------------- 2 files changed, 34 insertions(+), 117 deletions(-) diff --git a/test/e2e-plans/java-dep-autorefresh-targeted.yaml b/test/e2e-plans/java-dep-autorefresh-targeted.yaml index 954176a7..202c573c 100644 --- a/test/e2e-plans/java-dep-autorefresh-targeted.yaml +++ b/test/e2e-plans/java-dep-autorefresh-targeted.yaml @@ -1,37 +1,16 @@ -# Test Plan: Java Dependency — Auto-refresh surfaces a new package via the -# targeted (scoped) server refresh +# Validates the targeted (scoped) auto-refresh optimization for issue #914. +# Companion to java-dep-refresh-generated-files.yaml (which tests manual Refresh). # -# Companion to java-dep-refresh-generated-files.yaml. That plan validates the -# MANUAL Refresh command (which deep-refreshes the whole source root). This plan -# validates the performance optimization for issue #914 (方案A / option A): +# On auto-refresh the extension passes the changed URI to the server +# (PackageParams.syncPaths); the server refreshes only the affected subtree (the +# nearest existing ancestor) instead of the whole source root, then closes the +# package root to rebuild its package-fragment list. This plan does NOT call +# java.view.package.refresh — it relies solely on the FileSystemWatcher, so it +# exercises the syncPaths path. Auto-refresh is on by default. # -# On AUTO-refresh, the extension passes the changed resource URI to the server -# (PackageParams.syncPaths). The server then refreshes ONLY the affected -# subtree (the nearest existing ancestor of the changed path) instead of doing -# a DEPTH_INFINITE refresh of the entire source tree, and closes the package -# root so its cached package-fragment list is rebuilt. -# -# Flow under test: -# syncHandler FileSystemWatcher.onDidCreate(uri) -# -> getParentNodeInExplorer(uri) resolves to the (rendered) source root, -# because the new package is not yet rendered -# -> stashes uri in PackageRootNode.pendingSyncPaths -# -> refresh(rootNode) -# -> PackageRootNode.loadData() sends syncPaths to the server -# -> PackageCommand targeted refresh + IPackageFragmentRoot.close() -# -> the brand-new package appears WITHOUT a manual Refresh. -# -# This deliberately does NOT call java.view.package.refresh — it relies solely on -# the file watcher's auto-refresh, so it exercises the syncPaths code path. -# Auto-refresh must be enabled (java.dependency.autoRefresh defaults to true). -# -# Note on tree layout: the Java Projects tree is virtualized, so off-screen rows -# are not in the DOM. The plan keeps the tree compact — the Explorer file tree is -# collapsed and explorer.autoReveal is disabled — and asserts the new package -# while every package node is collapsed. -# -# Expected: PASSES after the 方案A change (targeted auto-refresh surfaces the -# new package). Pre-fix (DEPTH_ONE) it never appears. +# Tree layout and verify policy are the same as the manual-Refresh plan: compact +# virtualized tree, deterministic verifyTreeItem assertions, no `verify:` on the +# file-write step (no reliable visual signal for the LLM judge). # # Usage: # npx autotest run test/e2e-plans/java-dep-autorefresh-targeted.yaml \ @@ -59,7 +38,7 @@ steps: action: "waitForLanguageServer" timeout: 180 - # ── Free vertical space so the Java Projects tree is not occluded ── + # Free vertical space so the Java Projects tree is not virtualized. - id: "close-aux-bar" action: "executeVSCodeCommand workbench.action.closeAuxiliaryBar" @@ -74,20 +53,18 @@ steps: - id: "focus-java-projects" action: "executeVSCodeCommand javaProjectExplorer.focus" - verify: "Java Projects view is focused" - id: "wait-tree-load" action: "wait 3 seconds" - # ── Establish a well-defined baseline: the source root must be EXPANDED so a - # PackageRootNode exists for the watcher to target. ── + # The source root must be expanded so a PackageRootNode exists to target. - id: "expand-project" action: "expandTreeItem my-app" verify: "my-app project expanded" - id: "expand-source-root" action: "expandTreeItem src/main/java" - verify: "source root src/main/java expanded; package nodes visible" + verify: "source root src/main/java expanded" - id: "baseline-existing-pkg" action: "wait 1 seconds" @@ -95,34 +72,21 @@ steps: name: "com.mycompany.app" visible: true - # ── Simulate a code generator writing a file straight to disk into a - # BRAND-NEW sub-package (com.mycompany.app.autogen). insertLineInFile uses - # fs.writeFileSync, bypassing VS Code's file service — exactly an external - # generator — which the FileSystemWatcher then observes. ── + # Write a file straight to disk into a brand-new sub-package. - id: "gen-file-new-pkg" action: "insertLineInFile src/main/java/com/mycompany/app/autogen/Gen914AutoNewPkg.java 1 package com.mycompany.app.autogen;\n\npublic class Gen914AutoNewPkg {\n}\n" - verify: "Gen914AutoNewPkg.java written to disk under brand-new package com.mycompany.app.autogen" - # Dismiss any transient editor/quick-open overlay left by opening the file. - id: "dismiss-overlay" action: "pressKey Escape" - # Give the file watcher + debounced auto-refresh time to fire. NO manual - # Refresh is issued — surfacing the package here proves the auto-refresh path. + # Let the file watcher + debounced auto-refresh fire. No manual Refresh. - id: "wait-for-auto-refresh" action: "wait 6 seconds" - # Make sure the source root is expanded so its (collapsed) package nodes show. - id: "reexpand-source-root" action: "expandTreeItem src/main/java" - # ── Assertion (the targeted auto-refresh) ───────────────────────────── - # Every package node is collapsed here, so the tree is compact and the new - # sibling package node is within the viewport. waitForTreeItem polls until the - # item is visible or the timeout elapses. - # No `verify:` — verifyTreeItem is the authoritative deterministic assertion. - # An LLM screenshot diff here is timing-dependent (the package may appear just - # before or during this wait), so we rely solely on the DOM check. + # The brand-new package appears via auto-refresh alone. - id: "check-new-pkg-autorefresh" action: "wait 1 seconds" verifyTreeItem: diff --git a/test/e2e-plans/java-dep-refresh-generated-files.yaml b/test/e2e-plans/java-dep-refresh-generated-files.yaml index a0603573..28432406 100644 --- a/test/e2e-plans/java-dep-refresh-generated-files.yaml +++ b/test/e2e-plans/java-dep-refresh-generated-files.yaml @@ -1,42 +1,19 @@ -# Test Plan: Java Dependency — Refresh surfaces externally generated files +# Regression test for issue #914: manual Refresh must surface externally written +# .java files (a brand-new sub-package and a class in an existing package). +# Pre-fix the shallow DEPTH_ONE refresh never discovered the new package folder. # -# Regression test for https://github.com/microsoft/vscode-java-dependency/issues/914 -# "Refresh option doesn't work, classes not visible" +# Tree layout: the Java Projects tree is virtualized, so off-screen rows are not +# in the DOM. The Explorer file tree is collapsed and explorer.autoReveal is off +# so assertions run while the tree is compact and every package node collapsed. # -# Root cause: jdtls.ext PackageCommand.getPackageFragmentRootContent() called -# refreshLocal(root.getResource(), IResource.DEPTH_ONE), a shallow refresh that -# only syncs the source root's immediate children. A file written into a -# BRAND-NEW nested package folder (e.g. by a code generator or a refactor-move) -# was therefore never discovered: JDT created no IPackageFragment for it, so the -# Java Projects view stayed empty even after a manual Refresh — while the Folder -# (Explorer) view, which reads the filesystem directly, showed the file. -# -# The fix deep-refreshes the source root (DEPTH_INFINITE) and closes the -# IPackageFragmentRoot so its cached child list is rebuilt from the refreshed -# resource tree. -# -# This test writes .java files straight to disk (insertLineInFile uses -# fs.writeFileSync, bypassing VS Code's file service — i.e. exactly an external -# generator), then invokes the explicit Refresh command and asserts that it -# surfaces BOTH a brand-new sub-package AND a new class in an existing package. -# The brand-new package is the decisive case: pre-fix it never appears because -# DEPTH_ONE never descends into the source root to discover the new folder. -# -# Note on tree layout: the Java Projects tree is virtualized, so an off-screen -# row is not present in the DOM. The plan therefore keeps the tree compact — -# the Explorer file tree is collapsed (and explorer.autoReveal is disabled so -# opening generated files does not re-expand it), and the brand-new package is -# asserted while every package node is collapsed, before the existing package is -# expanded for its class-level check. -# -# Expected: FAILS before the fix (the brand-new package never appears), -# PASSES after the PackageCommand.java fix. +# insertLineInFile uses fs.writeFileSync, bypassing VS Code's file service — i.e. +# exactly an external generator. Assertions use the deterministic verifyTreeItem +# DOM check; file-write and refresh steps carry no `verify:` because the LLM +# screenshot judge has no reliable visual signal for them. # # Usage: # npx autotest run test/e2e-plans/java-dep-refresh-generated-files.yaml \ # --override extensionPath= -# # or in CI with the built VSIX: -# npx autotest run test/e2e-plans/java-dep-refresh-generated-files.yaml --vsix name: "Java Dependency — Refresh surfaces externally generated files (#914)" description: | @@ -52,9 +29,6 @@ setup: settings: java.configuration.checkProjectSettingsExclusions: false workbench.startupEditor: "none" - # Keep the Explorer file tree collapsed when generated files are opened, so - # the Java Projects view keeps its full height and rows are not virtualized - # out of the DOM. explorer.autoReveal: false steps: @@ -62,7 +36,7 @@ steps: action: "waitForLanguageServer" timeout: 180 - # ── Free vertical space so the Java Projects tree is not occluded ── + # Free vertical space so the Java Projects tree is not virtualized. - id: "close-aux-bar" action: "executeVSCodeCommand workbench.action.closeAuxiliaryBar" @@ -72,27 +46,22 @@ steps: - id: "collapse-timeline" action: "collapseSidebarSection TIMELINE" - # Collapse the Explorer "folders" (file tree) section entirely so the Java - # Projects view section gets the full sidebar height — otherwise its rows are - # virtualized out of the DOM and cannot be asserted on. - id: "collapse-explorer-folders" action: "collapseSidebarSection MAVEN" - id: "focus-java-projects" action: "executeVSCodeCommand javaProjectExplorer.focus" - verify: "Java Projects view is focused" - id: "wait-tree-load" action: "wait 3 seconds" - # ── Establish a well-defined baseline tree state ── - id: "expand-project" action: "expandTreeItem my-app" verify: "my-app project expanded" - id: "expand-source-root" action: "expandTreeItem src/main/java" - verify: "source root src/main/java expanded; package nodes visible" + verify: "source root src/main/java expanded" - id: "baseline-existing-pkg" action: "wait 1 seconds" @@ -100,44 +69,31 @@ steps: name: "com.mycompany.app" visible: true - # ── Simulate a code generator writing files directly to disk ────────── - # A) new class inside a BRAND-NEW sub-package (com.mycompany.app.gen) + # Write files straight to disk: one in a brand-new sub-package, one in an + # existing package. - id: "gen-file-new-pkg" action: "insertLineInFile src/main/java/com/mycompany/app/gen/Gen914InNewPkg.java 1 package com.mycompany.app.gen;\n\npublic class Gen914InNewPkg {\n}\n" - verify: "Gen914InNewPkg.java written to disk under brand-new package com.mycompany.app.gen" - # B) new class inside an EXISTING package (com.mycompany.app) - id: "gen-file-existing-pkg" action: "insertLineInFile src/main/java/com/mycompany/app/Gen914InExisting.java 1 package com.mycompany.app;\n\npublic class Gen914InExisting {\n}\n" - verify: "Gen914InExisting.java written to disk under existing package" - # Dismiss any transient editor/quick-open overlay left by opening the files. - id: "dismiss-overlay" action: "pressKey Escape" - id: "settle" action: "wait 3 seconds" - # ── The behaviour under test: an explicit manual Refresh ────────────── + # The behaviour under test: an explicit manual Refresh. - id: "manual-refresh" action: "executeVSCodeCommand java.view.package.refresh" - verify: "Java Projects view refreshed via the Refresh command" - id: "wait-after-refresh" action: "wait 4 seconds" - # Make sure the source root is expanded so its (collapsed) package nodes show. - id: "reexpand-source-root" action: "expandTreeItem src/main/java" - # ── Assertion 1 (the core regression) ───────────────────────────────── - # Every package node is still collapsed here, so the tree is compact and the - # brand-new sibling package node is within the viewport. Pre-fix this fails - # (DEPTH_ONE never discovers the new folder); post-fix it passes. - # No `verify:` — verifyTreeItem is the authoritative deterministic assertion. - # The new package becomes visible during the earlier wait-after-refresh step, - # so an LLM screenshot before/after diff on this pure wait would see no delta - # and yield a false negative. The DOM check below is what actually matters. + # Core regression: the brand-new package appears after Refresh. - id: "check-new-pkg" action: "wait 1 seconds" verifyTreeItem: @@ -145,14 +101,11 @@ steps: visible: true timeout: 15 - # ── Assertion 2 (sanity) ────────────────────────────────────────────── - # A new class added to an already-known package also surfaces after Refresh. + # Sanity: a new class in an existing package also appears. - id: "expand-existing-pkg" action: "expandTreeItem com.mycompany.app" verify: "existing package com.mycompany.app expanded" - # No `verify:` — verifyTreeItem is the authoritative deterministic assertion - # (see check-new-pkg). The class is already visible by the time this wait runs. - id: "check-existing-pkg-class" action: "wait 1 seconds" verifyTreeItem: From cb0a2c82b4f4fa315dcf77b9937b0ee8de5c0799 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Thu, 4 Jun 2026 14:18:31 +0800 Subject: [PATCH 4/5] test: add deterministic negative baseline to #914 plans MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Each plan only asserted the new package was PRESENT after refresh, never that it was ABSENT before — the missing "before" half of the before/after comparison. Add a deterministic verifyTreeItem (visible: false) baseline proving the package does not exist prior to the file write, so the later appearance is a genuine refresh-driven transition rather than a pre-existing or leftover node. Uses the DOM check (waitForTreeItemGone), so no LLM-judge flakiness. Verified locally: manual 21/21, auto-refresh 16/16. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- test/e2e-plans/java-dep-autorefresh-targeted.yaml | 10 ++++++++++ test/e2e-plans/java-dep-refresh-generated-files.yaml | 10 ++++++++++ 2 files changed, 20 insertions(+) diff --git a/test/e2e-plans/java-dep-autorefresh-targeted.yaml b/test/e2e-plans/java-dep-autorefresh-targeted.yaml index 202c573c..057a2ab2 100644 --- a/test/e2e-plans/java-dep-autorefresh-targeted.yaml +++ b/test/e2e-plans/java-dep-autorefresh-targeted.yaml @@ -72,6 +72,16 @@ steps: name: "com.mycompany.app" visible: true + # Negative baseline: the brand-new package must be ABSENT before the file is + # written, so check-new-pkg-autorefresh later observes a genuine appearance + # driven by the watcher, not a pre-existing node. + - id: "baseline-new-pkg-absent" + action: "wait 1 seconds" + verifyTreeItem: + name: "com.mycompany.app.autogen" + visible: false + timeout: 5 + # Write a file straight to disk into a brand-new sub-package. - id: "gen-file-new-pkg" action: "insertLineInFile src/main/java/com/mycompany/app/autogen/Gen914AutoNewPkg.java 1 package com.mycompany.app.autogen;\n\npublic class Gen914AutoNewPkg {\n}\n" diff --git a/test/e2e-plans/java-dep-refresh-generated-files.yaml b/test/e2e-plans/java-dep-refresh-generated-files.yaml index 28432406..5489290d 100644 --- a/test/e2e-plans/java-dep-refresh-generated-files.yaml +++ b/test/e2e-plans/java-dep-refresh-generated-files.yaml @@ -69,6 +69,16 @@ steps: name: "com.mycompany.app" visible: true + # Negative baseline: the brand-new package must be ABSENT before any file is + # written. This is the "before" half of the regression's before/after check — + # it proves check-new-pkg later observes a genuine appearance, not a leftover. + - id: "baseline-new-pkg-absent" + action: "wait 1 seconds" + verifyTreeItem: + name: "com.mycompany.app.gen" + visible: false + timeout: 5 + # Write files straight to disk: one in a brand-new sub-package, one in an # existing package. - id: "gen-file-new-pkg" From 37638d6b2cafb75a7920a75cd4fe5fc9e9baaff8 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Thu, 4 Jun 2026 15:28:23 +0800 Subject: [PATCH 5/5] fix: address PR review - restore syncPaths on failure, dedup refresh targets - packageRootNode: restore the pending syncPaths snapshot if getPackageData rejects so a transient server error does not drop a targeted refresh - PackageCommand: dedup resolved refresh targets via LinkedHashSet so multiple files in the same new package only refresh the subtree once - e2e plans: align Maven section id to lowercase 'maven' for consistency Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../jdtls/ext/core/PackageCommand.java | 6 ++++- src/views/packageRootNode.ts | 23 ++++++++++++------- .../java-dep-autorefresh-targeted.yaml | 2 +- .../java-dep-refresh-generated-files.yaml | 2 +- 4 files changed, 22 insertions(+), 11 deletions(-) diff --git a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java index 025d014b..9baf72fb 100644 --- a/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java +++ b/jdtls.ext/com.microsoft.jdtls.ext.core/src/com/microsoft/jdtls/ext/core/PackageCommand.java @@ -16,6 +16,7 @@ import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; +import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; @@ -533,7 +534,7 @@ public static List getPackageFragmentRootContent(IPackageFragmentRoot ro // See https://github.com/microsoft/vscode-java-dependency/issues/914 boolean refreshedTargets = false; if (syncPaths != null && !syncPaths.isEmpty()) { - List targets = new ArrayList<>(); + Set targets = new LinkedHashSet<>(); boolean allResolved = true; for (String syncPath : syncPaths) { IResource target = findNearestExistingResource(syncPath, (IContainer) rootResource); @@ -541,6 +542,9 @@ public static List getPackageFragmentRootContent(IPackageFragmentRoot ro allResolved = false; break; } + // Multiple changed paths can resolve to the same existing + // ancestor (e.g. several new files in one new package); the + // set keeps each distinct subtree so it is refreshed once. targets.add(target); } if (allResolved && !targets.isEmpty()) { diff --git a/src/views/packageRootNode.ts b/src/views/packageRootNode.ts index 46f18d30..01327579 100644 --- a/src/views/packageRootNode.ts +++ b/src/views/packageRootNode.ts @@ -40,14 +40,21 @@ export class PackageRootNode extends DataNode { syncPaths = Array.from(this.pendingSyncPaths); this.pendingSyncPaths.clear(); } - return Jdtls.getPackageData({ - kind: NodeKind.PackageRoot, - projectUri: this._project.nodeData.uri, - rootPath: this.nodeData.path, - handlerIdentifier: this.nodeData.handlerIdentifier, - isHierarchicalView: Settings.isHierarchicalView(), - syncPaths, - }); + try { + return await Jdtls.getPackageData({ + kind: NodeKind.PackageRoot, + projectUri: this._project.nodeData.uri, + rootPath: this.nodeData.path, + handlerIdentifier: this.nodeData.handlerIdentifier, + isHierarchicalView: Settings.isHierarchicalView(), + syncPaths, + }); + } catch (error) { + // Restore the snapshot so a transient server error does not drop the + // pending paths; the next refresh will retry the targeted sync. + syncPaths?.forEach((path) => this.pendingSyncPaths.add(path)); + throw error; + } } protected createChildNodeList(): ExplorerNode[] { diff --git a/test/e2e-plans/java-dep-autorefresh-targeted.yaml b/test/e2e-plans/java-dep-autorefresh-targeted.yaml index 057a2ab2..124c8d77 100644 --- a/test/e2e-plans/java-dep-autorefresh-targeted.yaml +++ b/test/e2e-plans/java-dep-autorefresh-targeted.yaml @@ -49,7 +49,7 @@ steps: action: "collapseSidebarSection TIMELINE" - id: "collapse-explorer-folders" - action: "collapseSidebarSection MAVEN" + action: "collapseSidebarSection maven" - id: "focus-java-projects" action: "executeVSCodeCommand javaProjectExplorer.focus" diff --git a/test/e2e-plans/java-dep-refresh-generated-files.yaml b/test/e2e-plans/java-dep-refresh-generated-files.yaml index 5489290d..cf3420d0 100644 --- a/test/e2e-plans/java-dep-refresh-generated-files.yaml +++ b/test/e2e-plans/java-dep-refresh-generated-files.yaml @@ -47,7 +47,7 @@ steps: action: "collapseSidebarSection TIMELINE" - id: "collapse-explorer-folders" - action: "collapseSidebarSection MAVEN" + action: "collapseSidebarSection maven" - id: "focus-java-projects" action: "executeVSCodeCommand javaProjectExplorer.focus"