28

I'm testing a website that includes a logo, and I want to make sure the logo does not appear on some pages.

How can I assert that an element does NOT exist? I checked the Playwright assertions documentation, but it only has examples of checking for things that do exist.

async assertNoLog(): Promise<boolean> {
  await this.page.locator('div#page-id'); // now the page has loaded
  // How do I check if logo is on page without throwing an error if it is missing
}

I'm not sure what to write here to assert that my element is not found anywhere on the page.

emery
  • 8,603
  • 10
  • 44
  • 51
Patrick Kenny
  • 4,515
  • 7
  • 47
  • 76

6 Answers6

21

I wanted to know that an element wasn't on screen, but I also wanted to wait until it was gone, and this is the way to do that:

await expect(locator).toHaveCount(0);

Found here

Erik Živković
  • 4,867
  • 2
  • 35
  • 53
20

You can use .count() and assert that it returns 0.

expect(await page.locator('.notexists').count()).toEqual(0);

https://playwright.dev/docs/api/class-locator#locator-count

emery
  • 8,603
  • 10
  • 44
  • 51
  • For me, `count()` times out! What is it possibly waiting for? – Daniel Darabos Dec 15 '22 at 13:49
  • I was missing an `await`. (Still weird.) – Daniel Darabos Dec 15 '22 at 14:03
  • 1
    My Visual Studio Code puts 3 little dots to highlight unnecessary awaits so that makes it easy to delete them. Then I can just add "await" to the beginning of pretty much everything. – emery Dec 18 '22 at 19:09
  • 1
    Great idea! I've managed to set up the [`no-floating-promises`](https://typescript-eslint.io/rules/no-floating-promises/) ESLint rule now. – Daniel Darabos Dec 19 '22 at 08:44
  • 1
    [Erik's answer](https://stackoverflow.com/a/74209034/72321) is now preferred; follows Playwright guidelines on using [web first assertions](https://playwright.dev/docs/best-practices#use-web-first-assertions). – Dennis Feb 16 '23 at 08:02
  • @DanielDarabos without an await then it was returning a promise instead of a value and expecting the promise to equal zero. I would think that it should just fail the assertion but if the timing gets messed up sometimes that expresses itself as a timeout instead – emery Feb 27 '23 at 22:22
  • The `await` should be before expect to follow playwright guideline of web first assertion https://playwright.dev/docs/best-practices#use-web-first-assertions. So it should be `await expect(page.locator('.notexists')).toEqual(0);` or `await expect(page.locator('.notexists')).toHaveCount(0);`something like this. This is not exactly right but somewhere along the lines that `await` should be before `expect`. – utkarsh-k Jul 17 '23 at 07:32
  • I disagree, @utkarsh-k. There's nothing in the playwright best practices doc that says you should have an unnecessary await before an expect. Web first assertions are an unrelated topic. – emery Aug 10 '23 at 00:00
4

You can play with the conditions you expect your element to have. For example, at Playwright's homepage you expect an element by the class .navbar__brand to be visible, but you also expect an element by the class .notexists NOT to be visible (in this case this element would not exist). Then you can do:

test('element does exist @pass', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  const locator = await page.locator('.navbar__brand').isVisible();
  expect(locator).toBeTruthy();
});

test('element does NOT exist @fail', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  const locator = await page.locator('.notexists').isVisible();
  expect(locator).toBeTruthy();
});

Doing this, of course, would return the same results:

test('element does exist @pass', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  expect(await page.locator('.navbar__brand').isVisible()).toBe(true);
});

test('element does NOT exist @fail', async ({ page }) => {
  await page.goto('https://playwright.dev/');
  expect(await page.locator('.notexists').isVisible()).toBe(true);
});

As I say, the element's conditions are up to you. For example, if you want to assert an element with visibility:hidden is also not present in the DOM, because it simply shouldn't be, you can wrap the visibility and a .isHidden() conditions within a if/else, etc. And of course, feel free to play with booleans (toBe(true)/toBe(false), toBeTruthy()/toBeFalsy()).

These are not the most elegant solutions out there, but I hope they can help.

Lee Goddard
  • 10,680
  • 4
  • 46
  • 63
Víctor
  • 533
  • 3
  • 7
  • 2
    I was tempted to downvote this because it's not a very elegant solution as compared to using count() but it does provide some very useful information. – emery Aug 02 '22 at 22:46
4

https://playwright.dev/docs/api/class-locatorassertions#locator-assertions-to-be-attached

toBeAttached

Added in: v1.33

Ensures that Locator points to an attached DOM node.

With this, now we can do:

expect(locator).not.toBeAttached() 
  • This should be the accepted answer - clean and with no magic constants like zero in "toHaveCount(0)" – Dušan Aug 24 '23 at 16:51
1

Another way using Chai assertions is using the "!" symbol before the element:

should.exist(!your selector here)
nosequeweaponer
  • 511
  • 10
  • 38
1

You could try using "not" before "toBeVisible" assertion on the routes you don't want it to be.

async assertNoLog(): Promise<boolean> {
  await this.page.locator('div#page-id'); // now the page has loaded

  // Check if the logo is on the page without throwing an error if it is missing
await expect(page.locator('div#page-id')).not.toBeVisible();
}

EDIT

It's recommended to use more direct assertions when available:

await expect(page.locator('div#page-id')).toBeHidden();