-
Notifications
You must be signed in to change notification settings - Fork 3.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Question] Wait for image to be loaded #6046
Comments
You should do: const response = await page.waitForResponse(src);
await response.finished(); |
Unfortunately it doesn't work either. It waits endlessly. |
Could you provide a snippet with the reproduction? |
This may work. |
|
Unfortunately it doesn't work either. It waits endlessly. |
@pavelfeldman I'll try to make a test case with public image. |
@Pooort Any luck with a public repro case? |
Closing this one as we are unable to reproduce. Please comment here if you have a public repro case and we'll reopen. |
That doesn't seem to be working, actually. const image = ... // image locator
const imageSrc = await image.getAttribute("src");
if (imageSrc) {
const imageResponse = await page.waitForResponse(imageSrc);
await imageResponse.finished();
const imagesTuples = await page.evaluate(async () => {
const selectors = Array.from(document.querySelectorAll("img"));
return selectors.map((img) => [img.src, img.complete]);
});
console.log("Images", imagesTuples);
} As a result I get
Do you need a reproduction of that? |
I think you will have a race condition here. To use |
I believe you assume whenever I get to my |
await page.waitForFunction(() => {
const images = Array.from(document.querySelectorAll('img'));
return images.every(img => img.complete);
}); This is works for me |
Edit: We've had problems with waiting for all images to load using the The version that we're currently using tests for // Wait for all network requests for images to complete
await page.waitForLoadState('networkidle');
// Scroll into view for lazy-loaded images
await page.locator('img').scrollIntoViewIfNeeded();
expect(
await (
await page.locator('img').evaluateHandle(
(element, elementProperty) => {
return element[elementProperty];
},
'naturalWidth',
)
).jsonValue(),
).toBeGreaterThan(0); Can be abstracted to a function to make it a bit nicer: import { Locator } from '@playwright/test';
export async function getLocatorProperty<Element extends HTMLElement>(
locator: Locator,
property: string & keyof Element,
) {
return (
await locator.evaluateHandle(
(element: Element, elementProperty: string) => {
return element[elementProperty as keyof Element];
},
property,
)
).jsonValue();
} Usage: // Wait for all network requests for images to complete
await page.waitForLoadState('networkidle');
// Scroll into view for lazy-loaded images
await page.locator('img').scrollIntoViewIfNeeded();
expect(
await getLocatorProperty<HTMLImageElement>(
page.locator('img'),
'naturalWidth',
),
).toBeGreaterThan(0);
|
@dgozman @pavelfeldman @mxschmitt what does the Playwright team think about paving the cowpaths on this one? Eg. finding the best, robust recommended solution in the list above (including the part of waiting for network requests to finish) and then doing one or the other of the following:
To be clear, one statement of the value here is to test that programmatically-generated images are not broken in some way (which can easily happen silently, leaving them still "visible" and "in the viewport", but broken to website visitors) If the Playwright team is open to this, I'm happy to create a new issue with full description and feature request or docs request. |
@karlhorky This depends on what exactly you would like to achieve. Something like |
@dgozman interesting that there's not much uptake here. Maybe users don't know that they would want it? Broken images can happen for a variety of reasons, including the one that I mentioned in the post above:
Pretty common to create programmatically-created image paths (or even programmatically-generated image files themselves) these days, especially with things like Seems like a pretty big use case, and would be great if the Playwright team agrees that this common pattern is enough to justify a built-in matcher.
I'm not against having custom recipe-style code for this (instead of a built-in matcher) - this was my option 2 I posted above:
But I would ask that the Playwright team comes up with their recommendation here with the fewest pitfalls and possibility for flaky tests. (already ran into some problems with my approach above since yesterday) |
@karlhorky Unfortunately, I cannot speak to what is the best approach here, without testing it on a variety of different sites. This solution seems to be very straightforward, but it waits instead of expecting. Perhaps the following expect will be good for your usecase? await expect(page.locator('img')).toHaveJSProperty('complete', true); If await expect(page.locator('img')).not.toHaveJSProperty('naturalWidth', 0);
// or
await expect.poll(() => page.locator('img').evaluate(img => img.naturalWidth)).toBeGreaterThan(0);
Let's figure out the solution that works for most sites, and then we can definitely add it to the docs. |
Great, those options look nice - I'll try
|
Started a demo for all of these test cases here: https://github.com/karlhorky/playwright-image-loading-tests-with-next-js Now just need to find the time to actually hammer out the page HTML and test cases, maybe @ProchaLu will be able to drop in for some help here... 🤔 |
Hm, also just thinking that if // readyState 4 means "Enough data is available to play video"
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLMediaElement/readyState
const video = page.locator('video').first();
await video.scrollIntoViewIfNeeded();
await expect(video).toHaveJSProperty('readyState', 4); Edit: Seems to work 👍 |
@dgozman @karlhorky We recognized in these test scenarios that the Here are the test results of the different scenarios: 1. Single image: image loads successfully<Image src="next.svg" alt="image" width="{300}" height="{300}" /> The following is the console output from the test run for
Playwright test is successful. await page.goto('/01-single-success');
await expect(page.locator('img')).toHaveJSProperty('complete', true);
await expect(page.locator('img')).not.toHaveJSProperty('naturalWidth', 0); 2. Single image: image loads successfully (slow - large image on 3G network speed)<Image src="next.svg" alt="image" width="{300}" height="{300}" />
Playwright test is successful. For this test the network speed was throttled to 3G. const client = await page.context().newCDPSession(page);
await client.send('Network.enable');
await client.send('Network.emulateNetworkConditions', {
offline: false,
downloadThroughput: ((500 * 1000) / 8) * 0.8,
uploadThroughput: ((500 * 1000) / 8) * 0.8,
latency: 70,
});
await page.goto('/02-single-success-slow');
await expect(page.locator('img')).toHaveJSProperty('complete', true);
await expect(page.locator('img')).not.toHaveJSProperty('naturalWidth', 0); 3. Single image: image loads unsuccessfully (404)<Image src="404.svg" alt="image" width={300} height={300} />
The Playwright test below for for
await page.goto('/03-single-fail-404');
await expect(page.locator('img')).toHaveJSProperty('complete', true);
await expect(page.locator('img')).not.toHaveJSProperty('naturalWidth', 0); 4. Single image: image loads unsuccessfully (malformed image)<Image src="/malformed.svg" alt="image" width={300} height={300} />
The Playwright test below for for
await page.goto('/04-single-fail-malformed');
await expect(page.locator('img')).toHaveJSProperty('complete', true);
await expect(page.locator('img')).not.toHaveJSProperty('naturalWidth', 0); 5. Single image: image loads unsuccessfully (host unreachable)<Image src="https://i/img.jpg" alt="image" width={300} height={300} />
The Playwright test below for for
await page.goto('/05-single-fail-unreachable');
await expect(page.locator('img')).toHaveJSProperty('complete', true);
await expect(page.locator('img')).not.toHaveJSProperty('naturalWidth', 0); 6. Multiple images: all images load successfully <Image src="next.svg" alt="image" width={200} height={200} />
<br />
<Image src="next.svg" alt="image" width={200} height={200} />
<br />
<Image src="next.svg" alt="image" width={200} height={200} />
<br />
<Image src="next.svg" alt="image" width={200} height={200} />
<br />
<Image src="next.svg" alt="image" width={200} height={200} />
<br />
<Image src="next.svg" alt="image" width={200} height={200} />
<br />
<Image src="next.svg" alt="image" width={200} height={200} />
<br />
Playwright test is successful. await page.goto('/06-multiple-success');
for (const img of await page.getByRole('img').all()) {
await expect(img).toHaveJSProperty('complete', true);
await expect(img).not.toHaveJSProperty('naturalWidth', 0);
} 7. Multiple images: all images load successfully (slow - large images on 3G network speed) <Image src="/large.png" alt="image" width={1800} height={1000} />
<br />
<Image src="/large.png" alt="image" width={1800} height={1000} />
<br />
<Image src="/large.png" alt="image" width={1800} height={1000} />
<br />
<Image src="/large.png" alt="image" width={1800} height={1000} />
<br />
<Image src="/large.png" alt="image" width={1800} height={1000} />
<br />
<Image src="/large.png" alt="image" width={1800} height={1000} />
<br />
<Image src="/large.png" alt="image" width={1800} height={1000} />
Playwright test is successful. For this test the network speed was throttled to 3G. const client = await page.context().newCDPSession(page);
await client.send('Network.enable');
await client.send('Network.emulateNetworkConditions', {
offline: false,
downloadThroughput: ((500 * 1000) / 8) * 0.8,
uploadThroughput: ((500 * 1000) / 8) * 0.8,
latency: 70,
});
await page.goto('/07-multiple-success-slow');
for (const img of await page.getByRole('img').all()) {
await expect(img).toHaveJSProperty('complete', true);
await expect(img).not.toHaveJSProperty('naturalWidth', 0);
} 8. Multiple images: all images load successfully (lazy loading out of viewport)...
<Image
src="next.svg"
alt="image"
width={200}
height={200}
loading="lazy"
/>
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<Image
src="next.svg"
alt="image"
width={200}
height={200}
loading="lazy"
/>
...
Playwright test is successful. await page.goto('/08-multiple-success-lazy');
for (const img of await page.getByRole('img').all()) {
await expect(img).toHaveJSProperty('complete', true);
await expect(img).not.toHaveJSProperty('naturalWidth', 0);
} 9. Multiple images: 1st image loads unsuccessfully (404), rest load successfully <Image src="404.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
The Playwright test below for
await page.goto('/09-multiple-1st-fail-rest-success');
for (const img of await page.getByRole('img').all()) {
await expect(img).toHaveJSProperty('complete', true);
await expect(img).not.toHaveJSProperty('naturalWidth', 0);
} 10. Multiple images: all images but last load successfully, last image loads unsuccessfully (404) <Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="next.svg" alt="image" width={300} height={300} />
<br />
<Image src="404.svg" alt="image" width={300} height={300} />
The Playwright test below for
await page.goto('/10-multiple-last-fail-rest-success');
for (const img of await page.getByRole('img').all()) {
await expect(img).toHaveJSProperty('complete', true);
await expect(img).not.toHaveJSProperty('naturalWidth', 0);
} 11. Multiple images: all images load unsuccessfully (404) <Image src="404.svg" alt="image" width={300} height={300} />
<br />
<Image src="404.svg" alt="image" width={300} height={300} />
<br />
<Image src="404.svg" alt="image" width={300} height={300} />
<br />
<Image src="404.svg" alt="image" width={300} height={300} />
<br />
<Image src="404.svg" alt="image" width={300} height={300} />
<br />
<Image src="404.svg" alt="image" width={300} height={300} />
<br />
<Image src="404.svg" alt="image" width={300} height={300} />
The Playwright test below for
await page.goto('/11-multiple-fail-404');
for (const img of await page.getByRole('img').all()) {
await expect(img).toHaveJSProperty('complete', true);
await expect(img).not.toHaveJSProperty('naturalWidth', 0);
} 12. Multiple images: all images load unsuccessfully (host unreachable)<div>
<Image src="https://i/img.jpg" alt="image" width={300} height={300} />
<br />
<Image src="https://i/img.jpg" alt="image" width={300} height={300} />
<br />
<Image src="https://i/img.jpg" alt="image" width={300} height={300} />
<br />
<Image src="https://i/img.jpg" alt="image" width={300} height={300} />
<br />
<Image src="https://i/img.jpg" alt="image" width={300} height={300} />
<br />
<Image src="https://i/img.jpg" alt="image" width={300} height={300} />
<br />
<Image src="https://i/img.jpg" alt="image" width={300} height={300} />
</div>
The Playwright test below for
await page.goto('/12-multiple-fail-unreachable');
for (const img of await page.getByRole('img').all()) {
await expect(img).toHaveJSProperty('complete', true);
await expect(img).not.toHaveJSProperty('naturalWidth', 0);
} |
For waiting for all (visible) lazy-loaded images on the page to load, I wrote a new trick for this, which also tests image loading with const lazyImages = await page.locator('img[loading="lazy"]:visible').all();
for (const lazyImage of lazyImages) {
await lazyImage.scrollIntoViewIfNeeded();
await expect(lazyImage).not.toHaveJSProperty('naturalWidth', 0);
} One workaround for this is to assert the length of the const lazyImagesLocator = page.locator('img[loading="lazy"]:visible');
// Assert on length to wait for image visibility to stabilize
// after client-side JavaScript hides some images
// https://github.com/microsoft/playwright/issues/31737#issuecomment-2233775909
await expect(lazyImagesLocator).toHaveCount(13);
const lazyImages = await lazyImagesLocator.all();
for (const lazyImage of lazyImages) {
await lazyImage.scrollIntoViewIfNeeded();
await expect(lazyImage).not.toHaveJSProperty('naturalWidth', 0);
} Source: https://github.com/karlhorky/playwright-tricks#load-all-lazy-images |
Hi.
I need to wait for a heavy image (6Mb) to be loaded.
I use:
await page.waitForResponse(src);
as suggested in #4782
But it doesn't work. Event fired at the start of loading. I see how it continue loading after this.
Thank you for help and for excellent product.
The text was updated successfully, but these errors were encountered: