Seu agente de IA escreve o teste uma vez, e o Optics Framework roda sem modelo
Trabalho no Optics Framework, então leve isso em conta. Este texto é a versão em português de um artigo em inglês no optics-framework.org, onde também estão os vídeos.
Muitas ferramentas de teste de UI com IA colocam um modelo no controle de cada execução. Cada execução tem custo de modelo, e o modelo pode seguir um caminho diferente pelo app a cada vez.
O Optics Framework usa o modelo em outro ponto. O seu agente de IA navega pelo app uma vez, pelo servidor MCP do Optics, e escreve uma suíte de testes em CSV. Depois disso, cada execução só reproduz a suíte e não chama nenhum modelo.
Fiz isso no saucedemo.com, um site público de demonstração. O agente foi o Claude Code com claude-opus-5-5, e o servidor foi o Optics Framework 1.11.0.
O prompt
Dei ao Claude Code um prompt e um servidor MCP. Skills e configurações de usuário estavam desligadas, então o agente só tinha as informações que vêm do Optics.
Use the optics tools to open https://www.saucedemo.com in Playwright with headless false.
Sign in as standard_user / secret_sauce, add the Sauce Labs Backpack to the cart,
open the cart, check out as Ava Birch with postcode 560001, finish the order,
and check that "Thank you for your order!" appears.
Then write it as an Optics suite in ./checkout, run optics dry_run on it,
and tell me how to run it.
O que o servidor MCP entrega ao agente
O optics mcp é um servidor Model Context Protocol. Na versão 1.11.0 ele tem 41 ferramentas: 3 controlam a sessão e 38 são keywords do Optics, como press_element, enter_text e assert_presence. O agente também lê estes recursos:
optics://session/{id}/elements: uma lista curta dos elementos da tela, cada um com rótulo, ações permitidas e posição.optics://session/{id}/screenshoteoptics://session/{id}/source: a tela como imagem e a árvore de UI completa.optics://project-format: as regras dos arquivos da suíte.optics://keywords: a lista de keywords.
Numa página web, um item da lista pode ter um valor loc. É um seletor CSS que o Optics gera no navegador, e o Optics confere que ele encontra só aquele elemento. Este é o item do botão Login:
{"i": 3, "label": "Login", "cls": "input", "bounds": [494, 302, 786, 351], "act": ["tap"], "rid": "login-button", "loc": "[data-test=\"login-button\"]"}
O agente usou um loc em todos os 11 cliques e entradas de texto.
O servidor não tem ferramenta para gravar ou salvar um teste. O agente escreve os arquivos com as regras de optics://project-format. As ferramentas que tocam numa coordenada da tela ficam ocultas por padrão, porque uma coordenada tirada de um screenshot pode cair em outro elemento numa tela de outro tamanho.
A sessão
A sessão levou 71 segundos e 32 turnos do modelo. O agente fez 21 chamadas de ferramentas do Optics, leu o formato do projeto uma vez e leu a lista de elementos 6 vezes, uma por página. O Claude Code mostrou um custo de modelo de US$ 0,44.
Primeiro o agente fez o checkout completo no navegador e encontrou "Thank you for your order!" na última página. Depois escreveu quatro arquivos e rodou optics dry_run, que passou.
Os arquivos
O caso de teste chama cinco módulos em sequência. O arquivo de módulos tem uma linha por ação, e todos os valores são variáveis:
module_name,module_step,param_1,param_2
Open Site,Launch App,${site_url}
Login,Enter Text,${username_field},${username}
Login,Enter Text,${password_field},${password}
Login,Press Element,${login_button}
Add Backpack To Cart,Press Element,${add_backpack_button}
Add Backpack To Cart,Press Element,${cart_link}
Add Backpack To Cart,Press Element,${checkout_button}
Enter Checkout Details,Enter Text,${first_name_field},${first_name}
Enter Checkout Details,Enter Text,${last_name_field},${last_name}
Enter Checkout Details,Enter Text,${postal_code_field},${postal_code}
Enter Checkout Details,Press Element,${continue_button}
Finish Order,Press Element,${finish_button}
Finish Order,Assert Presence,${order_complete_header}
O arquivo de elementos guarda os dados de teste e os localizadores. Na maioria dos elementos, o agente pôs o loc verificado primeiro e o rótulo em segundo:
Element_Name,Element_ID,Element_ID_2
site_url,https://www.saucedemo.com,
username,standard_user,
first_name,Ava,
username_field,"[data-test=""username""]",Username
login_button,"[data-test=""login-button""]",Login
checkout_button,"[data-test=""checkout""]",Checkout
finish_button,"[data-test=""finish""]",Finish
order_complete_header,text=Thank you for your order!,"[data-test=""complete-header""]"
Para outro usuário ou outro produto, basta trocar os valores nesse arquivo. Os módulos continuam iguais. Esses quatro arquivos são todo o resultado da sessão: dá para ler, revisar num pull request e versionar no Git junto com o app.
As reexecuções
Rodei a suíte 10 vezes com optics execute, sem nenhuma chave de API no ambiente. As 10 passaram. A mais rápida levou 2,8 s, a mais lenta 4,2 s, e a mediana foi 3,3 s, contando a inicialização do Python e do navegador.
Para contar as chamadas ao modelo, registrei cada consulta DNS e cada conexão de rede do processo Python do Optics. Nas 10 execuções o registro ficou vazio. Só o navegador acessou a rede, para carregar o saucedemo.com. Para confirmar que o registro funcionava, mandei uma requisição para example.com a partir do mesmo processo, e ela apareceu.
O código-fonte bate com isso: o Optics só cria um cliente de LLM quando ai_self_heal está true, e o padrão é false.

| Sessão do agente | Cada reexecução | |
|---|---|---|
| Tempo | 71 s | 3,3 s (mediana de 10) |
| Turnos do modelo | 32 | 0 |
| Custo do modelo | US$ 0,44 | US$ 0 |
| Resultado | 4 arquivos da suíte | 10 de 10 passaram |
Quando a página muda
A suíte escrita pelo agente tem o mesmo problema de uma suíte escrita por gente: o app muda. Para simular um refactor, troquei o localizador do botão de login no elements.csv por [data-test="login-btn-v2"], que não existe na página.
A execução passou. O Optics não achou o primeiro localizador e tentou o segundo, o rótulo "Login". O log mostra a sequência:
Locating element: [data-test="login-btn-v2"] of type: CSS...
Locating element: Login of type: Text...
Pressing element 'Login'
Esse passo levou 10,2 s. Com o primeiro localizador correto, leva cerca de 0,1 s, porque o Optics espera o primeiro localizador até o tempo limite antes de tentar o rótulo. O segundo localizador mantém o teste passando, mas mais lento. Quando o log mostrar um fallback, corrija o primeiro localizador.
Para testar
Você precisa de Python 3.12 ou mais recente e de um cliente MCP. Os passos abaixo usam o Claude Code.
- Instale o Optics Framework com o servidor MCP:
pip install "optics-framework[mcp]" - Instale o navegador:
optics setup --install playwright - Adicione o servidor ao Claude Code:
claude mcp add optics -- optics mcp - Escreva um prompt com as mesmas partes do prompt acima: a URL, os passos, a verificação final e a pasta da suíte.
- Leia os arquivos que o agente escrever, como você revisaria um pull request.
- Rode a suíte:
optics execute checkout
Para Cursor, VS Code e Claude Desktop, veja Let your AI agent build tests. No CI, defina headless: true no config.yaml e veja Run tests in CI.
Limitações
- A qualidade da suíte depende do modelo e do prompt. Leia os arquivos antes do commit.
optics dry_runsó verifica os arquivos e não abre o app. Rodeoptics executeuma vez antes de pôr uma suíte nova no CI.- O
locsó existe em páginas web. Em Android e iOS, o agente usa os ids e rótulos da lista de elementos. - Loops, condições e arquivos de dados não são ferramentas MCP, porque só funcionam numa execução completa da suíte. O agente pode escrevê-los direto nos módulos.
- O transporte HTTP (
optics mcp --transport http) não tem autenticação. Use em 127.0.0.1 ou numa rede que você controla.
Se tiverem dúvidas ou críticas, comentem aqui. O artigo original, com os vídeos, está em optics-framework.org/blog/ai-agent-writes-the-test-once, e o código em github.com/mozarkai/optics-framework.

