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