Dá cobertura de navegador ao CSP e ao JS da aplicação - #94
Merged
Merged
Conversation
Nada que dependesse de JavaScript tinha teste automatizado. O custo disso
não era teórico: a CSP da v2.0.0 precisou ser corrigida duas vezes por
comportamento que só aparece num navegador de verdade — o widget do
VLibras carregando imagem de outro CDN, e o nonce por requisição
quebrando a navegação do Turbo Drive. Nenhuma das duas quebraria um spec
de request.
Cinco system specs (Capybara + Chrome headless), deliberadamente poucos.
Não replicam o que os request specs cobrem: cobrem os lugares onde a
falha é SILENCIOSA — a página renderiza, o servidor responde 200, e só o
navegador sabe que algo foi bloqueado ou deixou de rodar.
Violação de CSP não levanta erro nem quebra a renderização: o navegador
recusa o recurso e escreve no console. Por isso o driver liga
goog:loggingPrefs e os exemplos leem o log. O filtro é pelo texto
"Content Security Policy", e não "console limpo": o layout carrega
Bootstrap, fontes do Google e o VLibras de CDNs externos, e uma falha de
rede vira "Failed to load resource" — ruído que não é o que se verifica
aqui.
Os specs discriminam, e isso foi medido, não suposto: reintroduzindo o
nonce por requisição no initializer, o exemplo da navegação do Turbo
falha com a mensagem real do navegador ("Executing inline script violates
the following Content Security Policy directive... The action has been
blocked"). Com o nonce de sessão, passa.
Dois achados que só apareceram por rodar um navegador de verdade:
O app não declarava favicon, então todo navegador pedia /favicon.ico por
conta própria — e sem rota para isso, cada primeira visita virava um
ActionController::RoutingError. Em produção é ruído de log; nos system
specs, falha imediata, porque a Capybara repropaga erro de servidor.
Corrigido com um <link rel="icon"> e um ícone SVG na identidade visual do
projeto, que é o que faz o navegador parar de pedir o .ico.
O tour guiado dispara sozinho no painel de quem tem tour_completed_at
nulo, e o overlay dele intercepta todo clique. Num request spec ele é só
markup; com navegador, é um elemento que existe e recebe o clique no
lugar do botão. Os specs usam um usuário que já passou pelo tour, que é
quem eles representam.
Decisões:
selenium-webdriver em vez de cuprite/ferrum: é o driver padrão dos system
tests do Rails, e o Chrome já vem na imagem do runner — nenhum pacote de
sistema a instalar no CI. É a única dependência pesada do projeto, e
entra só no grupo de teste (BUNDLE_WITHOUT no Dockerfile mantém a imagem
de produção intacta).
Os system specs rodam no `bundle exec rspec` normal, não num job à parte,
para que "rodar os testes" signifique a mesma coisa aqui e no CI. O preço
é que o container de verificação local precisa de Chromium — está na
skill verificar-local e no CLAUDE.md.
Verificado no container: RuboCop 112 arquivos sem ofensas,
zeitwerk:check limpo, RSpec 399 exemplos e 0 falhas em 13s (eram 394 em
7,5s — os cinco novos custam ~5,5s).
Closes #85
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fecha a #85.
Nada que dependesse de JavaScript tinha teste automatizado. O custo não era teórico: a CSP da v2.0.0 precisou ser corrigida duas vezes por comportamento que só aparece num navegador de verdade — o widget do VLibras carregando imagem de outro CDN, e o nonce por requisição quebrando a navegação do Turbo Drive. Nenhuma das duas quebraria um spec de request.
Cinco system specs (Capybara + Chrome headless), deliberadamente poucos. Não replicam o que os request specs cobrem: cobrem os lugares onde a falha é silenciosa — a página renderiza, o servidor responde
200, e só o navegador sabe que algo foi bloqueado ou deixou de rodar.application.jsnão executando — nonce do importmap recusado deixa a página bonita e inertedata-turbo-confirmparando de funcionar, e a exclusão acontecendo sem perguntarEles discriminam — e isso foi medido
Reintroduzi o nonce por requisição no initializer e rodei. O exemplo da navegação do Turbo falha com a mensagem real do navegador:
Com o nonce de sessão, passa. Um spec de invariante que nunca falha não vale nada, e este repositório já me mostrou isso uma vez (a factory que quase fez os specs de papel passarem por vacuidade, na #79).
Dois achados que só apareceram por rodar um navegador
O app não declarava favicon. Todo navegador pede
/favicon.icopor conta própria e, sem rota para isso, cada primeira visita virava umActionController::RoutingError— ruído de log em produção, e falha imediata nos system specs, já que a Capybara repropaga erro de servidor. Corrigido com um<link rel="icon">e um ícone SVG na identidade visual do projeto (o gradiente verde da navbar), que é o que faz o navegador parar de pedir o.ico.O tour guiado intercepta cliques. Ele dispara sozinho no painel de quem tem
tour_completed_atnulo. Num request spec é só markup; com navegador, é um overlay que recebe o clique no lugar do botão. Os specs usam um usuário que já passou pelo tour — que é quem eles representam.Decisões
selenium-webdriverem vez de cuprite/ferrum: é o driver padrão dos system tests do Rails, e o Chrome já vem na imagem do runner — nenhum pacote de sistema a instalar no CI. É a única dependência pesada do projeto e entra só no grupo de teste; oBUNDLE_WITHOUTdo Dockerfile mantém a imagem de produção intacta.Rodam no
bundle exec rspecnormal, não num job à parte, para que "rodar os testes" signifique a mesma coisa aqui e no CI. O preço é que o container de verificação local precisa de Chromium — documentado na skillverificar-locale noCLAUDE.md, porque sem ele a suíte inteira falha, não só os cinco.O que continua sem cobertura
Tour guiado, dropdown de busca, busca por voz e o widget do VLibras. São falhas visíveis, não silenciosas — e a issue pedia poucos specs nos fluxos onde a falha passa despercebida, não uma segunda suíte.
Verificação
No container (com Chromium instalado): RuboCop 112 arquivos / 0 ofensas,
zeitwerk:checkAll is good!, RSpec 399 exemplos / 0 falhas em 13s — eram 394 em 7,5s, então os cinco novos custam ~5,5s. Bem dentro do critério de "tempo de CI continua aceitável".