puppeteer

UI testovanie pomocou Puppeteer a Gitlab CI

Bill Gates raz povedal: „Programátori by mali byť dostatočne leniví na to, aby na náročné úlohy nachádzali jednoduché riešenia.“ Jednou z náročných úloh je určite aj kontrola webovej stránky, ktorá obsahuje hneď niekoľko podstránok v 8 rozlíšeniach a je dostupná v 3 jazykových variáciách. Súhlasíte?

 

Na jednom z našich pravidelných interných stretnutí zameranom na zdieľanie vedomostí a zručností sme riešili aj tému testovania používateľského rozhrania (UI = user interface) vo webových aplikáciách. Jeden z mojich kolegov prišiel s prehľadom aktuálne dostupných riešení. Počas ich prezentovania sa druhý kolega nadchol, keď počul o nástroji Puppeteer (od tvorcov prehliadača Chrome) a ihneď sa pustil do experimentovania.

 

Puppeteer je tzv. „headless browser“, t.j. webový prehliadač, ktorý nemá žiadne grafické užívateľské rozhranie. Je to teda akýsi softvér, ktorý pristupuje k webovým stránkam, ale nezobrazuje ich

 

Puppeteer má však svoje vlastné nespočetné množstvo problémov, a preto bol prvý kolegov pokus neúspešný. Puppeteer odolal tomu, aby bol nainštalovaný z Dockerfile, neustále sa zasekával a „padal“. Potom som však prišiel s lenivou myšlienkou - je na Docker Hub už nejaký Docker image? A samozrejme, že bol.

 

Po troške hrania sa s Dockerovým image sa mi podarilo z nášho Gitlab CI spustiť „Hello world“ aplikáciu, na to aby sme mali dvojkrokovú pipeline pri nasadzovaní. Po prvom úspešnom nasadení som tento krok dal preskakovať. Však viete, 30 sekúnd života navyše. V tomto kroku si ale môžete nasadiť vašu webovú stránku, ktorú chcete následne testovať.

 


stages:
 - build
 - test

build:
  stage: build
  image: node:carbon
  when: manual
  script:
   - cd app
   - npm install
  tags:
    - docker

test:
  stage: test
  image: alekzonder/puppeteer:1.5.0-0
  script:
   - cd test
   - yarn install
   - node screenshot-test.js
  artifacts:
    paths:
     - test/screenshots/*/*
    expire_in: 3 days
  tags:
    - docker

 

Keď sa pozrieme na súbor „gitlab-ci.yml“ – krok „build“ nainštaluje Node aplikáciu a krok „test“ spustí UI testovací skript napísaný v JavaScripte.

 

„script“ obsahuje príkazy, ktoré sa majú vykonať. Vojdeme do priečinku „test“, ktorý obsahuje testovací skript a definíciu potrebných treťostranných knižníc, ktoré inštalujeme pomocou Yarn a nakoniec sa spustí samotný test pomocou Node.js.

 

Artefakty sú súbory prezentované reťazcom CI – v našom prípade sú to jednotlivé screenshoty. Je možné ich nastaviť na dobu určitú – napríklad ich môžete nastaviť na 3 dni, čo znamená, že po ubehnutí tohto času sa zmažú zo serveru a uvoľnia miesto.

 

„docker“ tag spúšťa náš Gitlab Runner podporujúci Docker.

 


{
  "name": "hello-test",
  "scripts": {
    "start": "node screenshot-test.js"
  },
  "private": true,
  "dependencies": {
    "chromeless": "^1.5.0",
    "puppeteer": "1.5.0"
  }
}

 

Súbor „package.json“ (uvedený vyššie) nám definuje štartovací príkaz a obsahuje požiadavku na puppeteer balík, ktorý ďalej vyžaduje sťahovanie a inštaláciu prehliadaču Chromium.

 

Odkedy je dostupná verzia 1.7.0, je možné použiť puppeteer-core balík a použiť váš lokálne alebo vzdialene nainštalovaný Chromium.

 


const puppeteer = require('puppeteer');
const fs = require('fs');
/**
 * Launcher
 */
(async () => {
  //
  // set up Puppeteer
  //
  let browser;
  try {
    browser = await puppeteer.launch({
      args: [
        '--no-sandbox',
        '--disable-setuid-sandbox',
        '--disable-dev-shm-usage'
      ]
    });
  } catch (err) {
    console.log('Error launching browser');
    throw err;
  }

  // launch browser
  try {
    fs.mkdirSync('screenshots/' + name);
    // Web page
    const page = await browser.newPage();
    await page.setViewport({
      width: 1920,
      height: 1080
    });
    await page.goto('https://example.com/', {
      timeout: 10000,
      waitUntil: 'networkidle0'
    });

    // full page screenshot
    const bodyHandle = await page.$('body');
    const boundingBox = await bodyHandle.boundingBox();

    await page.screenshot({
      path: 'screenshots/example-com-fullpage.jpg',
      clip: {
        x: 0,
        y: 0,
        width: parseInt(boundingBox.width),
        height: parseInt(boundingBox.height)
      },
      type: 'jpeg'
    });

    await bodyHandle.dispose();

 

Jednoduchý skript, ktorý spúšťame v súbore „screenshot-test.js“, robí screenshot načítanej webovej stránky.

 

Ako prvé musíme otvoriť prehliadač v počítači. Aby sme zabránili obmedzeniu pamäte na „vysokých“ stránkach, zadáme príkaz "disable-dev-shm-usage".

 

Ako druhé musíme otvoriť novú kartu v prehliadači, nastaviť cieľové rozlíšenie a potom už len ideme na požadovanú stránku. Aby sme sa uistili, že sa správne načíta, nastavíme časový limit čakania na 10 000 ms a čakáme, kým sa sieť stane nečinnou a neukončia sa všetky načítavania.

 

Keďže chceme vytvoriť snímku z celej webovej stránky, musíme sa s tým trochu „pohrať“ (hacknúť) a vytiahnuť jej rozmery v podobe výšky a šírky screenshotu. V tomto bode sa môžu objaviť problémy súvisiace s extrémne vysokými webovými stránkami, ale budete mať už predstavu, ako daný web vyzerá.

 

Potom už môžete pokojne zatvoriť celý prehliadač. Avšak, čo keď chceme otestovať viac webových stránok alebo rozlíšení? Musíme zatvárať postupne jednotlivé stránky alebo celý prehliadač? Samozrejme, že nie. No na druhej strane sa to oplatí, pretože chcete načítať stránku po tom, ako sa vykoná nejaká zmena v rozlíšení. Vďaka tomu totiž zaistíte, že všetky CSS štýly sú správne.

 


await page.reload({ timeout: 10000, waitUntil: 'networkidle0' });

 

Čo ešte môžete robiť pomocou programu Puppeteer? Viac menej je to len na vás. Vezmite si, že máte k dispozícií prehliadač s rozsiahlym API, ktorý môže byť použitý v Node.js. Znie to dobre, však?

 

Poďme sa pozrieť na to, ako by ste testovali „status code“ odpovede vášho serveru:

 


const assert = require('assert');
const STATUS_OK = 200;
try {
  let response = await page.goto(url, {
    timeout: 10000,
    waitUntil: 'networkidle0'
  });
} catch (err) {
  return Promise.reject(err);
}
if (!response) {
  return Promise.reject(new Error('Response empty'));
}
assert.equal(response.status(), STATUS_OK, 'Wrong status code');

 

Alebo ako by ste testovali konkrétne elementy na webovej stránke:

 


// Link hostname only
let result = await page.$eval('a#sk', link => link.hostname);
assert.equal(result, 'example.sk');
// Link full URL
result = await page.$eval('a#sk', link => link.href);
assert.equal(result, 'https://example.sk/link');
// Element content
result = await page.$eval('h1', link => link.innerHTML);
assert.equal(result, 'Main header');