Reputation: 446
I'm using Playwright 1.15.2 for testing and facing a problem with elements' visibility. I want to check if a modal is visible on screen so I can close it. The modal starts with display:none
and turns into display:block
. Also, the modal informs incorrectness in form data so it may or may not appear (i.e. I can't waitForSelector
).
Currently, I have a code similar to the following:
const myModal = await page.$("#modal");
if (await myModal.isVisible()) {
await page.waitForSelector('#modal > .modal-dialog > .modal-content > .modal-footer > .btn-close');
await page.click('#modal > .modal-dialog > .modal-content > .modal-footer > .btn-close');
}
I've also tried:
const myModal = await page.$("#modal:visible");
if (myModal) {
...
Using page.$("text=modal title >> visible=true")
or switching page.$
to page.locator
(with all the aforementioned selectors) also didn't work.
The accepted answer for this question didn't work as well.
Could anyone help me with that?
Upvotes: 23
Views: 99403
Reputation: 56885
If you're ultimately clicking something, locator clicks auto wait for the element to exist and be visible, so all you should need to do is
await page.locator("your selector").click();
That said, long, rigid chains of CSS selectors are discouraged. Prefer an accessible aria role, text, a test id, or at least make your CSS selector a bit less hyper-fixated on a specific hierarchy, like "#modal .btn-close"
.
If locator.click()
fails, then you probably have some site-specific behavior at play and will need to share more details. Shadow DOMs, iframes, dynamic JS, cloudflare blocks and other factors can make a simple click fail. Outside of tests, forcing the click or using an untrusted DOM click can be useful.
Disclosure: I'm the author of the linked blog post.
Upvotes: 0
Reputation: 1178
function checkElement(locator: Locator){
return new Promise<void>(resolve =>{
locator.waitFor({state:"visible"})
.then(async ()=>{
// do something when visible
})
.catch( async ()=>{
// do something when hidden
})
.finally( ()=>{
// do something regardless of visible or hidden
resolve();
})
})
}
Upvotes: 1
Reputation: 10587
There's two ways to do this:
const visible = await page.getByRole('button').isVisible();
const isTextVisible = await page.locator(':text("Some text")').isVisible();
const isElementVisible = await page.locator('button').isVisible();
await elementHandle.isVisible();
I find the locator API to be more flexible.
Upvotes: 4
Reputation: 2553
Here are two ways to check element is NOT visible (check waitForSelector options):
await page.waitForSelector('.btn', { state: 'detached' });
or
expect(await page.$$('.btn')).toHaveLength(0);
Upvotes: 5
Reputation: 3152
page.$("text=modal title >> visible=true")
does not wait until the element is on the DOM and visible.
you need to use:
await expect(page.locator("text=modal title")).toBeVisible()
see here: https://playwright.dev/docs/test-assertions#expectlocatortobevisibleoptions
Upvotes: 23
Reputation: 5287
I solved it by using waitFor
with different state
constructor(page: Page) {
this.page = page;
this.myElement = page.locator('div.myElement');
}
async isMyElementVisible() {
await this.myElement.waitFor({ state: 'visible' });
return await this.myElement.isVisible();
}
async isMyElementHidden() {
await this.myElement.waitFor({ state: 'hidden' });
return await this.myElement.isHidden();
}
Upvotes: 7