I just tried this piece of code:
const playwright = require('playwright');
try {
await page.waitForSelector('.foo');
} catch (e) {
if (e instanceof playwright.errors.TimeoutError) {
// Do something if this is a timeout.
}
} else {
console.log(err);
}
}
I am running this with Jest however, so unsure if this affects it, but for me it ends up in the else branch. I assume that is not supposed to happen.
This does work:
if (err.name === 'TimeoutError')
What is the exact error you are getting logged? Is it the Playwright timeout error?
TimeoutError: waiting for selector ".foo" failed: timeout exceeded
at new RerunnableTask
at Frame._scheduleRerunnableTask
at Frame.waitForSelector
at Frame.<anonymous>
at Page.waitForSelector
at Page.<anonymous>
at test.js:12:17
name: 'TimeoutError'
Running this example seems to provide the expected output.
import playwright from 'playwright';
(async () => {
const browser = await playwright.chromium.launch({
headless: false
});
const context1 = await browser.newContext();
const page1 = await context1.newPage();
await page1.goto('https://google.com');
try {
await page1.waitForSelector('.nothing');
} catch (err) {
if (err instanceof playwright.errors.TimeoutError) {
console.log('timeout error');
} else {
console.log('not timeout error');
}
}
await context1.close();
browser.close();
})();
This might be due to that it's running inside a Jest env then, which launches a new Node.js vm, or something along those lines. This is the output:
TimeoutError: waiting for selector "[hidden] v0-dialog[show]" failed: timeout exceeded
at new RerunnableTask (/home/userA/code/projA/node_modules/playwright-core/lib/frames.js:722:30)
at Frame._scheduleRerunnableTask (/home/userA/code/projA/node_modules/playwright-core/lib/frames.js:660:32)
at Frame.waitForSelector (/home/userA/code/projA/node_modules/playwright-core/lib/frames.js:367:35)
at Frame.<anonymous> (/home/userA/code/projA/node_modules/playwright-core/lib/helper.js:64:31)
at Page.waitForSelector (/home/userA/code/projA/node_modules/playwright-core/lib/page.js:132:33)
at Page.waitForSelector (/home/userA/code/projA/node_modules/playwright-core/lib/helper.js:64:31)
at checkAndCloseUnexpectedDialog (/home/userA/code/projA/tests/e2e/suite/util.js:43:16)
at Object.<anonymous> (/home/userA/code/projA/tests/e2e/suite/healthCheckProfile/cdaProfile.e2e.test.js:29:11)
at Promise.then.completed (/home/userA/code/projA/node_modules/jest-circus/build/utils.js:198:28)
at new Promise (<anonymous>)
-- ASYNC --
at Frame.<anonymous> (/home/userA/code/projA/node_modules/playwright-core/lib/helper.js:63:23)
at Page.waitForSelector (/home/userA/code/projA/node_modules/playwright-core/lib/page.js:132:33)
at Page.waitForSelector (/home/userA/code/projA/node_modules/playwright-core/lib/helper.js:64:31)
at checkAndCloseUnexpectedDialog (/home/userA/code/projA/tests/e2e/suite/util.js:43:16)
at Object.<anonymous> (/home/userA/code/projA/tests/e2e/suite/healthCheckProfile/cdaProfile.e2e.test.js:29:11)
at Promise.then.completed (/home/userA/code/projA/node_modules/jest-circus/build/utils.js:198:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (/home/userA/code/projA/node_modules/jest-circus/build/utils.js:162:10)
at _callCircusHook (/home/userA/code/projA/node_modules/jest-circus/build/run.js:168:40)
at _runTest (/home/userA/code/projA/node_modules/jest-circus/build/run.js:145:5)
-- ASYNC --
at Page.waitForSelector (/home/userA/code/projA/node_modules/playwright-core/lib/helper.js:63:23)
at checkAndCloseUnexpectedDialog (/home/userA/code/projA/tests/e2e/suite/util.js:43:16)
at Object.<anonymous> (/home/userA/code/projA/tests/e2e/suite/healthCheckProfile/cdaProfile.e2e.test.js:29:11)
at Promise.then.completed (/home/userA/code/projA/node_modules/jest-circus/build/utils.js:198:28)
at new Promise (<anonymous>)
at callAsyncCircusFn (/home/userA/code/projA/node_modules/jest-circus/build/utils.js:162:10)
at _callCircusHook (/home/userA/code/projA/node_modules/jest-circus/build/run.js:168:40)
at _runTest (/home/userA/code/projA/node_modules/jest-circus/build/run.js:145:5)
at _runTestsForDescribeBlock (/home/userA/code/projA/node_modules/jest-circus/build/run.js:56:5)
at _runTestsForDescribeBlock (/home/userA/code/projA/node_modules/jest-circus/build/run.js:82:5) {
name: 'TimeoutError'
}
Running it inside of a simple test seems to work for me as well. I did have to add some timeouts for jest, so that it didn't give an error about jest not closing with an async test still running.
Clearly, your test are a lot more involved than this example.
I ran my test with npx jest or npm test where test is defined as jest in my npm script. I do not have any other jest config options.
const playwright = require('playwright');
test('it should catch a timeout error', async () => {
const browser = await playwright.chromium.launch({
headless: false
});
const context1 = await browser.newContext();
const page1 = await context1.newPage();
await page1.goto('https://google.com');
try {
await page1.waitForSelector('.nothing', { timeout: 1500 });
} catch (err) {
if (err instanceof playwright.errors.TimeoutError) {
console.log('timeout error');
} else {
console.log('not timeout error');
}
}
await context1.close();
browser.close();
}, 15000);
Probably the issue for that is that the Jest timeout is smaller an the Playwright timeout. So either increasing the Jest one to 30+ seconds or drecreasing the Playwright one should maybe fix it.
No, that is not the issue. I just copy&pasted the simple case above, but my case looks more like this:
try {
await page.waitForSelector('dialog', {
waitFor: 'detached',
timeout: 500,
});
} catch (err) {
if (err instanceof playwright.errors.TimeoutError) {
await page.click('dialog button.cancel');
} else {
// Unknown error occurred
throw err;
}
}
i.e. I do expect a timeout in some cases after 500ms for the above, and that is where the error occurs.
@mxschmitt Could it have any relation to running this in jest-playwright and that I have to have const playwright = require('playwright'); as an import to be able to use playwright.errors.TimeoutError?
I think the issue could be with TimeoutError class being a different instance in the test (executed in vm script) vs jest-playwright environment (executed in top-level Node context). We had a similar issue with instanceof RegExp.
More details: https://github.com/microsoft/playwright/pull/1048
@mxschmitt Yeah, so it was as I expected, it had to do with that the Jest tests run in a vm context. Do you have any recommendation how to solve this with jest-playwright, is there any patch made to jest-playwright that could fix this. Possibly exposing the playwright object somehow? (I believe you refrained from doing this, but if that is not an option a nice workaround for this would be cool).
Probably the easiest way is with checking against the actual class name as you did (if (err.name === 'TimeoutError')). Thats the upstream issue: https://github.com/facebook/jest/issues/2549. Seems like there is no other easy possible solution.
I assume https://jestjs.io/docs/en/configuration#extraglobals-arraystring will not work since I suspect it only work on built-in globals?
Even so, if you expose playwright (such as that you do with page) in the environment's setup(), would that not work? Or is there a reason jest-playwright does not want to do this?
(For reference I will close this issue soon, just keeping adding the questions here for @mxschmitt so others can read in the future befure I close.)
Most helpful comment
I think the issue could be with TimeoutError class being a different instance in the test (executed in vm script) vs jest-playwright environment (executed in top-level Node context). We had a similar issue with
instanceof RegExp.