I like Rails, but one thing that Rails falls short in is Javascript dependency management.
While Rails Assets, a proxy that allows for listing Bower packages in your Gemfile makes managing front-end libraries good enough for most front-end work, RailsAssets itself is mainly addressing asset management; it doesn’t allow for integrated management of additional development tools and binaries, useful for e.g. JavaScript-testing (besides the fact that Bower is kind of considered to be deprecated these days).
There are different ways of bundling Javascript, but since Rails 5.1, yarn
is the defacto choice for Rails.
You can install yarn either trough npm npm install -g yarn
, or if you’re on a mac, using homebrew: brew install yarn
. I chose the latter.
To prepare your rails project run rails yarn:install
.
There are different testing frameworks for javascript. But most things I work on are not very complex, so I choose Tape, a small testing tool, as a development module:
yarn add coffeetape --dev
After installed you get the coffeetape yarn command, so you can run:
yarn run coffeetape test/javascripts/test_*.*
Yep, here I followed the Rails convention of placing tests in the test directory and prefixing each test file with test_
. This also allows us to easily match both .coffee files as .js files (whenever you want to mix the two).
Let’s assume you’re using the typical animal.coffee
example
we create the test/javascript/test_animal.coffee
:
test = require 'tape'
obj = require '../../app/assets/javascripts/animal.coffee'
test 'animal', (t)->
This isn’t doing anything yet. If we would console.log(obj)
here we wouldn’t find anything, because CoffeeScript wraps everything in a self-executing anonymous function. So we have to bind something to ‘this’. So modify animal.coffee
to bind to ‘this’ at the end of the file:
@Animal = Animal
@Horse = Horse
@Snake = Snake
Without it, the functions in the script cannot be accessed outside the context of the script, so this is necessary for unit-style testing. this
in the front-end context is typically window
, in server side it gets bind to whatever is exported. Hence, Animal will be a property on the exported object, addressable through, in this case, obj.Animal
.
test = require 'tape'
obj = require '../../app/assets/javascripts/animal.coffee'
test 'animal', (t)->
a = new obj.Snake
t.plan 1
t.equal typeof(a.move), "function"
Of course you could also use the typical node ‘exports’ helper at the end of your File:
exports = Snake
Which would allow you to use Snake
without the intermediate object.
Call me lazy, but adding below to tests/javascripts/javascripts_test.rb
helps me to not forget about the tests.
require 'test_helper'
class JavascriptsTest < ActionDispatch::IntegrationTest
test "all javascripts" do
require "open3"
output = nil
cmd = "yarn run coffeetape test/javascripts/test_*.*"
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
if stderr.read.to_s.match /error/i
output = stdout.read
end
end
assert_nil(output)
end
end
By installing the coffeescript executable via homebrew (brew install coffeescript
I can also simply run the individual tape-test-files easily from within my editor (Textmate 2).
Photo by chuttersnap on Unsplash (Unsplash License)
Enjoyed this? Follow me on Mastodon or add the RSS, euh ATOM feed to your feed reader.
Dit artikel van murblog van Maarten Brouwers (murb) is in licentie gegeven volgens een Creative Commons Naamsvermelding 3.0 Nederland licentie .