puppeteer를 사용한 E2E 테스트에 수동 캡처 포함

19224 단어 Intro.jspuppeteer

요약



Puppeteer + Intro.js = E2E + Manual Capture

동기 부여



puppeteer을 사용하면 Google Chrome의 헤드리스 모드를 제어하여 쉽게 화면 캡처를 수행 할 수 있습니다. 이 기능을 사용하면 reg-suit 등과 연계하여 E2E 테스트를 비교적 쉽게 실현할 수 있습니다.

그런데 E2E의 본래의 목적은 무엇입니까?
  • E2E 시나리오는 응용 프로그램 본체의 사양입니다
  • 그 확인을위한 동선은 실제 매뉴얼과 같습니다

  • 그리고 나는 생각한다.
    그 때문에 동시에 매뉴얼용 화면 캡쳐도 함께 하고 싶다는 것이 이번 이야기의 발단입니다.

    말하지 않는 것



    Puppeteer의 자세한 사용법이나 reg-suit에 관해서는 이 기사에서는 언급하지 않습니다.
    E2E를 어떻게 CI에서 수행하는지에 대해서도 마찬가지입니다.

    사용하는 도구




    puppeteer 은 GoogleChrome을 DevTool 프로토콜을 통해 제어하는 ​​고수준 API를 제공합니다.
    물어보고 말하면 node.js에서 Google Chrome을 쉽게 조작 할 수 있습니다.



    Intro.js 은 데모나 튜토리얼적인 것을 간단하게 만들 수 있는 라이브러리입니다.
    스포트라이트와 같은 느낌으로, 서비스의 사용법을 가르쳐 주는 기능을 제공해 줍니다.

    준비



    시나리오 만들기



    우선 puppeteer를 사용한 시나리오 테스트가 없다면 의미가 없습니다.
    이번에는 다음과 같은 흐름 시나리오를 만듭니다.
  • Google 검색보기
  • 검색 결과 목록에서 connpass 사이트를 탐색
  • Burikaigi2018 엔트리 사이트보기

  • capture.js
    // node capture.js で実行するとactual_images/以下にそれぞれのスナプショットが保存される。
    const puppeteer = require("puppeteer");
    
    (async () => {
      // ブラウザを起動
      // わかりやすいように、ヘッドレスモードをOFFにして、各アクションに300msec遅延させる
      const browser = await puppeteer.launch({
        headless: false,
        slowMo: 300,
      });
    
      // ブラウザの新規ウィンドウ作成
      const page = await browser.newPage();
      // ウィンドウサイズを調整
      await page.setViewport({ width: 1600, height: 1200 });
    
      // Googleへ遷移し、ネットワーク通信が終わるまで待つ
      const URL = "http://google.co.jp";
      await page.goto(URL, {waitUntil: "networkidle2"});
      await page.screenshot({path: "./actual_images/1.png"});
    
      // 入力フィールドに検索文字列を入力
      await page.type("#lst-ib", "burikaigi2018 connpass");
      await page.screenshot({path: "./actual_images/2.png"});
    
      // 検索実行し検索結果が表示されるまで待つ
      await page.click("input[type=submit]");
      await page.waitFor("#resultStats");
      await page.screenshot({path: "./actual_images/3.png"});
    
      // 検索結果のタイトル一覧を取得し、connpass.comを含むコンテンツへ遷移
      const aHandles = await page.$$("h3 > a");
      for (var i = 0; i < aHandles.length; i++) {
        const href = await page.evaluate((handle) => handle.href, aHandles[i]);
        if (href.includes("connpass.com")) {
          await page.screenshot({path: "./actual_images/4.png"});
          await aHandles[i].click();
          break;
        }
      }
    
      // ナビゲーションが完了するまで待つ
      await page.waitForNavigation({ waitUntil: "networkidle2" });
      await page.screenshot({path: "./actual_images/5.png"});
    
      // ブラウザを閉じる
      await browser.close();
    })();
    

    도움말용 이미지 얻기



    위의 시나리오에 도움말용 이미지 취득 처리를 통합해 나갑니다.
    도움말 생성 Intro.js를 동적으로 로드하는 메서드를 정의합니다.
    async function appendIntro(page, setup) {
      // Intro.jsをCDNから読み込み
      await page.evaluate(() => {
        var script = document.createElement('script');
        script.src = 'https://cdn.jsdelivr.net/npm/[email protected]/intro.min.js';
        document.body.appendChild(script);
    
        var link = document.createElement('link');
        link.rel = "stylesheet";
        link.href = "https://cdnjs.cloudflare.com/ajax/libs/intro.js/2.7.0/introjs.min.css"
        document.body.appendChild(link);
      });
    
      // IntroJSがアクセス可能になるまで待つ
      await page.waitForFunction("window.introJs");
    
     // 対象のセレクタにIntro.jsのデータ属性を設定する
      await setup(page);
    }
    

    상기 메소드를, 화면 천이시에 읽어들입니다 (일부 발췌).
    const URL = "http://google.co.jp";
    await page.goto(URL, {waitUntil: "networkidle2"});
    await appendIntro(page, setupGoogleTop);
    

    appendIntro에 정의 된 setupGoogleTop는 각 선택기에 Intro.js 설정을 통합하기위한 것입니다.data-step 는 튜토리얼의 순서를, data-intro 는 실제로 표시되는 툴팁을 나타냅니다.
    자세한 내용은 Intro.js의 API을 참조하십시오.
    function setupGoogleTop(page) {
      return page.evaluate(() => {
        const textInput = document.getElementById("lst-ib");
        textInput.setAttribute('data-step', 1);
        textInput.setAttribute('data-intro', '入力フィールドを選択し、burikaigi2018 connpassと入力してください');
    
        const submitButton = document.querySelector("input[name=btnK]");
        submitButton.setAttribute('data-step', 2);
        submitButton.setAttribute('data-intro', '検索ボタンを押します');
      });
    }
    

    이것으로 Intro.js를 실행하는 메커니즘의 준비가 완료됩니다.
    다음과 같이 메소드를 실행하여 화면 전환 시 튜토리얼을 시작합니다.
    // チュートリアル開始
    await page.evaluate(() => window._intro = introJs().start());
    // 次のヘルプを表示
    await page.evaluate(() => window._intro.nextStep());
    

    완성형은 Gist 에 Up 하고 있습니다.
    실제로 실행한 경우의 캡처는 다음과 같습니다.
  • Google 보기

  • 검색 조건 입력

  • 검색 버튼을 누릅니다.

  • 목록 표시

  • 링크 클릭


  • 결론



    E2E 테스트를 만들 때 열심히 취득한 셀렉터를, 그 이외에도 유효 활용하고 싶다고 생각해, 실천한 안의 하나입니다.
    이 기사에서는 매뉴얼을위한 캡처 였지만 puppeteer는 DevTool에도 액세스 할 수 있습니다.
    예를 들어, 성능 측정 등도 포함할 수 있다고 생각합니다.



    Q. 직접 Intro.js만 읽어서, 릴리스시만 제외하면 좋지 않겠습니까?



    네, 맞습니다.

    Q. Intro.js의 접두사 처음부터 붙이면 좋을까요?



    네, 맞습니다.

    Q. 캡처만 취해도 매뉴얼이 아니지요?



    data-step/data-intro의 부분을 외부화해 생성할 수 있도록 생각하고 있습니다만 아직 하고 있지 않습니다.

    Q. 완성계의 Gist 보면, E2E용의 캡쳐가 사라지고 있는데?



    코드를 단순화하기 위해 일단 매뉴얼에 대한 캡처 이외의 요소는 사라졌습니다.

    참고


  • GoogleChrome/puppeteer
  • 헤드리스 Chrome 소개
  • Intro.js - API
  • 좋은 웹페이지 즐겨찾기