@diegosouzacdv/jev-browser-mcp 0.5.0 → 0.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +301 -251
- package/config/ui-testing.json +28 -19
- package/docs/jev-browser-mcp.md +301 -251
- package/mcp_servers/jev-browser-npm/src/config.mjs +91 -55
- package/mcp_servers/jev-browser-npm/src/flow.mjs +2434 -1803
- package/mcp_servers/jev-browser-npm/src/jev-client.mjs +22 -9
- package/mcp_servers/jev-browser-npm/src/server.mjs +197 -113
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,10 +17,10 @@ versão em produção, use o número explícito no argumento do pacote:
|
|
|
17
17
|
"mcpServers": {
|
|
18
18
|
"jev-browser": {
|
|
19
19
|
"command": "npx",
|
|
20
|
-
"args": ["--yes", "@diegosouzacdv/jev-browser-mcp@0.
|
|
20
|
+
"args": ["--yes", "@diegosouzacdv/jev-browser-mcp@0.6.1"],
|
|
21
21
|
"env": {
|
|
22
22
|
"OPENROUTER_API_KEY": "${OPENROUTER_API_KEY}",
|
|
23
|
-
"JEV_BROWSER_MODE": "computer"
|
|
23
|
+
"JEV_BROWSER_MODE": "computer"
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
}
|
|
@@ -31,30 +31,30 @@ O formato de interpolação de variáveis varia por harness. Injete a chave por
|
|
|
31
31
|
um secret manager ou pelo ambiente do processo; não grave a chave no arquivo
|
|
32
32
|
de configuração. Para instalar no projeto Node do próprio harness:
|
|
33
33
|
|
|
34
|
-
```sh
|
|
35
|
-
npm install @diegosouzacdv/jev-browser-mcp
|
|
36
|
-
```
|
|
37
|
-
|
|
38
|
-
Se o harness executa o MCP repetidamente nesta máquina, instale uma versão
|
|
39
|
-
fixa globalmente para evitar a resolução e o download feitos pelo `npx` em cada
|
|
40
|
-
inicialização:
|
|
41
|
-
|
|
42
|
-
```sh
|
|
43
|
-
npm install --global @diegosouzacdv/jev-browser-mcp@0.
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Depois configure o servidor MCP com `command: "jev-browser-mcp"` e `args: []`.
|
|
47
|
-
Para atualizar, rode `npm install --global
|
|
48
|
-
@diegosouzacdv/jev-browser-mcp@<versão>` e reinicie o processo do harness.
|
|
49
|
-
|
|
50
|
-
O padrão `computer` exige Chrome ou Edge instalado. Se optar por `harness`,
|
|
51
|
-
configure `JEV_BROWSER_MODE=harness` antes de instalar o navegador gerenciado
|
|
52
|
-
pelo Playwright com `npx --yes @diegosouzacdv/jev-browser-mcp --install-browser`.
|
|
53
|
-
|
|
54
|
-
O padrão é `computer`: o pacote abre o Chrome/Edge instalado e usa um perfil
|
|
55
|
-
persistente exclusivo em `browser.computer_user_data_dir`. No modo `harness`, a
|
|
56
|
-
instalação baixa uma vez o Chrome/Edge que o Playwright controlará. Personalize
|
|
57
|
-
as escolhas com `JEV_BROWSER_MODE`, `JEV_BROWSER_CHANNEL` e
|
|
34
|
+
```sh
|
|
35
|
+
npm install @diegosouzacdv/jev-browser-mcp@0.6.1
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Se o harness executa o MCP repetidamente nesta máquina, instale uma versão
|
|
39
|
+
fixa globalmente para evitar a resolução e o download feitos pelo `npx` em cada
|
|
40
|
+
inicialização:
|
|
41
|
+
|
|
42
|
+
```sh
|
|
43
|
+
npm install --global @diegosouzacdv/jev-browser-mcp@0.6.1
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Depois configure o servidor MCP com `command: "jev-browser-mcp"` e `args: []`.
|
|
47
|
+
Para atualizar, rode `npm install --global
|
|
48
|
+
@diegosouzacdv/jev-browser-mcp@<versão>` e reinicie o processo do harness.
|
|
49
|
+
|
|
50
|
+
O padrão `computer` exige Chrome ou Edge instalado. Se optar por `harness`,
|
|
51
|
+
configure `JEV_BROWSER_MODE=harness` antes de instalar o navegador gerenciado
|
|
52
|
+
pelo Playwright com `npx --yes @diegosouzacdv/jev-browser-mcp@0.6.1 --install-browser`.
|
|
53
|
+
|
|
54
|
+
O padrão é `computer`: o pacote abre o Chrome/Edge instalado e usa um perfil
|
|
55
|
+
persistente exclusivo em `browser.computer_user_data_dir`. No modo `harness`, a
|
|
56
|
+
instalação baixa uma vez o Chrome/Edge que o Playwright controlará. Personalize
|
|
57
|
+
as escolhas com `JEV_BROWSER_MODE`, `JEV_BROWSER_CHANNEL` e
|
|
58
58
|
`JEV_BROWSER_PROFILE`. O browser permanece aquecido enquanto o processo MCP
|
|
59
59
|
estiver ativo e fecha quando o harness encerra o processo. `JEV_PROVIDER_URL`
|
|
60
60
|
e `JEV_MODEL` podem substituir os valores centrais; a credencial continua no
|
|
@@ -64,35 +64,41 @@ Uma aplicação Node também pode importar `createJevBrowserServer` por
|
|
|
64
64
|
`@diegosouzacdv/jev-browser-mcp/server` e conectar o servidor ao transporte MCP
|
|
65
65
|
que ela já utiliza.
|
|
66
66
|
|
|
67
|
-
O pacote é montado em uma pasta temporária isolada: o publicador usa o campo
|
|
68
|
-
`files` do `package.json` para copiar somente o código Node, a configuração
|
|
69
|
-
compartilhada e esta documentação, que também vira o `README.md` da raiz do
|
|
70
|
-
pacote. Assim, o npm não inclui o README geral do orquestrador na página do MCP.
|
|
71
|
-
Para gerar e conferir o pacote antes de publicar, execute
|
|
72
|
-
`npm run pack:jev-browser-mcp`; para publicar uma versão já autenticada no npm, execute
|
|
73
|
-
`npm run publish:jev-browser-mcp`. A instalação por npm é a recomendada; use a
|
|
74
|
-
referência GitHub somente quando precisar experimentar uma revisão ainda não
|
|
75
|
-
publicada.
|
|
67
|
+
O pacote é montado em uma pasta temporária isolada: o publicador usa o campo
|
|
68
|
+
`files` do `package.json` para copiar somente o código Node, a configuração
|
|
69
|
+
compartilhada e esta documentação, que também vira o `README.md` da raiz do
|
|
70
|
+
pacote. Assim, o npm não inclui o README geral do orquestrador na página do MCP.
|
|
71
|
+
Para gerar e conferir o pacote antes de publicar, execute
|
|
72
|
+
`npm run pack:jev-browser-mcp`; para publicar uma versão já autenticada no npm, execute
|
|
73
|
+
`npm run publish:jev-browser-mcp`. A instalação por npm é a recomendada; use a
|
|
74
|
+
referência GitHub somente quando precisar experimentar uma revisão ainda não
|
|
75
|
+
publicada.
|
|
76
76
|
|
|
77
77
|
### Contrato do pacote
|
|
78
78
|
|
|
79
|
-
O executável oferece `choose_next_action` e `run_browser_flow`, mantém uma
|
|
79
|
+
O executável oferece `browser_health`, `choose_next_action` e `run_browser_flow`, mantém uma
|
|
80
80
|
sessão do browser por processo e reutiliza essa sessão entre chamadas. O plano
|
|
81
81
|
passado ao Jev continua declarativo: clique, preenchimento, seleção nativa,
|
|
82
82
|
hover, espera, teclas aprovadas, asserções por elemento, upload restrito a uma
|
|
83
83
|
raiz local configurada e reações idempotentes a um comentário único. Também
|
|
84
|
-
aceita um nome de iframe para as ações que ocorrem dentro dele. Não aceita
|
|
85
|
-
JavaScript enviado pelo harness nem coordenadas. Além de papel/nome acessível,
|
|
86
|
-
aceita `label`, `placeholder`, `title`, `text`, `test_id` e `selector`; CSS/XPath
|
|
87
|
-
são o último recurso e geram um aviso no resultado. Ele usa a
|
|
88
|
-
biblioteca Playwright diretamente, sem iniciar um segundo servidor MCP do
|
|
84
|
+
aceita um nome de iframe para as ações que ocorrem dentro dele. Não aceita
|
|
85
|
+
JavaScript enviado pelo harness nem coordenadas. Além de papel/nome acessível,
|
|
86
|
+
aceita `label`, `placeholder`, `title`, `text`, `test_id` e `selector`; CSS/XPath
|
|
87
|
+
são o último recurso e geram um aviso no resultado. Ele usa a
|
|
88
|
+
biblioteca Playwright diretamente, sem iniciar um segundo servidor MCP do
|
|
89
89
|
Playwright. O transporte MCP usa `stdio`; toda saída de diagnóstico vai para
|
|
90
90
|
`stderr` para não misturar com JSON-RPC.
|
|
91
91
|
|
|
92
92
|
O modo `computer` grava cookies no perfil exclusivo configurado, e conteúdo da
|
|
93
93
|
página pode conter instruções maliciosas. O Jev recebe a captura acessível com
|
|
94
94
|
uma instrução para tratar esse conteúdo como dado não confiável; não inclua
|
|
95
|
-
segredos no fluxo, no resultado esperado ou nas descrições dos planos.
|
|
95
|
+
segredos no fluxo, no resultado esperado ou nas descrições dos planos. Antes de
|
|
96
|
+
enviar contexto ao provedor, o cliente mascara valores de campos e padrões
|
|
97
|
+
detectados de CPF/CNPJ, email, telefone, nome de cliente e valores monetários.
|
|
98
|
+
Isso reduz exposição acidental, mas não substitui o cuidado com os dados que o
|
|
99
|
+
harness escolhe incluir no fluxo. Use `local_only: true` quando nenhuma chamada
|
|
100
|
+
externa ao Jev puder ocorrer; esse modo recusa fluxos que precisam escolher
|
|
101
|
+
entre vários planos.
|
|
96
102
|
|
|
97
103
|
Para um fluxo conhecido, o LLM do harness procura o cenário e os critérios de
|
|
98
104
|
aceitação no projeto e chama `jev_browser.run_browser_flow` com planos
|
|
@@ -101,111 +107,132 @@ a página inicial, captura o snapshot acessível e pede ao Jev que escolha um
|
|
|
101
107
|
plano. Em seguida, executa o plano inteiro na mesma sessão e confere o
|
|
102
108
|
resultado esperado na tela.
|
|
103
109
|
|
|
104
|
-
Cada plano pode usar:
|
|
105
|
-
|
|
106
|
-
- `click`, `type`, `hover`, `select_option`, `press`, `assert_*` e upload com
|
|
107
|
-
papel/nome acessível ou um localizador: `label`, `placeholder`, `title`,
|
|
108
|
-
`text`, `test_id` ou `selector`;
|
|
109
|
-
- `near: {"text":"..."}` para localizar o controle logo depois de um texto,
|
|
110
|
-
`within: {"row_containing":"..."}` para limitar por trecho e
|
|
111
|
-
`within: {"row_containing_exact":"..."}` para exigir um elemento com o
|
|
112
|
-
texto exato na linha, sem casar com `15287210` ao procurar `1528721`;
|
|
113
|
-
- `within: {"role":"dialog"}` sem `name` para usar o único diálogo visível;
|
|
114
|
-
o MCP ignora diálogos visualmente ocultos, inclusive os que ficaram no DOM
|
|
115
|
-
com `opacity: 0`;
|
|
116
|
-
- `name: ""` com `index` não negativo para controles sem nome. O resultado
|
|
117
|
-
inclui um aviso porque a posição pode mudar entre execuções. O índice é
|
|
118
|
-
zero-based para o mesmo papel e inclui nomes vazios ou compostos só por
|
|
119
|
-
espaços/glyphs de uso privado, como ícones Font Awesome; controles
|
|
120
|
-
desabilitados não aparecem no diagnóstico, mas continuam contando para que o
|
|
121
|
-
índice aponte ao controle correto;
|
|
122
|
-
- `selector` com CSS ou XPath como último recurso. O MCP identifica essa escolha
|
|
123
|
-
em `warnings`; não é permitido enviar JavaScript nem coordenadas;
|
|
124
|
-
- `timeout_seconds` opcional em cada etapa, limitado pela configuração central;
|
|
125
|
-
- `type` com `mode: "keys"` para digitar sequencialmente em campos com máscara,
|
|
126
|
-
`blur: true` para desfocar o campo e `sensitive: false` para permitir que o
|
|
127
|
-
valor apareça nas evidências. Nesse modo, `\n` envia Enter e `\t` envia Tab;
|
|
128
|
-
- `wait_for_text`
|
|
129
|
-
`
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
110
|
+
Cada plano pode usar:
|
|
111
|
+
|
|
112
|
+
- `click`, `type`, `hover`, `select_option`, `press`, `assert_*` e upload com
|
|
113
|
+
papel/nome acessível ou um localizador: `label`, `placeholder`, `title`,
|
|
114
|
+
`text`, `test_id` ou `selector`;
|
|
115
|
+
- `near: {"text":"..."}` para localizar o controle logo depois de um texto,
|
|
116
|
+
`within: {"row_containing":"..."}` para limitar por trecho e
|
|
117
|
+
`within: {"row_containing_exact":"..."}` para exigir um elemento com o
|
|
118
|
+
texto exato na linha, sem casar com `15287210` ao procurar `1528721`;
|
|
119
|
+
- `within: {"role":"dialog"}` sem `name` para usar o único diálogo visível;
|
|
120
|
+
o MCP ignora diálogos visualmente ocultos, inclusive os que ficaram no DOM
|
|
121
|
+
com `opacity: 0`;
|
|
122
|
+
- `name: ""` com `index` não negativo para controles sem nome. O resultado
|
|
123
|
+
inclui um aviso porque a posição pode mudar entre execuções. O índice é
|
|
124
|
+
zero-based para o mesmo papel e inclui nomes vazios ou compostos só por
|
|
125
|
+
espaços/glyphs de uso privado, como ícones Font Awesome; controles
|
|
126
|
+
desabilitados não aparecem no diagnóstico, mas continuam contando para que o
|
|
127
|
+
índice aponte ao controle correto;
|
|
128
|
+
- `selector` com CSS ou XPath como último recurso. O MCP identifica essa escolha
|
|
129
|
+
em `warnings`; não é permitido enviar JavaScript nem coordenadas;
|
|
130
|
+
- `timeout_seconds` opcional em cada etapa, limitado pela configuração central;
|
|
131
|
+
- `type` com `mode: "keys"` para digitar sequencialmente em campos com máscara,
|
|
132
|
+
`blur: true` para desfocar o campo e `sensitive: false` para permitir que o
|
|
133
|
+
valor apareça nas evidências. Nesse modo, `\n` envia Enter e `\t` envia Tab;
|
|
134
|
+
- `wait_for_text`, `wait_for_value`, `wait_for_enabled` e `wait_for_condition`
|
|
135
|
+
(`network_idle`, `hidden`, `text_hidden` ou `angular_idle`). `wait` recebe
|
|
136
|
+
`ms` para uma pausa curta e limitada. `angular_idle` aguarda requisições
|
|
137
|
+
`$http` e digest do AngularJS. `wait_for_text` aceita `fail_on: {"role":"alert"}` para
|
|
138
|
+
interromper a espera assim que um alerta visível aparecer e incluir seu texto
|
|
139
|
+
no erro. Se o alerta contiver o texto esperado, a etapa passa; caso contrário,
|
|
140
|
+
o alerta interrompe a espera. `network_idle` aceita `url_contains` para
|
|
141
|
+
aguardar só as requisições correspondentes;
|
|
142
|
+
- `press` (o alias legado `press_key` também é aceito) com `PageDown`, `PageUp`,
|
|
143
|
+
`Home`, `End`, setas, `Enter`, `Escape`, `Tab` ou `Shift+Tab`. Sem alvo,
|
|
144
|
+
envia a tecla ao elemento focado; com alvo, usa o localizador informado;
|
|
145
|
+
- `assert_text`, `assert_value`, `assert_visible`, `assert_hidden` e
|
|
146
|
+
`assert_enabled`; `assert_text`
|
|
147
|
+
pode receber apenas `role` quando a região, como `alert`, não tem nome acessível;
|
|
139
148
|
- `upload_file` para input rotulado, botão que abre o seletor de arquivo ou
|
|
140
149
|
dropzone;
|
|
141
150
|
- `audit_accessibility` com axe-core para WCAG 2.1 A/AA;
|
|
142
|
-
- `like_comment` e `unlike_comment`, que localizam uma linha pelo autor e texto,
|
|
143
|
-
não repetem uma reação já no estado pedido e distinguem `Curtir` de
|
|
144
|
-
`Descurtir`.
|
|
145
|
-
|
|
146
|
-
As proteções e evidências por etapa usam estes campos:
|
|
147
|
-
|
|
148
|
-
- `confirm_dialog` recebe `expected_text` e `button`.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
`
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
`
|
|
172
|
-
|
|
173
|
-
`
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
`
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
151
|
+
- `like_comment` e `unlike_comment`, que localizam uma linha pelo autor e texto,
|
|
152
|
+
não repetem uma reação já no estado pedido e distinguem `Curtir` de
|
|
153
|
+
`Descurtir`.
|
|
154
|
+
|
|
155
|
+
As proteções e evidências por etapa usam estes campos:
|
|
156
|
+
|
|
157
|
+
- `confirm_dialog` recebe `expected_text` e `button`. Em diálogos HTML, o MCP
|
|
158
|
+
confere o texto antes de localizar e clicar no botão. Também trata diálogos
|
|
159
|
+
nativos `alert`/`confirm`: precisa haver uma etapa `confirm_dialog` logo após
|
|
160
|
+
o clique que os abre, o texto deve corresponder e um diálogo inesperado é
|
|
161
|
+
fechado sem aceitar;
|
|
162
|
+
- `mutating: true` marca etapas que gravam dados ou disparam efeitos externos.
|
|
163
|
+
O MCP também classifica botões conhecidos como `Salvar`, `Emitir`, `Cancelar`
|
|
164
|
+
ou `Sim` pelo nome e pelo controle resolvido, inclusive quando o plano usa um
|
|
165
|
+
seletor CSS. `options.dry_run: true` executa até a primeira etapa mutável e
|
|
166
|
+
para antes dela; retorna `dry_run_stopped_before_step` e um
|
|
167
|
+
`confirmation_token` temporário, de uso único e vinculado ao fluxo e à página.
|
|
168
|
+
Reenvie a mesma chamada com `options.confirmation_token` para autorizar essa
|
|
169
|
+
etapa. O MCP pausa novamente antes de cada outra etapa mutável. Sem `dry_run`,
|
|
170
|
+
o primeiro pedido de ação mutável também retorna `status: "confirmation_required"`
|
|
171
|
+
e token; nenhuma etapa mutável roda sem essa autorização. O token expira após
|
|
172
|
+
dez minutos por padrão;
|
|
173
|
+
- `duration_ms` aparece em cada etapa concluída ou falha. `mutating_steps`
|
|
174
|
+
lista etapas mutáveis que foram executadas ou falharam;
|
|
175
|
+
- `screenshot: true` salva uma captura depois da etapa e inclui seu caminho na
|
|
176
|
+
evidência da etapa;
|
|
177
|
+
- `options.report_path` grava um resumo `.md` ou JUnit `.xml`. O caminho deve
|
|
178
|
+
ser absoluto e estar dentro de `JEV_BROWSER_ARTIFACT_DIR` (ou do diretório de
|
|
179
|
+
artefatos configurado). O relatório resume versão/status, duração, alvo e
|
|
180
|
+
`resolved_target`, asserções de rede, falhas de rede e caminhos das capturas.
|
|
181
|
+
|
|
182
|
+
`run_browser_flow` também aceita `params` como mapa de texto, números e
|
|
183
|
+
booleanos. Use `{pedido}` em `flow`, `expected_outcome` e nos campos textuais do
|
|
184
|
+
plano para reutilizar um valor sem editar o roteiro em vários lugares. A ação
|
|
185
|
+
`extract` lê `text` (padrão), `value` ou `attribute` de um elemento e salva o
|
|
186
|
+
resultado na variável indicada por `as`; etapas seguintes podem usar
|
|
187
|
+
`{documento}`. O valor extraído é tratado como sensível e fica oculto no
|
|
188
|
+
resultado por padrão; use `sensitive: false` só quando for apropriado exibi-lo.
|
|
189
|
+
`resolved_target.match_strategy` informa quando um rótulo foi
|
|
190
|
+
associado por proximidade (`label-proximity`) em vez de um `label[for]` direto.
|
|
191
|
+
|
|
192
|
+
A ação `assert_network` verifica respostas observadas depois da etapa anterior,
|
|
193
|
+
incluindo respostas 2xx ou erros esperados como 404. Exemplo:
|
|
194
|
+
|
|
195
|
+
```json
|
|
196
|
+
{
|
|
197
|
+
"action": "assert_network",
|
|
198
|
+
"url_contains": "/documentoFinanceiro/atualizar",
|
|
199
|
+
"method": "PUT",
|
|
200
|
+
"status": 404,
|
|
201
|
+
"message_contains": "Boleto não encontrado"
|
|
202
|
+
}
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Essa asserção aparece na evidência como `response_status`, separado do campo
|
|
206
|
+
`status` da etapa (`passed` ou `failed`). `message_contains` lê somente resposta
|
|
207
|
+
da mesma origem e respeita o limite configurado para captura de corpos.
|
|
208
|
+
|
|
209
|
+
Exemplo de parâmetro e extração: passe `params: {"pedido":"1528721"}`, filtre
|
|
210
|
+
com `within: {"row_containing_exact":"{pedido}"}`, e use uma etapa
|
|
211
|
+
`{"action":"extract","role":"cell","name":"{pedido}","within":{"row_containing_exact":"{pedido}"},"as":"documento"}`.
|
|
212
|
+
A etapa seguinte pode localizar o mesmo documento com `name: "{documento}"`.
|
|
213
|
+
|
|
214
|
+
Em `wait_for_condition` com `condition: "hidden"`, use qualquer um desses
|
|
215
|
+
localizadores, `near` ou `within`; `text_hidden` recebe o texto direto. Para
|
|
216
|
+
aguardar o fim de uma chamada específica, use `condition: "network_idle"` com
|
|
217
|
+
`url_contains`; o MCP precisa observar a requisição depois da etapa anterior e
|
|
218
|
+
esperar que ela termine.
|
|
219
|
+
|
|
220
|
+
Quando vários controles têm o mesmo papel e nome, `within` limita a busca a um
|
|
221
|
+
container acessível único, como uma linha, card ou diálogo. `index` escolhe uma
|
|
222
|
+
ocorrência zero-based dentro desse escopo; sem `index`, o MCP exige exatamente
|
|
223
|
+
um alvo. Para uma página com rótulo `for` quebrado, `label` também procura o
|
|
224
|
+
controle próximo ao texto visível do rótulo.
|
|
225
|
+
|
|
226
|
+
`within` pode combinar um container e uma linha. Use, por exemplo,
|
|
227
|
+
`{"role":"cell","name":"Documento A","within":{"role":"table","row_containing_word":"1528721"}}`.
|
|
228
|
+
`row_containing_word` usa limites de palavra para não confundir `1528721` com
|
|
229
|
+
`15287210`; `row_containing_exact` continua disponível para texto de célula
|
|
230
|
+
exato. `check` e `uncheck` alteram checkboxes, e `select_option` aceita rótulo
|
|
231
|
+
exato (`option`), valor (`value`) ou rótulo parcial único (`label_contains`).
|
|
232
|
+
`navigate_menu` percorre uma lista de rótulos exatos e abre `#open_btn` quando o
|
|
233
|
+
primeiro item ainda está oculto. Se vários itens visíveis tiverem o mesmo
|
|
234
|
+
rótulo, seleciona a última ocorrência, útil quando o submenu recém-aberto
|
|
235
|
+
repete o nome da categoria pai.
|
|
209
236
|
|
|
210
237
|
```json
|
|
211
238
|
{
|
|
@@ -217,51 +244,52 @@ controle próximo ao texto visível do rótulo.
|
|
|
217
244
|
```
|
|
218
245
|
|
|
219
246
|
```json
|
|
220
|
-
{"action":"click","role":"button","name":"Add to cart","index":0}
|
|
221
|
-
```
|
|
222
|
-
|
|
223
|
-
Exemplos para controles legados sem nome acessível:
|
|
224
|
-
|
|
225
|
-
```json
|
|
226
|
-
{"action":"type","role":"textbox","label":"Vencimento","text":"05/10/2026"}
|
|
227
|
-
{"action":"type","role":"textbox","target_text":"Vencimento","text":"05/10/2026"}
|
|
228
|
-
{"action":"click","role":"link","title":"Editar documento","within":{"row_containing":"1528721"}}
|
|
229
|
-
{"action":"press","key":"Enter","role":"textbox","placeholder":"Busca rápida"}
|
|
230
|
-
{"action":"click","role":"button","name":"","index":0}
|
|
231
|
-
{"action":"click","selector":"#save-document"}
|
|
232
|
-
```
|
|
233
|
-
|
|
234
|
-
O reconhecimento retorna `unnamed_controls_initial` e
|
|
235
|
-
`unnamed_controls_final`, cada um com papel, índice Playwright por papel, rótulo
|
|
236
|
-
mais próximo e um trecho HTML sanitizado dos controles visíveis e habilitados
|
|
237
|
-
sem nome. Nomes vazios ou compostos apenas por espaços e glyphs da área Unicode
|
|
238
|
-
de uso privado (como ícones Font Awesome) entram nessa lista e podem ser
|
|
239
|
-
selecionados com `name: ""` e o mesmo índice. `unnamed_controls` continua
|
|
240
|
-
disponível como alias da lista final. O placeholder conta como nome acessível.
|
|
241
|
-
O snapshot também resume campos de formulário com `id`, `name`, valor, estado
|
|
242
|
-
desabilitado, rótulo e índice zero-based entre campos de mesmo nome. Valores
|
|
243
|
-
sensíveis, como senha, token e cartão, são ocultados. `snapshot_include_hidden`
|
|
244
|
-
é `false` por padrão; defina `true` somente quando precisar inspecionar campos
|
|
245
|
-
ocultos também.
|
|
246
|
-
|
|
247
|
-
Em uma etapa `type`, `text` contém o valor a digitar; use `target_text` para
|
|
248
|
-
localizar pelo texto visível próximo ao campo. Esse localizador exige `role`
|
|
249
|
-
`textbox`, `searchbox` ou `combobox`.
|
|
250
|
-
|
|
251
|
-
Por padrão, o MCP executa todos os passos antes de avaliar `expected_outcome`.
|
|
252
|
-
Valores dentro de campos não contam como resultado visível. `stop_on_expected:
|
|
253
|
-
true` habilita parada antecipada quando o texto esperado aparece fora dos
|
|
254
|
-
campos; mantenha `false` para fluxos com várias etapas.
|
|
255
|
-
|
|
256
|
-
Os erros de validação apontam o campo inválido e os campos aceitos. Os limites
|
|
257
|
-
de `timeout_seconds` informam o máximo configurado, em vez de exigir tentativa
|
|
258
|
-
e erro.
|
|
247
|
+
{"action":"click","role":"button","name":"Add to cart","index":0}
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Exemplos para controles legados sem nome acessível:
|
|
251
|
+
|
|
252
|
+
```json
|
|
253
|
+
{"action":"type","role":"textbox","label":"Vencimento","text":"05/10/2026"}
|
|
254
|
+
{"action":"type","role":"textbox","target_text":"Vencimento","text":"05/10/2026"}
|
|
255
|
+
{"action":"click","role":"link","title":"Editar documento","within":{"row_containing":"1528721"}}
|
|
256
|
+
{"action":"press","key":"Enter","role":"textbox","placeholder":"Busca rápida"}
|
|
257
|
+
{"action":"click","role":"button","name":"","index":0}
|
|
258
|
+
{"action":"click","selector":"#save-document"}
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
O reconhecimento retorna `unnamed_controls_initial` e
|
|
262
|
+
`unnamed_controls_final`, cada um com papel, índice Playwright por papel, rótulo
|
|
263
|
+
mais próximo e um trecho HTML sanitizado dos controles visíveis e habilitados
|
|
264
|
+
sem nome. Nomes vazios ou compostos apenas por espaços e glyphs da área Unicode
|
|
265
|
+
de uso privado (como ícones Font Awesome) entram nessa lista e podem ser
|
|
266
|
+
selecionados com `name: ""` e o mesmo índice. `unnamed_controls` continua
|
|
267
|
+
disponível como alias da lista final. O placeholder conta como nome acessível.
|
|
268
|
+
O snapshot também resume campos de formulário com `id`, `name`, valor, estado
|
|
269
|
+
desabilitado, rótulo e índice zero-based entre campos de mesmo nome. Valores
|
|
270
|
+
sensíveis, como senha, token e cartão, são ocultados. `snapshot_include_hidden`
|
|
271
|
+
é `false` por padrão; defina `true` somente quando precisar inspecionar campos
|
|
272
|
+
ocultos também.
|
|
273
|
+
|
|
274
|
+
Em uma etapa `type`, `text` contém o valor a digitar; use `target_text` para
|
|
275
|
+
localizar pelo texto visível próximo ao campo. Esse localizador exige `role`
|
|
276
|
+
`textbox`, `searchbox` ou `combobox`.
|
|
277
|
+
|
|
278
|
+
Por padrão, o MCP executa todos os passos antes de avaliar `expected_outcome`.
|
|
279
|
+
Valores dentro de campos não contam como resultado visível. `stop_on_expected:
|
|
280
|
+
true` habilita parada antecipada quando o texto esperado aparece fora dos
|
|
281
|
+
campos; mantenha `false` para fluxos com várias etapas.
|
|
282
|
+
|
|
283
|
+
Os erros de validação apontam o campo inválido e os campos aceitos. Os limites
|
|
284
|
+
de `timeout_seconds` informam o máximo configurado, em vez de exigir tentativa
|
|
285
|
+
e erro.
|
|
259
286
|
|
|
260
287
|
### Captura de downloads
|
|
261
288
|
|
|
262
289
|
Marque o clique que deve iniciar um download com `expect_download: true`. O
|
|
263
|
-
Playwright começa a aguardar o evento antes do clique;
|
|
264
|
-
|
|
290
|
+
Playwright começa a aguardar o evento antes do clique; isso também captura
|
|
291
|
+
downloads gerados por `URL.createObjectURL` (por exemplo, PDFs Blob). Após a
|
|
292
|
+
transferência terminar, o MCP verifica o tamanho, sanitiza o nome sugerido e copia o arquivo
|
|
265
293
|
para `JEV_BROWSER_ARTIFACT_DIR` (ou para o diretório de artefatos configurado).
|
|
266
294
|
O resultado inclui a lista `downloaded_files`, com nome, caminho local e bytes;
|
|
267
295
|
cada passo também contém o arquivo capturado. Os limites contam arquivos e bytes
|
|
@@ -292,13 +320,13 @@ trechos da página. Qualquer violação reprova esse fluxo. A auditoria automát
|
|
|
292
320
|
encontra problemas comuns, mas não comprova conformidade WCAG completa; combine-a
|
|
293
321
|
com revisão manual e testes com usuários assistivos.
|
|
294
322
|
|
|
295
|
-
Para ações em iframe, acrescente `"frame": "payment-iframe"`; o valor precisa
|
|
296
|
-
corresponder ao atributo `name` ou `title` do iframe. O snapshot inclui o
|
|
297
|
-
conteúdo acessível dos iframes nomeados dentro do escopo, limitado pela
|
|
298
|
-
configuração. Se um alvo estiver ausente, o erro inclui até três nomes acessíveis
|
|
299
|
-
próximos quando o snapshot os encontrar. CSS/XPath são permitidos somente como
|
|
300
|
-
localizadores explícitos de último recurso e geram aviso. O plano não aceita
|
|
301
|
-
JavaScript enviado pelo harness nem coordenadas.
|
|
323
|
+
Para ações em iframe, acrescente `"frame": "payment-iframe"`; o valor precisa
|
|
324
|
+
corresponder ao atributo `name` ou `title` do iframe. O snapshot inclui o
|
|
325
|
+
conteúdo acessível dos iframes nomeados dentro do escopo, limitado pela
|
|
326
|
+
configuração. Se um alvo estiver ausente, o erro inclui até três nomes acessíveis
|
|
327
|
+
próximos quando o snapshot os encontrar. CSS/XPath são permitidos somente como
|
|
328
|
+
localizadores explícitos de último recurso e geram aviso. O plano não aceita
|
|
329
|
+
JavaScript enviado pelo harness nem coordenadas.
|
|
302
330
|
|
|
303
331
|
Os aliases `value` → `text` em `type`/`wait_for_text` e `value` → `expected` em
|
|
304
332
|
asserções são aceitos. `comment` também é aceito em planos e passos, mas é
|
|
@@ -381,9 +409,9 @@ do pacote Node ficam no bloco opcional `jev_browser_mcp`, que não altera o
|
|
|
381
409
|
contrato lido pelo servidor Python. Preserve a estrutura completa exigida pelos
|
|
382
410
|
validadores.
|
|
383
411
|
|
|
384
|
-
`browser.mode` aceita `harness` ou `computer`; o padrão é `computer`:
|
|
412
|
+
`browser.mode` aceita `harness` ou `computer`; o padrão é `computer`:
|
|
385
413
|
|
|
386
|
-
- `harness` usa Chrome headless e contexto isolado, adequado a execuções do
|
|
414
|
+
- `harness` usa Chrome headless e contexto isolado, adequado a execuções do
|
|
387
415
|
harness e CI; o estado de autenticação é descartado ao final da chamada.
|
|
388
416
|
- `computer` abre o Chrome ou Edge instalado em modo visível e usa o diretório
|
|
389
417
|
persistente `browser.computer_user_data_dir`, separado por navegador. Não
|
|
@@ -392,23 +420,23 @@ validadores.
|
|
|
392
420
|
|
|
393
421
|
`browser.max_flow_steps` limita a soma de passos declarados entre os planos e
|
|
394
422
|
`browser.max_text_entry_chars` limita cada valor digitado. O resultado contém
|
|
395
|
-
`status`, o plano escolhido, as ações executadas, a última captura acessível e
|
|
396
|
-
se o critério esperado apareceu. Quando existem asserções explícitas, `status`
|
|
397
|
-
também pode ser `passed` com todas elas satisfeitas; `assertions_passed` registra
|
|
398
|
-
esse resultado e `expected_outcome_visible` continua descrevendo somente o
|
|
399
|
-
texto global. `incomplete` significa que nenhum critério foi comprovado;
|
|
400
|
-
confiança do Jev não substitui essa verificação. `timings_ms.ready_ms` mede a
|
|
401
|
-
espera da SPA; `warnings` registra capturas vazias durante transições; e
|
|
402
|
-
`failed_step` identifica índice, ação, localizador, timeout e erro resumido
|
|
403
|
-
quando uma etapa falha. Cada etapa executada também registra a composição do
|
|
404
|
-
localizador usado e, quando disponível, `resolved_target` com tag, `id`, papel,
|
|
405
|
-
nome acessível, `title` casado e `href` sanitizado do elemento resolvido.
|
|
406
|
-
Etapas `type` só incluem o valor final do campo quando `sensitive: false`.
|
|
407
|
-
|
|
408
|
-
Com `capture_network_error_bodies: true`, respostas JSON de erro da mesma
|
|
409
|
-
origem são capturadas mesmo quando usam transferência chunked e não enviam
|
|
410
|
-
`Content-Length`. Corpos acima do limite, com formato inválido ou que não
|
|
411
|
-
podem ser lidos com segurança são omitidos e explicados em `warnings`.
|
|
423
|
+
`status`, o plano escolhido, as ações executadas, a última captura acessível e
|
|
424
|
+
se o critério esperado apareceu. Quando existem asserções explícitas, `status`
|
|
425
|
+
também pode ser `passed` com todas elas satisfeitas; `assertions_passed` registra
|
|
426
|
+
esse resultado e `expected_outcome_visible` continua descrevendo somente o
|
|
427
|
+
texto global. `incomplete` significa que nenhum critério foi comprovado;
|
|
428
|
+
confiança do Jev não substitui essa verificação. `timings_ms.ready_ms` mede a
|
|
429
|
+
espera da SPA; `warnings` registra capturas vazias durante transições; e
|
|
430
|
+
`failed_step` identifica índice, ação, localizador, timeout e erro resumido
|
|
431
|
+
quando uma etapa falha. Cada etapa executada também registra a composição do
|
|
432
|
+
localizador usado e, quando disponível, `resolved_target` com tag, `id`, papel,
|
|
433
|
+
nome acessível, `title` casado e `href` sanitizado do elemento resolvido.
|
|
434
|
+
Etapas `type` só incluem o valor final do campo quando `sensitive: false`.
|
|
435
|
+
|
|
436
|
+
Com `capture_network_error_bodies: true`, respostas JSON de erro da mesma
|
|
437
|
+
origem são capturadas mesmo quando usam transferência chunked e não enviam
|
|
438
|
+
`Content-Length`. Corpos acima do limite, com formato inválido ou que não
|
|
439
|
+
podem ser lidos com segurança são omitidos e explicados em `warnings`.
|
|
412
440
|
|
|
413
441
|
`jev_browser_mcp.browser.max_action_timeout_seconds` limita esperas por ações,
|
|
414
442
|
seletores e condições. `JEV_BROWSER_UPLOAD_ROOT` libera uploads somente dentro
|
|
@@ -424,54 +452,76 @@ Os limites de download ficam no mesmo bloco: `max_download_files`,
|
|
|
424
452
|
limita quantas descrições de violações axe entram no resultado; a contagem total
|
|
425
453
|
continua informada mesmo quando a lista é truncada.
|
|
426
454
|
|
|
427
|
-
O `run_browser_flow` aceita `options` com `fast_path`, `snapshot_scope`,
|
|
428
|
-
`block_trackers`, `
|
|
429
|
-
`
|
|
430
|
-
`
|
|
431
|
-
`
|
|
432
|
-
`
|
|
433
|
-
|
|
434
|
-
`
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
`
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
455
|
+
O `run_browser_flow` aceita `options` com `fast_path`, `snapshot_scope`,
|
|
456
|
+
`block_trackers`, `block_fonts`, `local_only`, `busy_selectors`,
|
|
457
|
+
`auto_angular_idle`, `login_url_contains`, `login_text`, `capture_console_errors`,
|
|
458
|
+
`capture_network_errors`, `capture_network_error_bodies`, `ready_timeout_seconds`,
|
|
459
|
+
`ready_network_idle`, `ready_stable_ms`, `ready_text`, `continue_from_current_page`,
|
|
460
|
+
`reuse_page`, `step_timeout_seconds`, `max_flow_steps`, `return_snapshot`,
|
|
461
|
+
`screenshot_on_failure`, `trace_on_failure`, `snapshot_include_hidden`,
|
|
462
|
+
`stop_on_expected`, `dry_run`, `confirmation_token` e `report_path`. Sem override,
|
|
463
|
+
os padrões são lidos de `jev_browser_mcp` em
|
|
464
|
+
`config/ui-testing.json`: um plano candidato pula a chamada Decisions;
|
|
465
|
+
captura de console e screenshot de falha ficam ligadas; trace, rede e bloqueio
|
|
466
|
+
de recursos ficam desligados; o plano roda até o fim e campos ocultos não são
|
|
467
|
+
acrescentados ao snapshot. O browser espera a SPA renderizar uma captura
|
|
468
|
+
acessível estável na navegação inicial. Depois, a continuidade fica ligada por
|
|
469
|
+
padrão: `continue_from_current_page: true` mantém a página e seu estado entre
|
|
470
|
+
chamadas na mesma origem, não repete `page.goto` e consulta um snapshot atual
|
|
471
|
+
sem esperar novamente por `load`/`networkidle`. As etapas continuam aguardando
|
|
472
|
+
seus próprios alvos e condições. `reuse_page` segue como alias legado; defina
|
|
473
|
+
`continue_from_current_page: false` para iniciar cada chamada em `initial_url`.
|
|
474
|
+
Se a página atual e `initial_url` tiverem origens diferentes, o MCP navega para
|
|
475
|
+
`initial_url`. Se forem da mesma origem, continua a página atual e avisa quando
|
|
476
|
+
as URLs completas forem diferentes. `snapshot_scope` aceita `body`, `main` ou
|
|
477
|
+
`dialog`; se `main` não existir, o snapshot usa `body`.
|
|
478
|
+
|
|
479
|
+
`block_trackers: true` bloqueia analytics/trackers e os tipos de recurso
|
|
480
|
+
configurados. Fontes ficam habilitadas por padrão para preservar ícones e
|
|
481
|
+
glyphs; `block_fonts: true` bloqueia fontes separadamente. `busy_selectors`
|
|
482
|
+
complementa `aria-busy="true"` ao aguardar overlays de carregamento.
|
|
483
|
+
`auto_angular_idle` aguarda AngularJS depois de cliques e digitação quando a
|
|
484
|
+
página expõe o injector. `login_url_contains` e `login_text` substituem a
|
|
485
|
+
detecção padrão de autenticação. `local_only: true` recusa qualquer etapa que
|
|
486
|
+
precisaria chamar o provedor Jev; use exatamente um plano com `fast_path: true`.
|
|
487
|
+
`step_timeout_seconds` define o timeout padrão por etapa e cada etapa pode
|
|
488
|
+
substituí-lo com `timeout_seconds`. `max_flow_steps` reduz o limite por chamada,
|
|
489
|
+
sem superar o teto da configuração. `return_snapshot` aceita `full`, `diff` ou
|
|
490
|
+
`none`; `diff` retorna linhas acessíveis novas desde o snapshot inicial.
|
|
446
491
|
|
|
447
492
|
Com `capture_console_errors` e `capture_network_errors`, o retorno traz
|
|
448
493
|
`console_errors` e `network_failures`, limitados em quantidade e tamanho.
|
|
449
|
-
Respostas HTTP 4xx/5xx e requisições que falham são listadas; query strings,
|
|
450
|
-
fragmentos, valores de formulário e nomes de arquivo são removidos ou
|
|
451
|
-
sanitizados. `capture_network_error_bodies: true` lê somente respostas JSON
|
|
452
|
-
4xx/5xx da mesma origem, limita o corpo e inclui apenas o campo textual
|
|
453
|
-
`message`, também sanitizado. Em falhas, `screenshot_on_failure` salva
|
|
454
|
-
screenshot local e retorna
|
|
494
|
+
Respostas HTTP 4xx/5xx e requisições que falham são listadas; query strings,
|
|
495
|
+
fragmentos, valores de formulário e nomes de arquivo são removidos ou
|
|
496
|
+
sanitizados. `capture_network_error_bodies: true` lê somente respostas JSON
|
|
497
|
+
4xx/5xx da mesma origem, limita o corpo e inclui apenas o campo textual
|
|
498
|
+
`message`, também sanitizado. Em falhas, `screenshot_on_failure` salva
|
|
499
|
+
screenshot local e retorna
|
|
455
500
|
`screenshot_path`. `trace_on_failure: true` também grava um `.zip` compatível
|
|
456
501
|
com o Trace Viewer do Playwright e retorna `trace_path`. O diretório padrão é
|
|
457
502
|
`~/.cache/orquestrador/jev-browser-artifacts`; `JEV_BROWSER_ARTIFACT_DIR` pode
|
|
458
503
|
substituí-lo por uma pasta absoluta fora do pacote. Screenshots e traces podem
|
|
459
504
|
conter dados visíveis da aplicação: mantenha o diretório local protegido e
|
|
460
|
-
compartilhe os arquivos somente se o teste permitir.
|
|
461
|
-
|
|
462
|
-
Screenshots por etapa usam `screenshot: true` no próprio passo; o MCP os grava
|
|
463
|
-
depois que a ação termina. `options.report_path` pode apontar para `.md` ou
|
|
464
|
-
JUnit `.xml` dentro da raiz de artefatos. O relatório inclui a versão, tempos,
|
|
465
|
-
localizadores resolvidos, respostas verificadas por `assert_network`, falhas de
|
|
466
|
-
rede e caminhos dos screenshots, para anexar a um PR ou card.
|
|
467
|
-
|
|
468
|
-
Se o Chrome/Edge não iniciar porque o perfil persistente já está aberto, o erro
|
|
469
|
-
identifica o PID que o mantém ocupado quando o sistema consegue associar o
|
|
470
|
-
perfil ao processo. Em Windows, o MCP consulta a linha de comando dos processos
|
|
471
|
-
Chrome/Edge; em outros sistemas, usa o `SingletonLock` do Chromium. Configure
|
|
472
|
-
`JEV_BROWSER_PROFILE` com outro diretório absoluto para usar uma sessão isolada.
|
|
473
|
-
Resultados MCP incluem `server_version`; erros também começam com a versão do
|
|
474
|
-
servidor para facilitar a comparação entre instalações.
|
|
505
|
+
compartilhe os arquivos somente se o teste permitir.
|
|
506
|
+
|
|
507
|
+
Screenshots por etapa usam `screenshot: true` no próprio passo; o MCP os grava
|
|
508
|
+
depois que a ação termina. `options.report_path` pode apontar para `.md` ou
|
|
509
|
+
JUnit `.xml` dentro da raiz de artefatos. O relatório inclui a versão, tempos,
|
|
510
|
+
localizadores resolvidos, respostas verificadas por `assert_network`, falhas de
|
|
511
|
+
rede e caminhos dos screenshots, para anexar a um PR ou card.
|
|
512
|
+
|
|
513
|
+
Se o Chrome/Edge não iniciar porque o perfil persistente já está aberto, o erro
|
|
514
|
+
identifica o PID que o mantém ocupado quando o sistema consegue associar o
|
|
515
|
+
perfil ao processo. Em Windows, o MCP consulta a linha de comando dos processos
|
|
516
|
+
Chrome/Edge; em outros sistemas, usa o `SingletonLock` do Chromium. Configure
|
|
517
|
+
`JEV_BROWSER_PROFILE` com outro diretório absoluto para usar uma sessão isolada.
|
|
518
|
+
Resultados MCP incluem `server_version`; erros também começam com a versão do
|
|
519
|
+
servidor para facilitar a comparação entre instalações. A ferramenta
|
|
520
|
+
`browser_health` informa se a sessão está ativa e tenta reconectar um browser
|
|
521
|
+
que encerrou desde a chamada anterior. Passe `{"restart":true}` para fechar e
|
|
522
|
+
reiniciar somente o navegador administrado por este processo MCP. Um fluxo pode
|
|
523
|
+
ser repetido automaticamente uma vez após `BROWSER_DISCONNECTED` quando nenhuma
|
|
524
|
+
etapa mutável foi executada.
|
|
475
525
|
|
|
476
526
|
`harness_browser` e `computer_browser` aceitam `chrome` ou `msedge`.
|
|
477
527
|
`computer_user_data_dir` deve ficar fora do repositório e conter `{browser}`;
|