Matware E2E Runner
Executor de testes E2E orientado por JSON com execução paralela em pool do Chrome, verificação visual e 16 ferramentas MCP.
Documentação
English · Español
@matware/e2e-runner
O executor de testes E2E nativo para IA que escreve, executa e depura testes para você.
E2E Runner permite testar seu aplicativo web sem escrever código de teste. Os testes são JSON puro — e você nem precisa escrever isso: basta pedir ao Claude Code.
🎬 Escreva um teste pedindo — e veja-o rodar
O painel ao vivo enquanto uma suíte é executada — cada etapa transmite uma captura de tela no feed, em tempo real.
Com o servidor MCP integrado, criar um teste é uma conversa — sem documentação, sem sintaxe para memorizar:
Você: Crie um teste E2E para o fluxo de login e execute-o.
Claude Code: escreve o teste, executa-o em um navegador real e reporta — ✅
login-flowpassou em 2,3s · captura de tela salva · sem erros de rede.
Nos bastidores, o Claude acabou de escrever e executar isso. Um teste é apenas JSON — uma lista ordenada do que um usuário faz:
[
{ "name": "login-flow", "actions": [
{ "type": "goto", "value": "/login" },
{ "type": "type", "selector": "#email", "value": "user@test.com" },
{ "type": "type", "selector": "#password", "value": "secret" },
{ "type": "click", "text": "Sign In" },
{ "type": "assert_text", "text": "Welcome back" },
{ "type": "screenshot", "value": "logged-in.png" }
]}
]
Sem imports, sem describe/it, sem etapa de build. Se você consegue ler, consegue escrever — ou apenas peça.
Conecte-o ao Claude Code (2 comandos):
claude plugin marketplace add fastslack/mtw-e2e-runner
claude plugin install e2e-runner@matware
Agora diga "crie um teste para X e execute-o" — o Claude obtém 17 ferramentas MCP, comandos de barra e agentes especializados.
Usando um agente diferente (Cursor, Codex, Copilot, mais de 40)? Instale a skill:
npx skills add fastslack/mtw-e2e-runner
📖 Conteúdo
| Seção | O que contém | |
|---|---|---|
| 🚀 | Instalação & primeiro teste | configuração npm · execute com seu próprio Chrome (sem Docker), Obscura ou um pool Docker |
| ✨ | O que você obtém | visão geral dos recursos de relance |
| ✍️ | Escrevendo testes | formato de teste · catálogo completo de ações · tentativas · serial · módulos · autenticação · hooks |
| 🤖 | Integração com IA | Claude Code · OpenCode · 17 ferramentas MCP · verificação visual · issue-para-teste |
| 📊 | Painel & insights | painel ao vivo · sistema de aprendizado · logs de rede · captura de tela |
| 🌐 | Drivers de navegador | browserless · cdp · lightpanda · obscura · steel |
| ⚙️ | CLI, configuração & CI | comandos · flags · e2e.config.js · GitHub Actions · API programática |
🚀 Instalação — é minúsculo
npm install --save-dev @matware/e2e-runner
npx e2e-runner init # scaffolds e2e/ with a sample test + config
Depois escolha como executar o navegador. Você não precisa de Docker a menos que queira o pool paralelo:
Opção 1 · Use o Chrome que você já tem — sem Docker ⭐
Inicie qualquer navegador Chromium com uma porta de depuração e aponte o executor para ele:
google-chrome --headless=new --remote-debugging-port=9222 & # or brave / chromium / msedge
CHROME_POOL_URL=http://localhost:9222 POOL_DRIVER=cdp npx e2e-runner run --all
Ou coloque no e2e.config.js para nunca repetir:
export default {
baseUrl: 'http://localhost:3000', // your app — plain localhost, no docker hostname
poolUrls: ['http://localhost:9222'],
poolDriver: 'cdp',
};
Nada para instalar além do npm, e baseUrl é apenas localhost (o navegador está na sua máquina).
Opção 2 · Obscura — um binário minúsculo, sem Docker
Um único binário de ~30 MB com anti-detecção integrada. Instale uma vez, execute-o, aponte o executor para ele:
obscura serve --port 9222 --stealth &
CHROME_POOL_URL=http://localhost:9222 POOL_DRIVER=obscura npx e2e-runner run --all
npx e2e-runner pool start (com poolDriver: 'obscura' na sua configuração) imprime o comando de instalação exato para o seu SO.
Opção 3 · Pool Docker — paralelo, para CI & suítes grandes
Um pool de Chrome compartilhado e gerenciado por fila que executa muitos testes de uma vez:
npx e2e-runner run --all # the first run auto-starts the Docker pool for you
Requer Docker. Defina baseUrl: 'http://host.docker.internal:3000' para que o Chrome em contêiner alcance seu aplicativo.
Por que host.docker.internal (somente opção Docker)?
Com o pool Docker, o Chrome roda dentro de um contêiner, então localhost lá significa o contêiner — não sua máquina. host.docker.internal faz a ponte para o seu host. No Linux (Docker Engine, não Docker Desktop) adicione --add-host=host.docker.internal:host-gateway, ou use seu IP LAN. As opções 1 & 2 não têm isso — o navegador é local, então localhost simplesmente funciona.
Escreva seu primeiro teste
Abra e2e/tests/sample.json — um fluxo é uma lista ordenada de ações:
[
{ "name": "homepage loads", "actions": [
{ "type": "goto", "value": "/" },
{ "type": "assert_text", "text": "Welcome" },
{ "type": "screenshot", "value": "home.png" }
]}
]
Execute com npx e2e-runner run --all. Resultados — passou/falhou, tempo, capturas de tela, erros de rede — são impressos no seu terminal e no painel web se estiver aberto.
Add OpenCode (opcional)
cp node_modules/@matware/e2e-runner/opencode.json ./
mkdir -p .opencode && cp -r node_modules/@matware/e2e-runner/.opencode/* .opencode/
Veja OPENCODE.md para detalhes.
Atualização
Cada método de instalação atualiza separadamente — atualize o(s) que você usa:
# npm dependency (per project)
npm install --save-dev @matware/e2e-runner@latest
# Claude Code plugin
claude plugin update e2e-runner@matware
# MCP-only install (npx caches the package — pin @latest to force a refresh)
claude mcp add --transport stdio --scope user e2e-runner \
-- npx -y -p @matware/e2e-runner@latest e2e-runner-mcp
[!NOTE] Duas pegadinhas: (1)
npxprefere uma cópia encontrada nonode_modulesdo projeto em vez do próprio cache — se um projeto fixar uma versão antiga, o servidor MCP e o painel executam essa versão antiga, então atualize também a dependência do projeto. (2) Processos já em execução mantêm o código antigo na memória: após atualizar, reinicie o painel e reconecte o servidor MCP (/mcp→e2e-runner→ Reconectar, ou reinicie sua sessão).
✨ O que você obtém
🧪 Testes sem código — arquivos JSON que qualquer pessoa da sua equipe pode ler e escrever. Sem JavaScript, sem compilação, sem dependência de framework.
🤖 Testes com IA — Claude Code cria, executa e depura testes nativamente por meio de 17 ferramentas MCP. Peça para "testar o fluxo de checkout" e ele constrói o JSON, executa e reporta.
🐛 Pipeline de issue-para-teste — Cole uma URL de issue do GitHub ou GitLab. O executor a busca, gera testes E2E, executa-os e informa: bug confirmado ou não reproduzível.
👁️ Verificação visual — Descreva como a página deve parecer em inglês simples. A IA captura uma captura de tela e julga passou/falhou com base na sua descrição. Sem configuração de diff de pixels.
🧠 Sistema de aprendizado — Rastreia a estabilidade dos testes entre execuções. Detecta testes instáveis, seletores instáveis, APIs lentas e padrões de erro — e então apresenta insights acionáveis.
⚡ Execução paralela — Execute N testes simultaneamente contra um pool de navegadores compartilhado (browserless, CDP bruto, Lightpanda, Obscura ou Steel). Modo serial disponível para testes que compartilham estado.
🎯 Drivers de navegador plugáveis — Escolha o mecanismo que se adapta a cada teste: Chrome real via browserless, Lightpanda ou Obscura para execuções rápidas e leves, Steel para sessões gerenciadas. Defina driver por teste ou substitua toda a execução com --driver.
📊 Painel em tempo real — Visualização de execução ao vivo, histórico de execuções com gráficos de taxa de aprovação, galeria de capturas de tela com busca por hash, logs de solicitações de rede expansíveis.
🔁 Tentativas inteligentes — Tentativas em nível de teste e de ação com atrasos configuráveis. Testes instáveis são detectados e sinalizados automaticamente.
📦 Módulos reutilizáveis — Extraia fluxos comuns (login, navegação, configuração) em módulos parametrizados e referencie-os com $use.
🏗️ Pronto para CI — Saída XML JUnit, código de saída 1 em falha, capturas de tela de erro capturadas automaticamente. Exemplo de GitHub Actions incluído.
🌐 Multi-projeto — Um painel agrega resultados de testes de todos os seus projetos. Um pool de Chrome atende a todos.
🐳 Portátil — Chrome roda em Docker, testes são arquivos JSON no seu repositório. Funciona em qualquer máquina com Node.js e Docker.
✍️ Escrevendo testes
Tudo sobre autoria de testes — o formato de arquivo, o vocabulário completo de ações, tentativas, isolamento de estado e reutilização. Expanda o que precisar:
Test format & file layout
Cada arquivo .json em e2e/tests/ contém um array de testes. Cada teste tem um name e actions sequenciais:
[
{
"name": "homepage-loads",
"actions": [
{ "type": "goto", "value": "/" },
{ "type": "assert_visible", "selector": "body" },
{ "type": "assert_url", "value": "/" },
{ "type": "screenshot", "value": "homepage.png" }
]
}
]
Arquivos de suíte podem ter prefixos numéricos para ordenação (01-auth.json, 02-dashboard.json). A flag --suite corresponde com ou sem o prefixo, então --suite auth encontra 01-auth.json.
Action catalog — navegação, entrada & interação
| Ação | Campos | Descrição |
|---|---|---|
goto | value | Navegar para URL (relativa a baseUrl ou absoluta) |
click | selector ou text | Clicar por seletor CSS ou texto visível. O modo texto também aceita scope: "dialog", visible: true, last: true |
type / fill | selector, value | Limpar campo e digitar texto |
wait | selector, text, gone, ou value (ms) | Aguardar elemento/texto aparecer, gone desaparecer (spinner/diálogo), ou atraso fixo. Prefira condições a value fixos |
screenshot | value (nome do arquivo) | Capturar uma captura de tela |
select | selector, value | Selecionar uma opção de dropdown |
clear | selector | Limpar um campo de entrada |
press | value | Pressionar uma tecla (Enter, Tab, etc.) |
scroll | selector ou value (px) | Rolar até elemento ou por quantidade de pixels |
hover | selector | Passar o mouse sobre um elemento |
evaluate | value | Executar JavaScript no contexto do navegador |
navigate | value | Navegação do navegador (back, forward, reload) |
clear_cookies | — | Limpar todos os cookies da página atual |
wait_network_idle | opcional value (ms ocioso, padrão 500), timeout | Aguardar até a rede ficar ociosa por value ms — útil após ações que disparam solicitações em segundo plano |
set_storage | value ("key=val"), opcional selector: "session" | Definir uma chave localStorage (ou sessionStorage com selector: "session") |
gql | value (consulta), opcional text (JSON de variáveis), opcional selector (asserção) | Executar consulta/mutação GraphQL via fetch na página, com o token de autenticação lido de localStorage. Falha em erros GraphQL. selector é uma expressão JS avaliada contra a resposta r (ex.: "r.data.users.length > 0"). Instala window.__e2eGql para etapas evaluate posteriores |
Clicar por texto — quando click usa text em vez de selector, ele busca em elementos interativos e de conteúdo comuns:
button, a, [role="button"], [role="tab"], [role="menuitem"], [role="option"],
[role="listitem"], div[class*="cursor"], span, li, td, th, label, p, h1-h6
{ "type": "click", "text": "Sign In" }
Assertions — verificar texto, elementos, URLs, contagens & rede
| Ação | Campos | Descrição | |--------|--------|-------------| | `assert_text` | `text` | Verifica se o texto existe em qualquer lugar da página (substring) | | `assert_no_text` | `text` | Verifica se o texto NÃO aparece em nenhum lugar da página — oposto de `assert_text` | | `assert_text_in` | `selector`, `text`, opcional `value: "exact"` | Verifica texto dentro de um contêiner com escopo. `text` é uma regex sem diferenciar maiúsculas/minúsculas por padrão; `value: "exact"` alterna para substring com diferenciação de maiúsculas/minúsculas | | `assert_element_text` | `selector`, `text`, opcional `value: "exact"` | Verifica se o texto do elemento contém (ou corresponde exatamente) ao texto esperado | | `assert_url` | `value` | Verifica o caminho da URL atual ou a URL completa. Caminhos (`/dashboard`) comparam apenas o pathname | | `assert_visible` | `selector` | Verifica se o elemento existe e está visível | | `assert_not_visible` | `selector` | Verifica se o elemento está oculto ou não existe | | `assert_attribute` | `selector`, `value` | Verifica atributo: `"type=email"` para valor, `"disabled"` para existência | | `assert_class` | `selector`, `value` | Verifica se o elemento possui uma classe CSS | | `assert_input_value` | `selector`, `value` | Verifica se o `.value` de input/select/textarea contém texto | | `assert_matches` | `selector`, `value` (regex) | Verifica se o texto do elemento corresponde a um padrão de regex | | `assert_count` | `selector`, `value` | Verifica a contagem de elementos: exata (`"5"`) ou operadores (`">3"`, `">=1"`, `"<10"`) | | `assert_no_network_errors` | — | Falha se alguma requisição de rede falhar (ex.: `ERR_CONNECTION_REFUSED`) | | `assert_storage` | `value` (`"key"` ou `"key=expected"`), opcional `selector: "session"` | Verifica se uma chave de `localStorage`/`sessionStorage` existe ou possui um valor específico | | `assert_visual` | `value` (imagem dourada), opcional `selector`, `text` (diferença máxima, ex.: `"0.02"`), `fullPage`, `maskRegions`, `threshold` | Regressão visual: compara uma captura de tela com uma referência dourada. A primeira execução salva a dourada; execuções posteriores falham se mais pixels diferirem do limite (padrão 2%) e gravam uma imagem de diff | | `get_text` | `selector` | Extrai o texto do elemento (sem verificação, nunca falha). Resultado: `{ value: "..." }` |
Ações com conhecimento de framework — React/MUI sem boilerplate de evaluate
Estas ações lidam com padrões comuns em aplicativos React/MUI que normalmente exigem boilerplate verboso de evaluate:
| Ação | Campos | Descrição |
|---|---|---|
type_react | selector, value, opcional blur, waitAfter | Digita em inputs controlados do React usando o setter de valor nativo. Dispara eventos input + change para que o estado do React seja atualizado corretamente. blur: true confirma no blur; waitAfter: "<ms>" aguarda após (autocomplete com debounce). |
click_regex | text (regex), opcional selector, opcional value: "last" | Clica no elemento cujo textContent corresponde a uma regex (sem diferenciar maiúsculas/minúsculas). Padrão: primeira correspondência. Use value: "last" para a última correspondência. |
click_option | text | Clica em um elemento [role="option"] por texto — comum em dropdowns de autocomplete/select. |
select_combobox | text, opcional selector, filter, openWait/filterWait/waitAfter | Abre um Autocomplete/Select do MUI, opcionalmente digita filter, e então clica na opção que corresponde a text. Faz fallback entre [role="option"], .MuiAutocomplete-option, li.MuiMenuItem-root. |
focus_autocomplete | text (texto do label) | Foca um input de autocomplete pelo texto do seu label. Suporta MUI e [role="combobox"] genérico. |
click_chip | text | Clica em um elemento chip/tag por texto. Pesquisa em [class*="Chip"], [class*="chip"], [data-chip]. |
click_icon | value (id do ícone), opcional selector (escopo) | Clica em um ícone por fragmento de data-testid/data-icon/aria-label/classe ou SVG <title> — MUI, FontAwesome, Heroicons, etc. Clica no ancestral clicável mais próximo (botão, link, aba). |
click_menu_item | text, opcional selector (escopo) | Clica em um item de menu por texto em [role="menuitem"], .dropdown-item, .menu-item, MUI MenuItem. |
click_in_context | text (texto do contêiner), selector (filho) | Clica em um elemento filho dentro do menor contêiner que corresponde a text — ex.: o botão de excluir de um card/linha específico. |
// Before: 5 lines of evaluate boilerplate
{ "type": "evaluate", "value": "const input = document.querySelector('#search'); const nativeSet = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; nativeSet.call(input, 'term'); input.dispatchEvent(new Event('input', {bubbles: true})); input.dispatchEvent(new Event('change', {bubbles: true}));" }
// After: 1 action
{ "type": "type_react", "selector": "#search", "value": "term" }
Ações de múltiplas abas — popups, janelas OAuth e fluxos entre abas
| Ação | Campos | Descrição |
|---|---|---|
open_tab | value (URL), opcional text (label) | Abre uma nova aba e navega para a URL (relativa a baseUrl ou absoluta). O label padrão é tab-<n> |
switch_tab | value | Alterna a aba ativa por label, índice numérico ou correspondência de título/URL (regex ou substring). "default" retorna para a aba original |
wait_for_tab | opcional text (label), timeout | Aguarda uma nova aba/popup aberta pelo aplicativo (window.open, target="_blank") e a torna a aba ativa |
assert_tab_count | value | Verifica o número de abas abertas: exato ("2") ou operadores (">=2") |
close_tab | opcional value (label) | Fecha a aba atual (ou nomeada) e alterna de volta para a última restante |
Todas as ações subsequentes são executadas na aba ativa:
{ "type": "click", "text": "Open report" }
{ "type": "wait_for_tab", "text": "report" }
{ "type": "assert_text", "text": "Quarterly results" }
{ "type": "close_tab" }
Tentativas e detecção de flakiness
Tentativa em nível de teste — tenta novamente um teste inteiro em caso de falha. Defina globalmente via config ou por teste:
{ "name": "flaky-test", "retries": 3, "timeout": 15000, "actions": [...] }
Testes que passam após a tentativa são marcados como flaky no relatório e no sistema de aprendizado.
Tentativa em nível de ação — tenta novamente uma única ação sem reexecutar o teste inteiro. Útil para cliques e esperas sensíveis a tempo:
{ "type": "click", "selector": "#dynamic-btn", "retries": 3 }
{ "type": "wait", "selector": ".lazy-loaded", "retries": 2 }
Defina globalmente: actionRetries na config, --action-retries <n> na CLI ou variável de ambiente ACTION_RETRIES. Atraso entre tentativas: actionRetryDelay (padrão 500ms).
Testes seriais — para testes que compartilham estado
Testes que compartilham estado (ex.: dois testes modificando o mesmo registro) podem competir quando executados em paralelo. Marque-os como seriais:
{ "name": "create-patient", "serial": true, "actions": [...] }
{ "name": "verify-patient-list", "serial": true, "actions": [...] }
Testes seriais são executados um de cada vez após todos os testes paralelos terminarem — prevenindo interferência sem desacelerar testes independentes.
Testando aplicativos autenticados
A abordagem mais simples — faça login pela interface como um usuário real:
{
"hooks": {
"beforeEach": [
{ "type": "goto", "value": "/login" },
{ "type": "type", "selector": "#email", "value": "test@example.com" },
{ "type": "type", "selector": "#password", "value": "test-password" },
{ "type": "click", "text": "Sign In" },
{ "type": "wait", "selector": ".dashboard" }
]
},
"tests": [...]
}
Para SPAs com JWT, pule o formulário de login injetando o token diretamente:
{ "type": "set_storage", "value": "accessToken=eyJhbGciOiJIUzI1NiIs..." }
Ou defina globalmente na config:
// e2e.config.js
export default {
authToken: 'eyJhbGciOiJIUzI1NiIs...',
authStorageKey: 'accessToken',
};
Cada teste é executado em um contexto de navegador novo, então o estado de autenticação é automaticamente limpo entre testes.
Mais estratégias: Autenticação baseada em cookies, injeção de cabeçalho HTTP, bypass de OAuth/SSO, módulos de autenticação reutilizáveis e testes baseados em papéis — veja docs/authentication.md
Módulos reutilizáveis — extraia fluxos comuns com $use
Extraia fluxos comuns em módulos parametrizados:
// e2e/modules/login.json
{
"$module": "login",
"description": "Log in via the UI login form",
"params": {
"email": { "required": true, "description": "User email" },
"password": { "required": true, "description": "User password" }
},
"actions": [
{ "type": "goto", "value": "/login" },
{ "type": "type", "selector": "#email", "value": "{{email}}" },
{ "type": "type", "selector": "#password", "value": "{{password}}" },
{ "type": "click", "text": "Sign In" },
{ "type": "wait", "value": "2000" }
]
}
Use em testes:
{
"name": "dashboard-loads",
"actions": [
{ "$use": "login", "params": { "email": "user@test.com", "password": "secret" } },
{ "type": "assert_text", "text": "Dashboard" }
]
}
Módulos suportam validação de parâmetros (parâmetros obrigatórios falham rapidamente), blocos condicionais ({{#param}}...{{/param}}), composição aninhada e detecção de ciclos.
Hooks — beforeAll / beforeEach / afterEach / afterAll
Execute ações em pontos do ciclo de vida. Defina globalmente na config ou por suíte:
{
"hooks": {
"beforeAll": [{ "type": "goto", "value": "/setup" }],
"beforeEach": [{ "type": "goto", "value": "/" }],
"afterEach": [{ "type": "screenshot", "value": "after.png" }],
"afterAll": []
},
"tests": [...]
}
Importante:
beforeAllé executado em uma página de navegador separada que é fechada antes dos testes começarem. UsebeforeEachpara estado que os testes precisam (cookies, localStorage, tokens de autenticação).
Padrões de exclusão — pule rascunhos de execuções --all
Pule testes exploratórios ou rascunhos de execuções --all:
// e2e.config.js
export default {
exclude: ['explore-*', 'debug-*', 'draft-*'],
};
Execuções de suíte individuais (--suite) não são afetadas por padrões de exclusão.
🤖 Integração com IA
O ponto principal: seu agente escreve, executa e verifica testes para você.
Claude Code — instalação de plugin e instalação somente MCP
claude plugin marketplace add fastslack/mtw-e2e-runner
claude plugin install e2e-runner@matware
Isso dá ao Claude 17 ferramentas MCP, uma skill de fluxo de trabalho, 4 comandos de barra (/e2e-runner:run, /e2e-runner:create-test, /e2e-runner:verify-issue, /e2e-runner:capture) e 3 agentes especializados (test-analyzer, test-creator, test-improver).
Instalação somente MCP (apenas ferramentas, sem skill/comandos/agentes):
claude mcp add --transport stdio --scope user e2e-runner \
-- npx -y -p @matware/e2e-runner e2e-runner-mcp
OpenCode
cp node_modules/@matware/e2e-runner/opencode.json ./
mkdir -p .opencode && cp -r node_modules/@matware/e2e-runner/.opencode/* .opencode/
Veja OPENCODE.md para detalhes.
As 17 ferramentas MCP
| Ferramenta | Descrição |
|---|---|
e2e_run | Executa testes (todos, por suíte ou por arquivo) |
e2e_list | Lista as suítes de teste disponíveis |
e2e_create_test | Cria um novo arquivo JSON de teste |
e2e_create_module | Cria um módulo reutilizável |
e2e_pool_status | Verifica a saúde do pool de Chrome |
e2e_app_pool_status | Inspeciona o pool de ambiente do aplicativo (forks, portas, drivers) |
e2e_screenshot | Recupera uma captura de tela por hash |
e2e_capture | Captura captura de tela de qualquer URL |
e2e_analyze | Extrai a estrutura da página (elementos interativos, formulários, cabeçalhos) e emite esqueletos de teste |
e2e_dashboard_start | Inicia o painel web |
e2e_dashboard_stop | Para o painel web |
e2e_dashboard_restart | Reinicia o painel (novo diretório/porta do projeto, limpa sessões obsoletas) |
e2e_issue | Busca issue e gera testes |
e2e_network_logs | Consulta logs de rede de uma execução |
e2e_learnings | Consulta insights de estabilidade |
e2e_vars | Gerencia variáveis de projeto {{var.KEY}} com suporte a SQLite |
e2e_neo4j | Gerencia o grafo de conhecimento Neo4j |
Iniciar/parar o pool são apenas via CLI — não expostos via MCP.
Verificação visual — descreva a página, a IA julga
Descreva como a página deve parecer — a IA julga passou/falhou a partir das capturas de tela:
{
"name": "dashboard-loads",
"expect": "Patient list with at least 3 rows, no error messages, sidebar with navigation links",
"actions": [
{ "type": "goto", "value": "/dashboard" },
{ "type": "wait", "selector": ".patient-list" }
]
}
Após as ações do teste serem concluídas, o runner captura automaticamente uma captura de tela de verificação. A resposta do MCP inclui o hash da captura de tela — o Claude Code a recupera e verifica visualmente contra sua descrição expect. Nenhuma chave de API é necessária.
Issue-para-teste — transforme um relatório de bug em um teste executável
Transforme issues do GitHub e GitLab em testes E2E executáveis. Cole a URL de uma issue e obtenha testes executáveis — automaticamente.
Como funciona:
- Buscar — Obtém os detalhes da issue (título, corpo, labels) via CLI
ghouglab - Gerar — A IA cria ações de teste em JSON com base na descrição da issue
- Executar — Opcionalmente, executa os testes imediatamente para verificar se um bug é reproduzível
# Fetch and display
e2e-runner issue https://github.com/owner/repo/issues/42
# Generate a test file via Claude API
e2e-runner issue https://github.com/owner/repo/issues/42 --generate
# Generate + run + report
e2e-runner issue https://github.com/owner/repo/issues/42 --verify
# -> "BUG CONFIRMED" or "NOT REPRODUCIBLE"
No Claude Code, basta pedir:
"Busque a issue #42 e crie testes E2E para ela"
Lógica de verificação de bugs: Os testes gerados verificam o comportamento correto. Falha no teste = bug confirmado. Todos os testes passam = não reproduzível.
Autenticação: O GitHub exige a CLI gh, o GitLab exige a CLI glab. O GitLab self-hosted é suportado.
📊 Dashboard e insights
e2e-runner dashboard # Start on default port 8484
e2e-runner dashboard --port 9090 # Custom port
Tour do dashboard web — visualização ao vivo, histórico, galeria, status do pool
Execução ao vivo — monitore testes em tempo real com progresso passo a passo, durações e contagem de workers ativos.
Suites de teste — navegue por todas as suites entre projetos. Execute uma única suite ou todos os testes com um clique.
Histórico de execuções — acompanhe tendências de taxa de aprovação com o gráfico integrado. Clique em qualquer linha para expandir os detalhes completos.
Detalhe da execução — selos de PASS/FAIL, miniaturas de capturas de tela com hashes copiáveis (ss:77c28b5a), erros de console formatados e logs de requisições de rede.
Galeria de capturas de tela — navegue por todas as capturas de tela com busca por hash (capturas de ação, erro e verificação).
Status do pool — saúde do pool do Chrome: slots disponíveis, sessões em execução, pressão de memória.
Sistema de aprendizado — testes instáveis, seletores instáveis, APIs lentas
O runner aprende com cada execução de teste — construindo conhecimento sobre sua suite de testes ao longo do tempo. Consulte insights por meio da ferramenta MCP e2e_learnings:
| Consulta | Retorna |
|---|---|
summary | Visão geral completa da saúde: taxa de aprovação, testes instáveis, seletores instáveis, problemas de API |
flaky | Testes que passam apenas após novas tentativas |
selectors | Seletores CSS com altas taxas de falha |
pages | Páginas com erros de console, falhas de rede, problemas de tempo de carregamento |
apis | Endpoints de API com taxas de erro e latência (normalizados automaticamente: UUIDs, hashes, IDs) |
errors | Padrões de erro mais frequentes, categorizados |
trends | Taxa de aprovação ao longo do tempo (alterna automaticamente para horária quando todos os dados são de um único dia) |
test:<name> | Histórico detalhado de um teste específico |
page:<path> | Histórico detalhado de uma página específica |
selector:<value> | Histórico detalhado de um seletor específico |
Armazenamento e exportação:
- SQLite (
~/.e2e-runner/dashboard.db) — padrão, sem configuração - Grafo de conhecimento Neo4j — opcional, para análise baseada em relacionamentos. Gerencie por meio da ferramenta MCP
e2e_neo4joudocker compose - Relatório Markdown (
e2e/learnings.md) — gerado automaticamente após cada execução
Narração de teste: Cada execução de teste gera uma narrativa legível do que aconteceu passo a passo, visível na saída da CLI e no dashboard.
Tratamento de erros de rede — asserções, flag global, registro completo
Asserção explícita — coloque assert_no_network_errors após carregamentos críticos de página:
{ "type": "goto", "value": "/dashboard" },
{ "type": "wait", "selector": ".loaded" },
{ "type": "assert_no_network_errors" }
Flag global — defina failOnNetworkError: true para falhar automaticamente qualquer teste com erros de rede:
e2e-runner run --all --fail-on-network-error
Quando desabilitado (padrão), o runner ainda coleta e relata erros de rede — a resposta do MCP inclui um aviso quando os testes passam, mas têm erros de rede.
Registro completo de rede — todas as requisições XHR/fetch são capturadas com URL, método, status, duração, cabeçalhos de requisição/resposta e corpo da resposta (truncado em 50KB). Visualizável no dashboard com linhas expansíveis de detalhes de requisição.
Fluxo de detalhamento do MCP:
1. e2e_run → compact networkSummary + runDbId
2. e2e_network_logs(runDbId) → all requests (url, method, status, duration)
3. e2e_network_logs(runDbId, errorsOnly: true) → only failed requests
4. e2e_network_logs(runDbId, includeHeaders: true) → with headers
5. e2e_network_logs(runDbId, includeBodies: true) → full request/response bodies
A resposta do e2e_run permanece compacta (~5KB) independentemente de quantas requisições foram capturadas. Use e2e_network_logs com o runDbId retornado para detalhar sob demanda.
Captura de tela — snapshot de qualquer URL sob demanda
Capture capturas de tela de qualquer URL sob demanda — sem necessidade de suite de testes:
e2e-runner capture https://example.com
e2e-runner capture https://example.com --full-page --selector ".loaded" --delay 2000
Por meio do MCP, a ferramenta e2e_capture suporta authToken e authStorageKey para páginas autenticadas — ela injeta o token no localStorage antes de navegar.
Cada captura de tela recebe um hash determinístico (ss:a3f2b1c9). Use e2e_screenshot para recuperar qualquer captura de tela por hash — ela retorna a imagem com metadados (nome do teste, etapa, tipo).
🌐 Drivers de navegador
O runner pode se comunicar com vários mecanismos de navegador por meio de diferentes drivers. O padrão é auto — ele testa cada URL do pool e escolhe o driver certo por pool.
| Driver | Mecanismo | Sonda de detecção | Quando usar |
|---|---|---|---|
browserless | Chromium real via browserless | /pressure retorna JSON | Padrão. Execução de JS de nível de produção, screencast, comportamento completo do Chrome |
cdp | Compatível com CDP genérico (Chrome puro, etc.) | /json/version acessível | Fallback para qualquer servidor CDP que não seja um dos outros |
lightpanda | Lightpanda (Zig) | /json/version Browser=lightpanda | ~9× mais rápido, ~16× menos memória que o Chrome headless — ideal para testes de alto volume estilo scraping |
obscura | Obscura (Rust + V8) | /json/version Browser=obscura | ~30 MB de pegada de RAM, anti-detecção integrada (--stealth), permanece próximo do Chrome real via Puppeteer |
steel | Steel Browser | /v1/sessions retorna JSON | Ciclo de vida de sessão gerenciado, API REST para orquestração |
Escolha um driver por teste / force um por execução
{
"tests": [
{
"name": "checkout flow (heavy JS, real Chrome)",
"driver": "browserless",
"actions": [...]
},
{
"name": "scrape product page (lightweight)",
"driver": "obscura",
"fallbackDriver": "cdp",
"actions": [...]
}
]
}
driver é opcional. Se definido, apenas pools cujo driver detectado corresponda se tornam candidatos. fallbackDriver é opt-in explícito — sem ele, um driver de destino ausente falha o teste com uma mensagem clara. A ocupação do pool não aciona fallback; o runner aguarda dentro do conjunto filtrado.
Force um driver para uma execução inteira (as substituições da CLI vencem os campos por teste — útil para benchmarks A/B):
e2e-runner run --all --driver obscura
e2e-runner run --all --driver obscura --fallback-driver cdp
Executando cada driver localmente
# browserless (default) — managed by `pool start`
e2e-runner pool start
# Lightpanda — pool start uses templates/docker-compose-lightpanda.yml
e2e-runner pool start # with poolDriver: 'lightpanda' in config
# Obscura — install the binary and run it yourself
curl -LO https://github.com/h4ckf0r0day/obscura/releases/latest/download/obscura-x86_64-linux.tar.gz
tar xzf obscura-x86_64-linux.tar.gz
./obscura serve --port 9222 --stealth
# then point the runner at it: poolUrls: ['http://localhost:9222'], poolDriver: 'obscura'
⚙️ CLI, configuração e CI
Comandos da CLI
# Run tests
e2e-runner run --all # All suites
e2e-runner run --suite auth # Single suite
e2e-runner run --tests path/to.json # Specific file
e2e-runner run --inline '<json>' # Inline JSON
# Pool management (CLI only, not MCP)
e2e-runner pool start # Start Chrome container
e2e-runner pool stop # Stop Chrome container
e2e-runner pool status # Check pool health
# Issue-to-test
e2e-runner issue <url> # Fetch issue
e2e-runner issue <url> --generate # Generate test via AI
e2e-runner issue <url> --verify # Generate + run + report
# Dashboard
e2e-runner dashboard # Start web dashboard
# Other
e2e-runner list # List available suites
e2e-runner capture <url> # On-demand screenshot
e2e-runner init # Scaffold project
Opções da CLI
| Flag | Padrão | Descrição |
|---|---|---|
--base-url <url> | http://host.docker.internal:3000 | URL base do aplicativo |
--pool-url <ws> | ws://localhost:3333 | URL do WebSocket do pool do Chrome |
--concurrency <n> | 3 | Workers de teste paralelos |
--retries <n> | 0 | Repetir testes com falha N vezes |
--action-retries <n> | 0 | Repetir ações com falha N vezes |
--test-timeout <ms> | 60000 | Timeout por teste |
--timeout <ms> | 10000 | Timeout padrão de ação |
--output <format> | json | Relatório: json, junit, both |
--env <name> | default | Perfil de ambiente |
--fail-on-network-error | false | Falhar testes com erros de rede |
--project-name <name> | nome do diretório | Nome de exibição do projeto |
--driver <name> | (por teste) | Forçar driver do pool para a execução: browserless, cdp, lightpanda, obscura, steel |
--fallback-driver <name> | nenhum | Fallback explícito se nenhum pool com --driver estiver acessível |
Configuração — e2e.config.js e prioridade
Crie e2e.config.js na raiz do seu projeto:
export default {
baseUrl: 'http://host.docker.internal:3000',
concurrency: 4,
retries: 2,
actionRetries: 1,
testTimeout: 30000,
outputFormat: 'both',
failOnNetworkError: true,
exclude: ['explore-*', 'debug-*'],
hooks: {
beforeEach: [{ type: 'goto', value: '/' }],
},
environments: {
staging: { baseUrl: 'https://staging.example.com' },
production: { baseUrl: 'https://example.com', concurrency: 5 },
},
};
Prioridade de configuração (a maior vence):
- Flags da CLI
- Variáveis de ambiente
- Arquivo de configuração (
e2e.config.jsoue2e.config.json) - Padrões
Quando --env <name> está definido, o perfil correspondente substitui tudo.
CI/CD — JUnit XML e GitHub Actions
e2e-runner run --all --output junit
jobs:
e2e:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npx e2e-runner pool start
- run: npx e2e-runner run --all --output junit
- uses: mikepenz/action-junit-report@v4
if: always()
with:
report_paths: e2e/screenshots/junit.xml
API programática
import { createRunner } from '@matware/e2e-runner';
const runner = await createRunner({ baseUrl: 'http://localhost:3000' });
const report = await runner.runAll();
const report = await runner.runSuite('auth');
const report = await runner.runFile('e2e/tests/login.json');
const report = await runner.runTests([
{ name: 'quick-check', actions: [{ type: 'goto', value: '/' }] },
]);
Requisitos
- Node.js >= 20
- Docker — apenas para Opção 3 (o pool paralelo do Chrome). As Opções 1 e 2 não precisam dele.
Licença
Copyright 2026 Matias Aguirre (fastslack) — Matware
Licenciado sob a Apache License, Versão 2.0. Consulte LICENSE para detalhes.