Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
39 changes: 33 additions & 6 deletions tools/playwright/src/page-injector.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,37 @@ function getCallerLocation(
return null;
}

function isPageTransitionError(error: unknown): boolean {
let message: string;
if (error instanceof Error) {
message = error.message;
} else if (
typeof error === "object" &&
error !== null &&
"message" in error
) {
message = String(error.message);
} else {
message = String(error);
}

if (!message) {
return false;
}

return [
"has been closed",
"Target closed",
"Target page, context or browser has been closed",
"Execution context was destroyed",
"Cannot find context with specified id",
"Frame was detached",
"frame was detached",
"Object has been disposed",
"JSHandle is disposed",
].some((errorMessage) => message.includes(errorMessage));
}

/**
* Attaches AbleDOM accessibility checking methods to a Playwright Page.
*
Expand Down Expand Up @@ -203,12 +234,8 @@ export async function attachAbleDOMMethodsToPage(
{ markAsRead: pageMarkAsRead, timeout: pageTimeout },
);
} catch (error) {
// Page may have been closed between the isClosed() check and evaluate()
// This can happen during test teardown or navigation
if (
error instanceof Error &&
error.message.includes("has been closed")
) {
// Page may have navigated or closed between the action and evaluate().
if (isPageTransitionError(error)) {
return;
}
throw error;
Expand Down
24 changes: 24 additions & 0 deletions tools/playwright/tests/page-injector.test.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,30 @@ baseTest.describe("page closed handling", () => {
await context.close();
},
);

baseTest(
"should handle page.evaluate error gracefully when page transitions after an action",
async ({ page }, testInfo) => {
await page.goto(
"data:text/html,<html><body><button id='test'>Test</button></body></html>",
);

await attachAbleDOMMethodsToPage(page, testInfo);

const originalEvaluate = page.evaluate.bind(page);
page.evaluate = (async () => {
throw new Error(
"Execution context was destroyed, most likely because of a navigation.",
);
}) as typeof page.evaluate;

try {
await page.locator("#test").click();
} finally {
page.evaluate = originalEvaluate;
}
},
);
});

baseTest.describe("custom idle options via attachAbleDOMMethodsToPage", () => {
Expand Down
Loading