Coding

Some ideas about writing code, since 2003

June 13, 2016

Usando Blade Para Testar JavaScript no Rails

Motivação

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:

  • Fácil de configurar em bibliotecas javascript
  • Suporta sprockets
  • Ativamente usado no Turbolinks, Trix e ActionCable
  • Eu acredito que ele pode se tornar o testador padrão de javascript para o Rails no futuro

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 :)

Bonus: Fixtures

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 é:

  1. Nós podemos acessar test/javascripts/src/fixtures no blade via http://localhost:9876/fixtures
  2. Nós podemos acessar response.body no controlador de teste do Rails
  3. Vamos escrever em arquivos durante a executação de testes do Rails
  4. Executar teste conjunto Javascript com a fixture mais atualizada

Editei 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:

suite de teste do blade

Se a view for mudada, a fixture será atualizada, e se o JS quebrar, o teste falhará.

Finalizando

Para mim, atingiu todas as minhas expectativas para um teste javascript executando no Rails, porque é uma configuração poderosa nos seguintes pontos:

  1. Fácil de instalar
  2. Executa o teste em diversos navegadores
  3. Pode ser executado em CI utilizando sauce labs (talvez em blade-phantomjs no futuro?)

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!

About

This is my personal space where I take notes about tech/programming things. Being in love with Linux since 1998, Ruby/Rails since 2010 and React/ES6 since 2016 you have some idea what you'll find here.

Celso Fernandes

Mailing

Why a mailing list?

Copyright (c)  
2003 - 2021

Coding Logo