Marionetista com ES6 em um nó e navegador, ou por que o Zora é a melhor estrutura de teste em seu nicho

Tudo está bem desde que você precise executar fontes JS através dos manequins para criar um pacote, mas o inferno começa quando você deseja escrever testes para o seu site ou biblioteca. O problema é que todas as estruturas de teste usam funções específicas de um nó e / ou são gravadas no ES5. Assim, iniciar os testes E2E não é uma tarefa trivial e oferece dança com um pandeiro de transpilações e mapas de origem para cobrir o código. Você não deseja que os erros apontem para o lugar errado?



Neste artigo, descreverei minha experiência usando o Puppeteer para uma pequena tarefa,
e como lancei os módulos ES6 no nó e no navegador, tendo apenas uma fonte para testes sem coletores.


Por que Puppeteer, você pergunta, por que não o WebDriver? Acabei de notar como os criadores das populares bibliotecas WebGL de código aberto são atormentados, por exemplo, eles têm 300 páginas com exemplos, cada um dos quais pode romper com qualquer confirmação. Eles os verificam após cada alteração e, se esqueceram de abrir alguma coisa - desculpe ¯\_(ツ)_/¯ , ela quebrou. Se ninguém ainda resolveu esse problema, decidi experimentá-lo enquanto estava na minha pequena biblioteca. O primeiro pensamento que foi foi correr sem cabeça, mas é obsoleto. O Node-gles já suporta WebGL2, mas não a extensão rara que eu usei. WebDriver? Eu nem tentei. Não tenho certeza se isso é possível, não precisava de python / C # / Java, mas precisava de JS / TS com o nó mais recente e a API do navegador mais recente, para que os recursos voadores possam ser das especificações mais recentes.


Por que os módulos ES6? O suporte para WebGL e ES6 nos navegadores é praticamente o mesmo. E com módulos configuráveis ​​ou não, deixe o usuário decidir, você pode simplesmente coletar as duas versões. Porém, para testes de unidade, é muito conveniente usar a versão com os módulos, pois os mapas de origem são extraídos com muita simplicidade e, depois disso, você pode executar os testes sem gestos extras no nó ou no navegador. Executando-os em marionetistas, o E2E com cobertura de código é quase gratuito. Provavelmente o texto datilografado com um destino no ES6 era necessário, mas em um projeto pequeno coberto por testes, js normais também funcionarão.


Então, apresentações suficientes, coloquei puppeteer e puppeteer-to-istanbul no projeto e escrevi esse invólucro


 // 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(); })(); 

Que pode ser executado com o node --experimental-modules --no-warnings ./test/puppeteer.js comando node --experimental-modules --no-warnings ./test/puppeteer.js com um nó 11+ ou mesmo sem sinalizadores no nó 13.2+. Claro que você pode usar require , então ... Mas por quê? Geralmente, isso é um back-end; aqui, o suporte ao cliente nem é necessário! O código a seguir do package.json nos permite personalizar downloads SEM FIO no console e na nuvem do IC, se configurações diferentes forem necessárias para eles. No travs / circle-ci, o linux provavelmente será instalado e você poderá definir variáveis ​​de ambiente nesse formato. concurrently abre dois processos em um console em paralelo.


 // 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\"", } } 

Na máquina local, depois de inserir o comando npm run server , o npm run server http será iniciado e, no npm run test puppeteer, em uma janela separada, a janela chrome. Isso é basicamente tudo o que você precisa saber sobre marionetistas. Outros exemplos de capturas de tela, emulações de dispositivos, áreas administrativas, etc. estão localizados aqui . A propósito, junto com o pacote de puppeteer , você instalou um cromo separado em node_modules ; se você não precisar, substitua-o por puppeteer-core ou puppeteer-firefox . Deve-se observar que no exemplo acima, recebemos uma cobertura de código JS / CSS gratuita, escrita na pasta .nyc_output, até que nos concentremos nisso, nesta fase, não estamos com frio, não está quente, mas se houver alguma coisa, ela estará lá e estatísticas A cobertura do teste está quase pronta para exibição.



Agora, vamos aos testes em si, tentando escolher onde executarei o E2E em minha pequena biblioteca. Encontrei os gráficos a seguir, que comparavam o desempenho das estruturas de teste. Provavelmente, o tempo de execução não é tão importante, mas quando algum Jest os inicia 10 vezes mais devagar, surge a pergunta "o que é e por que é necessário". O principal critério de seleção foi executar o es6 com a linha <script type="module" src="./test.js"></script> na página html. Como no momento em que escrevi meu código, o nó ainda não era totalmente compatível com o ES6 (ontem foi lançado 12.3 no qual os sinalizadores foram removidos). Decidi que se você adotasse a estrutura com as fontes do TS ou ES6 +, ela definitivamente deveria começar. Em geral, você provavelmente poderia usar algum tipo de mocha, declará-lo mais alto na página e se referir à classe declarada, mas o que acontece se ocorrer um erro? Em geral, você pode nomear seu teste de corredor favorito aqui. Vou apenas dizer que o Zora suporta o formato TAP, e isso significa que um zoológico inteiro de comedores da TAP é adequado para ele. Ele tem a maioria das asserções, suporta assíncrono, é um dos mais rápidos, escrito em ES6 puro, sem dependências do próprio nó. Pareceu-me um verdadeiro diamante para pequenos projetos.


Como resultado, fiz alguns testes que funcionam tanto no navegador quanto no nó. A documentação do Zora possui instruções abrangentes sobre asserções e agrupamentos de comandos.


 // 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... 

Para mostrar o console sem montagens, tive que fazer um sniffer semelhante. O console bare não é muito apresentável, seria possível conectar a saída TAP em algum lugar para guiar a maratona. Mas o mais engraçado é que os resultados do teste em seu cliente podem ser visualizados online . Além disso, exatamente o mesmo código é executado no IC em qualquer confirmação.


 <!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> 

Mas isso não é tudo, com testes prontos, você pode conectar bots como renovate / greenkeeper / dependabot, que atualizariam dependências em sua biblioteca e executariam automaticamente, após verificar a correção das atualizações. E o travis / github-ci / circle-ci teria carregado uma nova versão dos pacotes npm.


Por exemplo, essa configuração do renovate, faz auto commit aos domingos e aumenta a versão


 { "automerge": true, "automergeType": "branch", "bumpVersion": "patch", "schedule": ["on sunday"], "ignorePaths": [".circleci"] } 

E o travis, quando você elevou a versão ou algum bot, pode enviar automaticamente o pacote para o npm. Para fazer isso, crie uma conta no travis-ci.org , ative f2a conforme descrito neste artigo , insira duas chaves secretas $NPM_EMAIL e $NPM_TOKEN e crie uma configuração semelhante.


 language: node_js node_js: '12' script: - npm run ci deploy: provider: npm edge: true email: $NPM_EMAIL api_key: $NPM_TOKEN on: branch: master 

No total, é de alguma forma possível, mas é difícil livrar-se das notificações do github de que uma vulnerabilidade apareceu em algum tipo de dependência: D

Source: https://habr.com/ru/post/pt466551/


All Articles