React+Cypress+StorybookでVisualRegressionTest

## はじめに
React＋CypressでVisualRegressionを実現するのにやったこと

## 環境構築
### react/storybook導入
```
npx create-react-app my-app
npx -p @storybook/cli sb init
```

### Cypress導入
```
yarn add cypress @testing-library/cypress -D

// 不要なテストを削除
rm -rf cypress/integration/examples
```

### Storybookのpreview-iframeを取得
Storybookのiframeを取得する

cypress/support/command.js

```
import '@testing-library/cypress/add-commands';

Cypress.Commands.add('getIframeBody', () => {
  cy.log('getIframeBody');

  return cy
      .get('#storybook-preview-iframe')
      .its('0.contentDocument.body').should('not.be.empty')
      .then((body) => cy.wrap(body))
});

```

### Cypress-VisualRegressionモジュールを追加
良さげだったのでこちらを利用させていただいた
https://www.npmjs.com/package/cypress-visual-regression

```
yarn add cypress-visual-regression -D
```

設定を追加する  
cypress/support/command.js

```
const compareSnapshotCommand = require('cypress-visual-regression/dist/command');
compareSnapshotCommand();
```
cypress/plugin/index.js

```
const getCompareSnapshotsPlugin = require('cypress-visual-regression/dist/plugin');

module.exports = (on) => {
  getCompareSnapshotsPlugin(on);
};

```

cypress.json

```
{
  "screenshotsFolder": "./cypress/snapshots/actual",
  "trashAssetsBeforeRuns": true
}

```


## テスト用コンポーネント準備
### Storyookのデフォルトコンポーネントを利用
 
src/stories/Button.js

```
export const Button = ({ primary, backgroundColor, size, label, ...props }) => {
  const mode = primary ? 'storybook-button--primary' : 'storybook-button--secondary';
  const [buttonLabel, setLabel] = React.useState(label);
  const onClick = () => { setLabel("hoge"); }

  return (
    <button
      type="button"
      className={['storybook-button', `storybook-button--${size}`, mode].join(' ')}
      style={backgroundColor && { backgroundColor }}
      data-testid="button"
      {...props}
      onClick={onClick}
    >
      {buttonLabel}
    </button>
  );
};
```

### buttonのテストを追加
cypress/integration/button_spec.js

```
describe('Button Component', () => {
  beforeEach(() => {
    cy.visit('http://localhost:6006/?path=/story/example-button--primary');
  });

  it('Button Click', () => {
    cy.getIframeBody().findByTestId('button').should('have.text', 'Button');
    cy.getIframeBody().findByTestId('button').click().should('have.text', 'hoge');
  });

  it('should display the Button correctly', () => {
    cy.getIframeBody().findByTestId('button').contains('Button');
    cy.compareSnapshot('button');
  });
});

```

### PackageにScriptを追加
package.json

```
"scripts" : {
    "cypress:base": "cypress run --env type=base --config screenshotsFolder=cypress/snapshots/base",
    "cypress:run": "cypress run --env type=actual",
    "cypress:open": "cypress open"
}
```

テストの実行

```
// Baseの作成
yarn cypress:base

// テストを実行
yarn cypress:run

```

## 結果
Base
![Button Component -- should display the Button correctly (failed).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255988/c6789db2-0abb-cf03-dbad-bd0d7207ef53.png)

Actual
![button-actual_hoge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255988/c6f49dff-2264-083f-d134-cc6f9600c618.png)

Diff
![button-diff_hoge.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/255988/bd7cbfb6-d91a-c49d-698a-758b4e65c791.png)

## TestをTypeScriptで実行する
ルートのtscofing.jsonに下記を追加

```
  "include": [
    "node_modules/cypress/types/*.ts",
    "cypress/*/*.ts"
  ]
```

## サンプルリポジトリ
[https://github.com/resqnet/cypress-storybook-visualregression:embed:cite]