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');
Kontaktujte nás:
Vyplňte formulár alebo nám pošlite e-mail. V prípade, že sa bojíte o svoj nápad, pošleme vám dohodu o mlčanlivosti a ochrane dôverných informácií a váš nápad bude v bezpečí.