Skip to content

Commit 68fc676

Browse files
wenytang-msCopilot
andcommitted
refactor: replace fixed sleep() with polling helpers in UI tests
Replace 12 fixed sleep() calls with condition-based polling: - waitForTreeItem / waitForTreeItemGone: poll tree view for item presence - waitForFileExists / waitForFileGone: poll filesystem for file state - waitForModalDialog: poll for dialog appearance with graceful fallback - waitForEditorTitle: poll for editor to show expected file title This makes tests resilient to CI timing variations instead of relying on hardcoded delays that may be too short on slow machines. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 90e9e4f commit 68fc676

File tree

1 file changed

+94
-33
lines changed

1 file changed

+94
-33
lines changed

test/ui/command.test.ts

Lines changed: 94 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -152,10 +152,9 @@ describe("Command Tests", function() {
152152
assert.ok(await inputBox.getPlaceHolder() === "Input the class name", `InputBox "Input the class name" should appear`);
153153
await inputBox.setText("App2");
154154
await inputBox.confirm();
155-
await sleep(1000);
156-
const editor = new TextEditor();
157-
await editor.save();
158-
assert.ok(await editor.getTitle() === "App2.java", `Editor's title should be "App2.java"`);
155+
const editor = await waitForEditorTitle("App2.java");
156+
assert.ok(editor, `Editor's title should be "App2.java"`);
157+
await editor!.save();
159158
assert.ok(await fse.pathExists(path.join(currentProjectPath!, "src", "main", "java", "App2.java")), `"App2.java" should be created in correct path`);
160159
});
161160

@@ -172,8 +171,7 @@ describe("Command Tests", function() {
172171
inputBox = await InputBox.create();
173172
await inputBox.setText("com.mycompany.app2");
174173
await inputBox.confirm();
175-
await sleep(1000);
176-
assert.ok(await fse.pathExists(path.join(currentProjectPath!, "src", "main", "java", "com", "mycompany", "app2")), `New package should be created in correct path`);
174+
assert.ok(await waitForFileExists(path.join(currentProjectPath!, "src", "main", "java", "com", "mycompany", "app2")), `New package should be created in correct path`);
177175
});
178176

179177
(platform() === "darwin" ? it.skip : it)("Test java.view.package.revealInProjectExplorer", async function() {
@@ -209,21 +207,20 @@ describe("Command Tests", function() {
209207
const inputBox = await InputBox.create();
210208
await inputBox.setText("AppRenamed");
211209
await inputBox.confirm();
212-
await sleep(1000);
213-
const dialog = new ModalDialog();
214-
const buttons = await dialog.getButtons();
210+
const dialog = await waitForModalDialog();
211+
assert.ok(dialog, `Rename confirmation dialog should appear`);
212+
const buttons = await dialog!.getButtons();
215213
for (const button of buttons) {
216214
if (await button.getText() === "OK") {
217215
await button.click();
218216
break;
219217
}
220218
}
221-
await sleep(5000);
219+
const editor = await waitForEditorTitle("AppRenamed.java");
220+
assert.ok(editor, `Editor's title should be "AppRenamed.java"`);
222221
// Use command palette to save because the editor input area may not be
223222
// interactable right after the rename refactoring dialog is dismissed.
224223
await new Workbench().executeCommand('workbench.action.files.save');
225-
const editor = new TextEditor();
226-
assert.ok(await editor.getTitle() === "AppRenamed.java", `Editor's title should be "AppRenamed.java"`);
227224
assert.ok(await section.findItem("AppRenamed"), `Item in Java Project section should be "AppRenamed"`);
228225
});
229226

@@ -242,16 +239,18 @@ describe("Command Tests", function() {
242239
}
243240
assert.ok(deleteItem, `"Delete" item should be found`);
244241
await deleteItem!.click();
245-
const dialog = new ModalDialog();
246-
const buttons = await dialog.getButtons();
247-
for (const button of buttons) {
248-
if (await button.getText() === "Move to Recycle Bin") {
249-
await button.click();
250-
break;
242+
const dialog = await waitForModalDialog();
243+
if (dialog) {
244+
const buttons = await dialog.getButtons();
245+
for (const button of buttons) {
246+
const text = await button.getText();
247+
if (text === "Move to Recycle Bin" || text === "Delete") {
248+
await button.click();
249+
break;
250+
}
251251
}
252252
}
253-
await sleep(1000);
254-
assert.ok(!await fse.pathExists(path.join(currentProjectPath!, "src", "main", "java", "AppToDelete.java")), `The source file "AppToDelete.java" should be deleted`);
253+
assert.ok(await waitForFileGone(path.join(currentProjectPath!, "src", "main", "java", "AppToDelete.java")), `The source file "AppToDelete.java" should be deleted`);
255254
});
256255

257256
it("Test change to invisible project", async function() {
@@ -275,16 +274,14 @@ describe("Command Tests", function() {
275274
const input = await InputBox.create();
276275
await input.setText(path.join(invisibleProjectPath, "libSource", "simple.jar"));
277276
await input.confirm();
278-
await sleep(5000);
277+
await sleep(1000);
279278
referencedItem = await section.findItem("Referenced Libraries") as TreeItem;
280279
await referencedItem.expand();
281-
let simpleItem = await section.findItem("simple.jar") as TreeItem;
280+
let simpleItem = await waitForTreeItem(section, "simple.jar") as TreeItem;
282281
assert.ok(simpleItem, `Library "simple.jar" should be found`);
283282
await simpleItem.click();
284283
await clickActionButton(simpleItem, 'Remove from Project Classpath');
285-
await sleep(5000);
286-
simpleItem = await section.findItem("simple.jar") as TreeItem;
287-
assert.ok(!simpleItem, `Library "simple.jar" should not be found`);
284+
assert.ok(await waitForTreeItemGone(section, "simple.jar"), `Library "simple.jar" should not be found`);
288285
});
289286

290287
it("Test java.project.addLibraryFolders", async function() {
@@ -298,14 +295,13 @@ describe("Command Tests", function() {
298295
.click(button)
299296
.keyUp(seleniumWebdriver.Key.ALT)
300297
.perform();
301-
await sleep(5000);
302298
const input = await InputBox.create();
303299
await input.setText(path.join(invisibleProjectPath, "libSource"));
304300
await input.confirm();
305-
await sleep(5000);
301+
await sleep(1000);
306302
referencedItem = await section.findItem("Referenced Libraries") as TreeItem;
307303
await referencedItem.expand();
308-
assert.ok(await section.findItem("simple.jar"), `Library "simple.jar" should be found`);
304+
assert.ok(await waitForTreeItem(section, "simple.jar"), `Library "simple.jar" should be found`);
309305
});
310306

311307
it("Test java.project.create", async function() {
@@ -317,17 +313,16 @@ describe("Command Tests", function() {
317313
const picks = await inputBox.getQuickPicks();
318314
assert.equal("No build tools", await picks[0].getLabel());
319315
await picks[0].select();
320-
await sleep(3000);
316+
await sleep(1000);
321317
inputBox = await InputBox.create();
322318
await inputBox.setText(projectFolder);
323319
await inputBox.confirm();
324-
await sleep(3000);
320+
await sleep(1000);
325321
inputBox = await InputBox.create();
326322
await inputBox.setText(newProjectName);
327323
await inputBox.confirm();
328-
await sleep(5000);
329-
assert.ok(await fse.pathExists(path.join(projectFolder, newProjectName, "src", "App.java")), `The template source file should be created`);
330-
assert.ok(await fse.pathExists(path.join(projectFolder, newProjectName, "README.md")), `The template README file should be created`);
324+
assert.ok(await waitForFileExists(path.join(projectFolder, newProjectName, "src", "App.java")), `The template source file should be created`);
325+
assert.ok(await waitForFileExists(path.join(projectFolder, newProjectName, "README.md")), `The template README file should be created`);
331326
});
332327

333328

@@ -432,6 +427,72 @@ async function dismissModalDialogIfPresent() {
432427
}
433428
}
434429

430+
async function waitForTreeItem(section: ViewSection, label: string, timeoutMs = 15000): Promise<TreeItem | undefined> {
431+
const start = Date.now();
432+
while (Date.now() - start < timeoutMs) {
433+
const item = await section.findItem(label) as TreeItem;
434+
if (item) return item;
435+
await sleep(1000);
436+
}
437+
return undefined;
438+
}
439+
440+
async function waitForTreeItemGone(section: ViewSection, label: string, timeoutMs = 15000): Promise<boolean> {
441+
const start = Date.now();
442+
while (Date.now() - start < timeoutMs) {
443+
const item = await section.findItem(label) as TreeItem;
444+
if (!item) return true;
445+
await sleep(1000);
446+
}
447+
return false;
448+
}
449+
450+
async function waitForFileExists(filePath: string, timeoutMs = 15000): Promise<boolean> {
451+
const start = Date.now();
452+
while (Date.now() - start < timeoutMs) {
453+
if (await fse.pathExists(filePath)) return true;
454+
await sleep(1000);
455+
}
456+
return false;
457+
}
458+
459+
async function waitForFileGone(filePath: string, timeoutMs = 15000): Promise<boolean> {
460+
const start = Date.now();
461+
while (Date.now() - start < timeoutMs) {
462+
if (!await fse.pathExists(filePath)) return true;
463+
await sleep(1000);
464+
}
465+
return false;
466+
}
467+
468+
async function waitForModalDialog(timeoutMs = 10000): Promise<ModalDialog | undefined> {
469+
const start = Date.now();
470+
while (Date.now() - start < timeoutMs) {
471+
try {
472+
const dialog = new ModalDialog();
473+
await dialog.getButtons();
474+
return dialog;
475+
} catch (_e) {
476+
await sleep(500);
477+
}
478+
}
479+
return undefined;
480+
}
481+
482+
async function waitForEditorTitle(expectedTitle: string, timeoutMs = 15000): Promise<TextEditor | undefined> {
483+
const start = Date.now();
484+
while (Date.now() - start < timeoutMs) {
485+
try {
486+
const editor = new TextEditor();
487+
if (await editor.getTitle() === expectedTitle) return editor;
488+
} catch (_e) {
489+
// Editor may not be ready yet
490+
}
491+
await sleep(1000);
492+
}
493+
return undefined;
494+
}
495+
435496
async function ensureExplorerIsOpen() {
436497
const control = await new ActivityBar().getViewControl('Explorer');
437498
if (control === undefined) {

0 commit comments

Comments
 (0)