39

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})
    }
  }
hardkoded
  • 18,915
  • 3
  • 52
  • 64
cole
  • 393
  • 1
  • 3
  • 4

10 Answers10

29
await expect(page.locator(".MyClass")).toHaveCount(0)
Victorio Berra
  • 2,760
  • 2
  • 28
  • 53
  • 19
    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. – AlexD May 02 '22 at 20:10
  • 4
    Also note that `expect` is not available when using the [playwright library](https://playwright.dev/docs/library) (as opposed to the playwright test runner). You might use some method of `locator`, like [`waitFor()`](https://playwright.dev/docs/api/class-locator#locator-wait-for) or [`count()`](https://playwright.dev/docs/api/class-locator#locator-count). – kca Feb 08 '23 at 08:43
29
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.

micka190
  • 742
  • 2
  • 8
  • 20
  • 2
    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](/help/how-to-answer). – Community Feb 24 '22 at 00:51
  • 1
    was the correct answerer for me. thanks. https://playwright.dev/docs/api/class-page#page-is-visible – gilad 1 Jun 19 '22 at 08:31
  • 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. – PixelPlex Jan 27 '23 at 11:12
27

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) {
    // ...
}
pavelsaman
  • 7,399
  • 1
  • 14
  • 32
15

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

if(await page.locator(your_selector).count()>0)
Gaj Julije
  • 1,538
  • 15
  • 32
  • 1
    this should be the top answer wtf – DexieTheSheep Jun 26 '23 at 18:39
  • 1
    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. – Anand Jul 01 '23 at 09:47
  • Use web assertion as recommended by playwright in recent versions- https://stackoverflow.com/a/75901956 – Vishal Aggarwal Jul 06 '23 at 09:41
  • @VishalAggarwal Assertions don't work when you're writing stuff besides test cases. https://playwright.dev/docs/library#key-differences – DexieTheSheep Jul 21 '23 at 18:47
  • @DexieTheSheep can you explain, what you mean by that? – Vishal Aggarwal Jul 21 '23 at 19:08
  • @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). – DexieTheSheep Jul 21 '23 at 19:34
  • 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. – Vishal Aggarwal Jul 25 '23 at 21:03
9
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

Raghu
  • 107
  • 1
  • 3
2

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.

1
await expect(page).toHaveSelectorCount('.floorNav__modal', 1);
sunkehappy
  • 8,970
  • 5
  • 44
  • 65
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'))
Vikash Rathee
  • 1,776
  • 2
  • 25
  • 43
1

page.isVisible is not an assertion.

isVisible does just return the visibility state in form of a boolean rather than throwing when the condition is not met.

What you are looking for is:

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

How to use in conditions:

let isVisible = await expect(locator).toBeVisible({timeout=2000});
    
    If(isVisible) { // Do something man! }

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

Vishal Aggarwal
  • 1,929
  • 1
  • 13
  • 23
  • 1
    toBeVisible retrun tipe is Promise, it does not return a boolean, therefore if statement will never be triggered below – felsher Jul 25 '23 at 20:38
  • Promise will eventually resolve to a boolean value and await will keep the execution suspended till it resolves. – Vishal Aggarwal Jul 25 '23 at 20:57
0

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.