Hey! These docs are for version 1, which is no longer officially supported. Click here for the latest version, 2-web!

Build your own SDK

A guide to integrating Percy visual testing into the framework of your choice

Integrating Percy visual testing into a new framework is simple using Percy Agent. In this document, we walk you through the necessary steps and provide pointers to examples you can use as a starting point.

If you're unfamiliar with Percy as a user, we strongly suggest that you first start with one of our 2-minute tutorials. This will help you better understand the steps involved in visual testing with Percy.

Overview

The @percy/agent package provides a binary that does the heavy lifting needed for a successful visual snapshot. It will also take care of detecting your Percy credentials and uploading the results to your Percy project.

The agent also provides a JavaScript library that acts as the bridge between the agent and your code. This library is provided in a couple of forms:

  • As a JavaScript library which you can require and use in the usual way.
  • As a one-file percy-agent.js, which you can inject into the web page that you want to snapshot. This format is useful for the situations where the only way you have to interface with your framework is by injecting JavaScript directly into an existing browser window. This is likely also your only option if you are using a programming language other than JavaScript.
  • As a one-file percy-agent.js, available over HTTP if Percy Agent is running, at http://localhost:5338/percy-agent.js.

To integrate with the Percy Agent, you will need the following steps:

  1. Make the PercyAgent class available inside the browser windows or webpages that you want to snapshot. Depending on your circumstances, this will look like requiring the PercyAgent class from @percy/agent, or injecting percy-agent.js into those pages.
  2. Provide a way to instantiate a PercyAgent and call its snapshot method, within the context of the web page to snapshot.
  3. Wrap the execution of your code in a percy exec -- call.

Below we'll go through these steps in detail.

Making the PercyAgent class available

The PercyAgent JavaScript class exposes the snapshot method that will capture a snapshot of your web page and communicate the data to the locally-running Percy agent service.

This class needs to be instantiated and executed within the context of the page that we are snapshotting. We offer you two different avenues to do this. The best method will depend on the capabilities of the framework you're working with.

Using @percy/agent as a library in your code

If your framework allows you to include arbitrary JavaScript code, you might be able to add require('@percy/agent') in the right place, and then expose the underlying snapshot call in whichever may makes sense for your use case. Your code will might end up looking something like:

const PercyAgent = require('@percy/agent')

function takeAPercySnapshot(snapshotName, snapshotOptions) {
  const percyAgentClient = new PercyAgent({
    clientInfo: '[email protected]',
    environmentInfo: 'some helpful os or browser information for debugging'
  })
  percyAgentClient.snapshot(snapshotName, snapshotOptions)
}

📘

This code has to be executed within the browser context of the page you want to snapshot.

For an example of this approach, see our @percy/cypress SDK implementation: https://github.com/percy/percy-cypress/blob/master/lib/index.ts

Injecting percy-agent.js into the browser

If your framework doesn't offer a good way to use the approach above, you can integrate Percy by injecting it directly into the browser window from which you want to take snapshots.

For this approach, we provide percy-agent.js, a one-file version of the libraries that are needed by the PercyAgent class. The full path to the percy-agent.js file is available through the function agentJsFilename that @percy/agent exposes.

Another alternative for accessing the percy-agent.js is HTTP. Percy Agent exposes the file over HTTP at http://localhost:5338/percy-agent.js.

You'll need to find a way to inject JavaScript into your browser window or webpage, and then use that ability to:

  1. Inject percy-agent.js into the page
  2. Within the page, instantiate PercyAgent and call its snapshot method.

Many browser automation frameworks offer methods to execute arbitrary JavaScript in the underlying browser (often called, 'inject', 'addScriptTag', evaluate or execute). For example, to integrate with NightmareJS using this method, you can build a Percy library like this:

function nightmareSnapshot(nightmare) {
  nightmare
    .inject('js', agentJsFilename())
    .evaluate(function () {
      const percyAgentClient = new PercyAgent({clientInfo: `my-example-integration`})
      percyAgentClient.snapshot('My snapshot name')
    })
}

For an example of this approach, see our @percy/puppeteer SDK implementation: https://github.com/percy/percy-puppeteer/blob/master/lib/index.ts.

🚧

The agentJsFilename function is only available in @percy/agent version 0.1.7 and above.

Injecting percy-agent.js in languages other than JavaScript

If you are working in a language other than JavaScript, you can use vendorize a copy of percy-agent.js in a location of your choice inside your project, and use that copy to inject directly in the browser.

The percy-agent.js file is available at the location dist/public/percy-agent.js inside the npm @percy/agent package, which you can get by using npm install @percy/agent, or else download directly from npm here.

For an example of such an implementation, see the implementation of our Java Selenium SDK and, in particular, the Percy class.

Wrapping execution in 'percy exec -- '

The final step in the integration is to provide a way for your execution command to be wrapped in a percy exec command. This will make the Percy Agent run your code and listen for snapshots to be processed and uploaded.

You will need to set the PERCY_TOKEN environment variable for the agent to know what project to upload snapshots to. If the PERCY_TOKEN variable is not found, the agent will emit a warning and no snapshots will be produced (you can use this to your advantage, e.g. to turn off snapshot uploads during development).

$ export PERCY_TOKEN=<your token here>

If you were integrating Percy into a test runner that you invoke by calling run-my-tests, the new way to run your tests might look like:

$ percy exec -- run-my-tests

(Don't forget the spaces around -- !)

For an example, see how we wrap the npm run test command with percy exec -- for our our @percy/puppeteer tests: https://github.com/percy/percy-puppeteer/blob/master/package.json#L25

PercyAgent usage

new PercyAgent(options)

options is an optional hash that can include:

  • clientInfo: A string identifying the Percy client implementation, e.g. my-percy-integration/v0.0.1.

  • environmentInfo: A string identifying the operating system, browser and other useful environment information, e.g. Windows; chrome.

Both clientInfo and environmentInfo might appear under the "Environment" section of a build's metadata in the Percy.io user interface.

percyClient.snapshot(snapshotName)
percyClient.snapshot(snapshotName, options)

snapshotName is a required string that will be used as the snapshot name. It can be any string that makes sense to identify the page state. It should be unique and remain the same across builds. If you're providing your SDK as a library for others, this parameter will typically be provided by your users. You might also be able to automatically generate this string, easing the burden on your users; see Autogenerated snapshot names for more details on this.

options is an optional hash that can include:

  • widths: An array of integers representing the browser widths at which you want to take snapshots.

  • minHeight: An integer specifying the minimum height of the resulting snapshot, in pixels. Defaults to 1024px.

Examples

const percy = new PercyAgent({
  clientInfo: 'test-sdk/0.0.1/alpha',
  environmentInfo: 'Mac OS X ; unknown browser'
})

percy.snapshot('Homepage test')
percy.snapshot('Homepage responsive test', { widths: [768, 992, 1200] });

Use debug logs to inspect what happens within the agent

You can turn on verbose debug logging when running the percy exec by setting the environment variable LOG_LEVEL=debug. This will let you see in detail what is happening inside the agent.

$ LOG_LEVEL=debug percy exec -- run-my-tests

These debug logs will include, among other things, the full request that the agent is receiving from your SDK, which will help you troubleshoot any issues. Look for a set of logs that look like:

[percy] handling snapshot:
[percy] -> headers: {"host":"localhost:5338","connection":"keep-alive","content-length":"2163","origin":"http://localhost:8000","user-agent":"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_3) AppleWebKit/537.36 (KHTML, like Gecko) HeadlessChrome/72.0.3626.109 Safari/537.36","content-type":"application/json","accept":"*/*","referer":"http://localhost:8000/","accept-encoding":"gzip, deflate, br"}
[percy] -> body: {"name":"One todo","url":"http://localhost:8000/","widths":[768,992,1200],"clientInfo":"percy-java-selenium/unknown","environmentInfo":"selenium-java; MAC; chrome/72.0.3626.109","domSnapshot":"<!DOCTYPE html><html lang=\"en\" data-framework=\"es6\">[...DOM ommitted...]</html>"}

Global Configuration

You can configure Percy to use the same options over all snapshots. To see supported configuration including widths read our Configuration doc.