From 6889ea164620d739b7102a3d25c8e46cfaa440ef Mon Sep 17 00:00:00 2001 From: zedy Date: Fri, 20 Mar 2026 13:16:09 +0800 Subject: [PATCH] Fix instability issue in e2e testing --- .../test/extension/common/mock-dialogs.ts | 51 ++++++++++++------- .../test/extension/common/utils.ts | 3 ++ .../test/extension/create-typespec.test.ts | 3 ++ 3 files changed, 40 insertions(+), 17 deletions(-) diff --git a/packages/typespec-vscode/test/extension/common/mock-dialogs.ts b/packages/typespec-vscode/test/extension/common/mock-dialogs.ts index e8fd008ea2c..d9950b34573 100644 --- a/packages/typespec-vscode/test/extension/common/mock-dialogs.ts +++ b/packages/typespec-vscode/test/extension/common/mock-dialogs.ts @@ -37,7 +37,7 @@ export function stubDialog( return stubMultipleDialogs(app, [{ method, value }]); } -export function stubMultipleDialogs( +export async function stubMultipleDialogs( app: ElectronApplication, mocks: DialogMethodStubPartial[], ) { @@ -53,23 +53,40 @@ export function stubMultipleDialogs( }); // https://github.com/microsoft/playwright/issues/8278#issuecomment-1009957411 - return app.evaluate(({ dialog }, mocks) => { - mocks.forEach((mock) => { - const thisDialog = dialog[mock.method]; - if (!thisDialog) { - throw new Error(`can't find ${mock.method} on dialog module.`); - } - if (mock.method.endsWith("Sync")) { - dialog[mock.method] = () => { - return mock.value; - }; - } else { - dialog[mock.method] = async () => { - return mock.value; - }; + // Retry to handle transient "Execution context was destroyed" errors + // that occur when VS Code reloads during startup. + const maxRetries = 3; + const retryDelayMs = 1000; + + for (let attempt = 1; attempt <= maxRetries; attempt++) { + try { + return await app.evaluate(({ dialog }, mocks) => { + mocks.forEach((mock) => { + const thisDialog = dialog[mock.method]; + if (!thisDialog) { + throw new Error(`can't find ${mock.method} on dialog module.`); + } + if (mock.method.endsWith("Sync")) { + dialog[mock.method] = () => { + return mock.value; + }; + } else { + dialog[mock.method] = async () => { + return mock.value; + }; + } + }); + }, mocksRequired); + } catch (error: any) { + const isRetryable = + error.message?.includes("Execution context was destroyed") || + error.message?.includes("Target page, context or browser has been closed"); + if (!isRetryable || attempt === maxRetries) { + throw error; } - }); - }, mocksRequired); + await new Promise((resolve) => setTimeout(resolve, retryDelayMs)); + } + } } /** diff --git a/packages/typespec-vscode/test/extension/common/utils.ts b/packages/typespec-vscode/test/extension/common/utils.ts index 1624d8332bd..e9a4264ab0a 100644 --- a/packages/typespec-vscode/test/extension/common/utils.ts +++ b/packages/typespec-vscode/test/extension/common/utils.ts @@ -60,6 +60,9 @@ export const test = baseTest.extend<{ ].filter((v): v is string => !!v), }); const page = await app.firstWindow(); + // Wait for the page to fully load to reduce the chance of + // VS Code reloading the window and destroying the execution context. + await page.waitForLoadState("domcontentloaded"); const tracePath = join(projectRoot, "test-results", task.name, "trace.zip"); const artifactsDir = join(tempDir, "playwright-artifacts"); await fs.promises.mkdir(artifactsDir, { recursive: true }); // make sure the directory exists diff --git a/packages/typespec-vscode/test/extension/create-typespec.test.ts b/packages/typespec-vscode/test/extension/create-typespec.test.ts index cdbc7afefe5..bbf2efa5459 100644 --- a/packages/typespec-vscode/test/extension/create-typespec.test.ts +++ b/packages/typespec-vscode/test/extension/create-typespec.test.ts @@ -69,6 +69,9 @@ describe.each(CreateCasesConfigList)("CreateTypespecProject", async (item) => { workspacePath: workspacePath, }); await cs.screenshot(page, "after_launch"); + // Wait for VS Code UI to be ready before mocking dialogs to avoid + // "Execution context was destroyed" errors from window reloads. + await page.waitForSelector(".explorer-viewlet", { timeout: 30000 }); await mockShowOpenDialog(app, [workspacePath]); await startWithCommandPalette(page, "Create Typespec Project", cs); await cs.screenshot(page, "after_start_command");