{"id":13469650,"url":"https://github.com/reg-viz/storycap","last_synced_at":"2025-05-14T00:07:19.745Z","repository":{"id":39589217,"uuid":"101257364","full_name":"reg-viz/storycap","owner":"reg-viz","description":"A Storybook Addon, Save the screenshot image of your stories :camera: via puppeteer.","archived":false,"fork":false,"pushed_at":"2025-05-12T20:06:27.000Z","size":15258,"stargazers_count":730,"open_issues_count":90,"forks_count":90,"subscribers_count":9,"default_branch":"master","last_synced_at":"2025-05-12T21:25:20.797Z","etag":null,"topics":["addon","puppeteer","screenshot","screenshot-image","storybook","visual-testing"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/storycap","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/reg-viz.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-08-24T05:29:19.000Z","updated_at":"2025-04-04T06:04:14.000Z","dependencies_parsed_at":"2023-10-20T12:32:11.053Z","dependency_job_id":"0e2f8c26-195f-4fd0-942a-2377bb6c8dd7","html_url":"https://github.com/reg-viz/storycap","commit_stats":{"total_commits":1102,"total_committers":37,"mean_commits":"29.783783783783782","dds":0.6842105263157895,"last_synced_commit":"ce4afe40a09aafaf89a2d4142f34ea9ef45419da"},"previous_names":["tsuyoshiwada/storybook-chrome-screenshot"],"tags_count":75,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reg-viz%2Fstorycap","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reg-viz%2Fstorycap/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reg-viz%2Fstorycap/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/reg-viz%2Fstorycap/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/reg-viz","download_url":"https://codeload.github.com/reg-viz/storycap/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253827858,"owners_count":21970594,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["addon","puppeteer","screenshot","screenshot-image","storybook","visual-testing"],"created_at":"2024-07-31T15:01:48.622Z","updated_at":"2025-05-14T00:07:19.699Z","avatar_url":"https://github.com/reg-viz.png","language":"TypeScript","funding_links":[],"categories":["TypeScript","🛠️ Developer Tools"],"sub_categories":[],"readme":"# Storycap\n\n[storybook]: https://github.com/storybooks/storybook\n[puppeteer]: https://github.com/GoogleChrome/puppeteer\n\n![DEMO](https://raw.githubusercontent.com/reg-viz/storycap/artwork/demo_v2.gif)\n\n[![npm](https://img.shields.io/npm/v/storycap.svg?style=flat-square)](https://www.npmjs.com/package/storycap)\n\n\u003e A [Storybook][storybook] Addon, Save the screenshot image of your stories :camera: via [Puppeteer][puppeteer].\n\nStorycap crawls your Storybook and takes screenshot images.\nIt is primarily responsible for image generation necessary for Visual Testing such as [reg-suit](https://github.com/reg-viz/reg-suit).\n\n\u003c!-- toc --\u003e\n\n- [Features](#features)\n- [Install](#install)\n- [Getting Started](#getting-started)\n  - [Managed mode](#managed-mode)\n    - [Setup Storybook](#setup-storybook)\n    - [Setup your stories(optional)](#setup-your-storiesoptional)\n    - [Run `storycap` Command](#run-storycap-command)\n- [API](#api)\n  - [`withScreenshot`](#withscreenshot)\n  - [type `ScreenshotOptions`](#type-screenshotoptions)\n  - [type `Variants`](#type-variants)\n  - [type `Viewport`](#type-viewport)\n  - [function `isScreenshot`](#function-isscreenshot)\n- [Command Line Options](#command-line-options)\n- [Multiple PNGs from 1 story](#multiple-pngs-from-1-story)\n  - [Basic usage](#basic-usage)\n  - [Variants composition](#variants-composition)\n  - [Parallelisation across multiple computers](#parallelisation-across-multiple-computers)\n- [Tips](#tips)\n  - [Run with Docker](#run-with-docker)\n  - [Full control the screenshot timing](#full-control-the-screenshot-timing)\n    - [Example 1](#example-1)\n    - [Example 2](#example-2)\n- [Chromium version](#chromium-version)\n- [Storybook compatibility](#storybook-compatibility)\n  - [Storybook versions](#storybook-versions)\n  - [UI frameworks](#ui-frameworks)\n- [How it works](#how-it-works)\n- [Contributing](#contributing)\n- [License](#license)\n\n\u003c!-- tocstop --\u003e\n\n## Features\n\n- :camera: Take screenshots of each stories via [Puppeteer][puppeteer].\n- :zap: Extremely fast.\n- :package: Zero configuration.\n- :rocket: Provide flexible screenshot shooting options.\n- :tada: Independent of any UI framework(React, Angular, Vue, etc...)\n\n## Install\n\n```sh\n$ npm install storycap\n```\n\nOr\n\n```sh\n$ npm install storycap puppeteer\n```\n\nInstalling puppeteer is optional. See [Chromium version](#chromium-version) to get more detail.\n\n## Getting Started\n\nStorycap runs with 2 modes. One is \"simple\" and another is \"managed\".\n\nWith the simple mode, you don't need to configure your Storybook. All you need is give Storybook's URL, such as:\n\n```sh\n$ npx storycap http://localhost:9001\n```\n\nYou can launch your server via `--serverCmd` option.\n\n```sh\n$ storycap --serverCmd \"start-storybook -p 9001\" http://localhost:9001\n```\n\nOf course, you can use pre-built Storybook:\n\n```sh\n$ build-storybook -o dist-storybook\n$ storycap --serverCmd \"npx http-server dist-storybook -p 9001\" http://localhost:9001\n```\n\nAlso, Storycap can crawls built and hosted Storybook pages:\n\n```sh\n$ storycap https://next--storybookjs.netlify.app/vue-kitchen-sink/\n```\n\n### Managed mode\n\n#### Setup Storybook\n\nIf you want to control how stories are captured (timing or size or etc...), use managed mode.\n\nFirst, add `storycap` to your Storybook config file:\n\n```js\n/* .storybook/main.js */\n\nmodule.exports = {\n  stories: ['../src/**/*.stories.@(js|mdx)'],\n  addons: [\n    '@storybook/addon-actions',\n    '@storybook/addon-links',\n    'storycap', // \u003c-- Add storycap\n  ],\n};\n```\n\nNext, use `withScreenshot` decorator to tell how Storycap captures your stories.\n\n```js\n/* .storybook/preview.js */\n\nimport { withScreenshot } from 'storycap';\n\nexport const decorators = [\n  withScreenshot, // Registration the decorator is required\n];\n\nexport const parameters = {\n  // Global parameter is optional.\n  screenshot: {\n    // Put global screenshot parameters(e.g. viewport)\n  },\n};\n```\n\n\u003e [!NOTE]\n\u003e You can set configuration of screenshot with `addParameters` and `screenshot` key.\n\n#### Setup your stories(optional)\n\nAnd you can overwrite the global screenshot options in specific stories file via `parameters`.\n\n```js\nimport React from 'react';\nimport MyComponent from './MyComponent';\n\nexport default {\n  title: 'MyComponent',\n  component: MyComponent,\n  parameters: {\n    screenshot: {\n      delay: 200,\n    },\n  },\n};\n\nexport const Normal = {};\n\nexport const Small = {\n  args: {\n    text: 'small',\n  },\n  parameters: {\n    screenshot: {\n      viewport: 'iPhone 5',\n    },\n  },\n};\n```\n\n#### Run `storycap` Command\n\n```sh\n$ npx start-storybook -p 9009\n$ npx storycap http://localhost:9009\n```\n\nOr you can exec with one-liner via `--serverCmd` option:\n\n```sh\n$ npx storycap http://localhost:9009 --serverCmd \"start-storybook -p 9009\"\n```\n\n## API\n\n### `withScreenshot`\n\nA Storybook decorator to notify Storycap to captures stories.\n\n### type `ScreenshotOptions`\n\n`ScreenshotOptions` object is available as the value of the key `screenshot` of `addParameters` argument or `withScreenshot` argument.\n\n```ts\ninterface ScreenshotOptions {\n  delay?: number;                           // default 0 msec\n  waitAssets?: boolean;                     // default true\n  waitFor?: string | () =\u003e Promise\u003cvoid\u003e;   // default \"\"\n  fullPage?: boolean;                       // default true\n  hover?: string;                           // default \"\"\n  focus?: string;                           // default \"\"\n  click?: string;                           // default \"\"\n  skip?: boolean;                           // default false\n  viewport?: Viewport;\n  viewports?: string[] | { [variantName]: Viewport };\n  variants?: Variants;\n  waitImages?: boolean;                     // default true\n  omitBackground?: boolean;                 // default false\n  captureBeyondViewport?: boolean;          // default true\n  clip?: { x: number; y: number; width: number; height: number } | null; // default null\n}\n```\n\n- `delay`: Waiting time [msec] before capturing.\n- `waitAssets`: If set true, Storycap waits until all resources requested by the story, such as `\u003cimg\u003e` or CSS background images, are finished.\n- `waitFor` : If you set a function to return `Promise`, Storycap waits the promise is resolved. You can also set a name of global function that returns `Promise`.\n- `fullPage`: If set true, Storycap captures the entire page of stories.\n- `focus`: If set a valid CSS selector string, Storycap captures after focusing the element matched by the selector.\n- `hover`: If set a valid CSS selector string, Storycap captures after hovering the element matched by the selector.\n- `click`: If set a valid CSS selector string, Storycap captures after clicking the element matched by the selector.\n- `skip`: If set true, Storycap cancels capturing corresponding stories.\n- `viewport`, `viewports`: See type `Viewport` section below.\n- `variants`: See type `Variants` section below.\n- `waitImages`: Deprecated. Use `waitAssets`. If set true, Storycap waits until `\u003cimg\u003e` in the story are loaded.\n- `omitBackground`: If set true, Storycap omits the background of the page allowing for transparent screenshots. Note the storybook theme will need to be transparent as well.\n- `captureBeyondViewport`: If set true, Storycap captures screenshot beyond the viewport. See also [Puppeteer API docs](https://github.com/puppeteer/puppeteer/blob/v13.1.3/docs/api.md#pagescreenshotoptions).\n- `clip`: If set, Storycap captures only the portion of the screen bounded by x/y/width/height.\n\n### type `Variants`\n\n`Variants` is used to generate [multiple PNGs from 1 story](#multiple-pngs-from-1-story).\n\n```ts\ntype Variants = {\n  [variantName: string]: {\n    extends?: string | string[]; // default: \"\"\n    delay?: number;\n    waitAssets?: boolean;\n    waitFor?: string | () =\u003e Promise\u003cvoid\u003e;\n    fullPage?: boolean;\n    hover?: string;\n    focus?: string;\n    click?: string;\n    skip?: boolean;\n    viewport?: Viewport;\n    waitImages?: boolean;\n    omitBackground?: boolean;\n    captureBeyondViewport?: boolean;\n    clip?: { x: number; y: number; width: number; height: number } | null;\n  };\n};\n```\n\n- `extends`: If set other variant's name(or an array of names of them), this variant extends the other variant options. And this variant generates a PNG file with suffix such as `_${parentVariantName}_${thisVariantName}`.\n\n### type `Viewport`\n\n`Viewport` is compatible for [Puppeteer viewport interface](https://github.com/puppeteer/puppeteer/blob/main/docs/api/puppeteer.viewport.md).\n\n```ts\ntype Viewport =\n  | string\n  | {\n      width: number; // default: 800\n      height: number; // default: 600\n      deviceScaleFactor: ?number; // default: 1,\n      isMobile?: boolean; // default: false,\n      hasTouch?: boolean; // default: false,\n      isLandscape?: boolean; // default: false,\n    };\n```\n\n\u003e [!NOTE]\n\u003e You should choose a valid [device name](https://github.com/puppeteer/puppeteer/blob/main/packages/puppeteer-core/src/common/Device.ts) if set string.\n\n`Viewport` values are available in `viewports` field such as:\n\n```js\naddParameters({\n  screenshot: {\n    viewports: {\n      large: {\n        width: 1024,\n        height: 768,\n      },\n      small: {\n        width: 375,\n        height: 668,\n      },\n      xsmall: {\n        width: 320,\n        height: 568,\n      },\n    },\n  },\n});\n```\n\n### function `isScreenshot`\n\n```typescript\nfunction isScreenshot(): boolean;\n```\n\nReturns whether current process runs in Storycap browser. It's useful to change your stories' behavior only in Storycap (e.g. disable JavaScript animation).\n\n## Command Line Options\n\n\u003c!-- inject:clihelp --\u003e\n\n```txt\nusage: storycap [options] storybook_url\n\nOptions:\n      --help                       Show help                                                                   [boolean]\n      --version                    Show version number                                                         [boolean]\n  -o, --outDir                     Output directory.                               [string] [default: \"__screenshots__\"]\n  -p, --parallel                   Number of browsers to screenshot.                               [number] [default: 4]\n  -f, --flat                       Flatten output filename.                                   [boolean] [default: false]\n  -i, --include                    Including stories name rule.                                    [array] [default: []]\n  -e, --exclude                    Excluding stories name rule.                                    [array] [default: []]\n      --delay                      Waiting time [msec] before screenshot for each story.           [number] [default: 0]\n  -V, --viewport                   Viewport.                                              [array] [default: [\"800x600\"]]\n      --disableCssAnimation        Disable CSS animation and transition.                       [boolean] [default: true]\n      --disableWaitAssets          Disable waiting for requested assets                       [boolean] [default: false]\n      --trace                      Emit Chromium trace files per screenshot.                  [boolean] [default: false]\n      --silent                                                                                [boolean] [default: false]\n      --verbose                                                                               [boolean] [default: false]\n      --forwardConsoleLogs         Forward in-page console logs to the user's console.        [boolean] [default: false]\n      --serverCmd                  Command line to launch Storybook server.                       [string] [default: \"\"]\n      --serverTimeout              Timeout [msec] for starting Storybook server.               [number] [default: 60000]\n      --shard                      The sharding options for this run. In the format \u003cshardNumber\u003e/\u003ctotalShards\u003e.\n                                   \u003cshardNumber\u003e is a number between 1 and \u003ctotalShards\u003e. \u003ctotalShards\u003e is the total\n                                   number of computers working.                                [string] [default: \"1/1\"]\n      --captureTimeout             Timeout [msec] for capture a story.                          [number] [default: 5000]\n      --captureMaxRetryCount       Number of count to retry to capture.                            [number] [default: 3]\n      --metricsWatchRetryCount     Number of count to retry until browser metrics stable.       [number] [default: 1000]\n      --viewportDelay              Delay time [msec] between changing viewport and capturing.    [number] [default: 300]\n      --reloadAfterChangeViewport  Whether to reload after viewport changed.                  [boolean] [default: false]\n      --stateChangeDelay           Delay time [msec] after changing element's state.               [number] [default: 0]\n      --listDevices                List available device descriptors.                         [boolean] [default: false]\n  -C, --chromiumChannel            Channel to search local Chromium. One of \"puppeteer\", \"canary\", \"stable\", \"*\"\n                                                                                                 [string] [default: \"*\"]\n      --chromiumPath               Executable Chromium path.                                      [string] [default: \"\"]\n      --puppeteerLaunchConfig      JSON string of launch config for Puppeteer.\n               [string] [default: \"{ \"args\": [\"--no-sandbox\", \"--disable-setuid-sandbox\", \"--disable-dev-shm-usage\"] }\"]\n\nExamples:\n  storycap http://localhost:9009\n  storycap http://localhost:9009 -V 1024x768 -V 320x568\n  storycap http://localhost:9009 -i \"some-kind/a-story\"\n  storycap http://example.com/your-storybook -e \"**/default\" -V iPad\n  storycap --serverCmd \"start-storybook -p 3000\" http://localhost:3000\n\n```\n\n\u003c!-- endinject --\u003e\n\n## Multiple PNGs from 1 story\n\nBy default, storycap generates 1 screenshot image from 1 story. Use `variants` if you want multiple PNGs(e.g. viewports, element's states variation, etc...) for 1 story.\n\n### Basic usage\n\nFor example:\n\n```js\nimport React from 'react';\nimport MyButton from './MyButton';\n\nexport default {\n  title: 'MyButton',\n  component: MyButton,\n};\n\nexport const Normal = {\n  parameters: {\n    screenshot: {\n      variants: {\n        hovered: {\n          hover: 'button.my-button',\n        },\n      },\n    },\n  },\n};\n```\n\nThe above configuration generates 2 PNGs:\n\n- `MyButton/normal.png`\n- `MyButton/normal_hovered.png`\n\nThe variant key, `hovered` in the above example, is used as suffix of the generated PNG file name. And the almost all `ScreenshotOptions` fields are available as fields of variant value.\n\n**Note:** `variants` itself and `viewports` are prohibited as variant's field.\n\n### Variants composition\n\nYou can composite multiple variants via `extends` field.\n\n```js\nexport const Normal = {\n  parameters: {\n    screenshot: {\n      variants: {\n        small: {\n          viewport: 'iPhone 5',\n        },\n        hovered: {\n          extends: 'small',\n          hover: 'button.my-button',\n        },\n      },\n    },\n  },\n};\n```\n\nThe above example generates the following:\n\n- `MyButton/normal.png` (default\n- `MyButton/normal_small.png` (derived from the `small` variant\n- `MyButton/normal_hovered.png` (derived from the `hovered` variant\n- `MyButton/normal_small_hovered.png` (derived from the `hovered` and `small` variant\n\n\u003e [!NOTE]\n\u003e You can extend some viewports with keys of `viewports` option because the `viewports` field is expanded to variants internally.\n\n### Parallelisation across multiple computers\n\nTo process more stories in parallel across multiple computers, the `shard` argument can be used.\n\nThe `shard` argument is a string of the format: `\u003cshardNumber\u003e/\u003ctotalShards\u003e`. `\u003cshardNumber\u003e` is a number between 1 and `\u003ctotalShards\u003e`, inclusive. `\u003ctotalShards\u003e` is the total number of computers running the execution.\n\nFor example, a run with `--shard 1/1` would be considered the default behaviour on a single computer. Two computers each running `--shard 1/2` and `--shard 2/2` respectively would split the stories across two computers.\n\nStories are distributed across shards in a round robin fashion when ordered by their ID. If a series of stories 'close together' are slower to screenshot than others, they should be distributed evenly.\n\n## Tips\n\n### Run with Docker\n\nUse [regviz/node-xcb](https://cloud.docker.com/u/regviz/repository/docker/regviz/node-xcb).\n\nOr create your Docker base image such as:\n\n```Dockerfile\nFROM node:18\n\nRUN apt-get update -y \\\n    \u0026\u0026 apt-get install -yq \\\n    ca-certificates \\\n    fonts-liberation \\\n    git \\\n    libayatana-appindicator3-1 \\\n    libasound2 \\\n    libatk-bridge2.0-0 \\\n    libatk1.0-0 \\\n    libc6 \\\n    libcairo2 \\\n    libcups2 \\\n    libdbus-1-3 \\\n    libexpat1 \\\n    libfontconfig1 \\\n    libgbm1 \\\n    libgcc1 \\\n    libglib2.0-0 \\\n    libgtk-3-0 \\\n    libnspr4 \\\n    libnss3 \\\n    libpango-1.0-0 \\\n    libpangocairo-1.0-0 \\\n    libstdc++6 \\\n    libx11-6 \\\n    libx11-xcb1 \\\n    libxcb1 \\\n    libxcomposite1 \\\n    libxcursor1 \\\n    libxdamage1 \\\n    libxext6 \\\n    libxfixes3 \\\n    libxi6 \\\n    libxrandr2 \\\n    libxrender1 \\\n    libxss1 \\\n    libxtst6 \\\n    lsb-release \\\n    wget \\\n    xdg-utils\n```\n\n### Full control the screenshot timing\n\nSometimes you may want to full-manage the timing of performing screenshot.\nUse the `waitFor` option if you think so. This parameter accepts function returning `Promise` or name of function should points a global function to return `Promise`.\n\n#### Example 1\n\nFor example, you can wait for specific HTML elements appearance with `screen` function provided `@storybook/test` package. It's useful when the elements are rendered lazy.\n\n```js\n/* MyComponent.stories.js */\n\nimport { screen } from '@storybook/test';\n\nexport const MyStory = {\n  screenshot: {\n    waitFor: async () =\u003e {\n      await screen.findByRole('link');\n    },\n  },\n};\n```\n\n#### Example 2\n\nAnother example, the following setting tells storycap to wait for resolving of `fontLoading`:\n\n```html\n\u003c!-- ./storybook/preview-head.html --\u003e\n\u003clink rel=\"preload\" href=\"/some-heavy-asset.woff\" as=\"font\" onload=\"this.setAttribute('loaded', 'loaded')\" /\u003e\n\u003cscript\u003e\n  function fontLoading() {\n    const loaded = () =\u003e !!document.querySelector('link[rel=\"preload\"][loaded=\"loaded\"]');\n    if (loaded()) return Promise.resolve();\n    return new Promise((resolve, reject) =\u003e {\n      const id = setInterval(() =\u003e {\n        if (!loaded()) return;\n        clearInterval(id);\n        resolve();\n      }, 50);\n    });\n  }\n\u003c/script\u003e\n```\n\n```js\n/* .storybook/preview.js */\n\nimport { withScreenshot } from 'storycap';\n\nexport const decorators = [withScreenshot];\n\nexport const parameters = {\n  screenshot: {\n    waitFor: 'fontLoading',\n  },\n};\n```\n\n## Chromium version\n\nSince v3.0.0, Storycap does not use Puppeteer directly. Instead, Storycap searches Chromium binary in the following order:\n\n1. Installed Puppeteer package (if you installed explicitly)\n1. Canary Chrome installed locally\n1. Stable Chrome installed locally\n\nYou can change search channel with `--chromiumChannel` option or set executable Chromium file path with `--chromiumPath` option.\n\n## Storybook compatibility\n\n### Storybook versions\n\nStorycap is tested with the followings versions:\n\n- Simple mode:\n  - [x] Storybook v7.x\n  - [x] Storybook v8.x\n- Managed mode:\n  - [x] Storybook v7.x\n  - [x] Storybook v8.x\n\nSee also packages in `examples` directory.\n\n### UI frameworks\n\nStorycap (with both simple and managed mode) is agnostic for specific UI frameworks(e.g. React, Angular, Vue.js, etc...). So you can use it with Storybook with your own favorite framework :smile: .\n\n## How it works\n\nStorycap accesses the launched page using [Puppeteer][puppeteer].\n\n## Contributing\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n## License\n\n[MIT © reg-viz](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freg-viz%2Fstorycap","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Freg-viz%2Fstorycap","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Freg-viz%2Fstorycap/lists"}