Continuando os estudos com Javascript e Ruby, esses dias tive um problema bem chato: existe uma gem (muito boa, por sinal) para Rails chamada “cells“, que basicamente cria uma camada, semelhante a “mini controllers” para coisas específicas em Rails (tipo “sidebar”, “carrinhos de compra”, “menus” e outras funcionalidades que são, basicamente, fragmentos de “views”, normalmente feitas com a combinação “partials+helpers” mas que ficam relativamente difíceis de testar). Junto com o Cells, foi criado o Apotomo, uma gem para criar “widgets”, e aí que está o problema: testar um widget. Claro, é possível testá-lo com integration test, mas “unit-tests”, quando envolvem Javascript+Rails, envolvem HTML Fixtures, linguagens de teste diferentes, enfim, nada produtivo.
Aí, entrou a gem Johnson. Basicamente, é um interpretador Javascript dentro de Ruby, de forma que seja possível rodar código JS dentro do Ruby (e o resultado vem como uma Ruby String, ou um Numeric, enfim). Já falei sobre isso quando estudei “The Ruby Racer” e os testes com V8 no Ruby, até me interessei pelo “env.js”, porém na época a versão que rodava com Johson estava muito ruim ainda (e infelizmente, ainda está)
Porém, o novo Env.JS (1.3) já roda no Johnson. E está muito melhor.
Enfim, o env.js, porém, nessa versão virou mais um “browser” do que uma “biblioteca”, então acabei fazendo umas adaptações para ele rodar como biblioteca do Johnson. Junto com isso, fiz um pequeno hack:
document.oldGetElementById = document.getElementById; document.getElementById = function(id) { var element = document.oldGetElementById(id); if(element) return element; element = document.oldGetElementById('someDivThatWillNeverBeOverwrited'); var newDiv = document.createElement('div'); newDiv.id = id; newDiv.innerHTML = ""; element.appendChild(newDiv) return newDiv; }
A idéia, aqui, é ao invés do “getElementById” retornar “undefined” se o elemento não for encontrado, criar um novo elemento sempre. Isso é importante porque, criando sempre um novo elemento, é possível testar o comportamento de um javascript, sem necessitar de “fixtures”. Esse foi o primeiro passo, basicamente. O segundo, foi implementar uma classe para abstrair todas as alterações na página, rodar o Javascript, identificar eventos e ligar com o Env.JS. Como estou usando Johnson, chamei essa classe de EnvJohnson.
Enfim, a idéia principal é: criar um ambiente Javascript, re-escrever o “getElementById” do “document”, salvar a página como estava antes e rodar um script que veio do Rails nele, salvando a página que veio depois. Isso traz um sumário de tudo o que foi alterado na página, como por exemplo, elemento que foi substituído, elemento que foi alterado, elemento que mudou a cor, mudou as propriedades tipo “style”, “class”, etc. A API que achei, por hora, é assim:
js_for response.body, :prototype do |js| js.should replace_inner_html_of(:example) js.should_not replace_inner_html_of(:example2) end
A primeira linha inclui o “prototype”, e cria um “EnvJohnson” para o que vier de resultado no “response.body”. A partir daí, o resultado é computado e pode-se usar os modelos tipo “replace_inner_html_of” e outros que ainda vão surgir.
Claro, a biblioteca ainda está bem no começo, mas confio que muitas alterações serão feitas para ser uma abordagem possível para testes unitários bem menos “dolorosos” no Rails. Como sempre, o código está no github. Ele não funciona muito bem com prototype (problemas no Env.JS) mas JQuery roda bem, pelo menos o que eu testei até agora.
Aguardo pull requests 🙂