/playwright-tricks

A collection of helpful tricks for Playwright tests

Playwright Tricks

A collection of helpful tricks for Playwright tests

Load All Lazy Images

Scroll to all visible lazy-loaded images and wait for successful loading of image:

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);
}

Be aware, using .all() can be problematic if new images are being added, removed, shown or hidden while the test code is running.

One workaround for this is to assert the length of the .all() array (if you know it) to wait for it to stabilize:

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);
}

Screenshot Comparison Tests of PDFs

Playwright does not (as of June 2024) have support for visual comparison testing with PDFs.

There are many issues asking for this feature, but the current position of the Playwright team is that PDF.js should be used instead, to render the PDF to a canvas.

It's not clear how the Playwright team suggests to do this, but one way is to navigate to about:blank, use page.setContent() to add a PDF.js viewer to the page, which accepts a URL, and then use expect(page).toHaveScreenshot():

// HTML template string no-op for VS Code highlighting / formatting
function html(strings: TemplateStringsArray, ...values: unknown[]) {
  return strings.reduce((result, string, i) => {
    return result + string + (values[i] ?? '');
  }, '');
}

test('PDF has screenshot', async ({ page }) => {
  // Go to page without Content-Security-Policy header, to avoid CSP
  // prevention of script loading from https://mozilla.github.io
  await page.goto('about:blank');

  await page.setContent(html`
    <!doctype html>
    <html>
      <head>
        <meta charset="UTF-8" />
      </head>
      <body>
        <canvas></canvas>
        <script src="https://mozilla.github.io/pdf.js/build/pdf.mjs" type="module"></script>
        <script type="module">
          pdfjsLib.GlobalWorkerOptions.workerSrc =
            'https://mozilla.github.io/pdf.js/build/pdf.worker.mjs';

          try {
            const pdf = await pdfjsLib.getDocument(
               'https://raw.githubusercontent.com/mozilla/pdf.js/ba2edeae/examples/learning/helloworld.pdf',
            ).promise;

            const page = await pdf.getPage(1);
            const viewport = page.getViewport({ scale: 1.5 });

            const canvas = document.querySelector('canvas');
            canvas.height = viewport.height;
            canvas.width = viewport.width;

            await page.render({
              canvasContext: canvas.getContext('2d'),
              viewport,
            }).promise;
          } catch (error) {
            console.error('Error loading PDF:', error);
          }
        </script>
      </body>
    </html>
  `);

  await page.waitForTimeout(1000);

  await expect(page).toHaveScreenshot({ fullPage: true });
});

Test Image Loading

Test that <img> elements have a src attribute that is reachable and responds with image data:

const img = page.locator('img');
await expect(img).not.toHaveJSProperty('naturalWidth', 0);

Source: microsoft/playwright#6046 (comment)