Skip to content

Percy.skip parameter ignored when --include CLI flag is present #1286

@xwellingtonx

Description

@xwellingtonx

The problem

When using the --include CLI flag with npx percy storybook, story-level parameters.percy.skip: true is silently ignored and snapshots are taken anyway. The skip flag works correctly when --include is not used.

Environment

  • Node version: v24.13.1
  • @percy/cli version: 1.31.4
  • Version of Percy SDK you’re using: 9.1.0
  • If needed, a build or snapshot ID: N/A
  • OS version: Ubuntu (GitHub Actions runner)
  • Type of shell command-line [interface]: bash

Details

In shouldSkipStory (within the Storybook SDK), when any global filter (config.include or config.exclude) is present, the function switches filter from options (the story) to config (the CLI config):

// if a global filter is present, disregard story filters
let filter = (config?.include || config?.exclude) ? config : options;
...
let skip = include?.length ? !include.some(matches) : options.skip;

Because filter is now config, the include array is built from the CLI flags. When the story name matches an --include pattern, include.some(matches) returns true and skip is set to false options.skip (parameters.percy.skip: true) is never evaluated.

This means that any story explicitly included via --include can never be skipped at the story level. The comment in the code says "if a global filter is present, disregard story filters", but this also discards the skip flag, which is not a filter — it is an opt-out mechanism independent of inclusion/exclusion logic.

Expected: a story with parameters.percy.skip: true should always be skipped, regardless of whether it was matched by a --include pattern.

Actual: the story is snapshotted.

function shouldSkipStory(name, options, config) {
let matches = regexp => {
/* istanbul ignore else: sanity check */
if (typeof regexp === 'string') {
let [, parsed, flags] = /^\/(.+)\/(\w+)?$/.exec(regexp) || [];
regexp = new RegExp(parsed ?? regexp, flags);
}
return regexp?.test?.(name);
};
// if a global filter is present, disregard story filters
let filter = (config?.include || config?.exclude) ? config : options;
let include = [].concat(filter?.include).filter(Boolean);
let exclude = [].concat(filter?.exclude).filter(Boolean);
// if included, don't skip; if excluded always exclude
let skip = include?.length ? !include.some(matches) : options.skip;
if (!skip && !exclude?.some(matches)) return false;
return true;
}

Suggested fix:

let skip = include?.length ? !include.some(matches) : false;
if (!skip && options.skip) skip = true; // always honour story-level skip

Debug logs

N/A — the behaviour is deterministic and the root cause is identified directly in the source.

Code to reproduce issue

Story file:

// component.stories.tsx
export const Default: Story = {
  parameters: { percy: { skip: true } },
  render: ...,
};

Percy invocation:
npx percy storybook ./dist/storybook --include "Atoms/Component*"
Result: Atoms/Component/Default is snapshotted despite percy.skip: true.

Without --include: Atoms/Component/Default is correctly skipped.

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐛 bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions