Delving into Playwright for the first time, coming from a Cypress background where I am familiar with the cypress-wait-until function.
I'm currently trying to wait until a specific number of DOM elements exist, and although I've found various solutions, I'm curious why my initial approach failed.
Perhaps someone else has encountered the same issue?
Below is the test case I am working on:
Open the web page
Accept the cookies
Search for Rembrandt van Rijn Pictures
Wait until the WORKS OF ART image carousel displays 10 items
test('search for rembrandt', async ({page, browser, context}) => {
await page.goto('https://www.rijksmuseum.nl/en/content-search');
// accept cookies
await page.click('text=Accept');
const inputSearch = page.locator("input.search-bar-input")
await inputSearch.fill('Rembrandt van Rijn')
const buttonSearch = page.locator("button[class$='search-bar-button']")
await buttonSearch.click()
// waiting logic goes here
})
Attempt: ❌
await page.waitForFunction( (page)=> page.locator('div[aria-label="Carousel with links to works of art"] div.carousel-item') .count() .then(nrOfElements => nrOfElements === 10), page)
Attempt: ❌
await page.waitForFunction( async (page)=> await page.locator('div[aria-label="Carousel with links to works of art"] div.carousel-item') .count() .then(nrOfElements => nrOfElements === 10), page)
NOTE: Ensure that you pass the 'page' variable as args to avoid the 'ReferenceError: page is not defined'
An error message stating: 'Error: Unexpected value' always appears. https://i.sstatic.net/FnYTWhVo.png
The
page.waitForFunction
method can handle Promises correctly like this:await page.waitForFunction(async() => await new Promise((resolve) => setTimeout(() => resolve(true), 10_000)))
await page.waitForFunction(() => new Promise((resolve) => setTimeout(() => resolve(true), 10_000)))
A quick but effective solution:
while(!await page.locator('div[aria-label="Carousel with links to works of art"] div.carousel-item').count().then(nrOfElements => nrOfElements ===10))
WARNING: This method requires an additional timeout to ensure the test stops if the element count doesn't match!
The AI's recommendation also functions, but might not be as elegant:
const extendArtQuerySelector = 'div[aria-label="Carousel with links to works of art"] div.carousel-item'; // Transfer only the selector to the browser context await page.waitForFunction(ellSelector => { const elements = document.querySelectorAll(ellSelector); return elements.length >= 10; }, extendArtQuerySelector);
Lastly, the most efficient solution:
await expect(page.locator('div[aria-label="Carousel with links to works of art"] div.carousel-item')).toHaveCount(10)
Any thoughts on why the page.locator does not work within the waitForFunction?