resqnet's blog

技術的なことか、Amazonアソシエイト・プログラムの参加者です

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 f:id:resqnet:20200904111917p:plain Actual f:id:resqnet:20200904111913p:plain Diff f:id:resqnet:20200904111922p:plain

TestをTypeScriptで実行する

ルートのtscofing.jsonに下記を追加

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

サンプルリポジトリ

github.com