Passer au contenu principal

Visual Testing

What can it do?

WebdriverIO provides image comparisons on screens, elements or a full-page for

  • 🖥️ Desktop browsers (Chrome / Firefox / Safari / Microsoft Edge)
  • 📱 Mobile / Tablet browsers (Chrome on Android emulators / Safari on iOS Simulators / Simulators / real devices) via Appium
  • 📱 Native Apps (Android emulators / iOS Simulators / real devices) via Appium (🌟 NEW 🌟)
  • 📳 Hybrid apps via Appium

through the @wdio/visual-service which is a lightweight WebdriverIO service.

This allows you to:

  • save or compare screens/elements/full-page screens against a baseline
  • automatically create a baseline when no baseline is there
  • block out custom regions and even automatically exclude a status and or toolbars (mobile only) during a comparison
  • increase the element dimensions screenshots
  • hide text during website comparison to:
    • improve stability and prevent font rendering flakiness
    • only focus on the layout of a website
  • use different comparison methods and a set of additional matchers for better readable tests
  • verify how your website will support tabbing with your keyboard), see also Tabbing through a website
  • and much more, see the service and method options

The service is a lightweight module to retrieve the needed data and screenshots for all browsers/devices. The comparison power comes from ResembleJS. If you want to compare images online you can check the online tool.

NOTE For Native/Hybrid Apps

The methods saveScreen, saveElement, checkScreen, checkElement and the matchers toMatchScreenSnapshot and toMatchElementSnapshot can be used for Native Apps/Context.

Please use the property isHybridApp:true in your service settings when you want to use it for Hybrid Apps.

Installation

The easiest way is to keep @wdio/visual-service as a dev-dependency in your package.json, via:

npm install --save-dev @wdio/visual-service

Usage

@wdio/visual-service can be used as a normal service. You can set it up in your configuration file with the following:

import path from "node:path";

// wdio.conf.ts
export const config = {
// ...
// =====
// Setup
// =====
services: [
[
"visual",
{
// Some options, see the docs for more
baselineFolder: path.join(process.cwd(), "tests", "baseline"),
formatImageName: "{tag}-{logName}-{width}x{height}",
screenshotPath: path.join(process.cwd(), "tmp"),
savePerInstance: true,
// ... more options
},
],
],
// ...
};

More service options can be found here.

Once set up in your WebdriverIO configuration, you can go ahead and add visual assertions to your tests.

Capabilities

To use the Visual Testing module, you don’t need to add any extra options to your capabilities. However, in some cases, you may want to add additional metadata to your visual tests, such as a logName.

The logName allows you to assign a custom name to each capability, which can then be included in the image filenames. This is particularly useful for distinguishing screenshots taken across different browsers, devices, or configurations.

To enable this, you can define logName in the capabilities section and ensure the formatImageName option in the Visual Testing service references it. Here's how you can set it up:

import path from "node:path";

// wdio.conf.ts
export const config = {
// ...
// =====
// Setup
// =====
capabilities: [
{
browserName: 'chrome',
'wdio-ics:options': {
logName: 'chrome-mac-15', // Custom log name for Chrome
},
}
{
browserName: 'firefox',
'wdio-ics:options': {
logName: 'firefox-mac-15', // Custom log name for Firefox
},
}
],
services: [
[
"visual",
{
// Some options, see the docs for more
baselineFolder: path.join(process.cwd(), "tests", "baseline"),
screenshotPath: path.join(process.cwd(), "tmp"),
// The format below will use the `logName` from capabilities
formatImageName: "{tag}-{logName}-{width}x{height}",
// ... more options
},
],
],
// ...
};

How it works

  1. Setting Up the logName:

    • In the capabilities section, assign a unique logName to each browser or device. For example, chrome-mac-15 identifies tests running on Chrome on macOS version 15.
  2. Custom Image Naming:

    • The formatImageName option integrates the logName into the screenshot filenames. For example, if the tag is homepage and the resolution is 1920x1080, the resulting filename might look like this:

      homepage-chrome-mac-15-1920x1080.png

  3. Benefits of Custom Naming:

    • Distinguishing between screenshots from different browsers or devices becomes much easier, especially when managing baselines and debugging discrepancies.
  4. Note on Defaults:

    -If logName is not set in the capabilities, the formatImageName option will show it as an empty string in the filenames (homepage--15-1920x1080.png)

WebdriverIO MultiRemote

We also support MultiRemote. To make this work properly make sure that you add wdio-ics:options to your capabilities as you can see below. This will make sure that each screenshot will have its own unique name.

Writing your tests will not be any different in comparison to using the testrunner

// wdio.conf.js
export const config = {
capabilities: {
chromeBrowserOne: {
capabilities: {
browserName: "chrome",
"goog:chromeOptions": {
args: ["disable-infobars"],
},
// THIS!!!
"wdio-ics:options": {
logName: "chrome-latest-one",
},
},
},
chromeBrowserTwo: {
capabilities: {
browserName: "chrome",
"goog:chromeOptions": {
args: ["disable-infobars"],
},
// THIS!!!
"wdio-ics:options": {
logName: "chrome-latest-two",
},
},
},
},
};

Running Programmatically

Here is a minimal example of how to use @wdio/visual-service via remote options:

import { remote } from "webdriverio";
import VisualService from "@wdio/visual-service";

let visualService = new VisualService({
autoSaveBaseline: true,
});

const browser = await remote({
logLevel: "silent",
capabilities: {
browserName: "chrome",
},
});

// "Start" the service to add the custom commands to the `browser`
visualService.remoteSetup(browser);

await browser.url("https://webdriver.io/");

// or use this for ONLY saving a screenshot
await browser.saveFullPageScreen("examplePaged", {});

// or use this for validating. Both methods don't need to be combined, see the FAQ
await browser.checkFullPageScreen("examplePaged", {});

await browser.deleteSession();

Tabbing through a website

You can check if a website is accessible by using the keyboard TAB-key. Testing this part of accessibility has always been a time-consuming (manual) job and pretty hard to do through automation. With the methods saveTabbablePage and checkTabbablePage, you can now draw lines and dots on your website to verify the tabbing order.

Be aware of the fact that this is only useful for desktop browsers and NOT** for mobile devices. All desktop browsers support this feature.

remarque

The work is inspired by Viv Richards his blog post about "AUTOMATING PAGE TABABILITY (IS THAT A WORD?) WITH VISUAL TESTING".

The way tabbable elements are selected is based on the module tabbable. If there are any issues regarding the tabbing please check the README.md and especially the More Details section.

How does it work

Both methods will create a canvas element on your website and draw lines and dots to show you where your TAB would go if an end-user would use it. After that, it will create a full-page screenshot to give you a good overview of the flow.

important

**Use the saveTabbablePage only when you need to create a screenshot and DON'T want to compare it **with a baseline image.****

When you want to compare the tabbing flow with a baseline, then you can use the checkTabbablePage-method. You DON'T need to use the two methods together. If there is already a baseline image created, which can automatically be done by providing autoSaveBaseline: true when you instantiate the service, the checkTabbablePage will first create the actual image and then compare it against the baseline.

Options

Both methods use the same options as the saveFullPageScreen or the compareFullPageScreen.

Example

This is an example of how the tabbing works on our guinea pig website:

WDIO tabbing example

Automatically update failed Visual Snapshots

Update the baseline images through the command line by adding the argument --update-visual-baseline. This will

  • automatically copy the actual take screenshot and put it in the baseline folder
  • if there are differences it will let the test pass because the baseline has been updated

Usage:

npm run test.local.desktop  --update-visual-baseline

When running logs info/debug mode you will see the following logs added

[0-0] ..............
[0-0] #####################################################################################
[0-0] INFO:
[0-0] Updated the actual image to
[0-0] /Users/wswebcreation/Git/wdio/visual-testing/localBaseline/chromel/demo-chrome-1366x768.png
[0-0] #####################################################################################
[0-0] ..........

Typescript support

This module includes TypeScript support, allowing you to benefit from auto-completion, type safety, and improved developer experience when using the Visual Testing service.

Step 1: Add Type Definitions

To ensure TypeScript recognizes the module types, add the following entry to the types field in your tsconfig.json:

{
"compilerOptions": {
"types": ["@wdio/visual-service"]
}
}

Step 2: Enable Type Safety for Service Options

To enforce type checking on the service options, update your WebdriverIO configuration:

// wdio.conf.ts
import { join } from 'node:path';
// Import the type definition
import type { VisualServiceOptions } from '@wdio/visual-service';

export const config = {
// ...
// =====
// Setup
// =====
services: [
[
"visual",
{
// Service options
baselineFolder: join(process.cwd(), './__snapshots__/'),
formatImageName: '{tag}-{logName}-{width}x{height}',
screenshotPath: join(process.cwd(), '.tmp/'),
} satisfies VisualServiceOptions, // Ensures type safety
],
],
// ...
};

System Requirements

Version 5 and up

For version 5 and up, this module is a purely JavaScript-based module with no additional system dependencies beyond the general project requirements. It uses Jimp which is an image processing library for Node written entirely in JavaScript, with zero native dependencies.

Version 4 and Lower

For version 4 and lower, this module relies on Canvas, a canvas implementation for Node.js. Canvas depends on Cairo.

Installation Details

By default, binaries for macOS, Linux and Windows will be downloaded during your project's npm install. If you don't have a supported OS or processor architecture, the module will be compiled on your system. This requires several dependencies, including Cairo and Pango.

For detailed installation information, see the node-canvas wiki. Below are one-line installation instructions for common operating systems. Note that libgif/giflib, librsvg, and libjpeg are optional and only needed for GIF, SVG, and JPEG support, respectively. Cairo v1.10.0 or later is required.

Using Homebrew:

brew install pkg-config cairo pango libpng jpeg giflib librsvg pixman

Mac OS X v10.11+: If you have recently updated to Mac OS X v10.11+ and are experiencing trouble when compiling, run the following command: xcode-select --install. Read more about the problem on Stack Overflow. If you have Xcode 10.0 or higher installed, to build from source you need NPM 6.4.1 or higher.

Welcome! How can I help?

WebdriverIO AI Copilot