forked from OpenLiberty/liberty-tools-vscode
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMavenTestServerXmlLCLS.ts
More file actions
351 lines (294 loc) · 15.1 KB
/
Copy pathMavenTestServerXmlLCLS.ts
File metadata and controls
351 lines (294 loc) · 15.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
/*
* IBM Confidential
* Copyright IBM Corp. 2025, 2026
*/
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() {
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() {
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)', () => {
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() {
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');
});
});
});