Capturing lazy loading images

👍

Covered in this doc

How to capture lazy loading images for Percy snapshots

Lazy loading images (and other content) has become a popular way to optimize page rendering performance. It can also make your full page snapshots appear incorrect if the page isn't captured in the right state.

Background

If you're using a custom lazy image loading library, you most likely will have to scroll the page before taking a Percy snapshot. Libraries like lozad.js or lazyload wait to set the <img>'s src attribute until the page is scrolled near the image. Once the page scroll intersects with the image, the library will apply the src with the correct image.

Percy's SDKs capture the DOM of the page and then send that off to Percy to be rendered concurrently across browsers and widths. For snapshots to render correctly, the page will need to be scrolled before a Percy snapshot is captured. Percy does not automatically scroll the page for you. It's also important to note by default JavaScript is disabled when Percy renders the captured DOM in our browsers.

If images are not rendering correctly in your snapshots, it's likely the page did not scroll past the image (and did not trigger the intersection observer).

Examples

Scrolling the page to the bottom and waiting for that scroll to happen before capturing a snapshot can be tricky. The examples below will use the scroll-to-bottomjs package on NPM, but ultimately you can use any JavaScript that scrolls the page. This package is promise aware and resolves the promise once the page has reached the bottom.

The key to this is scrolling the page past all of the lazy-loaded images, triggering them to load their full resolution image.

You can see what a Percy build looks like that properly scrolls a page with lazy loading images before capturing a snapshot here: https://percy.io/percy/lazy-loading-example/builds/24053522

That build used the Selenium example below.

Selenium

This example uses JavaScript. With all Selenium-based projects, you will be using executeScript to run the needed JavaScript to scroll the page.

const { Builder } = require("selenium-webdriver");
const scrollToBottom = require("scroll-to-bottomjs");
const percySnapshot = require('@percy/selenium-webdriver');

(async function () {
    let driver = await new Builder().forBrowser("chrome").build();

    try {
        await driver.get("http://localhost:8001/lazy-loading");
        await driver.executeScript(scrollToBottom);
        await percySnapshot(driver, "Lazy loading images", { discovery: { 'allowed-hostnames': ['via.placeholder.com'] } });
    } finally {
        await driver.quit();
    }
})();

Puppeteer

let puppeteer = require("puppeteer");
let percySnapshot = require("@percy/puppeteer");
let scrollToBottom = require("scroll-to-bottomjs");

(async () => {
  let browser = await puppeteer.launch({ headless: false });
  let page = await browser.newPage();

  await page.goto("https://sdk-test.percy.dev/lazy-loading");
  await page.evaluate(scrollToBottom);
  await percySnapshot(page, "Lazy loading");
  await browser.close();
})();

Cypress

let scrollToBottom = require("scroll-to-bottomjs");

describe("Lazy loading example", () => {
  it("captures lazy loading images", () => {
    cy.visit("https://sdk-test.percy.dev/lazy-loading");
    cy.window().then(cyWindow => scrollToBottom({ remoteWindow: cyWindow }));
    cy.percySnapshot("Lazy loading images");
  });
});

Troubleshooting

If images are still not appearing correctly in your snapshots, adjustments to how fast the page is scrolling will likely need to be made. It would be worth observing the browser while the test runs (and the network panel). Placing a debugger right before you take a snapshot with Percy will allow you to inspect the webpages DOM to see if the problematic images have the correct src attribute.

If you're still having trouble getting lazy loading images to capture properly, feel free to reach out to support.