How to check if an element exists on the page in Playwright.js
Asked Answered
N

12

53

I am using playwright.js to write a script for https://target.com, and on the page where you submit shipping information, it will provide the option to use a saved address if you have gone through the checkout process previously on that target account.

I would like to enter fresh shipping information every time I run the script, so I must have playwright click delete if it exists on the page, and then enter the shipping information.

The function that is shown below works to click delete, but then it times out at if (await page.$$("text='Delete'") != []) rather than executing the else part of the function.

How can I rewrite this function so that it simply checks if the element (selector: text='Delete') exists, and clicks it if it does, and executes the filling part of the function if it doesn't?

  async function deliveryAddress() {
    if (await page.$$("text='Delete'") != []) {
      await page.click("text='Delete'", {force:true})
      await deliveryAddress()
    } else {
      await page.focus('input#full_name')
      await page.type('input#full_name', fullName, {delay: delayms});
      await page.focus('input#address_line1')
      await page.type('input#address_line1', address, {delay: delayms});
      await page.focus('input#zip_code')
      await page.type('input#zip_code', zipCode, {delay: delayms});
      await page.focus('input#mobile')
      await page.type('input#mobile', phoneNumber, {delay: delayms});
      await page.click("text='Save & continue'", {force:true})
    }
  }
Newly answered 11/11, 2020 at 10:37 Comment(1)
You can't compare arrays like this in JavaScript, you can check array length or use if (await page.$('text=Delete')) {}Disrepute
F
33

You have two main options:

const deletes = await page.$$("text='Delete'");
if (deletes) {
    // ...
}

or

const deletes = await page.$$("text='Delete'");
if (deletes.length) {
    // ...
}

I'd personally leave the $$ command outside the if statement for readability, but that might me only me.

It also seems you have only one element with attribute text with value "Delete" on the page since you're not doing anything with the array that $$ returns. If so, you might use $:

const del = await page.$("text='Delete'");
if (del) {
    // ...
}
Fears answered 18/11, 2020 at 21:13 Comment(1)
const deletes = await page.$$("text='Delete'"); if (deletes) { doesn't work--page.$$ returns an array, and arrays are always truthy even if there's nothing in them. I suggest removing that.Dialogist
D
37
await page.isVisible("text='Delete'")

maybe this is the one you're looking for.

From the .isVisible() documentation:

Returns whether the element is visible. selector that does not match any elements is considered not visible.

Duvall answered 23/2, 2022 at 15:42 Comment(5)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Hydroxyl
was the correct answerer for me. thanks. playwright.dev/docs/api/class-page#page-is-visibleEastbound
Although this answer is correct, PlayWright recommends using the isVisible() function on the locator instead. For me that makes more sense since I have a separate class returning locators. That way I only need to define my locators once and I can reuse them anywhere.Invasive
Why you should NOT rely anymore on isVisible? See this - stackoverflow.com/a/75901956Posthumous
isVisible() only works if an element is in the viewable area and not if it is lower on the page offscreen. The only good work around is to check "locator.count()" followed by "locator.scrollIntoViewIfNeeded()" to ensure it gets scrolled into view before trying to click itChu
F
33

You have two main options:

const deletes = await page.$$("text='Delete'");
if (deletes) {
    // ...
}

or

const deletes = await page.$$("text='Delete'");
if (deletes.length) {
    // ...
}

I'd personally leave the $$ command outside the if statement for readability, but that might me only me.

It also seems you have only one element with attribute text with value "Delete" on the page since you're not doing anything with the array that $$ returns. If so, you might use $:

const del = await page.$("text='Delete'");
if (del) {
    // ...
}
Fears answered 18/11, 2020 at 21:13 Comment(1)
const deletes = await page.$$("text='Delete'"); if (deletes) { doesn't work--page.$$ returns an array, and arrays are always truthy even if there's nothing in them. I suggest removing that.Dialogist
P
31
await expect(page.locator(".MyClass")).toHaveCount(0)
Pridemore answered 15/9, 2021 at 17:20 Comment(2)
Notice that the question is "how to check if an element exists", doesn't mean to actually verify that an element is present... and this is the case. This answer will cause an exception and I am sure that's not the goal of the question. A lot of times, there is only a need to see if something is there or not, and then decide what to do based on the result of the check... and this is the reason for the question.Tarbes
Also note that expect is not available when using the playwright library (as opposed to the playwright test runner). You might use some method of locator, like waitFor() or count().Rosenthal
N
26

Usualy this can help in lot of situations, when checking element presence.

if(await page.locator(your_selector).count()>0)
Nikolaus answered 6/3, 2022 at 22:48 Comment(8)
For how much time is it going to wait to return the count()? Would it return immediately or wait for the timeout set in the playwright config? Because in either case, it wouldn't be of any help as I am trying to click on the element only if its visible within 3 seconds of page load completion.Suksukarno
Use web assertion as recommended by playwright in recent versions- stackoverflow.com/a/75901956Posthumous
@VishalAggarwal Assertions don't work when you're writing stuff besides test cases. playwright.dev/docs/library#key-differencesSupination
@Supination can you explain, what you mean by that?Posthumous
@VishalAggarwal From what I can tell, expect() isn't part of the Playwright Library (playwright), and it's only in Playwright Test (@playwright/test), so if you're writing code that exists as its own module instead of a test case, you need to use this method. Interestingly, this isn't an issue for the .NET or Python versions, but I can't check the docs for Java (says there isn't a page for it).Supination
If you are writing stuff beyond test cases , you are still free to use any third party assertion libraries which are even bigger than web assertions like Jest.Posthumous
@VishalAggarwal Playwright is used for scraping and automating websites, which doesn't use the @playwight/test library. There isn't a need for any assertions or test framework at all when you're using it for scraping and automation purposes. It's just a plain Node script.Dialogist
Assertions only make sense if you are asserting. Sometimes you just want to know if an element exists as a precondition to your next step. Using .count() and .scrollIntoViewIfNeeded() appear to be the only optionChu
L
10
try {
  await page.waitForSelector(selector, { timeout: 5000 })
  // ...`enter code here`
} catch (error) {`enter code here`
  console.log("The element didn't appear.")
}

from - https://github.com/puppeteer/puppeteer/issues/1149#issuecomment-434302298

Lythraceous answered 10/6, 2021 at 13:30 Comment(0)
P
2

AS far as Playwright-test is concerned,What you are looking for is:

await expect(page.locator('.somethingdoesnotexistYET')).toBeVisible()

Use web assertion as recommended by playwright as best practice:

Per Playwright Docs(v1.20):

This will ensure that Locator points to an attached and visible DOM element.

Reference:https://github.com/microsoft/playwright/issues/18394

Posthumous answered 31/3, 2023 at 19:8 Comment(5)
toBeVisible retrun tipe is Promise<void>, it does not return a boolean, therefore if statement will never be triggered belowRosinski
@VishalAggarwal felsher's spelling does not change the fact that your code does not work. Maybe cut down on the snark until you understand what Promise<Void> means. if (await locator.isVisible()) {} is the correct solution (as in the most up-voted answer).Flip
@VishalAggarwal Which version of Playwright are you using? When I type your code into VS Code I get the error "an expression of type 'void' can not be tested for truthiness." When I look at the code for toBeVisible it is declared to return Promise<Void> so it will never resolve to a boolean. As far as I am aware all the toBeX web assertions throw exceptions and the isX methods return a boolean.Flip
Yes , you are right. Thanks for pointing out. I updated the answer.Posthumous
@VishalAggarwal Not the downvoter, and the answer is useful, but OP isn't asking about @playwright/test, just Playwright. Outside of testing, it's sometimes necessary to use a non-retrying conditional check.Dialogist
G
1

Found a neat solution here using locator WaitFor. As isVisible() does not wait for the element to become visible and returns immediately. instead we can use Locator WaitFor.

const orderSent = page.locator('#order-sent');
await orderSent.waitFor();

thus creating a helper func could help

import type { Locator } from '@playwright/test';

async function isElementVisible(
  elementLocator: Locator,
  waitTime: number = 3000,
): Promise<boolean> {
  try {
    await elementLocator.waitFor({
      state: 'visible',
      timeout: waitTime,
    });
  } catch {
    return false;
  }
  return true;
}

export { isElementVisible };
Geilich answered 18/5, 2024 at 7:21 Comment(0)
L
0

You can try this simple code to check the visibility of an element and take the necessary action.

Below code check for the visibility of an element and click on the same element if element is visible on DOM.

if(await page.locator('Your selector').isVisible()){
  await page.locator("Your selector").click()
}

Even if the locator is not visible on the page, it does not throw any exception or error.

Leaves answered 18/11, 2022 at 6:52 Comment(0)
B
0

This can be achieved using page.evaluate method:

const exists = await page.evaluate(() => {
    return document.querySelector(`#id`) !== null;
});

if (exists) console.log("element exist");
else console.log("not exist");

The page.evaluate() API can run a JavaScript function in the context of the web page and bring results back to the Playwright environment.

Binnacle answered 23/6, 2024 at 12:13 Comment(0)
B
-1
await expect(page).toHaveSelectorCount('.floorNav__modal', 1);
Barmecide answered 11/3, 2022 at 4:10 Comment(0)
T
-1

Try this, it handles all the scenarios with error handling -

async isSelectorExists(selector: string) {
  return await this._page.$(selector).catch(() => null) !== null;
}

Invalid selector - return false

console.log(await isSelectorExists('$'))

Valid selector and exists - return true

console.log(await isSelectorExists('body'))

Valid selector but not exists - return false

console.log(await isSelectorExists('h1'))
Truelove answered 19/9, 2022 at 10:53 Comment(0)
C
-1

The best way to check if an element exits is:

if selector_exits(page, 'div[aria-label="Next"]'):
    print('yeah it exits')

def selector_exists(page, selector, timeout=3000):
    try:
        element = page.locator(selector).is_visible(timeout=timeout)
        return element
    except:
        return False

Note: this is a python based approach. Convert in your desired language.

Cinerama answered 24/1, 2023 at 12:49 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.