Разделы портала

Онлайн-тренинги

.
Измерение производительности пути пользователя при помощи Lighthouse и WebdriverIO
25.05.2023 00:00

Автор: Хью МакКэмфилл (Hugh McCamphill)
Оригинал статьи
Перевод: Ольга Алифанова

У Lighthouse теперь есть новый API пути пользователя, позволяющий тестировать в лабораторных условиях в любой момент, пока существует страница. Он поддерживает генерацию Lighthouse-отчета из сценария Puppeteer, но я хотел посмотреть, как это получится при помощи WebdriverIO!

Зачем это, однако, делать?

Возможно, вы знаете, что WebdriverIO немного поддерживает тестирование производительности, задействуя также Chrome Devtools, но я считаю, что одно из огромных преимуществ Lighthouse – это генерируемый отчет, а не только сами показатели.

К тому же мы можем воспользоваться существующим кодом WebdriverIO для замеров в моменте и на отрезке времени – это тоже важные аспекты производительности фронтэнда.

Все примеры в статье ссылаются на web.dev/lighthouse-user-flows/#timespans, где можно увидеть оригинальный код Puppeteer и полученные отчеты Lighthouse; статья концентрируется на использовании WebdriverIO с Lighthouse.

Для начала разберемся, как выглядит базовый сценарий Puppeteer, совмещенный с новым API пути пользователя. Вот что мы имеем в примере web.dev/lighthouse-user-flows для базового анализа производительности загрузки страницы:

import fs from 'fs';
import open from 'open';
import puppeteer from 'puppeteer';
import { startFlow } from 'lighthouse/lighthouse-core/fraggle-rock/api.js';
 
async function captureReport() {
const browser = await puppeteer.launch({ headless: false });
const page = await browser.newPage();
 
const flow = await startFlow(page, {name: 'Single Navigation'});
await flow.navigate('https://web.dev/performance-scoring/');
 
await browser.close();
 
const report = flow.generateReport();
fs.writeFileSync('flow.report.html', report);
open('flow.report.html', { wait: false });
}
 
captureReport();

Чтобы сделать то же самое в WebdriverIO, нужно только вызвать Puppeteer и перейти на первую страницу:

const puppeteerBrowser = await browser.getPuppeteer();
const page = (await puppeteerBrowser.pages())[0];

WebdriverIO можно запустить в автономном режиме – большинство из вас знает, как это выглядит в тесте:

import fs from 'fs';
import open from 'open';
import { startFlow } from 'lighthouse/lighthouse-core/fraggle-rock/api.js';
 
describe('Lighthouse Performance ', () => {
it('Should be able to generate lighthouse report with Webdriverio', async () => {
const puppeteerBrowser = await browser.getPuppeteer();
const page = (await puppeteerBrowser.pages())[0];
 
const flow = await startFlow(page, {name: 'Single Navigation'});
await flow.navigate('https://web.dev/performance-scoring/');
 
await browser.close();
 
const report = flow.generateReport();
fs.writeFileSync('flow.report.html', report);
open('flow.report.html', {wait: false});
}
}

Однако тут нет никаких преимуществ перед прямым использованием Puppeteer. Это стоит использовать для вышеупомянутых замеров производительности в моменте или на отрезке времени, что даст нам (что спорно) более приятный синтаксис, а в идеале – задействование уже существующего кода WebdriverIO, который осуществит необходимые взаимодействия и навигацию.

К примеру, вместо:

async function captureReport() {
const browser = await puppeteer.launch({headless: false});
const page = await browser.newPage();
 
const flow = await startFlow(page, {name: 'Squoosh snapshots'});
 
await page.goto('https://squoosh.app/', { waitUntil: 'networkidle0'} );
 
// Дождаться первой кнопки демо-изображения и открыть его
const demoImageSelector = 'ul[class*="demos"] button';
await page.waitForSelector(demoImageSelector);
await flow.snapshot({ stepName: 'Page loaded' });
await page.click(demoImageSelector);
 
// Дождаться кнопки дополнительных настроек, открыть их
const advancedSettingsSelector = 'form label[class*="option-reveal"]';
await page.waitForSelector(advancedSettingsSelector);
await flow.snapshot({ stepName: 'Demo loaded' });
await page.click(advancedSettingsSelector);
 
await flow.snapshot({ stepName: 'Advanced settings opened' });
 
browser.close();
 
const report = flow.generateReport();
fs.writeFileSync('flow.report.html', report);
open('flow.report.html', {wait: false});
}
 
captureReport();

можно написать код ниже, используя WebdriverIO для навигации и взаимодействия:

import fs from 'fs';
import open from 'open';
import { startFlow } from 'lighthouse/lighthouse-core/fraggle-rock/api.js';
 
describe('Lighthouse Performance ', () => {
it('Should be able to generate lighthouse report with Webdriverio', async () => {
const puppeteerBrowser = await browser.getPuppeteer();
const page = (await puppeteerBrowser.pages())[0];
 
const flow = await startFlow(page, {name: 'Single Navigation'});
await browser.url('https://squoosh.app/');
 
// Дождаться первой кнопки демо-изображения и открыть его
const demoImageSelector = await $('ul[class*="demos"] button');
await demoImageSelector.waitForDisplayed();
await flow.snapshot({ stepName: 'Page loaded' });
await demoImageSelector.click();
 
// Дождаться кнопки дополнительных настроек, открыть их
const advancedSettingsSelector = $('label[class*="option-reveal"]');
await advancedSettingsSelector.waitForDisplayed();
 
await flow.snapshot({ stepName: 'Demo loaded' });
await advancedSettingsSelector.click();
 
await flow.snapshot({ stepName: 'Advanced settings opened' });
 
const report = flow.generateReport();
fs.writeFileSync('flow.report.html', report);
open('flow.report.html', { wait: false });
}
}

Навигация тут, конечно, тривиальная; далее приведу пример оценки производительности на отрезке времени, сделанной в моей компании, Glofox – тут мы используем API приложений для настройки и данных, и ряда высокоуровневых путей, которые используют WebdriverIO под капотом:

describe('Dashboard Performance', () => {
it('Lighthouse flow for booking class for member', async () => {
const { className } = fakeData();
const puppeteerBrowser = await browser.getPuppeteer();
const page = (await puppeteerBrowser.pages())[0];
 
const flow = await startFlow(page);
 
await flow.startTimespan({ stepName: 'Login' });
// WebdriverIO код выполняет первоначальную навигацию browser.url, заполняя данные на двух страницах
await Dashboard.login();
await flow.endTimespan();
 
await flow.snapshot({ stepName: 'Logged in' });
 
// создать "участника" через API
const member = new Member();
const { id: memberId } = await DashboardAPICalls.Members.create({ member });
 
// создать класс для бронирования участником через API
await DashboardAPICalls.Classes.createClass({ className });
 
await flow.startTimespan({ stepName: 'Book Class' });
// забронировать класс через WebdriverIO – участвует множество страниц и кликов
await Dashboard.bookClass({ member, className });
await flow.endTimespan();
}
}

Надеюсь, этот пример продемонстрировал, как мы используем существующий код WebdriverIO (и абстракции API для настройки данных!), чтобы осуществить навигацию и взаимодействия, а затем провести анализ пути Lighthouse.

Заключение

Новый Lighthouse API пути пользователя дает дополнительные возможности для анализа пользовательской производительности фронтэнда – как показано в статье, можно получить выгоду от существующего кода WebdriverIO, используя его для необходимой навигации и заполнения форм. Встает вопрос, как прогонять такие тесты в пайплайне – но это тема для отдельной статьи.

Приложение

Если вы хотите запустить тест на компьютере, его конфигурацию можно передать в метод startFlow:

const config = require('lighthouse/lighthouse-core/config/desktop-config.js');
 
const puppeteerBrowser = await browser.getPuppeteer();
const page = (await puppeteerBrowser.pages())[0];
const flow = await startFlow(page, { config });

Обсудить в форуме