diff --git a/src/test/GradleSingleModLCLSTest.ts b/src/test/GradleSingleModLCLSTest.ts
new file mode 100755
index 00000000..e3e66d03
--- /dev/null
+++ b/src/test/GradleSingleModLCLSTest.ts
@@ -0,0 +1,401 @@
+/**
+ * Copyright (c) 2024 IBM Corporation.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v. 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ */
+import { By, EditorView, SideBarView, TextEditor, VSBrowser } from "vscode-extension-tester";
+import * as utils from './utils/testUtils';
+import * as constants from './definitions/constants';
+
+const path = require('path');
+const assert = require('assert');
+
+describe('LCLS tests for Gradle Project', function () {
+ let editor: TextEditor;
+ let actualSeverXMLContent: string;
+
+ before(() => {
+ utils.copyConfig(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config'), path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2'));
+ });
+
+ it('Should coppy content of server.xml', async () => {
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ actualSeverXMLContent = await editor.getText();
+
+ assert(actualSeverXMLContent.length !== 0, 'Content of server.xml is not in coppied.');
+ console.log('Sever.xml content:', actualSeverXMLContent);
+
+ }).timeout(15000);
+
+ it('Should show diagnostic for server.xml invalid value', async () => {
+
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+
+ const hverExpectdOutcome = `'wrong' is not a valid value of union type 'booleanType'.`;
+ const testHverTarget = '';
+
+ await editor.typeTextAt(17, 5, testHverTarget);
+ const focusTargtElemnt = editor.findElement(By.xpath("//*[contains(text(), 'wrong')]"));
+ await utils.delay(3000);
+ focusTargtElemnt.click();
+ await editor.click();
+
+ const actns = VSBrowser.instance.driver.actions();
+ await actns.move({ origin: focusTargtElemnt }).perform();
+ await utils.delay(5000);
+
+ const hverContent = editor.findElement(By.className('hover-contents'));
+ const hverValue = await hverContent.getText();
+ console.log("Hover text:" + hverValue);
+
+ assert(hverValue.includes(hverExpectdOutcome), 'Did not get expected diagnostic in server.xml');
+
+ editor.clearText();
+ editor.setText(actualSeverXMLContent);
+ console.log("Content restored");
+
+ }).timeout(35000);
+
+ it('Should apply quick fix for invalid value in server.xml', async () => {
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ const stanzaSnipet = "";
+ const expectedHoverData = "";
+ await editor.typeTextAt(17, 5, stanzaSnipet);
+ await utils.delay(2000);
+ const flagedString = await editor.findElement(By.xpath("//*[contains(text(), '\"wrong\"')]"));
+ await utils.delay(7000);
+
+ const actions = VSBrowser.instance.driver.actions();
+ await actions.move({ origin: flagedString }).perform();
+ await utils.delay(3000);
+
+ const driver = VSBrowser.instance.driver;
+ const hoverTxt = await editor.findElement(By.className('hover-row status-bar'));
+ await utils.delay(2000);
+
+ const qckFixPopupLink = await hoverTxt.findElement(By.xpath("//*[contains(text(), 'Quick Fix')]"));
+ await qckFixPopupLink.click();
+
+ const hoverTaskBar = await editor.findElement(By.className('context-view monaco-component bottom left fixed'));
+ await hoverTaskBar.findElement(By.className('actionList'));
+ await utils.delay(2000);
+
+ const pointerBlockedElement = await driver.findElement(By.css('.context-view-pointerBlock'));
+ // Setting pointer block element display value as none to choose option from Quickfix menu
+ if (pointerBlockedElement) {
+ await driver.executeScript("arguments[0].style.display = 'none';", pointerBlockedElement);
+ } else {
+ console.log('pointerBlockElementt not found!');
+ }
+ const qckfixOption = await editor.findElement(By.xpath("//*[contains(text(), \"Replace with 'true'\")]"));
+ await qckfixOption.click();
+
+ const updatedSeverXMLContent = await editor.getText();
+ await utils.delay(3000);
+ console.log("Content after Quick fix : ", updatedSeverXMLContent);
+ assert(updatedSeverXMLContent.includes(expectedHoverData), 'Quick fix not applied correctly for the invalid value in server.xml.');
+
+ editor.clearText();
+ editor.setText(actualSeverXMLContent);
+ console.log("Content restored");
+
+ }).timeout(38000);
+
+ it('Should show hover text in server.xml for server platform', async () => {
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ const stanzaSnipet = "jakartaee-11.0";
+ const expectedDiagnosticData = `Description: This platform resolves the Liberty features that support the Jakarta EE 11.0 platform.`;
+ await editor.typeTextAt(15, 35, '\n');
+ await editor.typeTextAt(16, 9, stanzaSnipet);
+ await utils.delay(2000);
+ const focusTargtElemnt = await editor.findElement(By.xpath("//*[contains(text(), '\jakarta\')]"));
+ await utils.delay(3000);
+ focusTargtElemnt.click();
+ await editor.click();
+
+ const actns = VSBrowser.instance.driver.actions();
+ await actns.move({ origin: focusTargtElemnt }).perform();
+ await utils.delay(5000);
+
+ const hverContent = editor.findElement(By.className('hover-contents'));
+ const hverValue = await hverContent.getText();
+ console.log("Hover text:" + hverValue);
+
+ assert(hverValue.includes(expectedDiagnosticData), 'Did not get expected hover text in server.xml server platform');
+
+ editor.clearText();
+ editor.setText(actualSeverXMLContent);
+ console.log("Content restored");
+
+ }).timeout(38000);
+
+ it('Should show diagnostic for invalid value in server.xml for server platform', async () => {
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ const stanzaSnipet = "jakarta";
+ const expectedDiagnosticData = `ERROR: The platform "jakarta" does not exist.`;
+ await editor.typeTextAt(15, 35, '\n');
+ await editor.typeTextAt(16, 9, stanzaSnipet);
+ await utils.delay(2000);
+ const focusTargtElemnt = await editor.findElement(By.xpath("//*[contains(text(), '\jakarta\')]"));
+ await utils.delay(3000);
+ focusTargtElemnt.click();
+ await editor.click();
+
+ const actns = VSBrowser.instance.driver.actions();
+ await actns.move({ origin: focusTargtElemnt }).perform();
+ await utils.delay(5000);
+
+ const hverContent = editor.findElement(By.className('hover-contents'));
+ const hverValue = await hverContent.getText();
+ console.log("Hover text:" + hverValue);
+
+ assert(hverValue.includes(expectedDiagnosticData), 'Did not get expected diagnostic in server.xml server platform');
+
+ editor.clearText();
+ editor.setText(actualSeverXMLContent);
+ console.log("Content restored");
+
+ }).timeout(38000);
+
+ it('Should apply quick fix for invalid value in server.xml for server platform', async () => {
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ const stanzaSnipet = "jakarta";
+ const expectedHoverData = "jakartaee-11.0";
+ await editor.typeTextAt(15, 35, '\n');
+ await editor.typeTextAt(16, 9, stanzaSnipet);
+ await utils.delay(2000);
+ const flagedString = await editor.findElement(By.xpath("//*[contains(text(), '\jakarta\')]"));
+ await utils.delay(7000);
+
+ const actions = VSBrowser.instance.driver.actions();
+ await actions.move({ origin: flagedString }).perform();
+ await utils.delay(3000);
+
+ const driver = VSBrowser.instance.driver;
+ const hoverTxt = await editor.findElement(By.className('hover-row status-bar'));
+ await utils.delay(2000);
+
+ const qckFixPopupLink = await hoverTxt.findElement(By.xpath("//*[contains(text(), 'Quick Fix')]"));
+ await qckFixPopupLink.click();
+
+ const hoverTaskBar = await editor.findElement(By.className('context-view monaco-component bottom left fixed'));
+ await hoverTaskBar.findElement(By.className('actionList'));
+ await utils.delay(2000);
+
+ const pointerBlockedElement = await driver.findElement(By.css('.context-view-pointerBlock'));
+ // Setting pointer block element display value as none to choose option from Quickfix menu
+ if (pointerBlockedElement) {
+ await driver.executeScript("arguments[0].style.display = 'none';", pointerBlockedElement);
+ } else {
+ console.log('pointerBlockElementt not found!');
+ }
+ const qckfixOption = await editor.findElement(By.xpath("//*[contains(text(), \"Replace platform with jakartaee-11.0\")]"));
+ await qckfixOption.click();
+
+ const updatedSeverXMLContent = await editor.getText();
+ await utils.delay(3000);
+ console.log("Content after Quick fix : ", updatedSeverXMLContent);
+ assert(updatedSeverXMLContent.includes(expectedHoverData), 'Quick fix not applied correctly for the invalid value in server.xml server platform.');
+
+ editor.clearText();
+ editor.setText(actualSeverXMLContent);
+ console.log("Content restored");
+
+ }).timeout(38000);
+
+ it('Should show diagnostic for invalid value in server.xml for server feature', async () => {
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ const stanzaSnipet = "servlet";
+ const expectedDiagnosticData = `ERROR: The "servlet" versionless feature cannot be resolved since there are more than one common platform. Specify a platform or a feature with a version to enable resolution`;
+ await editor.typeTextAt(15, 35, '\n');
+ await editor.typeTextAt(16, 9, stanzaSnipet);
+ await utils.delay(2000);
+ const focusTargtElemnt = await editor.findElement(By.xpath("//*[contains(text(), '\servlet\')]"));
+ await utils.delay(3000);
+ focusTargtElemnt.click();
+ await editor.click();
+
+ const actns = VSBrowser.instance.driver.actions();
+ await actns.move({ origin: focusTargtElemnt }).perform();
+ await utils.delay(5000);
+
+ const hverContent = editor.findElement(By.className('hover-contents'));
+ const hverValue = await hverContent.getText();
+ console.log("Hover text:" + hverValue);
+
+ assert(hverValue.includes(expectedDiagnosticData), 'Did not get expected diagnostic in server.xml server feature');
+
+ editor.clearText();
+ editor.setText(actualSeverXMLContent);
+ console.log("Content restored");
+
+ }).timeout(38000);
+
+ it('Should apply quick fix for invalid value in server.xml for server feature', async () => {
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ const stanzaSnipet = "servlet";
+ const expectedHoverData = "servlet-3.1";
+ await editor.typeTextAt(15, 35, '\n');
+ await editor.typeTextAt(16, 9, stanzaSnipet);
+ await utils.delay(2000);
+ const flagedString = await editor.findElement(By.xpath("//*[contains(text(), '\servlet\')]"));
+ await utils.delay(7000);
+
+ const actions = VSBrowser.instance.driver.actions();
+ await actions.move({ origin: flagedString }).perform();
+ await utils.delay(3000);
+
+ const driver = VSBrowser.instance.driver;
+ const hoverTxt = await editor.findElement(By.className('hover-row status-bar'));
+ await utils.delay(2000);
+
+ const qckFixPopupLink = await hoverTxt.findElement(By.xpath("//*[contains(text(), 'Quick Fix')]"));
+ await qckFixPopupLink.click();
+
+ const hoverTaskBar = await editor.findElement(By.className('context-view monaco-component bottom left fixed'));
+ await hoverTaskBar.findElement(By.className('actionList'));
+ await utils.delay(2000);
+
+ const pointerBlockedElement = await driver.findElement(By.css('.context-view-pointerBlock'));
+ // Setting pointer block element display value as none to choose option from Quickfix menu
+ if (pointerBlockedElement) {
+ await driver.executeScript("arguments[0].style.display = 'none';", pointerBlockedElement);
+ } else {
+ console.log('pointerBlockElementt not found!');
+ }
+ const qckfixOption = await editor.findElement(By.xpath("//*[contains(text(), \"Replace feature with servlet-3.1\")]"));
+ await qckfixOption.click();
+
+ const updatedSeverXMLContent = await editor.getText();
+ await utils.delay(3000);
+ console.log("Content after Quick fix : ", updatedSeverXMLContent);
+ assert(updatedSeverXMLContent.includes(expectedHoverData), 'Quick fix not applied correctly for the invalid value in server.xml server feature.');
+
+ editor.clearText();
+ editor.setText(actualSeverXMLContent);
+ console.log("Content restored");
+
+ }).timeout(38000);
+
+ it('Should show type ahead support in server.xml Liberty Server platform', async () => {
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ const featureTag = "
{
+ const section = await new SideBarView().getContent().getSection(constants.GRADLE_PROJECT);
+ section.expand();
+ await VSBrowser.instance.openResources(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2', 'server.xml'));
+
+ editor = await new EditorView().openEditor('server.xml') as TextEditor;
+ const stanzaSnipetFeature = "servlet";
+ const stanzaSnipetPlatform = "jakartaee-9.1";
+ const expectedDiagnosticData = `ERROR: The "servlet" versionless feature cannot be resolved since there are more than one common platform. Specify a platform or a feature with a version to enable resolution`;
+ await editor.typeTextAt(15, 35, '\n');
+ await editor.typeTextAt(16, 9, stanzaSnipetFeature);
+ await utils.delay(2000);
+ const focusTargtElemnt = await editor.findElement(By.xpath("//*[contains(text(), '\servlet\')]"));
+ await utils.delay(3000);
+ focusTargtElemnt.click();
+ await editor.click();
+
+ const actns = VSBrowser.instance.driver.actions();
+ await actns.move({ origin: focusTargtElemnt }).perform();
+ await utils.delay(5000);
+
+ const hverContent = editor.findElement(By.className('hover-contents'));
+ const hverValue = await hverContent.getText();
+ console.log("Hover text:" + hverValue);
+ if (hverValue.includes(expectedDiagnosticData)) {
+ await editor.typeTextAt(16, 35, '\n');
+ await editor.typeTextAt(17, 9, stanzaSnipetPlatform);
+ await utils.delay(2000);
+ }
+ const updatedServerxmlContent = await editor.getText();
+ console.log("Updated server.xml content:" + updatedServerxmlContent);
+
+ assert(updatedServerxmlContent.includes(stanzaSnipetFeature) && updatedServerxmlContent.includes(stanzaSnipetPlatform), 'Did not get expected entries in server.xml for versionless combination for server feature and platform');
+
+ editor.clearText();
+ editor.setText(actualSeverXMLContent);
+ console.log("Content restored");
+
+ }).timeout(38000);
+
+ after(() => {
+ utils.removeConfigDir(path.join(utils.getGradleProjectPath(), 'src', 'main', 'liberty', 'config2'));
+ console.log("Removed new config folder:");
+ });
+
+});
\ No newline at end of file
diff --git a/src/test/resources/gradle/liberty.gradle.test.wrapper.app/src/main/liberty/config/server.env b/src/test/resources/gradle/liberty.gradle.test.wrapper.app/src/main/liberty/config/server.env
new file mode 100644
index 00000000..e69de29b
diff --git a/src/test/utils/testUtils.ts b/src/test/utils/testUtils.ts
index a2a0135b..dac3e348 100755
--- a/src/test/utils/testUtils.ts
+++ b/src/test/utils/testUtils.ts
@@ -5,6 +5,7 @@ import { MAVEN_PROJECT, STOP_DASHBOARD_MAC_ACTION } from '../definitions/consta
import { MapContextMenuforMac } from './macUtils';
import clipboard = require('clipboardy');
import { expect } from 'chai';
+import * as fse from 'fs-extra';
export function delay(millisec: number) {
return new Promise( resolve => setTimeout(resolve, millisec) );
@@ -192,4 +193,37 @@ export async function clearCommandPalette() {
expect(buttons.length).equals(2);
await dialog.pushButton('Clear');
}
+
+/**
+ * Remove newly created Project folder with content
+ */
+export async function removeConfigDir(projectPath: string): Promise {
+ try {
+ await fs.accessSync(projectPath);
+ const projectContent = await fs.readdirSync(projectPath);
+ await Promise.all(
+ projectContent.map(async (projectFiles) => {
+ const projectContentPath = path.join(projectPath, projectFiles);
+ const stats = await fs.lstatSync(projectContentPath);
+ if (stats.isDirectory()) {
+ await removeConfigDir(projectContentPath);
+ } else {
+ await fs.unlinkSync(projectContentPath);
+ }
+ })
+ );
+ await fs.rmdirSync(projectPath);
+ } catch (error) {
+ console.error(`Error removing new project: ${error}`);
+ }
+}
+
+/**
+ * Copy config directory and create new config
+ */
+export async function copyConfig(existingConfigPath: string, copyConfigPath: string): Promise {
+ fse.copy(existingConfigPath, copyConfigPath)
+ .then(() => console.log("New config folder created :" + copyConfigPath))
+ .catch(err => console.log("Error creating config folder"));
+}
\ No newline at end of file