From 3770438ddb1c51b7a75e93fd37c81a2d412f9ff0 Mon Sep 17 00:00:00 2001 From: wenyutang-ms Date: Fri, 20 Mar 2026 16:58:28 +0800 Subject: [PATCH 1/9] test: fix ui test case on linux --- .github/workflows/linuxUI.yml | 2 +- test/ui/command.test.ts | 19 +++++++++++++++++++ 2 files changed, 20 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linuxUI.yml b/.github/workflows/linuxUI.yml index b8c5f837..d8d0a134 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 diff --git a/test/ui/command.test.ts b/test/ui/command.test.ts index 3f317918..18551b70 100644 --- a/test/ui/command.test.ts +++ b/test/ui/command.test.ts @@ -83,6 +83,9 @@ describe("Command Tests", function() { await openProject(mavenProjectPath); await openFile(mavenJavaFilePath); await waitForLanguageServerReady(); + // Close any lingering hover popup (e.g. language status) that could overlay the sidebar + await VSBrowser.instance.driver.actions().sendKeys(seleniumWebdriver.Key.ESCAPE).perform(); + await clearNotificationsIfPresent(); }); after(async function() { @@ -175,6 +178,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 +339,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 +355,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 +436,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) { From f34f923e571c9107142154ca53979aa984e95227 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 24 Mar 2026 09:55:34 +0800 Subject: [PATCH 2/9] test: update --- test/ui/command.test.ts | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/ui/command.test.ts b/test/ui/command.test.ts index 18551b70..f8b682e6 100644 --- a/test/ui/command.test.ts +++ b/test/ui/command.test.ts @@ -77,13 +77,23 @@ 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(); - // Close any lingering hover popup (e.g. language status) that could overlay the sidebar + // Extra safety: send ESC in case any residual overlay is still present await VSBrowser.instance.driver.actions().sendKeys(seleniumWebdriver.Key.ESCAPE).perform(); await clearNotificationsIfPresent(); }); From 743a3998616a0b0ea725897f0d4fde743f26a659 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 24 Mar 2026 12:56:08 +0800 Subject: [PATCH 3/9] test: update --- test/ui/command.test.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/test/ui/command.test.ts b/test/ui/command.test.ts index f8b682e6..b399d130 100644 --- a/test/ui/command.test.ts +++ b/test/ui/command.test.ts @@ -109,6 +109,17 @@ 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); + console.log(`Screenshot saved for failed test: ${safeName}`); + } catch (e) { + console.warn(`Failed to take screenshot for ${safeName}: ${e}`); + } + } + }); it("Test javaProjectExplorer.focus", async function() { await new Workbench().executeCommand("javaProjectExplorer.focus"); From 496adad14ad34e1af96c1829faeea4c32df0e4a8 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 24 Mar 2026 13:39:07 +0800 Subject: [PATCH 4/9] ci: fix log find command and upload screenshots on failure --- .github/workflows/linuxUI.yml | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/.github/workflows/linuxUI.yml b/.github/workflows/linuxUI.yml index d8d0a134..fb22d17b 100644 --- a/.github/workflows/linuxUI.yml +++ b/.github/workflows/linuxUI.yml @@ -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: screenshots/ + if-no-files-found: ignore From f5c2f72f723c6f45911900806e0557dc1b724c08 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 24 Mar 2026 14:28:25 +0800 Subject: [PATCH 5/9] ci: update --- .github/workflows/windowsUI.yml | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/.github/workflows/windowsUI.yml b/.github/workflows/windowsUI.yml index a952e1a7..f730624b 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: screenshots/ + if-no-files-found: ignore From 12b59e09d5ed4dc175d37fa723edf11bf145e375 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 24 Mar 2026 14:29:19 +0800 Subject: [PATCH 6/9] ci: update --- test/ui/command.test.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/test/ui/command.test.ts b/test/ui/command.test.ts index b399d130..d15f51fa 100644 --- a/test/ui/command.test.ts +++ b/test/ui/command.test.ts @@ -114,7 +114,6 @@ describe("Command Tests", function() { const safeName = this.currentTest.title.replace(/[^a-zA-Z0-9_-]/g, '_'); try { await VSBrowser.instance.takeScreenshot(safeName); - console.log(`Screenshot saved for failed test: ${safeName}`); } catch (e) { console.warn(`Failed to take screenshot for ${safeName}: ${e}`); } From 7c9f058c6a08e9218c80f971d8d61d4d6521f81f Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 24 Mar 2026 15:51:35 +0800 Subject: [PATCH 7/9] ci: update --- .github/workflows/linuxUI.yml | 2 +- .github/workflows/windowsUI.yml | 2 +- test/ui/index.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/linuxUI.yml b/.github/workflows/linuxUI.yml index fb22d17b..eecbadff 100644 --- a/.github/workflows/linuxUI.yml +++ b/.github/workflows/linuxUI.yml @@ -86,5 +86,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-screenshots - path: screenshots/ + path: test-resources/screenshots/ if-no-files-found: ignore diff --git a/.github/workflows/windowsUI.yml b/.github/workflows/windowsUI.yml index f730624b..1e7e6ce7 100644 --- a/.github/workflows/windowsUI.yml +++ b/.github/workflows/windowsUI.yml @@ -89,5 +89,5 @@ jobs: uses: actions/upload-artifact@v4 with: name: test-screenshots - path: screenshots/ + path: test-resources/screenshots/ if-no-files-found: ignore diff --git a/test/ui/index.ts b/test/ui/index.ts index e8067d24..c5093477 100644 --- a/test/ui/index.ts +++ b/test/ui/index.ts @@ -9,7 +9,7 @@ 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"); From f194e27e55859d144b800a967236ac5a63885ad2 Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Tue, 24 Mar 2026 16:57:32 +0800 Subject: [PATCH 8/9] ci: update --- test/ui/command.test.ts | 4 ++++ test/ui/index.ts | 5 ++++- test/ui/test-settings.json | 3 +++ 3 files changed, 11 insertions(+), 1 deletion(-) create mode 100644 test/ui/test-settings.json diff --git a/test/ui/command.test.ts b/test/ui/command.test.ts index d15f51fa..1b7277a6 100644 --- a/test/ui/command.test.ts +++ b/test/ui/command.test.ts @@ -47,6 +47,10 @@ describe("Command Tests", function() { fse.copySync(projectPath, projectFolder); await VSBrowser.instance.openResources(projectFolder); currentProjectPath = projectFolder; + // On Linux the workspace trust dialog appears before the folder is actually loaded. + // Dismiss it here so subsequent steps see a fully opened workspace. + await sleep(2000); + await dismissModalDialogIfPresent(); await ensureExplorerIsOpen(); } diff --git a/test/ui/index.ts b/test/ui/index.ts index c5093477..5b06d148 100644 --- a/test/ui/index.ts +++ b/test/ui/index.ts @@ -15,7 +15,10 @@ async function main(): Promise { 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 +} From 68412057f159c8b9408e7c7ba1f744bb38f8c59e Mon Sep 17 00:00:00 2001 From: wenytang-ms Date: Wed, 25 Mar 2026 09:36:02 +0800 Subject: [PATCH 9/9] test: update --- test/ui/command.test.ts | 29 ++++++++++++++++++++++++++--- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/test/ui/command.test.ts b/test/ui/command.test.ts index 1b7277a6..4bbe3561 100644 --- a/test/ui/command.test.ts +++ b/test/ui/command.test.ts @@ -47,9 +47,12 @@ describe("Command Tests", function() { fse.copySync(projectPath, projectFolder); await VSBrowser.instance.openResources(projectFolder); currentProjectPath = projectFolder; - // On Linux the workspace trust dialog appears before the folder is actually loaded. - // Dismiss it here so subsequent steps see a fully opened workspace. - await sleep(2000); + // 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(); } @@ -554,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); + } +} +