JeanCHilger
JeanCHilger

Reputation: 446

Check if element is visible in Playwright

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

Answers (6)

ggorlen
ggorlen

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

Peter Huang
Peter Huang

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

There's two ways to do this:

  1. Check if a locator is visible
const visible = await page.getByRole('button').isVisible();
const isTextVisible = await page.locator(':text("Some text")').isVisible();
const isElementVisible = await page.locator('button').isVisible();
  1. Check if an element is visible
await elementHandle.isVisible();

I find the locator API to be more flexible.

Upvotes: 4

Dima Dorogonov
Dima Dorogonov

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

Max Schmitt
Max Schmitt

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

jfk
jfk

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

Related Questions