Skip to main content
Transform your testing process with: Company-wide Licences, Test Observability & App Percy
No Result Found

Take snapshots via scripts

How to grab Percy snapshots via scripts

The Percy snapshot is a powerful command that allows you to capture visual snapshots of web pages, lists, sitemaps, or static directories.

On this page, we’ll explore how to use Percy snapshot method in your test script and what are the supported parameters. You have the option to either capture a full screenshot or take a screenshot of a single element.

Percy snapshot method

Incorporate the Percy snapshot method into your test script as shown below. You must specify a snapshots name that you intend to capture. The options can be passed as an optional parameter.

Copy icon Copy
percySnapshot(name, OPTIONS)
Copy icon Copy
percySnapshot(driver, name, OPTIONS)
Copy icon Copy
percySnapshot(driver, name, OPTIONS)
  • All Percy SDKs that support @percy/cli can accept snapshot options as the final argument of the SDK’s percySnapshot method.
  • These snapshot options will override or be merged with their equivalent Percy config file options.

Per-snapshot configuration

Option Description
widths An array of numbers, in pixels, representing the widths you’d like to capture for each snapshot; overrides global snapshot widths.
min-height A number specifying the minimum height in pixels each snapshot should be.
percy-css A string containing Percy specific CSS; appended to any global percy-css.
enable-javascript A boolean that enables JavaScript for all snapshots in the build. This may cause side-effects like animations or redirects making your snapshots less reliable.
Note: When you capture the DOM, JavaScript remains enabled regardless of the “enable-javascript” option. JavaScript is only disabled in our rendering environment. You can use JavaScript to set the DOM state, take a Percy snapshot, and we will render it with JavaScript enabled or disabled based on the flag.
scope A string of a selector to scope the given screenshot to.
discovery A subset of discovery options to use for this snapshot’s asset discovery.
- allowed-hostnames: An array of additional hostnames to save asset contents from; merged with any existing allowed-hostnames.
- disallowed-hostnames: An array of hostnames to immediately abort requests from.
- request-headers: An object containing HTTP headers to be sent for each request made during asset discovery; merged with any existing request-headers.
- authorization: A username/password combo to authenticate requests for Percy.
- disable-cache: Disables asset discovery caches.
- user-agent: Custom user-agent string used when requesting assets.

More Info

The underlying CLI API accepts the following options in camelCase, PascalCase, snake_case, or kebab-case!

Exec command to execute script

Percy CLI commands for running a local snapshot server.

Commands

  • percy exec
  • percy exec:start
  • percy exec:stop
  • percy exec:ping

percy exec

Start and stop Percy around a supplied command

Copy icon Copy
Usage:
  $ percy exec [options] -- <command>
Subcommands:
  exec:start [options]               Starts a local Percy snapshot server
  exec:stop [options]                Stops a local running Percy snapshot server
  exec:ping [options]                Pings a local running Percy snapshot server
  help [command]                     Display command help
Options:
  -P, --port [number]                Local CLI server port (default: 5338)
  --parallel                         Marks the build as one of many parallel builds
  --partial                          Marks the build as a partial build
Percy options:
  -c, --config <file>                Config file path
  -d, --dry-run                      Print snapshot names only
  -h, --allowed-hostname <hostname>  Allowed hostnames to capture in asset discovery
  -t, --network-idle-timeout <ms>    Asset discovery network idle timeout
  --disable-cache                    Disable asset discovery caches
  --debug                            Debug asset discovery and do not upload snapshots
Global options:
  -v, --verbose                      Log everything
  -q, --quiet                        Log errors only
  -s, --silent                       Log nothing
  --help                             Display command help
Examples:
  $ percy exec -- echo "percy is running around this echo command"
  $ percy exec -- yarn test

percy exec:start

Starts a local Percy snapshot server

Copy icon Copy
Usage:
  $ percy exec:start [options]
Options:
  -P, --port [number]                Local CLI server port (default: 5338)
Percy options:
  -c, --config <file>                Config file path
  -d, --dry-run                      Print snapshot names only
  -h, --allowed-hostname <hostname>  Allowed hostnames to capture in asset discovery
  -t, --network-idle-timeout <ms>    Asset discovery network idle timeout
  --disable-cache                    Disable asset discovery caches
  --debug                            Debug asset discovery and do not upload snapshots
Global options:
  -v, --verbose                      Log everything
  -q, --quiet                        Log errors only
  -s, --silent                       Log nothing
  --help                             Display command help
Examples:
  $ percy exec:start &> percy.log

percy exec:stop

Stops a local running Percy snapshot server

Copy icon Copy
Usage:
  $ percy exec:stop [options]
Options:
  -P, --port [number]  Local CLI server port (default: 5338)
Global options:
  -v, --verbose        Log everything
  -q, --quiet          Log errors only
  -s, --silent         Log nothing
  -h, --help           Display command help

percy exec:ping

Pings a local running Percy snapshot server

Copy icon Copy
Usage:
  $ percy exec:ping [options]
Options:
  -P, --port [number]  Local CLI server port (default: 5338)
Global options:
  -v, --verbose        Log everything
  -q, --quiet          Log errors only
  -s, --silent         Log nothing
  -h, --help           Display command help

Exec command to execute script with a specific config for the snapshots

You can also specify the path directly to a config file by passing a --config or -c option to the percy exec command:

Copy icon Copy
percy exec --config ./configs/percy.conf.yml -- ...

Screenshot a single element

Sometimes capturing a full-page screenshot isn’t necessary. For example, if there are dynamic parts of the page that you don’t need to test or are only interested in a very specific region to test. For these cases, you can pass a scope snapshot option and Percy will only capture the scoped element on the given widths. This can be passed as a per-snapshot option. The scope selector accepts any valid selector you would be able to pass to document querySelector.

If there are multiple matching selectors on the page, Percy will select the first matching element.

Screenshot a single element using selector

Copy icon Copy
percySnapshot('name', { scope: '.selector' });
percySnapshot(driver, 'name', { scope: '.selector' });
Copy icon Copy
Map<String, Object> options = new HashMap<String, Object>();
options.put("scope", ".selector");
percy.snapshot("Site with options", options);
Copy icon Copy
percy_snapshot(driver=driver, name='name', scope='.selector');
Copy icon Copy
Percy.snapshot(driver, 'name', { scope: '.selector' })
Copy icon Copy
Percy.Options snapshotOptions = new Percy.Options();
snapshotOptions.Add("minHeight", 1280);
snapshotOptions.Add("scope", ".selector");
Percy.Snapshot(driver, "snapshot_name", snapshotOptions);

Screenshot a single element using xpath

Copy icon Copy
percySnapshot(driver, 'name', { scope: '//button[contains(., \"Contact Us\")]' });
Copy icon Copy
Map<String, Object> options = new HashMap<String, Object>();
options.put("scope", "//button[contains(., \"Contact Us\")]");
percy.snapshot("Site with options", options);
Copy icon Copy
percy_snapshot(driver=driver, name='name', scope='//button[contains(., \"Contact Us\")]');
Copy icon Copy
Percy.snapshot(driver, 'name', { scope: '//button[contains(., \"Contact Us\")]' })
Copy icon Copy
Percy.Options snapshotOptions = new Percy.Options();
snapshotOptions.Add("minHeight", 1280);
snapshotOptions.Add("scope",  "//button[contains(., \"Contact Us\")]");
Percy.Snapshot(driver, "snapshot_name", snapshotOptions);

Screenshot a scrollable single element

Copy icon Copy
percySnapshot(driver, 'name', { scope: '.selector', scopeOptions: { scroll: true } });
Copy icon Copy
Map<String, Object> options = new HashMap<String, Object>();
Map<String, Object> scopeOptions = new HashMap<String, Object>();
options.put("scope", ".selector");
scopeOptions.put("scroll", true);
options.put("scopeOptions", scopeOptions);
percy.snapshot("Site with options", options);
Copy icon Copy
percy_snapshot(driver=driver, name='name', scope='.selector', scopeOptions = { 'scroll': True });
Copy icon Copy
Percy.snapshot(driver, 'name', { scope: '.selector', scopeOptions: { scroll: true } })
Copy icon Copy
Percy.Options snapshotOptions = new Percy.Options();
snapshotOptions.Add("minHeight", 1280);
snapshotOptions.Add("scope", ".selector");
snapshotOptions.Add("scopeOptions", new
    {
        scroll = true
    });
Percy.Snapshot(driver, "snapshot_name", snapshotOptions);

Use cases

Multiple elements with the same selector

If you would like to scope a screenshot to a specific element that has the same matching selector as other elements on the page you’ll have to get more specific with your selector. This can be done by either adding another unique selector to that element or by using standard CSS selectors to get more specific. This is the same way you would write CSS – Percy doesn’t add anything to this process.

For example, given the below DOM:

Copy icon Copy
<html>
  <head>
    <title>Example</title>
  </head>
  <body>
    <h1 class="underline">My example</h1>
    <p class="underline">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tristique convallis sem, vitae sodales risus accumsan in</p>
    <p class="underline">Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas tristique convallis sem, vitae sodales risus accumsan in</p>
  </body>
</html>

Instead of using just .underline to select the element, you would want to either specify the element type (h1 / p) or by using CSS tree-structural pseudo-classes like :last-of-type or :nth-child.

Copy icon Copy
percySnapshot(driver, 'name', { scope: 'h1.underline' }); // selects the h1

Likewise,

  • p.underline scope selects the ‘first’ paragraph
  • p.underline:last-of-type scope selects last paragraph

Selector for elements serialized by Percy

It may happen that your single-element screenshots are not working correctly with canvas/video elements.

During DOM serialization <canvas>, <video> elements are converted to <img>. Check canvas elements in Percy’s SDK Workflow. This can cause the selector to not match, and an incorrect screenshot.

To work around this, we can use CSS tree structural pseudo-classes as suggested in the previous section.

Copy icon Copy
<!-- Skipping most of the stuff for brevity -->
<!-- Original DOM -->
<div class="selector">
  <canvas> </canvas>
  <canvas> </canvas>
  <video> </video>
</div>
<!-- Post DOM Serialization -->
<div class="selector">
  <img> </img>
  <img> </img>
  <img> </img>
</div>
  • Instead of canvas or video you’ll need to change it to img
  • If there is only a single canvas/video tag inside div we can directly use .selector as the scope.
Copy icon Copy
percySnapshot(driver, 'name', { scope: '.selector img:nth-of-type(1)' }); // selects first canvas

Likewise,

  • .selector img:nth-of-type(2) scope selects the ‘second’ canvas
  • .selector img:last-of-type scope selects the ‘last’ video

Usage when having Selenium's WebElement

When dealing with Web elements, you may use the below utility function that returns CSS selector i.e scope in our case, that could be easily passed to the percySnapshot function.

Make sure script eval is allowed in the test browser, Please check ref on how to verify.

Copy icon Copy
public String percyScope(WebDriver driver, WebElement webElement) {
    //  util for getting css selector using the DOM element
    String script = String.join(System.getProperty("line.separator"),
        "if(typeof UTILS === 'undefined') {",
        "return fetch('https://raw.githubusercontent.com/percy/percy-misc-examples/main/scripts/dom_css_path/dom_css_path.js')",
        ".then(res => res.text())",
        ".then(eval)",
        ".then( _ => UTILS.cssPath(arguments[0]));",
        "} else {",
        "return UTILS.cssPath(arguments[0]);",
        "}"
    );
    JavascriptExecutor jse = (JavascriptExecutor) driver;
    return (String) jse.executeScript(script, webElement);
}
Copy icon Copy
async function percyScope(driver, webElement) {
  // # util for getting css selector using the DOM element
  script = `
        if (typeof UTILS === 'undefined') {
            return fetch('https://raw.githubusercontent.com/percy/percy-misc-examples/main/scripts/dom_css_path/dom_css_path.js')
                .then(res => res.text())
                .then(eval)
                .then(_ => UTILS.cssPath(arguments[0]));
        } else {
            return UTILS.cssPath(arguments[0]);
        }
        `
  return await driver.executeScript(script, webElement)
}
// percySnapshot(driver, 'name', { scope: await percyScope(driver, webElement) });
Copy icon Copy
def percy_scope(driver, web_element):
    # util for getting css selector using the DOM element
    script = """
            if(typeof UTILS === 'undefined') {
                return fetch('https://raw.githubusercontent.com/percy/percy-misc-examples/main/scripts/dom_css_path/dom_css_path.js')
                        .then(res => res.text())
                        .then(eval)
                        .then( _ => UTILS.cssPath(arguments[0]));
            } else {
                return UTILS.cssPath(arguments[0]);
            }
            """
    return driver.execute_script(script, web_element)
# percy_snapshot(driver=driver, name='name', scope=percy_scope(driver,web_element));

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

Is this page helping you?

Yes
No

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!

Talk to an Expert
Download Copy Check Circle