Visual component testing using playwright

Colin Oakley
2 min readApr 27, 2023


Everything at scale gets a little bit more tricky, slowly but surely a lot of the building blocks of internal services have been DWP design system this includes structural components (header, key details, footer) and more functional ones like the timeline.

Example of headers at various sizes

Pre-this point in a lot of cases we had people making up components, or pasting from various prototypes other services and modifying the GOV.UK header.

In one connected service, we ended up with 5 variations of the header mostly around font size and alignment.

Problem solved?

Well, partly. Having a single point of truth is very useful but a couple of things still happened

  1. Local changes
  2. Bugs in the design system
  3. Changes in what is displayed

Some of these got caught, and some of them made it to production.

Visual regression allows us to test a component to use a rendered version of the component (with data) and using playwright to compare it.

This is easily broken but equally cheap to do and in the above it would have caught most of the unintentional issues.

When this is packaged up it becomes a design artifact that we can check across multiple services.

// playwright.config.js
const config = {
reporter: 'list',
testDir: './tests/playwright',
snapshotPathTemplate: '{testDir}/example/{arg}.png',

module.exports = config;

We can configure playwright in a couple of ways, the main point of this is to set where our templates are, next we need to write our actual test example.

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

const BASE_URL = '';

test.describe('visual tests', () => {
test.beforeEach(async ({ page }) => {
// Go to the starting url before each test.
await page.goto(`${BASE_URL}/government/topical-events/coronation`);
await expect(page).toHaveTitle(/Coronation - GOV.UK/);

test('should have a valid header', async ({ page }) => {
await expect(await page.locator('header.gem-c-layout-super-navigation-header')

This code will load the page and compare the component against a set screenshot, we can example this to cover variations of components, different page sizes.

We can also match this against code snapshots — this if using different frameworks could be beneficial as it asserts the html is produced in the same way.



Colin Oakley

front-end developer in Government into html, css, node.js and a11y. Co-orginizer of Frontend North East.