Storybook의 VRT(Visual Regression Testing)를 최소 단위로 테스트합니다.

개시하다


제목과 같이 Storybook을 사용하여 VRT(Visual Regression Testing)의 최소 구성을 시도했습니다.
※ 최소한의 구성으로 시도하기 때문에 아이템 도입은 좀 더 생각해봐야 합니다.

대상

  • VRT(Visual Regression Testing)에 관심 있음
  • Storybook을 사용하여 VRT 가져오기
  • VRT가 무엇인지 시험해 보고 싶습니다
  • 골대


    ↓ 보고서 출력을 시도한 상태
    image1

    환경 구축과 준비


    버전 및 환경
    - storybook/react: 6.3.12
    - storycap: 3.1.0
    - node: v12.22.0
    

    1. Docker 주위


    참조여기. Docker file 만들기잠시 후 사용하기 위해puppeteer 많이 넣었어요.
    FROM node:12.22.0-alpine
    
    RUN apk update && \
        apk add --no-cache \
          git \
          vim \
          chromium \
          nss \
          freetype \
          harfbuzz \
          ca-certificates \
          ttf-freefont \
    
    docker-compose.yml는 다음과 같다.
    version: '3.3'
    volumes:
      modules_data:
        driver: local
      next_data:
        driver: local
    
    services:
      vrt:
        build: .
        image: xxxx/vrt
        container_name: 'vrt'
        environment:
          - PUPPETEER_SKIP_CHROMIUM_DOWNLOAD=true
          - PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium-browser
        volumes:
          - .:/usr/src
          - modules_data:/usr/src/node_modules
          - next_data:/usr/src/.next
        command: ash -c "yarn install && yarn dev"
        ports:
          - '3000:3000'
          - '6006:6006'
        working_dir: /usr/src
    
    puppeteer의 설정용 환경 변수PUPPETEER_SKIP_CHROMIUM_DOWNLOADPUPPETEER_EXECUTABLE_PATH.
    실제 절차의 상세한 설명은 생략했지만npx create-next-app@latest --typescript 에서nextjs를 시작할 때까지 프로젝트를 만듭니다.
    (이번에는Next.js 프로젝트의 구상에 따라 실시되었는데storybook이 사용할 수 있다면 무엇이든 될 것 같다.)

    2. Storybook 설치에 필요한 패키지


    2-1. npxsb init로 설치


    $ npx sb init
    
    package.json에 추가된 스크립트와 패키지는 다음과 같다.
      "scripts": {
        "storybook": "start-storybook -p 6006",
        "build-storybook": "build-storybook"
      },
      "devDependencies": {
        "@babel/core": "^7.16.0",
        "@storybook/addon-actions": "^6.3.12",
        "@storybook/addon-essentials": "^6.3.12",
        "@storybook/addon-links": "^6.3.12",
        "@storybook/react": "^6.3.12",
        "babel-loader": "^8.2.3",
      }
    
    stories 목록 아래에 샘플을 제작했다.우선 이것stories의 샘플로 VRT를 시험해 보고 싶습니다.
    즉시 yarn storybook storybook을 시작합니다.
    시작 후 방문http://localhost:6006/하면 아래 화면을 표시할 수 있습니다.
    image2

    3. storycap 설치


    reg-viz/storycap: A Storybook Addon, Save the screenshot image of your stories via puppeteer. v3.0.0 별도 설치 시작Puppeteerpuppeteer도 같이 설치해요.참고 자료
    $ yarn add -D storycap puppeteer
    
    다음은 이번에 설치된 패키지의 버전입니다.
        "puppeteer": "^11.0.0",
        "storycap": "^3.1.0"
    
    package.jsonscript에서
    추가
    "storycap": "storycap --serverCmd \"yarn storybook\" http://localhost:6006 --serverTimeout 600000"
    
    , 시행해 보자.
    $ yarn storycap
    
    이 성공하면 이미지는 __screenshots__ 디렉토리에 저장됩니다.
    Storycap에는 simplemanaged 두 가지 모드가 있으며 위 모드simple를 그대로 유지하면
    이번에 세부적으로 제어하려면 managed 모드를 사용하는 것 같아요.

    4.reg-suit 설치


    reg-viz/reg-suit: Visual Regression Testing tool
    $ yarn add -D reg-suit
    
    초기 설정을 수행합니다.
    $ yarn reg-suit init --use-yarn
    
    image3
    ↑ 처음 필요한 Plugin 설치를 선택했지만 이번에는 시도였기 때문에 reg-publish-s3-plugin만 선택했습니다.
    뒤의 문제는 다음과 같이 대답한다.
    ? Working directory of reg-suit. .reg # デフォルトの.reg
    ? Append ".reg" entry to your .gitignore file. Yes
    ? Directory contains actual images. __screenshots__ # storycapの出力先の__screenshots__を指定
    ? Threshold, ranges from 0 to 1. Smaller value makes the comparison more sensitive. 0
    [reg-suit] info Set up reg-publish-s3-plugin:
    ? Create a new S3 bucket No # S3のBucketは別途用意するのでNoで名前も空にしました
    ? Existing bucket name 
    
    여기서 완성regconfig.json은 다음과 같다.
    {
      "core": {
        "workingDir": ".reg",
        "actualDir": "__screenshots__",
        "thresholdRate": 0,
        "addIgnore": true,
        "ximgdiff": {
          "invocationType": "client"
        }
      },
      "plugins": {
        "reg-publish-s3-plugin": {
          "bucketName": "$S3_BUCKET_NAME"
        }
      }
    }
    
    ※ 외부에서 설치해서 만든 통 이름을 지정$S3_BUCKET_NAME했습니다.
    package.jsonscript에서"regression": "reg-suit run"를 추가했다.

    5. S3에서 reg-suit용 물통 만들기


    자세한 내용은 차치하고 이번에 제작reg-suit-sample한 물통은 사용하겠습니다.
    또한reg-suit을 실행할 때 S3이 방문하는 계정을 지정하기 위해docker-composie를 지정합니다.yml에 접근 키 등을 추가했습니다.
        environment:
          AWS_DEFAULT_REGION: ap-northeast-1
          AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
          AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
          S3_BUCKET_NAME: ${S3_BUCKET_NAME}
    

    VRT 구현


    1. 빈 상태에서 S3 실행


    $ yarn storycap
    $ yarn regression
    
    실행이 완료되면 S3에 다음과 같은 내용이 업로드됩니다.
    ※ 나중에 나오는데, 이때key generator plugin가 지정되지 않아 snapshot_日時에서 제작됩니다.
    image4
    yarn regression에서 생성한 출력Report URL에 액세스한 후 다음 보고서를 출력했습니다.
    image5

    2. 차등 발생 후 다시 실시


    다시 실행하면 스냅샷을 따로 제작할 거예요.
    image6
    하지만 보고서를 보고도 차분이 검출되지 않았다.
    일지를 자세히 보다
    설정
    [reg-suit] info Skipped to detect the previous snapshot key because key generator plugin is not set up.
    [reg-suit] info Skipped to fetch the expected data because expected key is null.
    
    key generator plugin이 없기 때문에 비교 원본의 데이터를 찾지 못한 것 같습니다.
    다시 설치하고 싶습니다reg-simple-keygen-plugin.
    $ yarn add -D reg-simple-keygen-plugin
    
    regconfig.json에 ↓와 같이 첨가한다.
        "reg-simple-keygen-plugin": {
          "expectedKey": "hoge",
          "actualKey": "fuga"
        },
    
    이번에는 시도이기 때문에 키는 일회용으로 설정된 것이다.
    프로젝트에서 잘 사용할 때 reg-keygen-git-hash-plugin 또는
    CI에서 생성한hash값을 사용할 것 같습니다. ->여기.
    S3 데이터도 삭제되고 재설정된 상태에서 다시 실행됩니다.
    처음으로 액튜어만 있기 때문에 pluginsactualKey로 변경하여 실시한다.
    image7
    다음hogeactualKey로 돌려보내 차이를 준다.
    출력된 보고서에는 비교할 수 있는 다음과 같은 차이가 표시됩니다. sparkles:
    image1
    기쁘고 축하할 만하다:tada:
    ...종료가 아닌 fugastorycap 모드 사용
    나는 다시 한 번 더 시도하고 싶다.

    3. 새 구성 요소 및 작업 시 VRT 캡처


    구성 요소를 표시하고 레이아웃이 변형되었는지 테스트하는 경우에만 위와 같이 할 수 있습니다
    호버, 클릭, 조작이 있을 때 구성 요소가 반응하는 상황에서 테스트를 해보고 싶습니다.

    3-1. TogleButon 구성 요소 생성


    image8
    ↑ 이렇게 하면 hover에서 색깔이 변합니다. 클릭 후 ON/OFF를 전환하여 누구나 얻을 수 있는 구성 요소를 만듭니다.
    디렉토리 구성은 다음과 같습니다.
    ├── pages
    ├── components
    │   └── ToggleButton
    │       ├── index.css
    │       ├── index.stories.tsx
    │       └── index.tsx
    
    import React, { useState } from 'react';
    import './index.css';
    
    export type ToggleButtonProps = {};
    
    const ToggleButton: React.VFC = () => {
      const [on, setOn] = useState(false);
      return (
        <div style={{ padding: '0.5rem' }}>
          <button className="toggle-button" onClick={() => setOn(!on)}>
            {on ? 'ON' : 'OFF'}
          </button>
        </div>
      );
    };
    
    export default ToggleButton;
    
    .toggle-button {
      background-color: aliceblue;
    }
    
    .toggle-button:hover {
      background-color: bisque;
    }
    

    3-2. Storybook 설정

    managed.storybook/main.js는 각각 다음과 같이 수정했다.
    main.js
    module.exports = {
      "stories": [
        "../components/**/*.stories.@(js|jsx|ts|tsx)"
      ],
      "addons": [
        "@storybook/addon-links",
        "@storybook/addon-essentials",
        "storycap"
      ]
    }
    
    preview.js
    import { withScreenshot } from 'storycap';
    
    export const decorators = [
      withScreenshot,
    ];
    
    export const parameters = {
      actions: { argTypesRegex: "^on[A-Z].*" },
      controls: {
        matchers: {
          color: /(background|color)$/i,
          date: /Date$/,
        },
      },
    }
    
    다음.storybook/preview.js은 다음과 같다.
    import { Meta, Story } from '@storybook/react';
    import React from 'react';
    import ToggleButton, { ToggleButtonProps } from '.';
    
    export default {
      title: 'components/ToggleButton',
      component: ToggleButton,
    } as Meta;
    
    const Template: Story<ToggleButtonProps> = (args) => <ToggleButton {...args} />;
    
    // Basic
    export const Basic: Story<ToggleButtonProps> = Template.bind({});
    Basic.parameters = {
      screenshot: {
        variants: {
          hovered: {
            hover: '.toggle-button',
          },
          clicked: {
            click: '.toggle-button',
          },
        },
      },
    };
    Basic.args = {};
    
    screenshot의variants는 여러 상태를 지정할 수 있기 때문에 이번components/ToggleButton/index.stories.tsxhover의 상태도 포착할 수 있다.
    이 상태에서 한 번에 S3에 업로드합니다.
    그런 다음 hover의 색상을 약간 변경하여 VRT를 실행해 보십시오.
    .toggle-button:hover {
      background-color: antiquewhite;
    }
    
    ↓ hover 시 미묘한 색상 차이도 잘 검출: sparkles:
    image9
    그나저나 click를 바꿔본 곳은 thresholdRate 이상이면 ↑의 차이를 감지할 수 없고 0.002사이가 검출된다.

    총결산


    개인이 가져오면 계속 검사할 수 있고 가져오는 것도 어렵지 않아요.
    나는 비용과 효과가 모두 매우 좋다고 생각한다:sparkles:
    이번에 시도한 부분은 아래 창고에 업로드되었습니다.
    Slowhand0309/vrt-nextjs-storybook-sample: VRT(Visual Regression Testing) project with Next.js, Storybook

    참조 링크

  • Visual Regression Testing 시작 – Tips 활용 – PSYENCE: MEDIA
  • React/Next.js에서 Storybook을 가져옵니다.life
  • storybook-chrome-screenshot, zii, Storycap 및 | by YES Kurami | Medium
  • Reg-suit+CircleaCI로 Visual Regression Test 환경 구축
  • 좋은 웹페이지 즐겨찾기