Master Advanced Visual Testing Techniques with Playwright | Techwave story

The Challenge: Diving Deeper into Visual Testing

In our previous blog, Getting Started with Snapshot Testing in Playwright, we explored the basics of visual testing and how it can help catch UI inconsistencies early in the development cycle. Now, let’s continue the journey with TechWave Solutions as they delve deeper into advanced snapshot testing techniques to further enhance the stability and reliability of their flagship project management tool, WorkWave.

Revisiting TechWave Solutions

After successfully integrating basic visual testing into their workflow, Sarah and her team at TechWave Solutions had made significant strides in catching visual bugs before they reached production. However, as WorkWave grew in complexity, so did the challenges. They needed to ensure that their UI remained consistent across various devices and browsers, all while managing dynamic content and reducing test flakiness.

Advanced Snapshot Testing Techniques

1. Page vs. Element Snapshots

Liam, the senior developer, kicked off a meeting to explain the nuances of page and element snapshots.

a. When to Use Page Snapshots

“Page snapshots are perfect for verifying the overall layout, responsiveness, and accessibility of an entire page,” Liam explained. “However, they can be a bit flaky. Any minor change within the viewport can cause the snapshot to fail.”

To demonstrate, Liam showed a basic page snapshot test:

test('page snapshot', async ({page}) => {
  await page.goto('https://www.workwave.com');
  await expect(page).toHaveScreenshot();
});

It takes below snapshot of entire viewport area:

b. When to Use Element Snapshots

“Element snapshots, on the other hand, focus on specific components,” Liam continued. “This makes them less brittle and more reliable for testing isolated parts of the UI.”

He provided an example:

test('element snapshot', async ({page}) => {
  await page.goto('https://www.workwave.com');
  const $first_image = page.locator('img').first();
  await expect($first_image).toHaveScreenshot();
});

It will take below element screenshot on only first img:

2. Advanced Features for Page Snapshots

Sarah then outlined some advanced features for working with page snapshots.

a. Cropping Page Snapshots

“Sometimes, we don’t need the entire viewport. Cropping snapshots to focus on specific areas can help reduce flakiness,” Sarah said.

She shared an example of a cropped snapshot:

test('cropped snapshot', async ({page}) => {
  await page.goto('https://www.workwave.com');
  const {width, height} = page.viewportSize();

  await expect(page).toHaveScreenshot({
    clip: {
      x: (width - 400) / 2, 
      y: (height - 400) / 2, 
      width: 400, 
      height: 400,
    },
  });

  await expect(page).toHaveScreenshot({
    clip: {x: 0, y: 0, width: Infinity, height: 20},
  });
});

Here’s how the screenshot will look like for same page with above code :

b. Full Page Snapshots

“For scenarios where we need to verify the entire page across different browsers, full-page snapshots are invaluable,” Sarah noted.

She provided the following example:

test('full page snapshot', async ({page}) => {
  await page.goto('https://www.workwave.com');
  await expect(page).toHaveScreenshot({
    fullPage: true,
  });
});

3. Enhancing Element Snapshots

Next, Olivia, the junior developer, demonstrated advanced techniques for element snapshots.

a. Testing Element Interactivity

“Testing different states of an element ensures comprehensive coverage,” Olivia said. She showed how to capture a form input across various states:

test('element states', async ({page}) => {
  await page.goto('https://www.workwave.com/contact-us');
  const $input = page.locator('input').first();

  await expect($input).toHaveScreenshot();
  await $input.hover();
  await expect($input).toHaveScreenshot();
  await $input.focus();
  await expect($input).toHaveScreenshot();
  await $input.fill('Hey, cool cat!');
  await expect($input).toHaveScreenshot();
});
b. Testing Element Responsiveness

“For responsive designs, ensuring components look good across various screen sizes is crucial,” Olivia added.

She demonstrated this with the following code:

test('element responsiveness', async ({page}) => {
  const viewportWidths = [960, 760, 480];
  await page.goto('https://www.workwave.com/blog');
  const $post = page.locator('ultp-block-content-wrap').first();

  for (const width of viewportWidths) {
    await page.setViewportSize({width, height: 800});
    await expect($post).toHaveScreenshot(`post-${width}.png`);
  }
});
4. Advanced Snapshot Capturing Techniques

The team then explored advanced snapshot techniques to further enhance their testing strategies.

a. Masking Portions of a Snapshot

“Sometimes, we need to exclude dynamic content or sensitive information from snapshots,” Liam explained.

He shared an example of using masks:

test('masked snapshots', async ({page}) => {
  await page.goto('https://www.workwave.com');
  const $autoPlayVideo = page.locator('video');

  await expect(page).toHaveScreenshot({
    mask: [
      $autoPlayVideo
    ],
  });
});
b. Consistent Styles During Snapshots

“Keeping styles consistent during snapshots helps avoid flakiness,” Sarah emphasized.

She showed how to apply custom CSS for this purpose:

test('consistent styles', async ({page}) => {
  await page.goto('https://www.workwave.com');
  const $hero = page.locator('main > header');

  await expect(page).toHaveScreenshot({
    stylePath: [
      './hide-dynamic-elements.css',
      './disable-scroll-animations.css',
    ],
  });
});

Handling Flaky Snapshots

Sarah explained how to handle flaky snapshots, especially when dealing with animations or dynamic content.

“Playwright can automatically retry failed visual tests until it finds a valid match,” she said.

She shared an example:

const { test, expect } = require('@playwright/test');

test.describe(() => {
test.describe.configure({ retries: 2 });
  test('masked snapshots', async ({ page }) => {
    await page.goto('https://www.workwave.com');
    const autoPlayVideo = page.locator('video');
    await expect(page).toHaveScreenshot({
     mask: [autoPlayVideo],
    });
  });
});

It will try the test execution 3 times before it reports failure.

Visual Tests for Generated Images

Liam discussed the need for visual tests for generated images, such as QR codes or social share cards.

He demonstrated with the following code:

import {test, expect} from '@playwright/test';
import {buffer} from 'stream/consumers';

test('arbitrary snapshot', async ({page}) => {
  await page.goto('https://getavataaars.com');
  await page.locator('main form button').first().click();

  const avatar = await page.waitForEvent('download')
    .then((dl) => dl.createReadStream())
    .then((stream) => buffer(stream));

  expect(avatar).toMatchSnapshot('avatar.png');
});

Cross-Browser Snapshot Comparisons

Finally, Sarah introduced cross-browser snapshot comparisons using Playwright’s projects functionality.

“Comparing snapshots across different browsers ensures consistent behavior and appearance,” she explained.

She walked the team through the necessary configuration:

const crossBrowserConfig = {
  testDir: './tests/cross-browser',
  snapshotPathTemplate: '.test/cross/{testFilePath}/{arg}{ext}',
  expect: {
    toHaveScreenshot: {maxDiffPixelRatio: 0.1},
  },
};

export default defineConfig({
  projects: [
    {
      name: 'cross-chromium',
      use: {...devices['Desktop Chrome']},
      ...crossBrowserConfig,
    },
    {
      name: 'cross-firefox',
      use: {...devices['Desktop Firefox']},
      dependencies: ['cross-chromium'],
      ...crossBrowserConfig,
    },
    {
      name: 'cross-browser',
      use: {...devices['Desktop Safari']},
      dependencies: ['cross-firefox'],
      ...crossBrowserConfig,
    },
  ],
});

And created a new test file:

import {test, expect} from '@playwright/test';

test('cross-browser snapshots', async ({page}) => {
  await page.goto('https://www.workwave.com');
  await page.locator(':has(> a figure)')
    .evaluate(($el) => $el.remove());

  await expect(page).toHaveScreenshot(`home-page.png`, {
    fullPage: true,
  });
});

Conclusion: Embracing Advanced Visual Testing

The team at TechWave Solutions had come a long way in mastering visual testing with Playwright. By diving into advanced techniques, they enhanced their ability to catch visual discrepancies early, ensuring a seamless user experience for WorkWave.

As they continued to refine their testing strategies, the value of visual testing became increasingly evident. For those keen on further exploring advanced configurations and CI/CD integration, Sarah recommended checking out the Ultimate Guide to Visual Testing in Playwright which will be coming out soon.

With these tools and techniques, TechWave Solutions not only improved their product but also fortified their testing capabilities, setting a new standard for quality and reliability. Happy testing!

Share your love

Leave a Reply

Your email address will not be published. Required fields are marked *