Skip to content
Merged
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
351 changes: 351 additions & 0 deletions src/test/MavenTestServerXmlLCLS.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,351 @@
/*
* IBM Confidential
* Copyright IBM Corp. 2025, 2026
Comment thread
siddhusaladi marked this conversation as resolved.
Outdated
*/
import { expect } from 'chai';
import { By, EditorView, TextEditor, VSBrowser, WebDriver, Workbench, BottomBarPanel, MarkerType, Key } from 'vscode-extension-tester';
import * as utils from './utils/testUtils';
import { logger } from './utils/testLogger';
import * as path from 'path';

describe('Liberty Config Language Server Tests for Maven Project', () => {
let editorView: EditorView;
let editor: TextEditor;
let wait: any;
let driver: WebDriver;
let originalContent: string;

before(async function() {
this.timeout(60000);
driver = VSBrowser.instance.driver;
wait = utils.getWaitHelper();

// Open workspace
await VSBrowser.instance.openResources(utils.getMvnProjectPath());
await VSBrowser.instance.waitForWorkbench();
editorView = new EditorView();

// Open the real server.xml
const serverXmlPath = path.resolve(
utils.getMvnProjectPath(),
'src', 'main', 'liberty', 'config', 'server.xml'
);

await VSBrowser.instance.openResources(serverXmlPath, async () => {
await wait.sleep(3000);
});

editor = await editorView.openEditor('server.xml') as TextEditor;
originalContent = await editor.getText();
logger.info('Server.xml file opened and original content saved');
});

afterEach(async function() {
this.timeout(30000);
if (this.currentTest?.state === 'failed') {
await driver.takeScreenshot();
logger.error(`Test failed: ${this.currentTest?.title}`);
}

// Close the bottom bar and re-focus the editor
await new BottomBarPanel().toggle(false);
editor = await editorView.openEditor('server.xml') as TextEditor;
// Wait until getText() returns the actual server.xml content
await utils.waitForCondition(async () => {
const text = await editor.getText();
return text.includes('<server') ? true : undefined;
}, 15);

// Restore original content
if (originalContent) {
await editor.setText(originalContent);
await editor.save();
await wait.sleep(3000);
logger.info('Restored original server.xml content');
}
});

after(async function() {
this.timeout(30000);
try {
await new Workbench().executeCommand('revert file');
await wait.sleep(500);
await editorView.closeAllEditors();
logger.info('Closed all editors');
} catch (error) {
logger.error('Failed to close editors', error);
}
utils.copyScreenshotsToProjectFolder('maven');
});

it('Liberty Language Server should initialize', async function() {
this.timeout(60000);
logger.testStart('Liberty Language Server should initialize');
await utils.waitForLanguageServerInit(
'Language Support for Liberty',
'Initialized Liberty Language server',
60
);
logger.testComplete('Liberty Language Server initialized successfully');
});


// Diagnostic + Quick Fix
it('Should show diagnostic for invalid feature and apply quick fix', async function() {
Comment thread
siddhusaladi marked this conversation as resolved.
Outdated
this.timeout(120000);
logger.testStart('Testing diagnostic detection and quick fix for invalid feature');


editor = await editorView.openEditor('server.xml') as TextEditor;

logger.step(1, 'Finding feature line and replacing with invalid feature');
const lineNumber = await editor.getLineOfText('jsp-2.3');
logger.stepSuccess(1, `Found feature at line ${lineNumber}`);
const currentLine = await editor.getTextAtLine(lineNumber);
const modifiedLine = currentLine.replace('jsp-2.3', 'jsp-100.0');
await editor.setTextAtLine(lineNumber, modifiedLine);
await editor.save();
logger.stepSuccess(1, 'Changed to invalid feature jsp-100.0');

logger.step(2, 'Waiting for diagnostic to appear');
// LCLS produces: ERROR: The feature "jsp-100.0" does not exist. liberty-lemminx(incorrect_feature)
await wait.forCondition(async () => {
try {
const bottomBar = new BottomBarPanel();
await bottomBar.toggle(true);
const problemsView = await bottomBar.openProblemsView();
const markers = await problemsView.getAllVisibleMarkers(MarkerType.Error);
for (const marker of markers) {
const text = await marker.getText();
if (text.includes('does not exist')) {
logger.stepSuccess(2, `Diagnostic found: ${text}`);
return true;
}
}
return undefined;
} catch {
return undefined;
}
}, {
timeout: 45000,
pollInterval: 2000,
message: 'Diagnostic did not appear for invalid feature'
});


await new BottomBarPanel().toggle(false);
editor = await editorView.openEditor('server.xml') as TextEditor;
await utils.waitForCondition(async () => {
const text = await editor.getText();
return text.includes('<server') ? true : undefined;
}, 15);

logger.step(3, 'Selecting invalid feature text');
await editor.selectText('jsp-100.0');
logger.stepSuccess(3, 'Selected invalid feature');

logger.step(4, 'Opening quick fix menu and applying fix');
const modKey = process.platform === 'darwin' ? Key.COMMAND : Key.CONTROL;

// The quick-fix widget closes between polling iterations, so re-open it each time
const quickFixApplied = await wait.forCondition(async () => {
try {
await editor.selectText('jsp-100.0');
await driver.actions().keyDown(modKey).sendKeys('.').keyUp(modKey).perform();
await wait.sleep(1500);

const options = await driver.findElements(
By.css('.action-widget .action-list-item, .action-widget .monaco-list-row')
);
for (const opt of options) {
const text = await opt.getText();
logger.info(`Quick fix option: ${text}`);
// LCLS offers "Replace feature with jsp-2.2" and "Replace feature with jsp-2.3", accept either
if (text.includes('Replace feature with jsp-')) {
await driver.executeScript('arguments[0].click();', opt);
logger.stepSuccess(4, `Applied quick fix: ${text}`);
return true;
}
}
// Dismiss the menu so re-opening next iteration is clean
await driver.actions().sendKeys(Key.ESCAPE).perform();
return undefined;
} catch {
return undefined;
}
}, {
timeout: 30000,
pollInterval: 3000,
message: 'Quick fix option did not appear'
});

expect(quickFixApplied).to.be.true;

logger.step(5, 'Verifying fix was applied');
const updatedContent = await utils.waitForCondition(async () => {
const text = await editor.getText();
return !text.includes('jsp-100.0') ? text : undefined;
}, 15);
// Either jsp-2.2 or jsp-2.3 is a valid replacement — verify the bad value is gone
expect(updatedContent).to.match(/<feature>jsp-2\.\d<\/feature>/);
expect(updatedContent).to.not.include('jsp-100.0');
logger.stepSuccess(5, 'Quick fix successfully replaced invalid feature');

logger.testComplete('Diagnostic detected and quick fix applied successfully');
});


// Autocomplete for Features
it('Should provide autocomplete for Liberty features (#391)', async function() {
Comment thread
siddhusaladi marked this conversation as resolved.
Outdated
this.timeout(60000);
logger.testStart('Testing autocomplete for features');

editor = await editorView.openEditor('server.xml') as TextEditor;


// verify the list opens with items, then dismiss it and type the value directly
logger.step(1, 'Finding the closing </featureManager> line as insertion point');
const featureManagerEndLine = await editor.getLineOfText('</featureManager>');
logger.stepSuccess(1, `Found </featureManager> at line ${featureManagerEndLine}`);

logger.step(2, 'Typing empty feature tag on a new line');
await editor.typeTextAt(featureManagerEndLine, 1, ' <feature></feature>');
logger.stepSuccess(2, 'Typed empty feature tag');

logger.step(3, 'Positioning cursor inside the empty feature tag');
await editor.setCursor(featureManagerEndLine, 18);
logger.stepSuccess(3, 'Cursor positioned inside feature tag');

logger.step(4, 'Opening content assist and verifying feature list appears');
const assist = await editor.toggleContentAssist(true);

const items = await utils.waitForCondition(async () => {
try {
const all = await assist!.getItems();
return all && all.length > 0 ? all : undefined;
} catch {
return undefined;
}
}, 10);
expect(items.length).to.be.greaterThan(0);
logger.stepSuccess(4, `Content assist opened with ${items.length} feature suggestions`);

// Dismiss the list and type the value directly
await editor.toggleContentAssist(false);
await driver.actions().sendKeys(Key.ESCAPE).perform();

logger.step(5, 'Typing feature value directly after confirming list appeared');
await editor.typeTextAt(featureManagerEndLine, 18, 'servlet-4.0');
logger.stepSuccess(5, 'Typed feature value');

logger.step(6, 'Verifying feature tag contains the typed value');
const updatedContent = await utils.waitForCondition(async () => {
const text = await editor.getText();
return text.includes('<feature>servlet-4.0</feature>') ? text : undefined;
}, 10);
expect(updatedContent).to.include('<feature>servlet-4.0</feature>');
logger.stepSuccess(6, 'Feature tag correctly contains typed value');

logger.testComplete('Autocomplete for features worked');
});


// Autocomplete for Config Stanzas
describe('Autocomplete for Configuration Stanzas (#392)', () => {
Comment thread
siddhusaladi marked this conversation as resolved.
Outdated

it('Should autocomplete <logging> stanza', async function() {
this.timeout(60000);
logger.testStart('Testing autocomplete for logging stanza');

editor = await editorView.openEditor('server.xml') as TextEditor;

logger.step(1, 'Finding </featureManager> as insertion point');
const lineNumber = await editor.getLineOfText('</featureManager>');
logger.stepSuccess(1, `Found featureManager end at line ${lineNumber}`);

logger.step(2, 'Typing partial stanza name on a new line');
await editor.typeTextAt(lineNumber + 1, 1, ' <loggi');
logger.stepSuccess(2, 'Typed partial stanza name');

logger.step(3, 'Opening content assist for stanza completion');
const assist = await utils.waitForCondition(async () => {
return await editor.toggleContentAssist(true) ?? undefined;
}, 10);
// Wait until the target item is present and interactable in the list
await utils.waitForCondition(async () => {
try {
const item = await assist.getItem('logging');
return item ? true : undefined;
} catch {
return undefined;
}
}, 10);
logger.stepSuccess(3, 'Content assist opened');

logger.step(4, 'Selecting "logging" from the completion list');
await assist.select('logging');
await editor.toggleContentAssist(false);
logger.stepSuccess(4, 'Selected logging from completion list');

logger.step(5, 'Verifying logging stanza was inserted');
// Poll until the editor reflects the completion rather than sleeping blindly
const updatedContent = await utils.waitForCondition(async () => {
const text = await editor.getText();
return text.includes('<logging>') ? text : undefined;
}, 10);
expect(updatedContent).to.include('<logging>');
expect(updatedContent).to.include('</logging>');
logger.stepSuccess(5, 'Logging stanza autocomplete worked');

logger.testComplete('Logging stanza autocomplete worked');
});

it('Should autocomplete <application> stanza', async function() {
Comment thread
siddhusaladi marked this conversation as resolved.
Outdated
this.timeout(60000);
logger.testStart('Testing autocomplete for application stanza');

editor = await editorView.openEditor('server.xml') as TextEditor;

logger.step(1, 'Finding </featureManager> as insertion point');
const lineNumber = await editor.getLineOfText('</featureManager>');
logger.stepSuccess(1, `Found featureManager end at line ${lineNumber}`);

logger.step(2, 'Typing partial stanza name on a new line');
await editor.typeTextAt(lineNumber + 1, 1, ' <applicati');
logger.stepSuccess(2, 'Typed partial stanza name');

logger.step(3, 'Opening content assist for stanza completion');
const assist = await utils.waitForCondition(async () => {
return await editor.toggleContentAssist(true) ?? undefined;
}, 10);
// Wait until the target item is present and interactable in the list
await utils.waitForCondition(async () => {
try {
const item = await assist.getItem('application');
return item ? true : undefined;
} catch {
return undefined;
}
}, 10);
logger.stepSuccess(3, 'Content assist opened');

logger.step(4, 'Selecting "application" from the completion list');
await assist.select('application');
await editor.toggleContentAssist(false);
logger.stepSuccess(4, 'Selected application from completion list');

logger.step(5, 'Verifying application stanza was inserted');
// Poll until the editor reflects the completion rather than sleeping blindly
const updatedContent = await utils.waitForCondition(async () => {
const text = await editor.getText();
return /<application[>\s]/.test(text) ? text : undefined;
}, 10);
// Match <application> or <application > but not <webApplication> or <applicationManager>
expect(updatedContent).to.match(/<application[>\s]/);
expect(updatedContent).to.include('</application>');
logger.stepSuccess(5, 'Application stanza autocomplete worked');

logger.testComplete('Application stanza autocomplete worked');
});
});
});
Loading