Giter Club home page Giter Club logo

storycap's Introduction

Storycap

DEMO

npm

A Storybook Addon, Save the screenshot image of your stories ๐Ÿ“ท via Puppeteer.

Storycap crawls your Storybook and takes screenshot images. It is primarily responsible for image generation necessary for Visual Testing such as reg-suit.

Features

  • ๐Ÿ“ท Take screenshots of each stories via Puppeteer.
  • โšก Extremely fast.
  • ๐Ÿ“ฆ Zero configuration.
  • ๐Ÿš€ Provide flexible screenshot shooting options.
  • ๐ŸŽ‰ Independent of any UI framework(React, Angular, Vue, etc...)

Install

$ npm install storycap

Or

$ npm install storycap puppeteer

Installing puppeteer is optional. See Chromium version to get more detail.

Getting Started

Storycap runs with 2 modes. One is "simple" and another is "managed".

With the simple mode, you don't need to configure your Storybook. All you need is give Storybook's URL, such as:

$ npx storycap http://localhost:9001

You can launch your server via --serverCmd option.

$ storycap --serverCmd "start-storybook -p 9001" http://localhost:9001

Of course, you can use pre-built Storybook:

$ build-storybook -o dist-storybook
$ storycap --serverCmd "npx http-server dist-storybook -p 9001" http://localhost:9001

Also, Storycap can crawls built and hosted Storybook pages:

$ storycap https://next--storybookjs.netlify.app/vue-kitchen-sink/

Managed mode

Setup Storybook

If you want to control how stories are captured (timing or size or etc...), use managed mode.

First, add storycap to your Storybook config file:

/* .storybook/main.js */

module.exports = {
  stories: ['../src/**/*.stories.@(js|mdx)'],
  addons: [
    '@storybook/addon-actions',
    '@storybook/addon-links',
    'storycap', // <-- Add storycap
  ],
};

Next, use withScreenshot decorator to tell how Storycap captures your stories.

/* .storybook/preview.js */

import { withScreenshot } from 'storycap';

export const decorators = [
  withScreenshot, // Registration the decorator is required
];

export const parameters = {
  // Global parameter is optional.
  screenshot: {
    // Put global screenshot parameters(e.g. viewport)
  },
};

Note

You can set configuration of screenshot with addParameters and screenshot key.

Setup your stories(optional)

And you can overwrite the global screenshot options in specific stories file via parameters.

import React from 'react';
import MyComponent from './MyComponent';

export default {
  title: 'MyComponent',
  component: MyComponent,
  parameters: {
    screenshot: {
      delay: 200,
    },
  },
};

export const Normal = {};

export const Small = {
  args: {
    text: 'small',
  },
  parameters: {
    screenshot: {
      viewport: 'iPhone 5',
    },
  },
};

Run storycap Command

$ npx start-storybook -p 9009
$ npx storycap http://localhost:9009

Or you can exec with one-liner via --serverCmd option:

$ npx storycap http://localhost:9009 --serverCmd "start-storybook -p 9009"

API

withScreenshot

A Storybook decorator to notify Storycap to captures stories.

type ScreenshotOptions

ScreenshotOptions object is available as the value of the key screenshot of addParameters argument or withScreenshot argument.

interface ScreenshotOptions {
  delay?: number;                           // default 0 msec
  waitAssets?: boolean;                     // default true
  waitFor?: string | () => Promise<void>;   // default ""
  fullPage?: boolean;                       // default true
  hover?: string;                           // default ""
  focus?: string;                           // default ""
  click?: string;                           // default ""
  skip?: boolean;                           // default false
  viewport?: Viewport;
  viewports?: string[] | { [variantName]: Viewport };
  variants?: Variants;
  waitImages?: boolean;                     // default true
  omitBackground?: boolean;                 // default false
  captureBeyondViewport?: boolean;          // default true
  clip?: { x: number; y: number; width: number; height: number } | null; // default null
}
  • delay: Waiting time [msec] before capturing.
  • waitAssets: If set true, Storycap waits until all resources requested by the story, such as <img> or CSS background images, are finished.
  • 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.
  • fullPage: If set true, Storycap captures the entire page of stories.
  • focus: If set a valid CSS selector string, Storycap captures after focusing the element matched by the selector.
  • hover: If set a valid CSS selector string, Storycap captures after hovering the element matched by the selector.
  • click: If set a valid CSS selector string, Storycap captures after clicking the element matched by the selector.
  • skip: If set true, Storycap cancels capturing corresponding stories.
  • viewport, viewports: See type Viewport section below.
  • variants: See type Variants section below.
  • waitImages: Deprecated. Use waitAssets. If set true, Storycap waits until <img> in the story are loaded.
  • 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.
  • captureBeyondViewport: If set true, Storycap captures screenshot beyond the viewport. See also Puppeteer API docs.
  • clip: If set, Storycap captures only the portion of the screen bounded by x/y/width/height.

type Variants

Variants is used to generate multiple PNGs from 1 story.

type Variants = {
  [variantName: string]: {
    extends?: string | string[]; // default: ""
    delay?: number;
    waitAssets?: boolean;
    waitFor?: string | () => Promise<void>;
    fullPage?: boolean;
    hover?: string;
    focus?: string;
    click?: string;
    skip?: boolean;
    viewport?: Viewport;
    waitImages?: boolean;
    omitBackground?: boolean;
    captureBeyondViewport?: boolean;
    clip?: { x: number; y: number; width: number; height: number } | null;
  };
};
  • 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}.

type Viewport

Viewport is compatible for Puppeteer viewport interface.

type Viewport =
  | string
  | {
      width: number; // default: 800
      height: number; // default: 600
      deviceScaleFactor: ?number; // default: 1,
      isMobile?: boolean; // default: false,
      hasTouch?: boolean; // default: false,
      isLandscape?: boolean; // default: false,
    };

Note

You should choose a valid device name if set string.

Viewport values are available in viewports field such as:

addParameters({
  screenshot: {
    viewports: {
      large: {
        width: 1024,
        height: 768,
      },
      small: {
        width: 375,
        height: 668,
      },
      xsmall: {
        width: 320,
        height: 568,
      },
    },
  },
});

function isScreenshot

function isScreenshot(): boolean;

Returns whether current process runs in Storycap browser. It's useful to change your stories' behavior only in Storycap (e.g. disable JavaScript animation).

Command Line Options

usage: storycap [options] storybook_url

Options:
      --help                       Show help                                                                   [boolean]
      --version                    Show version number                                                         [boolean]
  -o, --outDir                     Output directory.                               [string] [default: "__screenshots__"]
  -p, --parallel                   Number of browsers to screenshot.                               [number] [default: 4]
  -f, --flat                       Flatten output filename.                                   [boolean] [default: false]
  -i, --include                    Including stories name rule.                                    [array] [default: []]
  -e, --exclude                    Excluding stories name rule.                                    [array] [default: []]
      --delay                      Waiting time [msec] before screenshot for each story.           [number] [default: 0]
  -V, --viewport                   Viewport.                                              [array] [default: ["800x600"]]
      --disableCssAnimation        Disable CSS animation and transition.                       [boolean] [default: true]
      --disableWaitAssets          Disable waiting for requested assets                       [boolean] [default: false]
      --trace                      Emit Chromium trace files per screenshot.                  [boolean] [default: false]
      --silent                                                                                [boolean] [default: false]
      --verbose                                                                               [boolean] [default: false]
      --forwardConsoleLogs         Forward in-page console logs to the user's console.        [boolean] [default: false]
      --serverCmd                  Command line to launch Storybook server.                       [string] [default: ""]
      --serverTimeout              Timeout [msec] for starting Storybook server.               [number] [default: 60000]
      --shard                      The sharding options for this run. In the format <shardNumber>/<totalShards>.
                                   <shardNumber> is a number between 1 and <totalShards>. <totalShards> is the total
                                   number of computers working.                                [string] [default: "1/1"]
      --captureTimeout             Timeout [msec] for capture a story.                          [number] [default: 5000]
      --captureMaxRetryCount       Number of count to retry to capture.                            [number] [default: 3]
      --metricsWatchRetryCount     Number of count to retry until browser metrics stable.       [number] [default: 1000]
      --viewportDelay              Delay time [msec] between changing viewport and capturing.    [number] [default: 300]
      --reloadAfterChangeViewport  Whether to reload after viewport changed.                  [boolean] [default: false]
      --stateChangeDelay           Delay time [msec] after changing element's state.               [number] [default: 0]
      --listDevices                List available device descriptors.                         [boolean] [default: false]
  -C, --chromiumChannel            Channel to search local Chromium. One of "puppeteer", "canary", "stable", "*"
                                                                                                 [string] [default: "*"]
      --chromiumPath               Executable Chromium path.                                      [string] [default: ""]
      --puppeteerLaunchConfig      JSON string of launch config for Puppeteer.
               [string] [default: "{ "args": ["--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage"] }"]

Examples:
  storycap http://localhost:9009
  storycap http://localhost:9009 -V 1024x768 -V 320x568
  storycap http://localhost:9009 -i "some-kind/a-story"
  storycap http://example.com/your-storybook -e "**/default" -V iPad
  storycap --serverCmd "start-storybook -p 3000" http://localhost:3000

Multiple PNGs from 1 story

By 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.

Basic usage

For example:

import React from 'react';
import MyButton from './MyButton';

export default {
  title: 'MyButton',
  component: MyButton,
};

export const Normal = {
  parameters: {
    screenshot: {
      variants: {
        hovered: {
          hover: 'button.my-button',
        },
      },
    },
  },
};

The above configuration generates 2 PNGs:

  • MyButton/normal.png
  • MyButton/normal_hovered.png

The 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.

Note: variants itself and viewports are prohibited as variant's field.

Variants composition

You can composite multiple variants via extends field.

export const Normal = {
  parameters: {
    screenshot: {
      variants: {
        small: {
          viewport: 'iPhone 5',
        },
        hovered: {
          extends: 'small',
          hover: 'button.my-button',
        },
      },
    },
  },
};

The above example generates the following:

  • MyButton/normal.png (default
  • MyButton/normal_small.png (derived from the small variant
  • MyButton/normal_hovered.png (derived from the hovered variant
  • MyButton/normal_small_hovered.png (derived from the hovered and small variant

Note

You can extend some viewports with keys of viewports option because the viewports field is expanded to variants internally.

Parallelisation across multiple computers

To process more stories in parallel across multiple computers, the shard argument can be used.

The shard argument is a string of the format: <shardNumber>/<totalShards>. <shardNumber> is a number between 1 and <totalShards>, inclusive. <totalShards> is the total number of computers running the execution.

For 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.

Stories 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.

Tips

Run with Docker

Use regviz/node-xcb.

Or create your Docker base image such as:

FROM node:18

RUN apt-get update -y \
    && apt-get install -yq \
    ca-certificates \
    fonts-liberation \
    git \
    libayatana-appindicator3-1 \
    libasound2 \
    libatk-bridge2.0-0 \
    libatk1.0-0 \
    libc6 \
    libcairo2 \
    libcups2 \
    libdbus-1-3 \
    libexpat1 \
    libfontconfig1 \
    libgbm1 \
    libgcc1 \
    libglib2.0-0 \
    libgtk-3-0 \
    libnspr4 \
    libnss3 \
    libpango-1.0-0 \
    libpangocairo-1.0-0 \
    libstdc++6 \
    libx11-6 \
    libx11-xcb1 \
    libxcb1 \
    libxcomposite1 \
    libxcursor1 \
    libxdamage1 \
    libxext6 \
    libxfixes3 \
    libxi6 \
    libxrandr2 \
    libxrender1 \
    libxss1 \
    libxtst6 \
    lsb-release \
    wget \
    xdg-utils

Full control the screenshot timing

Sometimes you may want to full-manage the timing of performing screenshot. Use the waitFor option if you think so. This string parameter should points a global function to return Promise.

For example, the following setting tells storycap to wait for resolving of fontLoading:

<!-- ./storybook/preview-head.html -->
<link rel="preload" href="/some-heavy-asset.woff" as="font" onload="this.setAttribute('loaded', 'loaded')" />
<script>
  function fontLoading() {
    const loaded = () => !!document.querySelector('link[rel="preload"][loaded="loaded"]');
    if (loaded()) return Promise.resolve();
    return new Promise((resolve, reject) => {
      const id = setInterval(() => {
        if (!loaded()) return;
        clearInterval(id);
        resolve();
      }, 50);
    });
  }
</script>
/* .storybook/preview.js */

import { withScreenshot } from 'storycap';

export const decorators = [withScreenshot];

export const parameters = {
  screenshot: {
    waitFor: 'fontLoading',
  },
};

Chromium version

Since v3.0.0, Storycap does not use Puppeteer directly. Instead, Storycap searches Chromium binary in the following order:

  1. Installed Puppeteer package (if you installed explicitly)
  2. Canary Chrome installed locally
  3. Stable Chrome installed locally

You can change search channel with --chromiumChannel option or set executable Chromium file path with --chromiumPath option.

Storybook compatibility

Storybook versions

Storycap is tested with the followings versions:

  • Simple mode:
    • Storybook v7.x
    • Storybook v8.x
  • Managed mode:
    • Storybook v7.x
    • Storybook v8.x

See also packages in examples directory.

UI frameworks

Storycap (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 ๐Ÿ˜„ .

How it works

Storycap accesses the launched page using Puppeteer.

Contributing

See CONTRIBUTING.md.

License

MIT ยฉ reg-viz

storycap's People

Contributors

3846masa avatar aaschmid avatar alexeybondarenko avatar avaly avatar coderkevin avatar dargmuesli avatar e-jigsaw avatar heavenshell avatar indigolain avatar inokawa avatar jamie5 avatar jtbandes avatar ka2jun8 avatar kogai avatar kokosabu avatar marcobiedermann avatar meghna avatar mh4gf avatar mike-dax avatar mugi-uno avatar nicbillen avatar no-yan avatar nodaguti avatar penicillin0 avatar quramy avatar renovate-bot avatar renovate[bot] avatar thekashey avatar wadackel avatar wreulicke avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

storycap's Issues

Parasite artifacts on screenshots

  1. Storybook stories panel is partially visible (left bottom corner, a few pixels). Iframe itself has background:white, but "story container" is 10% transparent

  2. Resize element from addon pannel is visible thought iframe due to z-index

Solution for both cases - remove all additional panels using get props.

ERROR Error: Failed to launch chrome!

Hi. I'm attempting to run this addon on CircleCI v2 and encountered with the following error:
(To full log, visit https://circleci.com/gh/Quramy/angular-sss-demo/2)

yarn screenshot
yarn run v1.3.2
$ storybook-chrome-screenshot -p 6007 --debug


 LAUNCH  Launching storybook server...
 DEBUG  [NODE] Inject files,  []
 DEBUG  [NODE] Filter of kind and story, (kind = undefined, story = undefined)

 ERROR  Error: Failed to launch chrome!
[0104/031044.165391:ERROR:zygote_host_impl_linux.cc(88)] Running as root without --no-sandbox is not supported. See https://crbug.com/638180.


TROUBLESHOOTING: https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md


error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Exited with code 1

I think this workarround will fix it.

Would you review if I send this as PR?

Support selenium driver

Visual regression testing can be also useful for cross-browser testing. It can be possible with this addon if add support of the selenium webdrivers.
headless_chrome is good for the local testing (because it's simple to install and use), but it can be nice to be ready capture the screenshot in the different browsers. And selenium can help to do that.

Output any kind of debug info to the output

Did the installation path, added one withScreenshot() for testing to a relatively big size (I think) storybook.

After the
$ ./node_modules/.bin/storybook-chrome-screenshot -p 9001 -c .storybook

I get

 LAUNCH  Launching storybook server ...
 PREPARE  Fetching the target components ...

And nothing else.

localhost:9001 shows me the storybook normal build version, no errors, shows element with withScreenshot as well.

Would want to debug it, but it seems futile without any kind of logs, the suggestions or something from the tool would be helpful

Sass/SCSS styles don't seem to be supported

Hi,

I get this error when running the plugin:

ERROR in ./src/views/Home.vue?vue&type=style&index=0&lang=scss& (./node_modules/vue-loader/lib??vue-loader-options!./src/views/Home.vue?vue&type=style&index=0&lang=scss&) 27:0
Module parse failed: Unexpected character '@' (27:0)
You may need an appropriate loader to handle this file type.
|
|
> @import '@/style/onboarding.scss';
|
| ion-button {
 @ ./src/views/Home.vue?vue&type=style&index=0&lang=scss& 1:0-128 1:144-147 1:149-274 1:149-274
 @ ./src/views/Home.vue
 @ ./src/stories/index.stories.js
 @ ./src/stories sync .stories.js$
 @ ./config/storybook/config.js
 @ multi ./node_modules/@storybook/core/dist/server/config/polyfills.js ./node_modules/@storybook/core/dist/server/config/globals.js ./config/storybook/config.js ./node_modules/webpack-hot-middleware/client.js?reload=true

It could be related to both issues I have just posted #86 #87 . Even if the ./src/views/Home.vue?vue&type=style&index=0&lang=scss path precises which "lang" to use, it seems the correct loader is not used just after.

I digged into it but I don't know enough about all this stuff to fix it ๐Ÿ˜ข

Thank you,

Distribute sourcemaps with npm package?

Would it be possible to distribute sourcemaps with the npm package?

I'm hitting a bug whereby after running screenshot I see:

LAUNCH Launching storybook server... โ ด

And it hangs forever. Trying to debug in vscode debugger but its pretty painful to step through the compiled output without sourcemaps ...

Fix compatibility with Storybook 4

Seems to be broken without any error message, which is quite confusing. See even this automatically generated PR: #91

Fetching the target components ...

Too long with no output (exceeded 10m0s)

Feature Request: Pass boolean to the storyFn when being run by puppeteer

Disclaimer: Love this project, combined with reg-suit, it's amazing.

It would be very nice if during the execution of storybook-chrome-screenshot, the withScreenshot decorator would pass a boolean to the story, which can be used to determine properties to pass to your components. An example would be if I were using the material UI tooltip in some of my UI, and I wanted it to be open in the screenshot pass, but not when I normally use storybook.

storiesOf('SomeComponent', module)
  .add('this one is showing tooltips open', withScreenshot()(props =>(
    <Tooltip open={props.isScreenshot} title="Can't do that now">
      <Button>Disabled</Button>
    </Tooltip>
  ))

Image screenshots slightly different from original

Not a major issue, but sometimes when the images are taken, they are slightly different from the original, so it fails the reg-viz diff test. I also tried using another diff tool and it appears to be the screenshots themselves that are different, not the reg-viz tester misreading them.

I am curious if anyone else experiences this issue, and how you have been dealing with it. Sometimes reg-viz comments on the PR that all our components have changed, when in reality they are just slightly off.

I can set the threshold on reg-viz, but I am curious why it is happening in the first place. Perhaps this is just specific to the docker image I am using with chromium?

Pictures below:
screen shot 2017-12-11 at 11 44 47 am
screen shot 2017-12-11 at 11 27 59 am
screen shot 2017-12-11 at 11 43 09 am

Testing & Switch to TypeScript

Creation of Test is necessary for features addition and stability.
Also, I'd like to define the type using TypeScript to provide a better interface.

Broken support for addDecorator with Storybook/angular 4.1.13

I got the following error when attempting to use the feature documented here:

Unexpected value 'undefined' declared by the module 'DynamicModule'

    syntaxError@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:8514:21
./node_modules/@angular/compiler/bundles/compiler.umd.js/</</CompileMetadataResolver</CompileMetadataResolver.prototype.getNgModuleMetadata/<@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:24754:44
./node_modules/@angular/compiler/bundles/compiler.umd.js/</</CompileMetadataResolver</CompileMetadataResolver.prototype.getNgModuleMetadata@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:24752:58
./node_modules/@angular/compiler/bundles/compiler.umd.js/</</JitCompiler</JitCompiler.prototype._loadModules@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:32078:32
./node_modules/@angular/compiler/bundles/compiler.umd.js/</</JitCompiler</JitCompiler.prototype._compileModuleAndComponents@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:32059:35
./node_modules/@angular/compiler/bundles/compiler.umd.js/</</JitCompiler</JitCompiler.prototype.compileModuleAsync@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:32019:36
./node_modules/@angular/platform-browser-dynamic/bundles/platform-browser-dynamic.umd.js/</CompilerImpl</CompilerImpl.prototype.compileModuleAsync@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:65300:20
compileNgModuleFactory__PRE_R3__@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:51093:16
./node_modules/@angular/core/bundles/core.umd.js/</</PlatformRef</PlatformRef.prototype.bootstrapModule@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:51276:20
draw@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:68924:23
./node_modules/@storybook/angular/dist/client/preview/angular/helpers.js/exports.renderNgApp@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:68936:5
render@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:69031:4
renderMain@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:70322:5
renderUI@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:70342:9
dispatch@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:130457:7
_renderMain@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:69936:7
render@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:69880:11
ConfigApi/<@http://localhost:9001/vendors~main.c219e1dfc72fe6123fb9.bundle.js:69909:9
./.storybook/config.ts/<@http://localhost:9001/main.c219e1dfc72fe6123fb9.bundle.js:45:1
./.storybook/config.ts@http://localhost:9001/main.c219e1dfc72fe6123fb9.bundle.js:12:29
__webpack_require__@http://localhost:9001/runtime~main.c219e1dfc72fe6123fb9.bundle.js:782:12
fn@http://localhost:9001/runtime~main.c219e1dfc72fe6123fb9.bundle.js:150:20
0@http://localhost:9001/main.c219e1dfc72fe6123fb9.bundle.js:256:1
__webpack_require__@http://localhost:9001/runtime~main.c219e1dfc72fe6123fb9.bundle.js:782:12
checkDeferredModules@http://localhost:9001/runtime~main.c219e1dfc72fe6123fb9.bundle.js:46:23
webpackJsonpCallback@http://localhost:9001/runtime~main.c219e1dfc72fe6123fb9.bundle.js:33:19
@http://localhost:9001/main.c219e1dfc72fe6123fb9.bundle.js:1:57

When applying withScreenshot to every single story, everything works as expected.

storybook-chrome-screenshot does not support "undefined".

I get this error when I follow README to use it.

FAIL  tests/storyshot/storyshot.spec.js
  โ— Test suite failed to run

    storybook-chrome-screenshot does not support "undefined".

      at Object.<anonymous> (node_modules/storybook-chrome-screenshot/lib/withScreenshot.js:20:15)
      at Object.<anonymous> (node_modules/storybook-chrome-screenshot/lib/index.js:6:24)
      at Object.<anonymous> (tests/storyshot/storyshot.spec.js:20:34)

Checking withScreenshot.js:20:15, it needs window.STORYBOOK_ENV. How to set it or pass it?

[Feature] Improve performance by parallelizing component screenshot

The tool is just awesome. I'm testing it on a large repo. Currently I've setup only 35 stories to be screenshot-ed( I have several hundred per total).

For 35 stories it takes around 150 seconds to execute.

I wonder if we can parallelize taking screenshots(maybe in multiple child process) and/or finding the components to screenshot so the tests can run more efficient even if the number of stories is increased.

Please let me know your thoughts on this.

Module parse failed for spread operator and jsx

Hi,
upon running plugin, I am getting errors pointing to spread operators and jsx

node: v8.9.0
npm: 5.5.1

Also, custom babel and webpack configs are used.
Any hints on why this could be happening?

ERROR
ERROR in ./node_modules/storybook-chrome-screenshot/src/with-screenshot.js
Module parse failed: Unexpected token (14:6)
You may need an appropriate loader to handle this file type.
|   const wrapperWithContext = (context) => {
|     const props = {
|       ...mergeScreenshotOptions(options),
|       channel,
|       context,
 @ ./node_modules/storybook-chrome-screenshot/src/index.js 2:0-62
 @ ./.storybook/config.js
 @ multi ./node_modules/@storybook/react/dist/server/config/polyfills.js ./node_modules/@storybook/react/dist/server/config/globals.js (webpack)-hot-middleware/client.js?reload=true ./.storybook/config.js

ERROR in ./node_modules/storybook-chrome-screenshot/src/screenshot-options.js
Module parse failed: Unexpected token (19:6)
You may need an appropriate loader to handle this file type.
|
|     viewport = options.viewport.map(vp => ({
|       ...baseViewport,
|       ...vp,
|     }));
 @ ./node_modules/storybook-chrome-screenshot/src/index.js 1:0-82
 @ ./.storybook/config.js
 @ multi ./node_modules/@storybook/react/dist/server/config/polyfills.js ./node_modules/@storybook/react/dist/server/config/globals.js (webpack)-hot-middleware/client.js?reload=true ./.storybook/config.js

ERROR in ./node_modules/storybook-chrome-screenshot/src/init-screenshot.js
Module parse failed: Unexpected token (8:10)
You may need an appropriate loader to handle this file type.
|   const channel = addons.getChannel();
|
|   return (<InitScreenshotWrapper channel={channel} context={ctx}>
|     { storyFn() }
|   </InitScreenshotWrapper>);
 @ ./node_modules/storybook-chrome-screenshot/src/index.js 3:0-62
 @ ./.storybook/config.js
 @ multi ./node_modules/@storybook/react/dist/server/config/polyfills.js ./node_modules/@storybook/react/dist/server/config/globals.js (webpack)-hot-middleware/client.js?reload=true ./.storybook/config.js
.babelrc
{
  "presets": [[
    "env", { "targets": { "browsers": ["last 2 versions"] } }],
    "react",
    "stage-2"
  ],
  "plugins": ["transform-decorators-legacy", "transform-class-properties"]
}
webpack.config.js
const path = require('path');

module.exports = {
  module: {
    rules: [
      {
        test: /\.(css|scss)$/,
        use: [
          "style-loader",
          {
            loader: "css-loader",
            options: { sourceMap: true }
          },
          {
            loader: "sass-loader",
            options: { sourceMap: true }
          }
        ]
      },
      {
        test: /\.svg$/,
        use: {
          loader: "svg-url-loader",
          options: { limit: 8192 }
        }
      },
      {
        test: /\.(png|jpg|gif)$/,
        use: {
          loader: "url-loader",
          options: { limit: 8192 }
        }
      },
      {
        test: /\.(js|jsx)$/,
        exclude: /node_modules/,
        use: {
          loader: "babel-loader",
          options: {
            presets: ["env", "react", "stage-2"],
            plugins: ["transform-decorators-legacy", "transform-class-properties"]
          }
        }
      },
    ],
  },
  resolve: {
    extensions: ['.js', '.jsx'],
  },
};

[Feature proposal] Provide a way of taking multiple screenshots of the same component(eg on various devices)

storybook-chrome-screenshot allows in a very nice manner to specificy the device and viewports for the screenshots. Currently, there isn't a way to screenshot a component on multiple devices and resolutions without creating additional stories for each device and resolution(which doesn't help since most probably the component should be tested in React storybook full screen mode and using Chrome device emulator).

One limitation that prevents us from using it to screenshot components across all devices that we support is that there isn't really a way for a story to be screen-shoted across multiple configurations. Eg, given a story like

 .add('Basic Sticky Button', withScreenshot()(() => (
        <StickyButton label={label} disabled={false} onClick={action(`click`)} />)))

In the example above StickyButton is a responsive component. We need to support and have different layouts on Mobile, Table and Desktop devices in normal and landscape mode.

It would be really cool to have the same story screenshoted in all 3 configurations by specifying them as an array of options:

setScreenshotOptions {
  delay: 0,               
  viewport: [
{                           // Desktop view configuration
    width: 1024,
    height: 768,
    deviceScaleFactor: 1,
    isMobile: false,
    hasTouch: false,
    isLandscape: false,
  },
 {              
    width: 300,  // Mobile configuration
    height: 420,
    deviceScaleFactor: 1,
    isMobile: true,
    hasTouch: true,
    isLandscape: false,
  },
  {              
    width: 768,  // Tablet configuration
    height: 800,
    deviceScaleFactor: 1,
    isMobile: true,
    hasTouch: true,
    isLandscape: false,
  }
],
}

In the example above for each component 3 screenshots would be taken according to the specified viewport settings.

[Feature proposal] Ability to disable animations via options

Animations tend to make screenshot testing difficult due to the fact that they make the screenshots non-deteministic.
Some existing tools have the ability to turn off the animations exactly for this reason( eg https://github.com/Huddle/PhantomCSS/blob/master/phantomcss.js ).
Maybe we could have an option that can control this behaviour. In PhantomCSS the process of disabling the animations is something similar to:

function disableAnimations() {
			var jQuery = window.jQuery;
			if ( jQuery ) {
				jQuery.fx.off = true;
			}

			var css = document.createElement( "style" );
			css.type = "text/css";
			css.innerHTML = "* { -webkit-transition: none !important; transition: none !important; -webkit-animation: none !important; animation: none !important; }";
			document.body.appendChild( css );
		}

		if ( document.readyState !== "loading" ) {
			disableAnimations();
		} else {
			window.addEventListener( 'load', disableAnimations, false );
		}

Screenshots are incorrect/duplicate stories

I've setup storybook-chrome-screenshot for my UI library and it loads all the stories and takes shots of each story, but the screenshot output contains many duplicate stories. Of my 80 components I'm getting ~5 stories of output x80 screenshots. So buttons, headings and icons are all of the same single button story for example.

I've tried increasing the delay over 5000 but still have the same problem. What could be happening to cause the screenshot output to be the incorrect story?

import { setOptions } from '@storybook/addon-options';
import infoAddon from '@storybook/addon-info';
import { initScreenshot, withScreenshot } from 'storybook-chrome-screenshot';

require(`../dist/styles.js`);
require(`../src/oui/oui.scss`);

setOptions({
  name: 'OUI Storybook',
  url: 'https://github.com/optimizely/oui',
  goFullScreen: true,
  showLeftPanel: false,
  showDownPanel: false,
  showSearchBox: false,
  downPanelInRight: false,
  sortStoriesByKind: false,
});

storybook.setAddon(infoAddon);
addDecorator(withScreenshot({
  delay: 5000,
  viewport: [
    {
      width: 1280,
      height: 800,
      deviceScaleFactor: 2,
    },
  ],
}));

function loadStories() {
  require('../src/components/Attention/Attention.story.js');
  require('../src/components/Badge/Badge.story.js');
  require('../src/components/Button/Button.story.js');
  require('../src/components/Dropdown/Dropdown.story.js');
  require('../src/components/TextField/TextField.story.js');
  require('../src/components/Token/Token.story.js');
}

storybook.configure(loadStories, module);

[Feature proposal] Provide a way to filter stories by regex

Sometimes you want to run just a subset of the tests in order to debug an issue or just because you would like to run the tests very quickly locally for the area affected by changes.
In a similar manner, Storyshots( https://github.com/storybooks/storybook/tree/master/addons/storyshots ) permits in using a regex to filter out tests by either name or story( eg storyNameRegex: /buttons/).

It would be really cool if the same could be done in storybook-chrome-screenshot by using a CLI param.

parallel issue

Check out this line:

https://github.com/tsuyoshiwada/storybook-chrome-screenshot/blob/d1762dc870d61653ded6e5d81aaaafc2ee6f4618/src/cli.js#L183

Let's say we have 3 stories, but parallel is set to 1, which means pages is initialized to an array of one page. Chunking 3 stories (e.g.[{story1}, {story2}, {story3}]) will yield: [[{story1}],[{story2}], [{story3}]]. When the second one is processed, i will equal 1. When that happens we'll be indirecting a page that doesn't exist. Womp womp.

You should rewrite your batching logic, but in the meantime, this will help avoid crashes:

const page = pages[i % pages.length]; 

Provide a way to decorate all stories in a file or all available stories

I'm trying to use the addDecorator API to decorate all stories in a certain file, something along these lines:

storiesOf('sticky-button', module)
    .addDecorator(withReadme(README))
    .addDecorator(checkA11y)
    .addDecorator(withScreenshot())
    .add('Basic Sticky Button', () => (
        <StickyButton label={label} disabled={false} onClick={action(`click`)} />))
    .add('Basic Disabled Sticky Button', () => (
        <StickyButton label={label} disabled={true} onClick={action(`click`)} />))

I would like to take screenshots of both of the stories without having to decorate them manually.
The Storybook build succeeds however when i try to take screenshots it gets stuck indefinetely at ''Fetching the target components" .

Also, it would be really usefull if one could take screenshots of all stories available without the need to decorate each and every one with withScreenshot.

Error: Browser does not exist

Hi, I installed storybook-chrome-screenshot 1.3.0 and face this error when running npm run screenshot on Windows 10:

UnhandledPromiseRejectionWarning: Error: Browser does not exist
    at Browser.<anonymous> (C:\app\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:70:27)
    at step (C:\app\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:32:23)
    at Object.next (C:\app\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:13:53)
    at C:\app\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:7:71
    at new Promise (<anonymous>)
    at __awaiter (C:\app\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:3:12)
    at Browser.close (C:\app\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:67:16)
    at C:\app\node_modules\storybook-chrome-screenshot\lib\core\app\App.js:217:100
    at Array.map (<anonymous>)
    at App.<anonymous> (C:\app\node_modules\storybook-chrome-screenshot\lib\core\app\App.js:217:72)

There are no warnings/errors when installing puppeteer.

Thanks!

Failing runnning with Docker

I had storybook-chrome-screenshot with reg-viz working on CircleCi without any problems. After updating from 0.10.0 to 1.0.0 it stopped working while trying to mount storybook. I coulnd't figured out what's causing the problem.

It breaks running on Docker (either locally or in Circle). I am using the recommended setup to run Puppeteer on Docker https://github.com/GoogleChrome/puppeteer/blob/master/docs/troubleshooting.md#running-puppeteer-in-docker

Here are the logs when I run it with --debug

yarn run v1.3.2
warning ../../../package.json: No license field
$ storybook-chrome-screenshot -p 9009 -c .storybook --debug
 DEBUG  [CLI Options] { port: '9009',
  host: 'localhost',
  staticDir: undefined,
  configDir: '.storybook',
  outputDir: '__screenshots__',
  filterKind: undefined,
  filterStory: undefined,
  browserTimeout: 30000,
  parallel: 4,
  injectFiles: [],
  silent: false,
  debug: true,
  ciMode: true,
  cwd: '/home/pptruser/website',
  cmd: '/home/pptruser/website/node_modules/.bin/start-storybook' }

 LAUNCH  Launching storybook server ...

 DEBUG  [Command Arguments] -p 9009 -c .storybook -h localhost
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
Exited with code 1

This is running using:

  • Node 8.9.4
  • yarn 1.3.2
  • npm 5.6.0

Get unknown option `--ci' when running 'storybook-chrome-screenshot'

AddOn: v1.4.0 
Node: v8.14.0
React:16.2.0
Storybook/react: 3.4.0
yarn screenshot
yarn run v1.12.3
$ storybook-chrome-screenshot -p 9001 --debug 
 DEBUG  [CLI Options] { port: '9001',
  host: 'localhost',
  staticDir: undefined,
  configDir: '.storybook',
  outputDir: '__screenshots__',
  filterKind: undefined,
  filterStory: undefined,
  browserTimeout: 30000,
  parallel: 4,
  injectFiles: [],
  puppeteerLaunchConfig: '{"args":["--no-sandbox","--disable-setuid-sandbox", "--disable-dev-shm-usage"]}',
  silent: false,
  debug: true,
  ciMode: false,
  cwd: '/Users/seanshadmand/repos/konch/konch-www',
  cmd: '/Users/seanshadmand/repos/konch/konch-www/node_modules/.bin/start-storybook' }


 LAUNCH  Launching storybook server ...

 DEBUG  [Command Arguments] --ci -p 9001 -c .storybook -h localhost
 DEBUG  [STDERR] error: unknown option `--ci'

not working with storybook 5.2

dependencies: {
"@storybook/addon-a11y": "^5.2.0-beta.40",
"@storybook/addon-actions": "^5.2.0-beta.40",
"@storybook/addon-info": "^5.2.0-beta.40",
"@storybook/addon-knobs": "^5.2.0-beta.40",
"@storybook/addon-options": "^5.2.0-beta.40",
"@storybook/addon-viewport": "^5.2.0-beta.40",
"@storybook/cli": "^5.2.0-beta.40",
"@storybook/react": "^5.2.0-beta.40"
}

when run "storybook-chrome-screenshot -p 9001 -c ./.storybook"

it throws error in console as below

ERROR Storybook does not exists. First, let's setup a Storybook!
See: https://storybook.js.org/basics/quick-start-guide/
LAUNCH Launching storybook server ...

ERROR An unexpected error occurred, Please make sure message below
spawn C:\Atlas\Features\scope-asb-ui$(npm bin)\start-storybook ENOENT

(node:5308) UnhandledPromiseRejectionWarning: Error: Browser does not exist
at Browser. (C:\Atlas\Features\scope-asb-ui\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:70:27)
at step (C:\Atlas\Features\scope-asb-ui\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:32:23)
at Object.next (C:\Atlas\Features\scope-asb-ui\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:13:53)
at C:\Atlas\Features\scope-asb-ui\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:7:71
at new Promise ()
at __awaiter (C:\Atlas\Features\scope-asb-ui\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:3:12)
at Browser.close (C:\Atlas\Features\scope-asb-ui\node_modules\storybook-chrome-screenshot\lib\core\app\Browser.js:67:16)
at C:\Atlas\Features\scope-asb-ui\node_modules\storybook-chrome-screenshot\lib\core\app\App.js:217:100
at Array.map ()
at App. (C:\Atlas\Features\scope-asb-ui\node_modules\storybook-chrome-screenshot\lib\core\app\App.js:217:72)
(node:5308) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 6)
(node:5308) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

Duplicate screenshots with 1.1.0-alpha.1

I have a project where I added storybook-chrome-screenshot for all its stories, the final count is
Screenshots: 147
Problem is I get a lot of duplicates, I basically get the same 4 screenshots all over. I tried to test a different parallel option but I was never able to perform a full run.

The project can be pulled at
https://github.com/gesposito/react-bootstrap-italia

I added storybook-chrome-screenshot's decorator to the stories with this commit
gesposito/react-bootstrap-italia@6e22ede

Is there a more efficient way of configuring such projects?

Pug templates seem to not be supported

Hi!

When I run the plugin on my Vue.js components, it fails when parsing the template:

ERROR in ./src/views/Home.vue?vue&type=template&id=fae5bece&lang=pug& (./node_modules/vue-loader/lib/loaders/templateLoader.js??vue-loader-options!./node_modules/vue-loader/lib??vue-loader-options!./src/views/Home.vue?vue&type=template&id=fae5bece&lang=pug&)
Module Error (from ./node_modules/vue-loader/lib/loaders/templateLoader.js):
(Emitted value instead of an instance of Error)
  Error compiling template:

  .onboarding.background-blue
    .wrapper(padding='')
         // Removed the rest to keep the sample clean

  - Component template requires a root element, rather than just text.

 @ ./src/views/Home.vue?vue&type=template&id=fae5bece&lang=pug& 1:0-211 1:0-211
 @ ./src/views/Home.vue
 @ ./src/stories/index.stories.js
 @ ./src/stories sync .stories.js$
 @ ./config/storybook/config.js
 @ multi ./node_modules/@storybook/core/dist/server/config/polyfills.js ./node_modules/@storybook/core/dist/server/config/globals.js ./config/storybook/config.js ./node_modules/webpack-hot-middleware/client.js?reload=true

Do you know what's missing to take in account Pug loader? I don't understand why it doesn't work because when I run Storybook on its own it works well...

Thank you,

Viewport resolution in bottom right corner "burned into" every screenshot

After upgrading Storybook from 3.3 to 3.4, some screenshots contains viewport resolution in bottom right corner. Like this:

form-field-component

This seems strange to me, because this resolution is displayed in browser when I resize window, and it is displayed for 1 second (this component is responsible for that https://github.com/storybooks/storybook/blob/release/3.4/lib/ui/src/modules/ui/components/layout/dimensions.js ). But it is not part of <iframe>, it is displayed in browser above this iframe.

First it seems strange to me, that storybook-chrome-screenshot is not screenshooting content of the <iframe>.

And second, as you probably guessed, I want to get rid of this viewport resolution from my screenshots. Setting delay to any value (I tried even extreme ones like 20 seconds) doesn't help. Resolution is still "burned into" my screenshot, although resolution should be hidden after 1 second. (see https://github.com/storybooks/storybook/blob/release/3.4/lib/ui/src/modules/ui/components/layout/dimensions.js#L26 )

And the funny thing is, resolution is burned only into stories that are longer (a.k.a. higher) - stories where is a need to scroll a page to see everything.

Screenshotting components hangs when the story uses both decorator and function wrap syntax

By mistake I've left the following piece of code:

storiesOf('shipping-screen', module)
    .addDecorator(checkA11y)
    .addDecorator(withReadme(README))
    .addDecorator(withCode(style, 'sass'))
    .addDecorator(withScreenshot())
    .add(
        'Shipping Screen',
        withScreenshot()(() =>
            Utils.RenderWithTheme(<ShippingScreen {...props} />)
        )
    )

Please notice that I've setup both the decorator and also had the story wrapped in withScreenshot.
When running the tests it hang up at 'Capturing components screenshots ' phase. It was easily fixed by removing the withScreenshot function wrapping.

I would have expected an error message to be displayed pointed to the cause of the hangup.

Cannot screenshot multiple stories with addDecorator

First of all, need to say this is a great add-on. Thanks!

Issue:
When looping over stories, addDecorator seems to get stuck after a single story. It will only generate the images for the one story.

As mentioned by @gcazaciuc in #3, it would be very handy not to have to decorate every story manually.

This does not work:

storiesOf('corp-site/atoms/Button', module)
  .addDecorator(withScreenshot(getScreenshotOptions))
  .add('with Black and Active', () => (
    <Button color="black" isActive>Button</Button>
  ))
  .add('with White and inactive, also inaccessible', () => (
    <Button color="white">Button</Button>
  ))
  .add('with Purple and inactive', () => (
    <Button color="purple">Button</Button>
  ))

It will only take screenshots of the first story: with Black and Active.

It would be great to be able to use the addDecorator globally within the config to take screenshots of all stories that are being imported and looped over.

Hangs on Prepare Forever

I haven't been able to get this to work - it just sits on Prepare endlessly.

"storybook-chrome-screenshot": "^1.0.1",

.storybook/config.js

import { setScreenshotOptions } from 'storybook-chrome-screenshot';

setScreenshotOptions({
  viewport: {
    width: 768,
    height: 400,
    deviceScaleFactor: 2,
  },
});

/stories.js

// @flow

import React from 'react';
import { MemoryRouter } from 'react-router-dom';
import { storiesOf } from '@storybook/react';
import { initScreenshot, withScreenshot } from 'storybook-chrome-screenshot';

import Nav from './index';

const location = { pathname: '/clients', hash: '', search: '' };

storiesOf('Nav', module)
  .addDecorator(story => <MemoryRouter initialEntries={['/']}>{story()}</MemoryRouter>)
  .add('primary', () => <Nav location={location} />)
  .addDecorator(initScreenshot())
  .addDecorator(withScreenshot());

[Feature proposal] Integrate with the knobs add-on

The Knobs addon for Storybook allows you to change the state of a component in the UI, rather than having to build a separate story for each state.

Are there any plans of integrating this library with knobs, so that one could screenshot multiple knob states? Off the top of my head, something like:

import { text } from '@storybook/addon-knobs/react'

storiesOf('submit button', module)
  .addDecorator(withScreenshot({
    knobs: {
      'Button label': [
        'Submit',
        'Go',
        'Search',
      ],
    },
  }))
  .add('submit button', () => (
    <input type="submit" value={text('Button label', 'Submit')} />
  ))

Thoughts? Without this feature, I have to convert all my knobs to discrete stories so that they can be screenshotted individually.

Setting $(npm bin) on Windows

I get this error on my Windows development machine.

  cmd: 'C:\\frontend\\$(npm bin)\\start-storybook' }

 ERROR  Storybook does not exists. First, let's setup a Storybook!
        See: https://storybook.js.org/basics/quick-start-guide/

 LAUNCH  Launching storybook server ...

 DEBUG  [Command Arguments] -p 6006 -c .storybook -h localhost
error Command failed with exit code 1.

Seems that this is environment specific issue, here is similar problem
Semantic-Org/Semantic-UI-React#315

Recommendation is to use something like this
https://github.com/kentcdodds/cross-env

SCS v2

Overview

We are planning to release v2 for the purpose of improving performance, function, and much more ๐Ÿ’ช
At the same time we aim to change the code base to maintainable.

Philosophy

Performance and stability are most important characteristics. SCS was designed in order to VRT(Visual Regression Testing) using Storybook. In other words, SCS runs on not users' local machine but CI environments. So SCS must complete capture tasks in realistic execution time and must not hangup in CI jobs.

Basic implementation plan

We'll integrate zisui to SCS. zisui is a tool very similar to SCS. It crawls Storybook and capture screenshot for each story using Puppeteer.

Features comparison

Feature SCS v1 zisui SCS v2 Priority
Storybook v4 compatibility โœ… โœ… ๐Ÿ‘ BETTER
Storybook v5 compatibility โœ… ๐Ÿ‘ MUST
Decorator for React โœ… โœ… ๐Ÿ‘ MUST
Decorator for Angular โœ… ๐Ÿ‘ MUST
Decorator for Vue โœ… ๐Ÿ‘ MUST
addParameters signature support ๐Ÿ‘ BETTER
Parallel Puppeteer bootstrapping โœ… โœ… ๐Ÿ‘ MUST
Puppeteer launch configuration โœ… ๐Ÿ‘ MUST
Serve Storybook within CLI โœ… โœ… ๐Ÿ‘ MUST
Use built or served Storybook โœ… ๐Ÿ‘ MUST
Zero configuration (Simple mode) โœ… ๐Ÿ‘ MUST
Filter story name to capture โœ… โœ… ๐Ÿ‘ MUST
Timeout handling to connect Storybook server โœ… โœ… ๐Ÿ‘ MUST
Timeout handling to capture each story โœ… ๐Ÿ‘ MUST
Automatic disable CSS animation โœ… ๐Ÿ‘ MUST
Exporting SCS specific environment info ๐Ÿ˜ BETTER
Inject arbitrary JavaScript files to Chrome โœ… ๐Ÿค” LOW
Multiple viewports for each story โœ… ๐Ÿ‘ MUST
Full page support โœ… ๐Ÿ‘ MUST
Namespace and overriding default namespace โœ… ๐Ÿค” LOW
Force override element's state (e.g. :focus ๐Ÿ‘ BETTER
addon-knobs support โœ… ๐Ÿšซ LOW
Wait for capture till <img> in the story loaded โœ… โœ… ๐Ÿ‘ MUST
Wait for capture during provided times elapsed โœ… โœ… ๐Ÿ‘ MUST
Wait for capture till user specified func called โœ… โœ… ๐Ÿ‘ MUST
Wait for capture till browser metrics stabled โœ… ๐Ÿ‘ MUST

Features details

Force override element's state (e.g. :focus

Example

storiesOf('Button', module)
  .addParameters({
    screenshot: {
      focus: 'button', // It should be a valid CSS selecotr in this story
      // or
      // hover: 'a[data-button-link-target]',
    },
  })
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)

How to implement

We can use Puppeteer API (see https://github.com/GoogleChrome/puppeteer/blob/master/docs/api.md#pagefocusselector) .

Additional parameters

The following features require function to generate from a single story definition to multiple screenshot captures.

  • Namespace and overriding default namespace
  • Multiple viewports
  • Force override element's state

We introduce variants section to do them. variants tells the additional configuration to SCS and defines suffix of the PNG file name corresponding.

type ScreenshotOptions = {
  delay?: number;
  viewport?: Viewport;
  // ... other options
  variants?: {
    [key: string]: ScreenshotOptions;
  };
};

Example 1

/* .storybook/config.js */
addParameters({
  screenshot: {
    viewport: {
      width: 1200,
      height: 800,
    },
  },
});

/* components/button.stories.jsx */

storiesOf('Button', module)
  .addParameters({
    screenshot: {
      variants: {
        focused: { // the key is used for suffix of the PNG file name
          focus: 'button',
        },
      },
    },
  })
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)

The above code generates:

  • Button/with text.png (derived from the default)
  • Button/with text_focused.png (derived from the focused variant)

Example 2

Next, more complex example:

If there is the following global configuration:

/* .storybook/config.js */
addParameters({
  screenshot: {
    viewport: {
      width: 1200,
      height: 800,
    },
    variants: {
      sp: { viewport: 'iPhone6' },
      sp_xs: { viewport: 'iPhone5SE' },
    },
  },
});

This generates 3 PNGs for each story.

And more configuration can be added by the story file:

/* components/button.stories.jsx */

storiesOf('Button', module)
  .addParameters({
    screenshot: {
      variants: {
        focused: {
          focus: 'button',
          followWith: ['sp'], // It indicates to add this config to both the default and "sp" configuration. 
        },
      },
    },
  })
  .add('with text', () => <Button onClick={action('clicked')}>Hello Button</Button>)

Finally the above story code generates:

  • Button/with text.png (derived from the default)
  • Button/with text_sp.png (derived from the global sp variant)
  • Button/with text_sp_xs.png (derived from the global sp_xs variant)
  • Button/with text_focused.png (derived from the story's focused variant)
  • Button/with text_sp_focused.png (derived from the combination sp and focused variants, which followWith directive creates)

How to implement

We can't know the screenshot option when SCS visits each story for the first time. withScreenshot decorator generates 1 or more screenshot options, so if the first time emitted option has additional configurations we should generate tasks to capture for each additional conf ( I think JavaScript generator is suitable to do this ) .


It's scheduled to be updated from time to time.

Not working with stories that use template-syntax

It seems that the storybook-stories only work when using with components. When using templates-syntax (see below example) in a story it stops generating screenshots at this story without any error message.

storiesOf('Some story...', module)
  .add(
    'default',
    () => ({
      template: `<some-template-code></some-template-code>`,
      ...
    })
  )

The "--browser-timeout"-option seems to be only for opening storybook, but another timeout for the screenshot-inits would also be useful to let the screenshoting fail if it can not be done in a defined period of time. Then at least the remaining screenshots can be generated and the problem can be easier located.

Is there a way generating screenshots from template-stories?

TypeScript decorators don't seem to be supported

Hi,

I got this error when testing the plugin:

ERROR in ./src/views/Home.vue?vue&type=script&lang=ts& (./node_modules/vue-loader/lib??vue-loader-options!./src/views/Home.vue?vue&type=script&lang=ts&) 18:0
Module parse failed: Unexpected character '@' (18:0)
You may need an appropriate loader to handle this file type.
| import { Component, Vue } from 'vue-property-decorator';
|
> @Component({})
| export default class Home extends Vue {
|     // public async create() {
 @ ./src/views/Home.vue?vue&type=script&lang=ts& 1:0-119 1:135-138 1:140-256 1:140-256
 @ ./src/views/Home.vue
 @ ./src/stories/index.stories.js
 @ ./src/stories sync .stories.js$
 @ ./config/storybook/config.js
 @ multi ./node_modules/@storybook/core/dist/server/config/polyfills.js ./node_modules/@storybook/core/dist/server/config/globals.js ./config/storybook/config.js ./node_modules/webpack-hot-middleware/client.js?reload=true

Any idea how to fix it?

Thank you,

Angular - stuck running "npm run screenshot"

Hi,

I'm facing an issue where the screenshots are not being taken.

What I did:

  • Set up an angular project (7.2.0)
  • Created a simple component

TS:


import { Component } from '@angular/core';

@Component({
  selector: 'app-test',
  templateUrl: './test.component.html',
  styleUrls: ['./test.component.scss']
})
export class TestComponent {
  title = 'test-testing';
}

HTML:

<div>
  It is just a test component
</div>
  • Wrote the following stories/index.stories.js:
import { storiesOf } from '@storybook/angular';
import { withScreenshot } from 'storybook-chrome-screenshot';

import { TestComponent } from '../src/app/test-component/test.component';

storiesOf('Welcome', module).add('to Storybook', withScreenshot()(() => ({
  component: TestComponent,
  props: {},
})));

  • Added the following .storybook/addons.js:

import 'storybook-chrome-screenshot/register';

  • Wrote the following .storybook/config.js

import { configure, addDecorator } from '@storybook/angular';
import { initScreenshot } from 'storybook-chrome-screenshot';

function loadStories() {
  require('../stories/index.stories.js');
}

addDecorator(initScreenshot());
configure(loadStories, module);

I followed the instructions on the homepage, and it is stuck for some reason. I'm expecting it to start taking screenshots, yet nothing happens. The browser isn't opened, and no screenshots are taken.

Here is the debug log:


 DEBUG  [BROWSER] info: %cDownload the React DevTools for a better development experience: https://fb.me/react-devtools font-weight:bold
 DEBUG  [BROWSER] debug: message arrived at manager setStories JSHandle@object
 DEBUG  [BROWSER] error: JSHandle@error
 DEBUG  [BROWSER] debug: message arrived at manager storyMissing
 DEBUG  [BROWSER] debug: message arrived at manager storiesConfigured
 DEBUG  [BROWSER] log: [HMR] connected
 DEBUG  [BROWSER] debug: message arrived at preview setCurrentStory JSHandle@object
 DEBUG  [BROWSER] debug: message arrived at manager storyRendered welcome--to-storybook
 DEBUG  [BROWSER] debug: message arrived at manager chrome-screenshot/component-init JSHandle@object
 DEBUG  [BROWSER] debug: message arrived at manager chrome-screenshot/component-mount JSHandle@object
 DEBUG  [BROWSER] debug: message arrived at manager chrome-screenshot/component-finish-mount JSHandle@object
 DEBUG  [BROWSER] debug: message arrived at manager chrome-screenshot/component-ready JSHandle@object
 DEBUG  [STDOUT] No type errors found
 DEBUG  [STDOUT] Version: typescript 3.2.4
Time: 12909ms

My package.json (starter angular project):


{
  "name": "snapshot-testing",
  "version": "0.0.0",
  "scripts": {
    "ng": "ng",
    "start": "ng serve",
    "build": "ng build",
    "test": "ng test",
    "lint": "ng lint",
    "e2e": "ng e2e",
    "storybook": "start-storybook -p 9000",
    "screenshot": "storybook-chrome-screenshot -p 9001 -c .storybook --debug --parallel 1"
  },
  "private": true,
  "dependencies": {
    "@angular/animations": "~7.2.0",
    "@angular/common": "~7.2.0",
    "@angular/compiler": "~7.2.0",
    "@angular/core": "~7.2.0",
    "@angular/forms": "~7.2.0",
    "@angular/platform-browser": "~7.2.0",
    "@angular/platform-browser-dynamic": "~7.2.0",
    "@angular/router": "~7.2.0",
    "core-js": "^2.5.4",
    "rxjs": "~6.3.3",
    "tslib": "^1.9.0",
    "zone.js": "~0.8.26"
  },
  "devDependencies": {
    "@angular-devkit/build-angular": "~0.13.0",
    "@angular/cli": "~7.3.6",
    "@angular/compiler-cli": "~7.2.0",
    "@angular/language-service": "~7.2.0",
    "@babel/core": "^7.3.4",
    "@storybook/addon-actions": "^5.0.3",
    "@storybook/addon-links": "^5.0.3",
    "@storybook/addon-notes": "^5.0.3",
    "@storybook/addons": "^5.0.3",
    "@storybook/angular": "^5.0.3",
    "@storybook/cli": "^5.0.3",
    "@types/jasmine": "~2.8.8",
    "@types/jasminewd2": "~2.0.3",
    "@types/node": "~8.9.4",
    "babel-loader": "^8.0.5",
    "codelyzer": "~4.5.0",
    "jasmine-core": "~2.99.1",
    "jasmine-spec-reporter": "~4.2.1",
    "karma": "~4.0.0",
    "karma-chrome-launcher": "~2.2.0",
    "karma-coverage-istanbul-reporter": "~2.0.1",
    "karma-jasmine": "~1.1.2",
    "karma-jasmine-html-reporter": "^0.2.2",
    "protractor": "~5.4.0",
    "react": "^16.8.4",
    "react-dom": "^16.8.4",
    "storybook-chrome-screenshot": "^1.4.0",
    "ts-node": "~7.0.0",
    "tslint": "~5.11.0",
    "typescript": "~3.2.2"
  }
}

Error: Navigation Timeout Exceeded: 30000ms exceeded

I'm trying to ran the tool against a storybook setup. Admitely the server takes a while to start up because it is ran in a monorepo of multiple components, compiles things with typescript, uses babel and also runs TSLint.

When the initialization gets to 'Fetching the target components...' it fails with the above error of 'Error: Navigation Timeout Exceeded: 30000ms exceeded'.

Any suggestions on how to debug this ? Or maybe the timout should be specified as a CLI param ?

Run cli from node JS

@tsuyoshiwada I'm working on the project, that has several themes and I have to capture some of these themes. I've written the addon that adds variants to the Storybook Pannel and they can be configurable in storybook config.

Now, I'm looking for the way to run capture phase multiple times, with changes in the storybook instance. Looks like this can be done if I would be able to run addon cli from NODE JS script, that will allow me to run capturing manually.

What do you think about this?

Custom screenshot file name

I would like to have customizable story2filename, to land target images, for example, next to .story file, not all in one directory.

By default aplha version gets installed

When I run yarn add storybook-chrome-screenshot or npm install storybook-chrome-screenshot by default new alpha version (1.1.0-alpha.1) gets installed which doesnt work correctly. Falling back to 1.0.1 solved my problems.

I think you should tag dev releases appropriately.

Anyways, great work :)

Check for config.ts as well as config.js

Storybook supports using a .ts file extension for the config file (see storybookjs/storybook#3913).
However, when using an extension other than .js, storybook-chrome-screenshot throws

ERROR  ".storybook/config.js" does not exists.

It still continues and generates the screenshots correctly. This check should be either removed or updated to check for .ts as well.

`storybook-chrome-screenshot does not support "undefined".` thrown when scs used with Storyshots

Hi. I'm attempting to use both SCS and Storyshots add-ons in my production project. storybook-chrome-screenshot exits successfully but jest was failed. The full jest log is the following:

$ NODE_ENV=test jest storyshots.js
 => Loading custom .babelrc
 FAIL  frontend/storyshots.js
  โ— Test suite failed to run

    storybook-chrome-screenshot does not support "undefined".

      at Object.<anonymous> (node_modules/storybook-chrome-screenshot/lib/withScreenshot.js:20:15)
      at Object.<anonymous> (node_modules/storybook-chrome-screenshot/lib/index.js:6:24)
      at newRequire (node_modules/@storybook/addon-storyshots/dist/require_context.js:76:12)
      at evalmachine.<anonymous>:224:34
      at runWithRequireContext (node_modules/@storybook/addon-storyshots/dist/require_context.js:102:3)
      at testStorySnapshots (node_modules/@storybook/addon-storyshots/dist/index.js:120:35)
      at Object.<anonymous> (frontend/storyshots.js:27:31)

Are there somebody whose some workarounds?

  • .storybook/config.js
import { configure, addDecorator } from '@storybook/react'
import bgs from '#/storybookDecorators/backgrounds'
import { initScreenshot, withScreenshot } from 'storybook-chrome-screenshot'

import 'babel-polyfill'

function loadStories() {
  const req = require.context('../src', true, /\.stories\.jsx$/)
  req.keys().forEach(filename => req(filename))
}

addDecorator(initScreenshot())
addDecorator(bgs())
addDecorator(withScreenshot())
configure(loadStories, module)
  • Entry point for Storyshots:
/* eslint-disable */
import initStoryshots from '@storybook/addon-storyshots'

// Workaround for https://github.com/facebook/react/issues/11565#issuecomment-368877149
const ReactDOM = require('react-dom')
const React = require('react')
ReactDOM.createPortal = node => React.createElement('portal-dummy', null, node)

initStoryshots()

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.