diff --git a/src/test/GradleTestDevModeActions.ts b/src/test/GradleTestDevModeActions.ts
index 90c0703e..a1ed691f 100755
--- a/src/test/GradleTestDevModeActions.ts
+++ b/src/test/GradleTestDevModeActions.ts
@@ -3,7 +3,7 @@
* Copyright IBM Corp. 2023, 2026
*/
import { expect } from 'chai';
-import { DefaultTreeItem, EditorView, InputBox, SideBarView, ViewSection, VSBrowser, Workbench } from 'vscode-extension-tester';
+import { DefaultTreeItem, EditorView, InputBox, SideBarView, ViewSection, VSBrowser, WebDriver, Workbench } from 'vscode-extension-tester';
import * as utils from './utils/testUtils';
import * as constants from './definitions/constants';
import { logger } from './utils/testLogger';
@@ -18,6 +18,7 @@ describe('Devmode action tests for Gradle Project', () => {
before(async function() {
this.timeout(30000);
// Wait for workbench to be ready
+ await VSBrowser.instance.openResources(utils.getGradleProjectPath());
await VSBrowser.instance.waitForWorkbench();
sidebar = new SideBarView();
});
diff --git a/src/test/GradleTestLSPHover.ts b/src/test/GradleTestLSPHover.ts
index 201a9c8d..06d52ec9 100644
--- a/src/test/GradleTestLSPHover.ts
+++ b/src/test/GradleTestLSPHover.ts
@@ -3,7 +3,7 @@
* Copyright IBM Corp. 2026
*/
import { expect } from 'chai';
-import { EditorView, TextEditor, VSBrowser, Workbench } from 'vscode-extension-tester';
+import { EditorView, TextEditor, VSBrowser, WebDriver, Workbench } from 'vscode-extension-tester';
import * as utils from './utils/testUtils';
import { logger } from './utils/testLogger';
import * as path from 'path';
@@ -12,11 +12,15 @@ describe('LSP Hover tests for Gradle Project', () => {
let editorView: EditorView;
let editor: TextEditor;
let wait: any;
+ let driver: WebDriver;
before(async function() {
this.timeout(60000);
logger.info('Setting up Gradle LSP Hover tests');
+ driver = VSBrowser.instance.driver;
+ await VSBrowser.instance.openResources(utils.getGradleProjectPath());
+
// Wait for workbench to be ready
await VSBrowser.instance.waitForWorkbench();
editorView = new EditorView();
diff --git a/src/test/GradleTestLSPRestSnippetAndDiagnostic.ts b/src/test/GradleTestLSPRestSnippetAndDiagnostic.ts
new file mode 100644
index 00000000..79b492d8
--- /dev/null
+++ b/src/test/GradleTestLSPRestSnippetAndDiagnostic.ts
@@ -0,0 +1,246 @@
+
+/*
+ * IBM Confidential
+ * Copyright IBM Corp. 2026
+ */
+import { expect } from 'chai';
+import { EditorView, TextEditor, VSBrowser, Workbench, WebDriver, BottomBarPanel, MarkerType, Key, By } from 'vscode-extension-tester';
+import * as utils from './utils/testUtils';
+import { logger } from './utils/testLogger';
+import * as path from 'path';
+
+describe('Rest Class Snippet Test for Gradle Project', () => {
+ let editorView: EditorView;
+ let javaEditor: TextEditor;
+ let wait: any;
+ let driver: WebDriver;
+
+ const testRestPath = path.resolve(
+ utils.getGradleProjectPath(),
+ 'src', 'main', 'java', 'test', 'gradle', 'liberty', 'web', 'app',
+ 'TestRest.java'
+ );
+
+ before(async function() {
+ this.timeout(60000);
+ logger.info('Setting up rest_class snippet test');
+
+ driver = VSBrowser.instance.driver;
+ await VSBrowser.instance.openResources(utils.getGradleProjectPath());
+
+ // Wait for workbench to be ready
+ await VSBrowser.instance.waitForWorkbench();
+ editorView = new EditorView();
+ wait = utils.getWaitHelper();
+
+ // Open the file
+ await VSBrowser.instance.openResources(testRestPath, async () => {
+ await wait.sleep(3000);
+ });
+ javaEditor = await editorView.openEditor('TestRest.java') as TextEditor;
+ });
+
+ afterEach(async function() {
+ // Take screenshot on failure but don't close editor
+ if (this.currentTest?.state === 'failed') {
+ const driver = VSBrowser.instance.driver;
+ const screenshot = await driver.takeScreenshot();
+ logger.error(`Test failed: ${this.currentTest.title}`);
+ }
+ });
+
+ after(async function() {
+ this.timeout(15000);
+ try {
+ if (javaEditor) {
+ // Select all text first, then clear
+ const currentText = await javaEditor.getText();
+ try {
+ await javaEditor.selectText(currentText);
+ await wait.sleep(300);
+ } catch (selectError) {
+ // selectText may fail but setText still works
+ }
+
+ await javaEditor.setText('');
+ await wait.sleep(500);
+ await javaEditor.save();
+ await wait.sleep(500);
+ logger.info('Reset TestRest.java to empty after test');
+ }
+ } catch (error) {
+ logger.error('Failed to reset TestRest.java', error);
+ }
+
+ try {
+ await editorView.closeAllEditors();
+ await wait.sleep(500);
+ logger.info('Closed all editors after test suite');
+ } catch (error) {
+ logger.error('Failed to close editors in after hook', error);
+ }
+
+ try {
+ utils.copyScreenshotsToProjectFolder('maven');
+ } catch (error) {
+ // Ignore screenshot errors
+ }
+ });
+
+ it('LSP4Jakarta Language Server should initialize', async function() {
+ this.timeout(60000);
+ logger.testStart('LSP4Jakarta Language Server should initialize');
+
+ try {
+ await utils.waitForLanguageServerInit(
+ 'Language Support for Jakarta EE',
+ 'Initializing Jakarta EE server',
+ 60
+ );
+ logger.testComplete('LSP4Jakarta Language Server initialized successfully');
+ } catch (error) {
+ logger.testFailed('LSP4Jakarta Language Server should initialize', error);
+ throw error;
+ }
+ });
+
+ it('rest_class snippet populates correct REST class', async function () {
+ this.timeout(275000);
+ logger.testStart('rest_class snippet inserts correct REST class');
+
+ // at the top of the rest_class snippet test, before positioning the cursor
+ await javaEditor.setText('');
+ await javaEditor.save();
+ await wait.sleep(1500); // let any auto-stub settle, then confirm
+ const check = await javaEditor.getText();
+ logger.info('Buffer before snippet: ' + JSON.stringify(check));
+
+ try {
+ logger.step(1, 'Positioning cursor for snippet insertion');
+ // Position cursor at end of file
+ const lastLine = (await javaEditor.getText()).split('\n').length;
+ await javaEditor.setCursor(lastLine - 1, 1);
+
+ logger.step(2, 'Typing "rest" to trigger snippet');
+ await javaEditor.typeText('rest');
+ logger.stepSuccess(2, 'Typed "rest"');
+
+ logger.step(3, 'Opening content assist');
+ const assist = await javaEditor.toggleContentAssist(true);
+ if (assist) {
+ logger.step(4,'Selecting rest_class snippet');
+ await assist.select('rest_class');
+ await new Promise(res => setTimeout(res, 600)); // 300–800ms
+ logger.stepSuccess(4, 'rest_class snippet selected');
+ }
+ await javaEditor.toggleContentAssist(false);
+
+ logger.step(5, 'Verifying snippet insertion');
+ const codeInsertion = await javaEditor.getText();
+ expect(codeInsertion).to.include('@GET')
+ expect(codeInsertion).to.include('methodname');
+ logger.stepSuccess(5, 'Snippet rest_class was inserted correctly');
+
+ logger.testComplete('rest_class snippet inserts correct REST class');
+ } catch (error) {
+ logger.testFailed('rest_class snippet inserts correct REST class', error);
+ throw error;
+ }
+ });
+ it('Show that private @GET method displays diagnostic and quick fix removes it ', async function () {
+ this.timeout(90000);
+ logger.testStart('Diagnostic for a private @GET method and quick fix clears it');
+ try {
+ // Find the method with @GET and change it to private
+ let lineNum = await javaEditor.getLineOfText('methodname');
+ if (lineNum < 1) throw new Error('Could not find the methodname line');
+ const oldLine = await javaEditor.getTextAtLine(lineNum);
+ const newLine = oldLine.replace("public", "private");
+ await javaEditor.setTextAtLine(lineNum, newLine);
+
+ // Save file and wait for reanalysis
+ await javaEditor.save();
+ await wait.sleep(500);
+
+ // Open problems view
+ const bottomBar = new BottomBarPanel();
+ await bottomBar.toggle(true);
+ let problemsView = await bottomBar.openProblemsView();
+ let markers = await problemsView.getAllVisibleMarkers(MarkerType.Any);
+
+ // Check if the marker is present
+ let found = false;
+ for (const marker of markers) {
+ const text = await marker.getText();
+ // Check if text contains your diagnostic message
+ if(text.includes('Only public methods can be exposed as resource methods.')){
+ found = true;
+ break;
+ }
+ }
+ expect(found).to.be.true;
+ logger.testComplete('Diagnostic shows for private @GET method');
+
+ // Quick fix implementation to get rid of diagnostic
+ // Re-find line and place cursor on "methodname()"
+ const buffer = await javaEditor.getText();
+ logger.info('Buffer before quick fix: ' + JSON.stringify(buffer));
+
+ // Get column on the method name
+ await javaEditor.selectText('methodname');
+ await wait.sleep(300);
+
+ // Open the quick-fix menu with Cmd+. (Mac) / Ctrl+. (Linux/Windows)
+ const modKey = process.platform === 'darwin' ? Key.COMMAND : Key.CONTROL;
+ await driver.actions().keyDown(modKey).sendKeys('.').keyUp(modKey).perform();
+ await wait.sleep(2000);
+
+ // Click on the option to make the method public within the quick-fixes options
+ const options = await driver.findElements(By.css('.action-widget .action-list-item, .action-widget .monaco-list-row'));
+ let clicked = false;
+ for (const opt of options) {
+ const text = await opt.getText();
+ logger.info('OPTION: ' + JSON.stringify(text));
+ if (text.toLowerCase().includes('make method public')) {
+ await driver.executeScript('arguments[0].click();', opt);
+ clicked = true;
+ break;
+ }
+ }
+ if (!clicked) {
+ await driver.actions().sendKeys(Key.ESCAPE).perform(); // close menu cleanly
+ throw new Error('No "make public" quick fix was offered — see OPTION logs above');
+ }
+
+ await wait.sleep(3000);
+ await javaEditor.save();
+ await wait.sleep(5000);
+
+ // Check quick fix was implemented at correct line number
+ const after = await javaEditor.getText();
+ expect(after).to.include('public String methodname');
+
+ problemsView = await bottomBar.openProblemsView();
+ markers = await problemsView.getAllVisibleMarkers(MarkerType.Any);
+
+ // Check if the marker is present
+ let stillPresent = false;
+ for (const marker of markers) {
+ const text = await marker.getText();
+ // Check if text contains diagnostic message
+ if(text.includes('Only public methods can be exposed as resource methods.')){
+ stillPresent = true;
+ break;
+ }
+ }
+ expect(stillPresent).to.be.false;
+ logger.testComplete('Diagnostic no longer shows for public @GET method as expected');
+
+ } catch (error) {
+ logger.testFailed('Diagnostic/quick-fix test flow failed', error);
+ throw error;
+ }
+
+ });
+ });
+
diff --git a/src/test/MavenTestDevModeActions.ts b/src/test/MavenTestDevModeActions.ts
index dfb05671..9a88dff8 100755
--- a/src/test/MavenTestDevModeActions.ts
+++ b/src/test/MavenTestDevModeActions.ts
@@ -3,7 +3,7 @@
* Copyright IBM Corp. 2023, 2026
*/
import { expect } from 'chai';
-import { DefaultTreeItem, EditorView, SideBarView, ViewItem, ViewSection, VSBrowser, Workbench } from 'vscode-extension-tester';
+import { DefaultTreeItem, EditorView, SideBarView, ViewItem, ViewSection, VSBrowser, Workbench, WebDriver } from 'vscode-extension-tester';
import * as utils from './utils/testUtils';
import * as constants from './definitions/constants';
import { logger } from './utils/testLogger';
@@ -18,8 +18,10 @@ describe('Devmode action tests for Maven Project', () => {
before(async function() {
this.timeout(30000);
- // Wait for workbench to be ready
+
+ await VSBrowser.instance.openResources(utils.getMvnProjectPath());
await VSBrowser.instance.waitForWorkbench();
+
sidebar = new SideBarView();
});
diff --git a/src/test/MavenTestLSPHover.ts b/src/test/MavenTestLSPHover.ts
index beb3e711..5e1467ce 100644
--- a/src/test/MavenTestLSPHover.ts
+++ b/src/test/MavenTestLSPHover.ts
@@ -3,7 +3,7 @@
* Copyright IBM Corp. 2026
*/
import { expect } from 'chai';
-import { EditorView, TextEditor, VSBrowser, Workbench } from 'vscode-extension-tester';
+import { EditorView, TextEditor, VSBrowser, Workbench, WebDriver } from 'vscode-extension-tester';
import * as utils from './utils/testUtils';
import { logger } from './utils/testLogger';
import * as path from 'path';
@@ -12,9 +12,13 @@ describe('LSP Hover tests for Maven Project', () => {
let editorView: EditorView;
let editor: TextEditor;
let wait: any;
+ let driver: WebDriver;
+
before(async function() {
this.timeout(60000);
+ driver = VSBrowser.instance.driver;
+ await VSBrowser.instance.openResources(utils.getMvnProjectPath());
logger.info('Setting up Maven LSP Hover tests');
// Wait for workbench to be ready
@@ -214,4 +218,5 @@ describe('LSP Hover tests for Maven Project', () => {
});
-// Made with Bob
+
+
diff --git a/src/test/MavenTestLSPRestSnippetAndDiagnostic.ts b/src/test/MavenTestLSPRestSnippetAndDiagnostic.ts
new file mode 100644
index 00000000..e08324c8
--- /dev/null
+++ b/src/test/MavenTestLSPRestSnippetAndDiagnostic.ts
@@ -0,0 +1,246 @@
+
+/*
+ * IBM Confidential
+ * Copyright IBM Corp. 2026
+ */
+import { expect } from 'chai';
+import { EditorView, TextEditor, VSBrowser, Workbench, WebDriver, BottomBarPanel, MarkerType, Key, By } from 'vscode-extension-tester';
+import * as utils from './utils/testUtils';
+import { logger } from './utils/testLogger';
+import * as path from 'path';
+
+describe('Rest Class Snippet Test for Maven Project', () => {
+ let editorView: EditorView;
+ let javaEditor: TextEditor;
+ let wait: any;
+ let driver: WebDriver;
+
+ const testRestPath = path.resolve(
+ utils.getMvnProjectPath(),
+ 'src', 'main', 'java', 'test', 'maven', 'liberty', 'web', 'app',
+ 'TestRest.java'
+ );
+
+ before(async function() {
+ this.timeout(60000);
+ logger.info('Setting up rest_class snippet test');
+
+ driver = VSBrowser.instance.driver;
+ await VSBrowser.instance.openResources(utils.getMvnProjectPath());
+
+ // Wait for workbench to be ready
+ await VSBrowser.instance.waitForWorkbench();
+ editorView = new EditorView();
+ wait = utils.getWaitHelper();
+
+ // Open the file
+ await VSBrowser.instance.openResources(testRestPath, async () => {
+ await wait.sleep(3000);
+ });
+ javaEditor = await editorView.openEditor('TestRest.java') as TextEditor;
+ });
+
+ afterEach(async function() {
+ // Take screenshot on failure but don't close editor
+ if (this.currentTest?.state === 'failed') {
+ const driver = VSBrowser.instance.driver;
+ const screenshot = await driver.takeScreenshot();
+ logger.error(`Test failed: ${this.currentTest.title}`);
+ }
+ });
+
+ after(async function() {
+ this.timeout(15000);
+ try {
+ if (javaEditor) {
+ // Select all text first, then clear
+ const currentText = await javaEditor.getText();
+ try {
+ await javaEditor.selectText(currentText);
+ await wait.sleep(300);
+ } catch (selectError) {
+ // selectText may fail but setText still works
+ }
+
+ await javaEditor.setText('');
+ await wait.sleep(500);
+ await javaEditor.save();
+ await wait.sleep(500);
+ logger.info('Reset TestRest.java to empty after test');
+ }
+ } catch (error) {
+ logger.error('Failed to reset TestRest.java', error);
+ }
+
+ try {
+ await editorView.closeAllEditors();
+ await wait.sleep(500);
+ logger.info('Closed all editors after test suite');
+ } catch (error) {
+ logger.error('Failed to close editors in after hook', error);
+ }
+
+ try {
+ utils.copyScreenshotsToProjectFolder('maven');
+ } catch (error) {
+ // Ignore screenshot errors
+ }
+ });
+
+ it('LSP4Jakarta Language Server should initialize', async function() {
+ this.timeout(60000);
+ logger.testStart('LSP4Jakarta Language Server should initialize');
+
+ try {
+ await utils.waitForLanguageServerInit(
+ 'Language Support for Jakarta EE',
+ 'Initializing Jakarta EE server',
+ 60
+ );
+ logger.testComplete('LSP4Jakarta Language Server initialized successfully');
+ } catch (error) {
+ logger.testFailed('LSP4Jakarta Language Server should initialize', error);
+ throw error;
+ }
+ });
+
+ it('rest_class snippet populates correct REST class', async function () {
+ this.timeout(275000);
+ logger.testStart('rest_class snippet inserts correct REST class');
+
+ // at the top of the rest_class snippet test, before positioning the cursor
+ await javaEditor.setText('');
+ await javaEditor.save();
+ await wait.sleep(1500); // let any auto-stub settle, then confirm
+ const check = await javaEditor.getText();
+ logger.info('Buffer before snippet: ' + JSON.stringify(check));
+
+ try {
+ logger.step(1, 'Positioning cursor for snippet insertion');
+ // Position cursor at end of file
+ const lastLine = (await javaEditor.getText()).split('\n').length;
+ await javaEditor.setCursor(lastLine - 1, 1);
+
+ logger.step(2, 'Typing "rest" to trigger snippet');
+ await javaEditor.typeText('rest');
+ logger.stepSuccess(2, 'Typed "rest"');
+
+ logger.step(3, 'Opening content assist');
+ const assist = await javaEditor.toggleContentAssist(true);
+ if (assist) {
+ logger.step(4,'Selecting rest_class snippet');
+ await assist.select('rest_class');
+ await new Promise(res => setTimeout(res, 600)); // 300–800ms
+ logger.stepSuccess(4, 'rest_class snippet selected');
+ }
+ await javaEditor.toggleContentAssist(false);
+
+ logger.step(5, 'Verifying snippet insertion');
+ const codeInsertion = await javaEditor.getText();
+ expect(codeInsertion).to.include('@GET')
+ expect(codeInsertion).to.include('methodname');
+ logger.stepSuccess(5, 'Snippet rest_class was inserted correctly');
+
+ logger.testComplete('rest_class snippet inserts correct REST class');
+ } catch (error) {
+ logger.testFailed('rest_class snippet inserts correct REST class', error);
+ throw error;
+ }
+ });
+ it('Show that private @GET method displays diagnostic and quick fix removes it ', async function () {
+ this.timeout(90000);
+ logger.testStart('Diagnostic for a private @GET method and quick fix clears it');
+ try {
+ // Find the method with @GET and change it to private
+ let lineNum = await javaEditor.getLineOfText('methodname');
+ if (lineNum < 1) throw new Error('Could not find the methodname line');
+ const oldLine = await javaEditor.getTextAtLine(lineNum);
+ const newLine = oldLine.replace("public", "private");
+ await javaEditor.setTextAtLine(lineNum, newLine);
+
+ // Save file and wait for reanalysis
+ await javaEditor.save();
+ await wait.sleep(500);
+
+ // Open problems view
+ const bottomBar = new BottomBarPanel();
+ await bottomBar.toggle(true);
+ let problemsView = await bottomBar.openProblemsView();
+ let markers = await problemsView.getAllVisibleMarkers(MarkerType.Any);
+
+ // Check if the marker is present
+ let found = false;
+ for (const marker of markers) {
+ const text = await marker.getText();
+ // Check if text contains your diagnostic message
+ if(text.includes('Only public methods can be exposed as resource methods.')){
+ found = true;
+ break;
+ }
+ }
+ expect(found).to.be.true;
+ logger.testComplete('Diagnostic shows for private @GET method');
+
+ // Quick fix implementation to get rid of diagnostic
+ // Re-find line and place cursor on "methodname()"
+ const buffer = await javaEditor.getText();
+ logger.info('Buffer before quick fix: ' + JSON.stringify(buffer));
+
+ // Get column on the method name
+ await javaEditor.selectText('methodname');
+ await wait.sleep(300);
+
+ // Open the quick-fix menu with Cmd+. (Mac) / Ctrl+. (Linux/Windows)
+ const modKey = process.platform === 'darwin' ? Key.COMMAND : Key.CONTROL;
+ await driver.actions().keyDown(modKey).sendKeys('.').keyUp(modKey).perform();
+ await wait.sleep(2000);
+
+ // Click on the option to make the method public within the quick-fixes options
+ const options = await driver.findElements(By.css('.action-widget .action-list-item, .action-widget .monaco-list-row'));
+ let clicked = false;
+ for (const opt of options) {
+ const text = await opt.getText();
+ logger.info('OPTION: ' + JSON.stringify(text));
+ if (text.toLowerCase().includes('make method public')) {
+ await driver.executeScript('arguments[0].click();', opt);
+ clicked = true;
+ break;
+ }
+ }
+ if (!clicked) {
+ await driver.actions().sendKeys(Key.ESCAPE).perform(); // close menu cleanly
+ throw new Error('No "make public" quick fix was offered — see OPTION logs above');
+ }
+
+ await wait.sleep(3000);
+ await javaEditor.save();
+ await wait.sleep(5000);
+
+ // Check quick fix was implemented at correct line number
+ const after = await javaEditor.getText();
+ expect(after).to.include('public String methodname');
+
+ problemsView = await bottomBar.openProblemsView();
+ markers = await problemsView.getAllVisibleMarkers(MarkerType.Any);
+
+ // Check if the marker is present
+ let stillPresent = false;
+ for (const marker of markers) {
+ const text = await marker.getText();
+ // Check if text contains diagnostic message
+ if(text.includes('Only public methods can be exposed as resource methods.')){
+ stillPresent = true;
+ break;
+ }
+ }
+ expect(stillPresent).to.be.false;
+ logger.testComplete('Diagnostic no longer shows for public @GET method as expected');
+
+ } catch (error) {
+ logger.testFailed('Diagnostic/quick-fix test flow failed', error);
+ throw error;
+ }
+
+ });
+ });
+
diff --git a/src/test/resources/gradle/liberty-gradle-test-wrapper-app/src/main/java/test/gradle/liberty/web/app/TestRest.java b/src/test/resources/gradle/liberty-gradle-test-wrapper-app/src/main/java/test/gradle/liberty/web/app/TestRest.java
new file mode 100644
index 00000000..e69de29b
diff --git a/src/test/resources/maven/liberty-maven-test-wrapper-app/pom.xml b/src/test/resources/maven/liberty-maven-test-wrapper-app/pom.xml
index 79a2ccbb..8d10039e 100755
--- a/src/test/resources/maven/liberty-maven-test-wrapper-app/pom.xml
+++ b/src/test/resources/maven/liberty-maven-test-wrapper-app/pom.xml
@@ -47,6 +47,12 @@
5.8.1
test
+
+ jakarta.ws.rs
+ jakarta.ws.rs-api
+ 3.1.0
+ provided
+
diff --git a/src/test/resources/maven/liberty-maven-test-wrapper-app/src/main/java/test/maven/liberty/web/app/TestRest.java b/src/test/resources/maven/liberty-maven-test-wrapper-app/src/main/java/test/maven/liberty/web/app/TestRest.java
new file mode 100644
index 00000000..e69de29b