You can check this post in english
Eu estava procurando por um executor de teste javascript com integração para o Rails, encontrei algumas opções, porém decidi escolher o Blade. Como isto não é uma comparação (eu não sou tão experiente para isto), aqui estão as razões do porque eu preferi o Blade:
Então comecei a configurar como seria de costume em uma biblioteca javascript
# Gemfile
group :development, :test do
gem 'blade'
end
Depois de editar o Gemfile, temos:
bundle install
E adicionando um arquivo .blade.yml
load_paths:
- test/javascripts/src
- test/javascripts/vendor
logical_paths:
- application.js
- test.js
Como isto é uma aplicação do Rails, minha idéia é definir os caminhos test/javascripts/{src/vendor}
para serem carregados, então Blade carrega meu teste e as bibliotecas de apoio.
O arquivo test.js
é o ponto de entrada para o conjunto de testes, e application.js
é o arquivo de entrada de javascript no Rails.
Consegui executar o Blade como esperado, mas não foi carregado application.js
. Perguntando para o @javan no Twitter, ele respondeu que não há integração built-in no Rails, então estamos por nossa própria conta.
A primeira tentativa foi adicionar app/assets/javascripts
para load_path, funcionou, mas nenhum javascript usado por gems era carregado (exemplo jquery-rails
)
Então porque não entrar no arquivo bin no blade, fazer o Rails carregar o caminho dos assets e configurar o blade?
Depois de algum tempo descobrindo como as coisas funcionavam, eu alterei o binário do Blade, vamos comentar cada passo:
#!/usr/bin/env ruby
# frozen_string_literal: true
#
# This file was generated by Bundler.
#
# The application 'blade' is installed as part of a gem, and
# this file is here to facilitate running it.
#
require "pathname"
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
Pathname.new(__FILE__).realpath)
require "rubygems"
require "bundler/setup"
# COMENTÁRIO: aqui termina o prelúdio do _bundler_
# COMENTÁRIO: Inicializar a aplicação rails
require_relative File.join('..', 'config/application.rb')
Rails.application.initialize!
# COMENTÁRIO: Inicializar o caminhos dos assets no Rails ...
paths = Rails.application.assets.paths
cwd = `pwd`.chomp + "/"
paths = paths.map { |x| x.gsub(cwd, '') }
# COMENTÁRIO: ... E concatenar com o caminho especificado em .blade.yml
blade_config = YAML::load_file(File.join(__dir__, '..', '.blade.yml'))
paths.concat(blade_config["load_paths"])
Blade.initialize!(interface: 'runner', load_paths: paths)
# COMENTÁRIO: bundler executa blade
load Gem.bin_path("blade", "blade")
Eu não tenho certeza se está é a melhor abordagem, mas tive um feedback positivo do @javan, então o próximo passo é fazer um plugin para o blade :)
Uma das minhas principais motivações em obter um javascript rodando era pela certeza que minhas fixtures foram sempre atualizadas, minha primeira idéia foi de processar o erb e... não funcionou!
Então eu tive uma luz, é uma abordagem padrão testar a saída (view) em um controller do Rails (ok, a idéia não é discutir se esta é a melhor abordagem, o importante é que é factível) porque nós temos o response.body
, então porque não escrever isto nas fixtures?
A lógica por trás é:
test/javascripts/src/fixtures
no blade via http://localhost:9876/fixtures
response.body
no controlador de teste do RailsEditei test_helper.rb
e adicionei
require 'fileutils'
class ActiveSupport::TestCase
def save_fixture(name, selector = nil)
fixture_dir = File.dirname(name)
fixture_dir = File.join(fixtures_dir, fixture_dir)
fixture_path = File.join(fixture_dir, File.basename(name) + ".html")
unless File.directory?(fixture_dir)
FileUtils.mkdir_p(fixture_dir)
end
# Get only body for fixture (or selector if set)
selector = selector.nil? ? 'body' : selector
output = Nokogiri::HTML(response.body)
if (selector == "body")
output = output.css(selector)[0].children.to_s
else
output = output.css(selector)[0].to_s
end
File.write(fixture_path, output)
end
end
# Setup JavaScript Fixtures Dir
def fixtures_dir
test_dir = File.dirname(File.expand_path(__FILE__))
File.join(test_dir, 'javascripts', 'src', 'fixtures')
end
puts fixtures_dir
unless File.directory?(fixtures_dir)
FileUtils.mkdir_p(fixtures_dir)
end
Em um teste do controller é necessário apenas:
require 'test_helper'
class ThingControllerTest < ActionController::TestCase
test "index" do
get :index
assert :success
# do not specify second parameter if you want the whole body
save_fixture('things/table', 'table#things')
end
end
Desta forma temos response.body
escrita para test/javascripts/src/fixtures/things/table.html
E você está pronto para escrever seu teste JS
QUnit.test( "ThingsController#index", function( assert ) {
assert.expect(1);
var done = assert.async();
$.get( "/fixtures/things/table.html", function( data ) {
$( "#qunit-fixture" ).html( data );
length = $("table#things tbody").find('tr').length;
assert.equal(length, 2, "Two things listed");
done();
});
});
O resultado é excelente:
Se a view for mudada, a fixture será atualizada, e se o JS quebrar, o teste falhará.
Para mim, atingiu todas as minhas expectativas para um teste javascript executando no Rails, porque é uma configuração poderosa nos seguintes pontos:
E o que você acha sobre essa abordagem, você tem alguma dica sobre testes JS no Rails que você gostaria de compartilhar?
ps: Esta abordagem se tornou um plugin para o Blade, aguardo feedback!
Faça ótimos testes com Rails!