I'm unsure if I've missed something, but I need to:
- Check an item is visible on screen (code below which is working)
- Do a count on the number of visible items on screen
Originally I was doing:
test('Desktop check - (Optimal on desktop / tablet) only shown on smaller screens (i.e mobile breakpoint)', async ({ page, isMobile }) => {
test.skip(isMobile)
await page.goto(url);
const buttonsCount = await page.getByText('(Optimal on desktop / tablet)').count();
expect(buttonsCount).toBe(0);
});
But this was also finding all of the hidden versions of this test, so whilst I was expecting 0, it was returning 1.
Easy fix for part one was to switch to .toBeVisible()
, but then there is no count property on .toBeVisible()
I can access.
How can I do a count on toBeVisible
? Or should I do a loop count and pass that into a .toBe()
?
I'm unsure if I've missed something, but I need to:
- Check an item is visible on screen (code below which is working)
- Do a count on the number of visible items on screen
Originally I was doing:
test('Desktop check - (Optimal on desktop / tablet) only shown on smaller screens (i.e mobile breakpoint)', async ({ page, isMobile }) => {
test.skip(isMobile)
await page.goto(url);
const buttonsCount = await page.getByText('(Optimal on desktop / tablet)').count();
expect(buttonsCount).toBe(0);
});
But this was also finding all of the hidden versions of this test, so whilst I was expecting 0, it was returning 1.
Easy fix for part one was to switch to .toBeVisible()
, but then there is no count property on .toBeVisible()
I can access.
How can I do a count on toBeVisible
? Or should I do a loop count and pass that into a .toBe()
?
- Could you share a simple page under test? – ggorlen Commented Mar 17 at 15:17
3 Answers
Reset to default 4Maybe easiest of the options is applying filter method to your getByText().
const buttonCount = await page.getByText('(Optimal on desktop / tablet)')
.filter({ visible: true })
.count()
See Matching only visible elements
If you look at the section below that Count items in a list there is a specific assertion toHaveCount()
you may use.
expect(page.getByText('(Optimal on desktop / tablet)').filter({ visible: true }))
.toHaveCount(0)
This is my minimal proof test:
test('filtering by visible', async ({ page }) => {
const html = `
<html><body>
<button style="display:none">(Optimal on desktop / tablet)</button>
</body></html>
`
await page.setContent(html);
const visibleButtons = page.getByText('(Optimal on desktop / tablet)')
.filter({ visible: true })
await expect(visibleButtons).toHaveCount(0)
})
If I remove the display:none
style, I can see the test failing which means the test meets expectations.
For interactive elements such as <input>
and <button>
the getByRole()
locator is recommended.
This locator has an implict visible:true
criteria.
For reference, see the options section for getByRole
includeHidden
boolean (optional)Option that controls whether hidden elements are matched. By default, only non-hidden elements, as defined by ARIA, are matched by role selector.
So for a button with display:none
style, this counts 0 elements:
const count = await page.getByRole('button', { name: /Optimal on desktop/i })
.count();
expect(count).toBe(0);
but changing the button style to display:block
the count becomes 1, and the test no longer passes.
If you're using Playwright 1.51 or above and can use Locator.filter({ visible: true })
(as Karen.Schlossberg describes here, that is an easier solution.
If you're using a version of Playwright below 1.51, continue reading:
You can use JavaScript's Array.filter()
alongside Playwright's Locator.isVisible()
to filter out any hidden elements. It gets a little trickier when remembering you'll have to use an async function inside of Array.filter()
.
// Get all elements
const elements = await page.getByText('(Optimal on desktop / tablet)').all();
// Make our promise array
const isVisiblePromises = elements.map(async element => await element.isVisible());
// Resolve our promises
const isVisible = await Promise.all(isVisiblePromises);
// Use our resolved promise array to filter original Locator array
const filteredElements = elements.filter((_, index) => isVisible[index])
// Validate our filtered elements length
expect(filteredElements.length).toBe(0);