Build your own SDK to run Percy tests
A guide to integrating Percy visual testing into the framework of your choice
If Percy currently doesn’t support the test framework you’re using, it’s possible to build your own SDK. Percy works by capturing a snapshot of the DOM in the test browser. From there the DOM is sent to Percy’s API to eventually be rendered concurrently across browsers/widths for screenshots.
Almost all of the heavy lifting is taken care of for you. Percy’s SDKs are thin integrations to the frameworks and are pretty simple under the covers. All SDKs need to:
- Check if Percy is running
- Fetch the DOM serialization JavaScript, then execute that JS inside of the test browser
- Serialize the DOM (by running a JS script in the test browser)
-
POST
that DOM & snapshot options (name, widths, etc) to the running Percy server
These steps can change slightly depending on if the SDK you are building is in JavaScript or any other language. If your framework is in JavaScript, skip to this section(you have options).
All Percy SDKs require @percy/cli
to process and upload snapshots from the SDK to your Percy project.
Tutorial
Let’s build our own SDK in Python for Selenium. Where this code lives and how it is structured can depend on the framework you’re integrating into. The example we’re going to build here is going to be a generic Selenium Python SDK (framework agnostic), so it will be a function.
For examples sake, we will create a new file called percy_snapshot.py
and create a percy_snapshot
function:
For this SDK, we’re going to have two required arguments:
-
driver
- The Selenium driver (so we can run the JS we need to extract the DOM) -
name
- Snapshot name (required by the Percy API).
We’ll also want to support all of the snapshot options that are available. In Python, that means we can use **kwargs
for the rest of these options.
Step 1
The first step to making our SDK work is to check if the local Percy server is running. If it’s not, none of the steps after this should be run (save time/resources).
For snapshots to be captured, processed, and sent to the Percy API, there needs to be a local Percy server running. You can start it by either using percy exec — [test command]
or percy exec:start
. This is required for Percy to work, so checking if this server is available is a good first step.
The local Percy server provides a /percy/healthcheck
endpoint. This endpoint returns info about the server. If this doesn’t respond, we know we can safely exit and not run any more code.
For the sake of the tutorial, we’re going to use the requests package to make HTTP requests. Feel free to use your favorite way of making HTTP requests in the language you’re building in.
Step 2
The second step is to fetch the DOM JavaScript. You can read more about what it does here. After fetching the DOM JavaScript, we can execute it in the browser to make PercyDOM.serialize()
available to us in the next step.
Step 3
With the DOM JavaScript library now executed and available in the browser, we can serialize & capture the DOM from the test browser. There are some snapshot options that might alter how the DOM is serialized, so we’ll also pass those along to the JavaScript serialize function.
Step 4
The last step to making this SDK work its to POST
that DOM we’ve captured (dom_snapshot
) to the locally running Percy server. We’ll also pass along any additional snapshot options that were provided to the percy_snapshot
function.
Once the DOM & options are POST
ed to the locally running Percy server, you should see a log from the Percy CLI:
Everything from here is taken care of by the Percy CLI. 🎉
Getting Production Ready
The percy_snapshot
function we have written so far has been slimmed down to make the example easier to understand. There are a few additional things you should do when writing your own SDK.
We’ll compare the SDK we’ve built here so far to our official Python SDK to highlight the finally polishing needed.
Supplying client & environment user agents
There are two additional keys you should pass along when POST
’ing the DOM snapshot to the local Percy server:
clientInfo
environmentInfo
In our Python SDK, these are the Percy SDK version, Selenium driver version, and Python version:
Then we send that along with the rest of the POST
data:
Allow changing the API URL / port
It’s possible for users to change the URL the local Percy server is running on OR the port. This is controlled through an environment variable, PERCY_CLI_API
. In our Python SDK, we set a variable which we then interpolate into the network request URLs:
Error handling
Missing from our slimmed example is error handling. You will want to wrap try/catches (or whatever your languages control flow is) to catch errors. The API may also respond with errors, so make sure those errors are raised when received.
Log level control
The Percy CLI logger will respond to various kinds of log level filtering. We make our SDKs follow the same pattern and you can too. We read the PERCY_LOGLEVEL
environment variable to determine what to log.
Caching network requests
Another optimization you should make before using this SDK in your workflow is to cache the health check & the DOM JavaScript that’s fetched from the server. For example, in our Python SDK:
This makes is so you’re only requesting these endpoints once per-run of the test suite.
JavaScript based SDKs
Everything discussed above applies to JavaScript based SDKs too. With that said, Percy’s SDK toolchain is built in JavaScript. This means you can consume various packages directly, if you need to.
For example, we have packaged up most of the common tasks up into @percy/sdk-utils
. In Puppeteer:
The full Puppeteer SDK is ~42 lines of code (notice the use of @percy/logger
):
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
We're sorry to hear that. Please share your feedback so we can do better
Contact our Support team for immediate help while we work on improving our docs.
We're continuously improving our docs. We'd love to know what you liked
Thank you for your valuable feedback!