diff --git a/.github/workflows/linuxUI.yml b/.github/workflows/linuxUI.yml index b8c5f837..eecbadff 100644 --- a/.github/workflows/linuxUI.yml +++ b/.github/workflows/linuxUI.yml @@ -18,7 +18,7 @@ jobs: run: | sudo apt-get update sudo apt-get install -y libxkbfile-dev pkg-config libsecret-1-dev libxss1 dbus xvfb libgtk-3-0 libgbm1 - sudo /usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 & + sudo /usr/bin/Xvfb :99 -screen 0 1920x1080x24 > /dev/null 2>&1 & sleep 3 - name: Set up JDK 21 @@ -75,4 +75,16 @@ jobs: - name: Print language server Log if: ${{ failure() }} - run: find ./test-resources/settings/User/workspaceStorage/*/redhat.java/jdt_ws/.metadata/.log -print -exec cat '{}' \;; + run: | + find ./test-resources/settings/User/workspaceStorage \ + -path "*/redhat.java/jdt_ws/.metadata/.log" \ + -print -exec cat '{}' \; \ + || echo "No language server log found" + + - name: Upload test screenshots + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: test-screenshots + path: test-resources/screenshots/ + if-no-files-found: ignore diff --git a/.github/workflows/windowsUI.yml b/.github/workflows/windowsUI.yml index a952e1a7..1e7e6ce7 100644 --- a/.github/workflows/windowsUI.yml +++ b/.github/workflows/windowsUI.yml @@ -75,4 +75,19 @@ jobs: - name: Print language server Log if job failed if: ${{ failure() }} - run: Get-ChildItem -Path ./test-resources/settings/User/workspaceStorage/*/redhat.java/jdt_ws/.metadata/.log | cat + run: | + $logFiles = Get-ChildItem -Path ./test-resources/settings/User/workspaceStorage -Recurse -Filter ".log" -ErrorAction SilentlyContinue | + Where-Object { $_.FullName -match 'redhat\.java.*jdt_ws.*\.metadata.*\.log$' } + if ($logFiles) { + $logFiles | ForEach-Object { Write-Host $_.FullName; Get-Content $_ } + } else { + Write-Host "No language server log found" + } + + - name: Upload test screenshots + if: ${{ failure() }} + uses: actions/upload-artifact@v4 + with: + name: test-screenshots + path: test-resources/screenshots/ + if-no-files-found: ignore diff --git a/test/ui/command.test.ts b/test/ui/command.test.ts index 3f317918..4bbe3561 100644 --- a/test/ui/command.test.ts +++ b/test/ui/command.test.ts @@ -47,6 +47,13 @@ describe("Command Tests", function() { fse.copySync(projectPath, projectFolder); await VSBrowser.instance.openResources(projectFolder); currentProjectPath = projectFolder; + // openResources() sends the CLI IPC command and returns immediately, before VS Code + // has actually reloaded the window with the new folder. On Linux the IPC + reload + // cycle is slower; calling openFile() right after can race with or even abort the + // folder load. Poll the window title until it contains the folder name so we know + // VS Code has finished the workspace transition before we proceed. + await waitForWorkspaceOpen(path.basename(projectFolder)); + await dismissModalDialogIfPresent(); await ensureExplorerIsOpen(); } @@ -77,12 +84,25 @@ describe("Command Tests", function() { await sleep(1000); } } + // The check above leaves the language status hover popup open. Close it by clicking + // the same element again (toggle behavior) so it does not overlay sidebar tree items + // on Linux, where ESC alone is not always reliable. + try { + const languageStatus = await statusBar.findElement(By.xpath('//*[@id="status.languageStatus"]')); + await languageStatus.click(); + await sleep(300); + } catch (_e) { + // popup may have already been dismissed — ignore + } } before(async function() { await openProject(mavenProjectPath); await openFile(mavenJavaFilePath); await waitForLanguageServerReady(); + // Extra safety: send ESC in case any residual overlay is still present + await VSBrowser.instance.driver.actions().sendKeys(seleniumWebdriver.Key.ESCAPE).perform(); + await clearNotificationsIfPresent(); }); after(async function() { @@ -96,6 +116,16 @@ describe("Command Tests", function() { } }); + afterEach(async function() { + if (this.currentTest?.state === 'failed') { + const safeName = this.currentTest.title.replace(/[^a-zA-Z0-9_-]/g, '_'); + try { + await VSBrowser.instance.takeScreenshot(safeName); + } catch (e) { + console.warn(`Failed to take screenshot for ${safeName}: ${e}`); + } + } + }); it("Test javaProjectExplorer.focus", async function() { await new Workbench().executeCommand("javaProjectExplorer.focus"); @@ -175,6 +205,8 @@ describe("Command Tests", function() { }); (platform() === "darwin" ? it.skip : it)("Test java.view.package.revealInProjectExplorer", async function() { + await dismissModalDialogIfPresent(); + await clearNotificationsIfPresent(); // Make sure App.java is not currently revealed in Java Projects const section = await new SideBarView().getContent().getSection("Java Projects"); const item = await section.findItem("my-app") as TreeItem; @@ -334,6 +366,8 @@ async function collapseFileSection() { } async function expandMainCodeInJavaProjects() { + await dismissModalDialogIfPresent(); + await clearNotificationsIfPresent(); const section = await new SideBarView().getContent().getSection("Java Projects"); await section.click(); const appNode = await section.findItem("my-app") as TreeItem; @@ -348,6 +382,8 @@ async function expandMainCodeInJavaProjects() { async function expandInJavaProjects(label: string, ...otherLabels: string[]): Promise<[TreeItem, ViewSection]> { // Dismiss any lingering modal dialog that could block sidebar clicks await dismissModalDialogIfPresent(); + // Clear notification toasts that could overlay sidebar elements + await clearNotificationsIfPresent(); // Collapse file section to make sure that the AppToRename tree item fits in the current viewport. // .findItem will only find tree items in the current viewport. await collapseFileSection(); @@ -427,6 +463,16 @@ async function dismissModalDialogIfPresent() { } } +async function clearNotificationsIfPresent() { + try { + const center = await new Workbench().openNotificationsCenter(); + await center.clearAllNotifications(); + await center.close(); + } catch (_e) { + // No notifications or center not available — nothing to clear + } +} + async function waitForTreeItem(section: ViewSection, label: string, timeoutMs = 15000): Promise { const start = Date.now(); while (Date.now() - start < timeoutMs) { @@ -511,3 +557,23 @@ async function ensureExplorerIsOpen() { await control.openView(); } +/** + * Poll the VS Code window title until it contains {@link folderName}, which signals that + * VS Code has finished the workspace-reload triggered by openResources(). Falls back + * silently after {@link timeoutMs} so tests can still run and produce useful error messages. + */ +async function waitForWorkspaceOpen(folderName: string, timeoutMs: number = 30000): Promise { + const start = Date.now(); + while (Date.now() - start < timeoutMs) { + try { + const title = await VSBrowser.instance.driver.getTitle(); + if (title.toLowerCase().includes(folderName.toLowerCase())) { + return; + } + } catch (_e) { + // VS Code is mid-reload; its window title is temporarily unavailable. + } + await sleep(1000); + } +} + diff --git a/test/ui/index.ts b/test/ui/index.ts index e8067d24..5b06d148 100644 --- a/test/ui/index.ts +++ b/test/ui/index.ts @@ -9,13 +9,16 @@ async function main(): Promise { try { // Run UI command tests const testPath = path.join(__dirname, "command.test.js"); - const exTester = new ExTester(); + const exTester = new ExTester("./test-resources"); await exTester.downloadCode(); await exTester.installVsix(); await exTester.installFromMarketplace("redhat.java"); await exTester.downloadChromeDriver(); await exTester.setupRequirements(); - process.exit(await exTester.runTests(testPath, {resources: []})); + // Disable workspace trust via settings to prevent the trust dialog from + // blocking project load on Linux (equivalent to --disable-workspace-trust). + const testSettings = path.join(__dirname, "..", "..", "..", "test", "ui", "test-settings.json"); + process.exit(await exTester.runTests(testPath, {resources: [], settings: testSettings})); } catch (err) { console.log(err); process.exit(1); diff --git a/test/ui/test-settings.json b/test/ui/test-settings.json new file mode 100644 index 00000000..9d314f25 --- /dev/null +++ b/test/ui/test-settings.json @@ -0,0 +1,3 @@ +{ + "security.workspace.trust.enabled": false +}