Все хорошо, пока вам нужно прогнать JS исходники через бабели-шмабели для создания бандла, но начинается сущий ад, когда вы захотите написать тесты для вашего сайта или бибилиотеки. Проблема в том, что все тест-фреймворки используют специфичные функции из ноды или/и написаны в ES5. Таким образом запуск E2E тестов становятся не тривиальной задачей и предлагает танцы с бубном транспиляций и sourcemap-ов для покрытия кода. Вы же не хотите что бы ошибки указывали не туда?

В этой статье я опишу свой опыт использования Puppeteer для небольшой задачи,
и как я запустил ES6 модули в ноде и браузере, имея всего один исходник для тестов без сборщиков.
Зачем вообще Puppeteer, спросите вы, почему не WebDriver? Просто я заметил, как мучаются создатели популярных опенсорс WebGL библиотек, например, у них есть в наличии 300 страниц с примерами, каждый из которых может сломаться при любом коммите. Они проверяют их после каждого изменения, и если что-то забыли открыть — извините ¯\_(ツ)_/¯, сломалось. Коли никто до сих пор не решил данную задачу, то я решил попробовать это, пока в своей небольшой либе. Первая мысль которая была, это запустить headless-gl, но он морально устарел. Node-gles уже поддерживает WebGL2, но не редкое расширение, которое я использовал. WebDriver? Даже не пробовал. Не уверен что это возможно, python/C#/Java были мне не нужны, а требовался JS/TS с последней нодой и с новейшим браузерным API, так залетающие фичи могут быть по последней спеке.
Почему ES6 модули? Поддержка WebGL и ES6 в браузерах примерно на одном уровне. А с модулями бандл или нет, пусть решает пользователь, просто можно собрать обе версии. Но оказывается для юнит тестирования, очень удобно использовать версию именно с модулями, так как sourcemap-ы добываются очень просто, а тесты после этого без каких либо лишних телодвижений можно запускать как в ноде, так и в браузере. Запуская их в puppeteer, E2E с покрытием кода дается почти бесплатно. Typescript c таргетом в ES6 наверно был нужен, но на маленьком проекте покрытым тестами, пойдет и обычный js.
Итак, хватит введений, я поставил в проект puppeteer и puppeteer-to-istanbul и написал такую обертку
// puppeteer.js import puppeteer from 'puppeteer'; import pup2ist from 'puppeteer-to-istanbul'; (async () => { const browser = await puppeteer.launch({ headless: process.env.HEADLESS, // headless customization slowMo: 250 // good fature for new configs }); const page = (await browser.pages())[0]; // enable coverage await page.coverage.startJSCoverage(); await page.coverage.startCSSCoverage(); // some additional code with console events here... // navigate to unit test page await page.goto('http://127.0.0.1:1234/'); // disable coverage const jsCoverage = await page.coverage.stopJSCoverage(); const cssCoverage = await page.coverage.stopCSSCoverage(); pup2ist.write([...jsCoverage, ...cssCoverage]) await new Promise(resolve => setTimeout(resolve, 6000)); await browser.close(); })();
Которую можно запускать командой node --experimental-modules --no-warnings ./test/puppeteer.js с 11+ нодой, или даже без флагов на node 13.2+. Конечно можно использовать require, то се… Но зачем? Это же вообще бэкенд, тут поддержка у клиентов даже не нужна! Следующий код из package.json позволяет нам кастомизировать HEADLESS загрузку в консоли и в CI облаке, если требуются различные настройки для них. В travs/circle-ci наверно будет стоять linux и можно устанавливать там переменные среды в таком формате. concurrently открывает параллельно два процесса в одной консоли.
// package.json { //bla-bla... "type": "module", // this line indicates that we are using es6 modules "scripts": { "test": "node --experimental-modules --no-warnings ./test/puppeteer.js", "server": "http-server -c-1 -p 1234", "not-bad-cmd--dude": "concurrently -k -s first \"npm:test\" \"npm:server\"", "ci": "HEADLESS=true concurrently -k -s first \"npm:test\" \"npm:server\"", } }
На локальной машине после ввода команды npm run server будет запускаться http сервер, а на npm run test puppeteer в отдельном окне окне хрома. Вот в принципе и все что нужно знать про puppeteer. Дальнейшие примеры по скриншотам, эмуляциям девайсов, админкам и т.д., расположены тут. Кстати, вместе с пакетом puppeteer вам установился отдельный хром в node_modules, если он вам не нужен, замените его на puppeteer-core или puppeteer-firefox. Следует заметить что в примере выше мы бесплатно получили JS/CSS покрытие кода которое пишется в папку .nyc_output, пока не будем заострять на этом внимание, на данном этапе нам от этого не холодно не жарко, но если что — оно там есть, и статистика покрытия тестов почти готова к просмотру.

Теперь перейдем к самим тестам, пытаясь выбрать в чем я буду запускать E2E в моей небольшой либе, я наткнулся на следующие графики, где сравнивали производительности фреймворков для тестирования. Наверно, время выполнения не так важно, но когда какой нибудь Jest запускает их в 10 раз медленнее, возникает вопрос «что это, и зачем это нужно». Основной критерий выбора это был запуск es6 со строчкой <script type="module" src="./test.js"></script> в html странице. Так как на момент написания моего кода, нода еще не поддерживала в полной мере ES6 (вчера вышла 12.3 в которой сняли флаги). Я решил, что если взять фреймворк с исходниками на TS или же ES6+, то оно точно должно запускаться. Вообще, наверное можно было взять какой нибудь mocha, объявить его выше на странице и обращаться к объявленному классу, но что будет если выпадет ошибка? В общем, можете назвать свой любимый тест раннер тут. Я лишь скажу что Zora поддерживает TAP формат, и это значит для нее подходит целый зоопарк TAP пожирателей. В ней есть большинство ассертов, она поддерживает async, она одна из самых быстрых, написана на чистом ES6 без зависимостей от самой ноды. Мне показалась она настоящим бриллиантом для небольших проектов.
В итоге у меня получились какие-то такие тесты, которые работают как в браузере так и в ноде. В документации по Zora есть исчерпывающая инструкция по ассертам и группировкам команд.
// test.js import MyLibrary from '../dist/my-library.module.js'; import { test } from 'https://cdn.jsdelivr.net/npm/zora@3.0.3/dist/bundle/module.js'; test('CPU', async (t) => { // some stuff here t.ok(tfps != null, 'fps = ' + (tfps != null ? tfps.toFixed(1) : 'null')); t.ok(tcpu != null, 'cpu = ' + (tcpu != null ? tcpu.toFixed(1) : 'null')); }); test('Memory', async (t) => { // some stuff here t.ok(tmem != null, 'mem = ' + (tmem != null ? tmem.toFixed(1) : 'null')); }); // etc...
Для того что бы показать консоль без сборок, мне пришлось сделать подобный снифер. Голая консоль не очень презентабельна, можно было бы подключить TAP вывод куда-нибудь для наведения марафета. Но самое забавное — это то, что результаты тестов на вашем клиенте можно посмотреть в онлайне. Так же, совершенно этот же код запускается в CI по любому коммиту.
<!DOCTYPE html> <html lang="en"> <head> <!-- some declarations in head --> </head> <body> <!-- some declarations in body --> <script> const addSniffer = (spyTarget) => function() { spyTarget.apply(window.console, arguments); sniffer([...arguments]); } window.console.log = addSniffer(window.console.log); window.console.error = addSniffer(window.console.error); let screen = document.getElementById('screen'); function sniffer(string) { let screen = document.getElementById("screen"); string.forEach(line => { let div = document.createElement("div"); let text = document.createTextNode(line); div.appendChild(text) screen.appendChild(div); }); } </script> <script type="module" src="./test.js"></script> </body> </html>
Но это еще не все, имея готовые тесты, можно подключить таких ботов как renovate/greenkeeper/dependabot, которые бы обновляли зависимости в вашей библиотеке, и делали автокоммиты, предварительно проверяя корректность обновлений. А travis/github-ci/circle-ci бы выкладывали бы новую версию npm пакетов.
Например такой конфиг от renovate, делает автокоммиты по воскресеньям, и поднимает версию
{ "automerge": true, "automergeType": "branch", "bumpVersion": "patch", "schedule": ["on sunday"], "ignorePaths": [".circleci"] }
А travis, когда вы сами подняли версию или же какой то бот, может автоматом выкладывать пакет в npm. Для этого нужно создать аккаунт на travis-ci.org, включить f2a как описано в данной статье, ввести два секретных ключа $NPM_EMAIL и $NPM_TOKEN, и создать подобный конфиг.
language: node_js node_js: '12' script: - npm run ci deploy: provider: npm email: $NPM_EMAIL api_key: $NPM_TOKEN on: branch: master
Итого, как-то так можно, но сложно избавится от оповещений от гитхаба о том, что в какой-то зависимости появилась уязвимость 😀
ссылка на оригинал статьи https://habr.com/ru/post/466551/
Добавить комментарий