Reputation: 217
I am trying to wait for an element that indicates a page is still loading, and exists on the page multiple time, to not be visible (think table with loading data placeholders).
Playwright documentation suggests using Locators is best practice, and therefore I initially tried to achieve this by doing:
locator.waitFor({state: "hidden")
However that errors due to Locators being strict and only being allow to match one element.
I'm now doing it with the following code:
page.waitForSelector(".foo .bar", {state: "hidden"})
This is non-ideal for a couple of reasons:
Is there any way to turn off the strictness constraint on a Locator? Or a way to achieve this using the Locator. I'm aware you can do .count
on a Locator which matches multiple elements, but I've not found a nice way to combine that with waiting for the count to be 0.
Upvotes: 14
Views: 41485
Reputation: 4177
Spinner or an "..loading" text element's presence cannot be verified for sure as it may or may not appear during page load depending on page performance .
However , it's eventual disappearance can be verified.
Use toHaveCount() (Added in: v1.20)
await expect(locator).toHaveCount(0,{timeout:5000});
Timeout: To avoid infinite loops timeout can be configured globally or passed as an parameter in the function call.
// playwright.config.ts
import { defineConfig } from '@playwright/test';
export default defineConfig({
expect: {
timeout: 5000
},
});
Tip: Its better to keep local timeouts lower than global timeout to avoid any race conditions if both exist.
Reference: https://github.com/microsoft/playwright/issues/11988
https://stackoverflow.com/a/74209034/1831456
How can I assert that an element is NOT on the page in playwright?
Upvotes: 4
Reputation: 217
I got this working in the end using the evaluateAll
method. Example code:
async waitForAllHidden(locator: Locator, timeout: number = 10000) {
const start = Date.now()
const elementsVisible = async () => (
await locator.evaluateAll(elements =>
elements.map(element => element.hidden))
).includes(false)
while (await elementsVisible()) {
if (start + timeout < Date.now()) {
throw (`Timeout waiting for all elements to be hidden.
Locator: ${locator}. Timeout: ${timeout}ms`);
}
}
console.log(`All elements hidden: ${locator}`)
}
There are new methods available which can now be used for this purpose:
toHaveCount() e.g.
await expect(yourLocator).toHaveCount(0);
poll() e.g.
await expect.poll(async () => {
for (const e of await yourLocator.all()) {
if (await e.isVisible()) return false
}
return true
}, {
message: 'youLocator is still visible', // optional custom error message
intervals: [1_000, 2_000, 10_000], // optional polling overrides
timeout: 10000, // optional timeout override
}).toBe(true);
Upvotes: 6
Reputation: 79
hope it will work
this code will check the next element each time the previous one disappears
while (await page.locator('.foo .bar').first().isVisible()) { //do nothing }
Upvotes: 3