diff --git a/test/e2e-plans/java-dep-delete-permanent.yaml b/test/e2e-plans/java-dep-delete-permanent.yaml new file mode 100644 index 00000000..8089ede7 --- /dev/null +++ b/test/e2e-plans/java-dep-delete-permanent.yaml @@ -0,0 +1,144 @@ +# Test Plan: Java Dependency — Permanent Delete +# +# Covers java.view.package.deleteFilePermanently (the "shift+delete" / +# non-trash branch of file removal). The companion command +# java.view.package.moveFileToTrash is already covered by +# java-dep-file-operations.yaml. +# +# Why this is a separate plan +# ─────────────────────────── +# The permanent-delete command has no plain-click UI affordance on regular +# local files: the JAVA PROJECTS context-menu entry (package.json:812-820) +# and the `delete` keybinding (package.json:485-490) are both gated on +# `!explorerResourceMoveableToTrash`, so files in `test/maven` only ever +# see "Delete" (move-to-trash). We therefore invoke the command directly +# via id and rely on tree selection to supply the target node — the same +# pattern the classpath plan uses for `removeLibrary`. +# +# The confirmation dialog also differs from moveFileToTrash: the prompt +# says "permanently delete" instead of "delete", and the action button is +# labelled "Delete" instead of "Move to Recycle Bin" (see +# src/explorerCommands/delete.ts line 17 + getInformationMessage). +# +# Usage: +# npx autotest run test/e2e-plans/java-dep-delete-permanent.yaml --vsix + +name: "Java Dependency — Permanent Delete" +description: | + Tests the java.view.package.deleteFilePermanently command on a regular + Maven-project file. Invokes the command by id (no UI affordance on local + files), confirms the "permanently delete" dialog, and verifies the file + is gone from disk and from the Java Projects tree. + +setup: + extension: "redhat.java" + vscodeVersion: "stable" + workspace: "../maven" + timeout: 180 + settings: + java.configuration.checkProjectSettingsExclusions: false + workbench.startupEditor: "none" + +steps: + # ── Setup: wait for LS, free sidebar space, focus Java Projects ── + - id: "ls-ready" + action: "waitForLanguageServer" + # No `verify:` — `waitForLanguageServer` is itself the deterministic + # readiness check. The AFTER screenshot may transiently show + # "Java: Building - 0%" which a strict LLM mis-reads as a failure. + timeout: 180 + + - id: "close-aux-bar" + action: "executeVSCodeCommand workbench.action.closeAuxiliaryBar" + verify: "Auxiliary bar (Chat) closed" + + - id: "collapse-outline" + action: "collapseSidebarSection OUTLINE" + + - id: "collapse-timeline" + action: "collapseSidebarSection TIMELINE" + + - id: "collapse-workspace-root" + action: "collapseWorkspaceRoot" + + - id: "focus-java-projects" + action: "executeVSCodeCommand javaProjectExplorer.focus" + verify: "Java Projects view is focused" + + - id: "wait-tree-load" + action: "wait 3 seconds" + + # ── Reveal App1.java via link-with-editor, then select it ── + # Opening the file makes link-with-editor expand the tree path and + # reveal+select App1 deterministically — much more reliable than + # manual expandTreeItem chains on the virtualised tree. + - id: "open-target-file" + action: "open file App1.java" + waitBefore: 2 + + - id: "collapse-workspace-root-2" + action: "collapseWorkspaceRoot" + + - id: "focus-java-projects-2" + action: "executeVSCodeCommand javaProjectExplorer.focus" + waitBefore: 2 + + # Click the tree item to guarantee it's the active selection — getCmdNode + # (explorerCommands/utility.ts:38) falls back to `selectedNodes[0]` when + # the command is invoked without a node argument. Linking-with-editor + # already auto-selected App1 when the file was opened, but clicking + # re-asserts the selection deterministically after the focus-java-projects + # round trip. + # + # We intentionally do NOT verify the App1 tree row here. On 1024x768 CI + # displays the row gets virtualised out of view in the brief window + # between `open file` and the click, and an inView:"Java Projects" + # verifyTreeItem then times out — even though the underlying selection + # is set correctly (proven by the subsequent delete succeeding). The + # post-delete `verify-file-gone` + `verify-tree-item-gone` block is the + # authoritative ground truth for whether the right node was targeted. + - id: "select-app1" + action: "click App1 tree item" + waitBefore: 1 + + # ── Invoke java.view.package.deleteFilePermanently ── + # The handler at views/dependencyExplorer.ts:177 takes `node?: DataNode`. + # With no node arg, getCmdNode uses the current selection (set by the + # `click App1 tree item` step above). DataNode is a class instance with + # methods (.getChildren()) so passing a POJO would crash — selection + # fallback is the only viable path from a smoke-test. + - id: "invoke-delete-permanently" + action: "executeVSCodeCommand java.view.package.deleteFilePermanently" + + # `expectConfirmDialog` waits for the dialog and clicks the first + # recognized confirm button (autotest knows "Delete" is one of them, see + # dialogOperations.ts:16). It's the strict variant — throws if no dialog + # appears, surfacing a silently-failed command invocation immediately + # instead of 15s later when verifyFile times out. + - id: "confirm-delete" + action: "expectConfirmDialog" + + - id: "wait-after-delete" + action: "wait 5 seconds" + + # ── Verify deletion on disk AND in the tree ── + # The disk check is the strongest signal: useTrash=false routes through + # workspace.fs.delete with the OS-level unlink, so a passing verifyFile + # exists:false proves the permanent-delete path actually fired (rather + # than silently downgrading to a no-op or moving to trash). + - id: "verify-file-gone" + action: "wait 1 seconds" + verifyFile: + path: "${workspaceFolder}/src/main/java/com/mycompany/app1/App1.java" + exists: false + timeout: 15 + + - id: "verify-tree-item-gone" + action: "wait 1 seconds" + # No `verify:` — verifyTreeItem is authoritative. + verifyTreeItem: + name: "App1" + exact: true + visible: false + inView: "Java Projects" + timeout: 15 diff --git a/test/e2e-plans/java-dep-export-jar.yaml b/test/e2e-plans/java-dep-export-jar.yaml new file mode 100644 index 00000000..3abecd71 --- /dev/null +++ b/test/e2e-plans/java-dep-export-jar.yaml @@ -0,0 +1,162 @@ +# Test Plan: Java Dependency — Export Jar +# +# Covers java.view.package.exportJar — the multi-step wizard that builds a +# runnable jar from a Java project. The command is contributed both as the +# title-bar `$(export)` icon on the JAVA PROJECTS workspace-root node +# (package.json:858-861, group=inline) and as a context-menu entry, but we +# invoke it directly by id: the inline icon is rendered against the +# `java:workspace` viewItem (not the Maven project node `my-app`), which +# is fragile to locate by name, and ResolveJavaProject auto-resolves when +# there's exactly one project in the workspace — so direct invocation +# bypasses one wizard step that's not the command's responsibility. +# +# Wizard step machine (BuildArtifactTaskProvider.ts:284 `createJarFile`): +# 1. ResolveJavaProject → auto when single-project; quick-pick otherwise +# 2. ResolveMainClass → quick-pick of main classes + "" +# 3. GenerateJar +# a. generateClasspaths → multi-select quick-pick if >1 dependency item +# b. showSaveDialog → only if outputPath === "" (skipped when +# java.project.exportJar.targetPath is set +# to a non-empty value; we set it via +# workspaceSettings to keep the output +# filename deterministic) +# c. Jdtls.exportJar → writes the jar file +# +# Verification strategy +# ───────────────────── +# The jar is written to a known absolute path inside the workspace +# (`output.jar`). We assert with `verifyFile exists: true` — the strongest +# possible signal that the full wizard completed end-to-end, not just that +# the command was dispatched. The Jdtls export runs in a hidden Pseudoterminal +# (BuildArtifactTaskProvider line 91: `presentationOptions.reveal = Never`), +# so there is no terminal text to inspect; the file on disk is the only +# unambiguous post-condition. +# +# Usage: +# npx autotest run test/e2e-plans/java-dep-export-jar.yaml --vsix + +name: "Java Dependency — Export Jar" +description: | + Exercises the multi-step Export Jar wizard end-to-end on the maven + fixture: triggers the command, picks the main class, accepts the default + classpath element selection, and verifies that the resulting jar file + exists at the configured target path. + +setup: + extension: "redhat.java" + vscodeVersion: "stable" + workspace: "../maven" + # Bumped above the standard 180s — the wizard runs a full workspace build + # (await buildWorkspace() in executeExportJarTask) before the first + # quick-pick appears, which on a cold JDT-LS warmup commonly takes 60-90s. + timeout: 360 + settings: + java.configuration.checkProjectSettingsExclusions: false + workbench.startupEditor: "none" + # Pinning java.project.exportJar.targetPath to an absolute deterministic + # path bypasses the showSaveDialog branch in GenerateJarExecutor + # (BuildArtifactTaskProvider lines 240-244 short-circuit only when + # outputPath === ""), and lets the verifyFile assertion target a stable + # location regardless of how the fixture's worktree is named. + # + # We use user-level `settings:` (not `workspaceSettings:`) because + # autotest's workspaceSettings merge uses JSON.parse on the existing + # `.vscode/settings.json`, and the maven fixture's settings.json + # contains JSONC `//` comments which fail JSON.parse. User settings + # are written fresh each run (user-data-dir is wiped on launch) so + # there is no JSONC merge hazard. The Settings.getExportJarTargetPath + # config read is unscoped, so user-level setting takes effect identically. + java.project.exportJar.targetPath: "${workspaceFolder}/output.jar" + +steps: + # ── Setup: wait for LS, free sidebar space, focus Java Projects ── + - id: "ls-ready" + action: "waitForLanguageServer" + # No `verify:` — `waitForLanguageServer` is itself the deterministic + # readiness check. The AFTER screenshot may transiently show + # "Java: Building - 0%" which a strict LLM mis-reads as a failure. + timeout: 180 + + - id: "close-aux-bar" + action: "executeVSCodeCommand workbench.action.closeAuxiliaryBar" + verify: "Auxiliary bar (Chat) closed" + + - id: "collapse-outline" + action: "collapseSidebarSection OUTLINE" + + - id: "collapse-timeline" + action: "collapseSidebarSection TIMELINE" + + - id: "collapse-workspace-root" + action: "collapseWorkspaceRoot" + + - id: "focus-java-projects" + action: "executeVSCodeCommand javaProjectExplorer.focus" + verify: "Java Projects view is focused" + + - id: "wait-tree-load" + action: "wait 3 seconds" + + # ── Trigger the Export Jar wizard ── + # Direct command invocation avoids the brittle inline-icon-on-workspace- + # node click path (the `$(export)` icon is contributed against viewItem + # `java:workspace`, not against the Maven project node `my-app`, and + # locating workspace-level inline icons in autotest is fragile). + # ResolveJavaProject auto-resolves for single-project workspaces. + - id: "invoke-export-jar" + action: "executeVSCodeCommand java.view.package.exportJar" + + # The wizard first triggers a full workspace build (await buildWorkspace() + # in executeExportJarTask) before any UI appears. On a cold JDT-LS this + # typically takes 30-60s; the Jdtls.getMainClasses fetch then needs + # another 2-5s before the first quick-pick is shown. Wait generously. + - id: "wait-build-complete" + action: "wait 60 seconds" + + # ── Step 2: pick the main class ── + # The maven fixture has a single class with a `public static void main` + # entry point: com.mycompany.app.App. App1 has no main, so the only + # quick-pick options are "App" and "". We pick "App" + # explicitly so the resulting jar is runnable. + - id: "pick-main-class" + action: "select App option" + # No `verify:` — selectPaletteOption is the action and verification in + # one. The quick-pick closes on selection; the AFTER screenshot may + # already show the next quick-pick (classpath elements), which a + # strict LLM could mis-read as "App selection was lost". + + - id: "wait-after-main-class" + action: "wait 5 seconds" + + # ── Step 3a: accept the pre-selected classpath elements ── + # GenerateJarExecutor.generateClasspaths builds a list of runtime / test + # output folders + dependency artifacts, pre-selects everything tagged + # `runtime`, and shows a multi-select QuickPick. For the maven fixture + # this includes target/classes plus any resolved JUnit jars under test + # scope. The pre-selection is the sane default — accepting it gives a + # runnable jar without needing to compute which items to check. + # + # `confirmQuickInput` presses Enter on the open quick-pick widget without + # typing anything, which leaves selections untouched and submits. + - id: "accept-classpath-elements" + action: "confirmQuickInput" + + # ── Step 3c: wait for Jdtls to generate the jar ── + # Jdtls.exportJar writes the jar via a JDT LSP request. The custom + # pseudoterminal stays hidden (presentationOptions.reveal=Never), so + # there is no terminal-text signal — we just wait long enough for the + # filesystem write to settle. + - id: "wait-jar-generated" + action: "wait 30 seconds" + + # ── Verification: jar file exists at the configured target path ── + # This is the strongest possible end-to-end check: the file appearing + # on disk proves ResolveJavaProject + ResolveMainClass + generateClasspaths + # + Jdtls.exportJar all completed in order. A failure here pinpoints + # the wizard breaking; a pass means the full happy path worked. + - id: "verify-jar-created" + action: "wait 1 seconds" + verifyFile: + path: "${workspaceFolder}/output.jar" + exists: true + timeout: 60 diff --git a/test/e2e-plans/java-dep-project-creation.yaml b/test/e2e-plans/java-dep-project-creation.yaml new file mode 100644 index 00000000..e705673c --- /dev/null +++ b/test/e2e-plans/java-dep-project-creation.yaml @@ -0,0 +1,166 @@ +# Test Plan: Java Dependency — Create Project +# +# Covers java.project.create — the flagship "Java: Create Java Project..." +# wizard. We exercise the "No build tools" branch (a.k.a. invisible / +# unmanaged-folder project) because it is the only project type whose +# scaffolding lives entirely inside this extension (`templates/invisible- +# project/`) — every other type (Maven, Gradle, Spring Boot, Quarkus, ...) +# delegates to a third-party extension's create-command, which is out of +# scope for this repo's E2E coverage. +# +# Wizard flow (controllers/projectController.ts — `createJavaProject` → +# `scaffoldSimpleProject`): +# 1. `window.showQuickPick` → user picks a project type +# 2. `window.showOpenDialog` (folder mode, `openLabel: "Select the project location"`) +# → user picks parent directory +# 3. `window.showInputBox` → user types the project name +# 4. `fse.copy(templates/invisible-project, /)` → scaffold +# 5. `commands.executeCommand("vscode.openFolder", Uri.file(...), openInNewWindow)` +# where openInNewWindow = workspace && !_.isEmpty(workspace.workspaceFolders) +# +# Why a new window does NOT break this test +# ───────────────────────────────────────── +# Because the test runs with a workspace open (`workspace: "../maven"`), +# step 5 opens the new project in a SEPARATE Electron window. The Playwright +# driver is attached via CDP to the original window's renderer process; the +# new window is a separate process the driver never sees. After step 4 +# (the fse.copy) the scaffolded files are already on disk regardless of +# whether step 5 succeeds in opening the new window — so the deterministic +# verifyFile assertions still pass. We deliberately put no UI steps after +# the project creation completes, to avoid any race with the second-window +# spawn momentarily stealing OS focus. +# +# Verification strategy +# ───────────────────── +# `templates/invisible-project/` ships exactly three files (plus an empty +# `lib/` ensure-dir): +# - README.md +# - .vscode/settings.json +# - src/App.java +# We assert two of them (the .vscode settings file and App.java) on disk. +# Notably absent: `.classpath` / `.project` — invisible projects rely on +# `java.import.generatesMetadataFilesAtProjectRoot` (off by default in the +# template), so testing for .classpath would yield a false negative. +# +# Usage: +# npx autotest run test/e2e-plans/java-dep-project-creation.yaml --vsix + +name: "Java Dependency — Create Project" +description: | + Exercises the java.project.create wizard end-to-end on the "No build + tools" path: triggers the command, picks the project type, drives the + folder picker, names the project, and verifies the scaffolded template + files appear on disk. + +setup: + extension: "redhat.java" + vscodeVersion: "stable" + workspace: "../maven" + timeout: 240 + settings: + java.configuration.checkProjectSettingsExclusions: false + workbench.startupEditor: "none" + +steps: + # ── Setup: wait for LS, free sidebar space, focus Java Projects ── + # We open the maven workspace to satisfy step 5's openInNewWindow=true + # branch (see header comment). LS readiness isn't strictly required for + # project creation, but the wait gives the workbench time to settle + # before we drive multiple chained quick-picks / input-boxes. + - id: "ls-ready" + action: "waitForLanguageServer" + # No `verify:` — `waitForLanguageServer` is itself the deterministic + # readiness check. The AFTER screenshot may transiently show + # "Java: Building - 0%" which a strict LLM mis-reads as a failure. + timeout: 180 + + - id: "close-aux-bar" + action: "executeVSCodeCommand workbench.action.closeAuxiliaryBar" + verify: "Auxiliary bar (Chat) closed" + + - id: "collapse-outline" + action: "collapseSidebarSection OUTLINE" + + - id: "collapse-timeline" + action: "collapseSidebarSection TIMELINE" + + - id: "collapse-workspace-root" + action: "collapseWorkspaceRoot" + + - id: "focus-java-projects" + action: "executeVSCodeCommand javaProjectExplorer.focus" + verify: "Java Projects view is focused" + + - id: "wait-tree-load" + action: "wait 3 seconds" + + # ── Step 1: trigger the wizard and pick the project type ── + # Direct command invocation is more reliable than navigating the title- + # bar `$(add)` icon on the Java Projects view, which routes through the + # `_java.project.create.from.javaprojectexplorer` proxy command — same + # underlying handler but extra menu-binding indirection. + - id: "invoke-create-project" + action: "executeVSCodeCommand java.project.create" + + # The project-type quick-pick lists 9 items (controllers/projectController.ts + # `projectTypes`). "No build tools" is index 0. We pick by exact label + # rather than index to keep the plan resilient if the list is re-ordered. + - id: "pick-no-build-tools" + action: "select No build tools option" + + # ── Step 2: drive the folder picker ── + # `window.showOpenDialog` (canSelectFolders: true) is intercepted by + # VS Code's smoke-test driver and re-surfaced as the internal + # simpleFileDialog quick-pick — the same mechanism the classpath plan + # uses for addLibraryFolders. In folder-pick mode, typing a path and + # pressing Enter NAVIGATES INTO the folder; the explicit confirmation + # button (labelled with the dialog's `openLabel`, here "Select the + # project location") is what actually returns the URI to the caller. + # + # We type the workspace root path so the scaffolded project lands + # directly inside the auto-copied worktree — that keeps the verifyFile + # paths simple and the worktree gets cleaned up by autotest at teardown. + - id: "type-project-location" + action: "fillQuickInput ${workspaceFolder}" + + - id: "confirm-project-location" + action: "tryClickButton Select the project location" + + # ── Step 3: name the project ── + # `window.showInputBox` surfaces as the quick-input widget too; + # `fillAnyInput` covers both quick-input and inline-rename widgets so + # it's slightly more robust if VS Code ever changes which surface the + # InputBox API targets. + - id: "type-project-name" + action: "fillAnyInput AutotestNewProject" + + # ── Step 4 + 5: wait for scaffold + new-window spawn ── + # `fse.copy(templates/invisible-project, ...)` is a few-KB synchronous- + # style copy that completes in <500ms, but `vscode.openFolder` triggers + # a second Electron window launch which can briefly steal focus and + # delay file flushing on slower disks. 8s is a comfortable margin. + - id: "wait-scaffold-and-open" + action: "wait 8 seconds" + + # ── Verification: scaffolded template files exist on disk ── + # These two assertions together prove: + # - the wizard reached step 4 (file copy) + # - the basePath (workspace root) and projectName (AutotestNewProject) + # were correctly threaded through showOpenDialog / showInputBox + # + # Verifying directly on disk is the strongest signal we can get: it + # decouples the test from whatever happens with the new-window spawn, + # which Playwright can't observe anyway (different Electron process). + - id: "verify-app-java" + action: "wait 1 seconds" + verifyFile: + path: "${workspaceFolder}/AutotestNewProject/src/App.java" + exists: true + timeout: 15 + + - id: "verify-vscode-settings" + action: "wait 1 seconds" + verifyFile: + path: "${workspaceFolder}/AutotestNewProject/.vscode/settings.json" + exists: true + timeout: 15