Compiling Frontend Assets with RSpec and Rails 7

Search for a command to run...

No comments yet. Be the first to comment.
While working on Paylooza it was really important to me to have a very well tested application. Paylooza strives to be an affordable, off-the-shelf recurring billing solution for individuals and small businesses and because we are dealing with transa...

With Rails 7 recently dropping there are a few improvements outside of the great new features that might take new projects by surprise. The first that I encountered after starting up a new Rails project was the bin/dev script that now uses foreman...

Hobby projects are great because they can expose you to problems you've never encountered before. While working on Scavenger Hunt Party! I was noticing some issues in the real-world that were not present in my development environment. The biggest o...

I'm subscribed to cassidoo's newsletter and one of the interesting things she always has in there is an Interview question of the week. Usually I don't spend too much time actually solving these but this one looked fun and I thought I had a pretty ...

Rails 7 ships with a lot of really great improvements to the frontend the biggest of which is removing webpacker in favor of a more generic approach to allow developers to use whatever they'd like to use to compile their assets like esbuild or import maps or even webpacker if that's still your thing. This works really great during development because there are some commands that get run to recompile your assets on the fly. Let's take a look at what I'm talking about.
Rails 7 comes with a couple of gems that better handle asset compilation. They are cssbundling-rails and jsbundling-rails. When installed, these add a new executable called bin/dev which uses the foreman gem to run multiple processes with one command. Those processes can be found in the Procfile.dev and handle compilation of the CSS and JS assets as they change in real time while running in development.
bin/devThis script checks if you have foreman installed and installs it if you don't. Then it starts running all the processes found in your Procfile.dev.
#!/usr/bin/env bash
if ! foreman version &> /dev/null
then
echo "Installing foreman..."
gem install foreman
fi
foreman start -f Procfile.dev "$@"
Here's an example of the Procfile.dev with processes for web, js, and css. You'll notice the --watch argument passed to the build commands. This "watches" your files and as you save changes they will be recompiled automatically. This is an important thing to note because your files while in development mode will always be recompiled into the latest version but we don't run foreman or these commands in test by default.
If you're using a PaaS like Heroku you can also have a regular Procfile that is used in production environments to outline what processes should be run there.
web: bin/rails server -p 3000
js: yarn build --watch
css: yarn build:css --watch
Great, now our assets are compiling as fast as we can change them and we are writing code like crazy. With all that new code we definitely need some tests to make sure things are working as expected. System tests are a great way to do that because it runs a full web server and a headless browser to click around pages, fill in forms, etc. But when we run our system tests we get this error:
ActionView::Template::Error:
The asset "application.js" is not present in the asset pipeline.
Huh, well that's not good. This is because while our bin/dev runs the asset compilation commands for our development environemtn our test environment does not. To fix this I added a support file to my RSpec setup that looks like this:
# spec/support/generate_assets.rb
# frozen_string_literal: true
RSpec.configure do |config|
config.before(:suite) do
system("yarn build", out: File::NULL)
system("yarn build:css", out: File::NULL)
end
end
Now before our tests are run we compile the assets using the same commands that are used during development. I added the out: File::NULL to quiet the output of those asset compilations from the test logs. Now the application.js is present and correct before every test run.
This works great when running locally but I found that our CI was doubling up efforts because the CI builds everything on its own so these commands were not relevant there. Here is the full version of the file I use with a check if we are in a CI environment or not.
# spec/support/generate_assets.rb
# frozen_string_literal: true
unless ENV.fetch("CI", false)
RSpec.configure do |config|
config.before(:suite) do
system("yarn build", out: File::NULL)
system("yarn build:css", out: File::NULL)
end
end
end
That's it! Now our development environment constantly recompiles assets while we edit them thanks to the --watch argument and our test assets are compiled before every run.
I don't really love this patch though. It seems to me that this should already be handled through the gems to better facilitate the Rails Magic™ and have things work out of the box. I understand that not everyone uses RSpec but I would think the gems could do a simple check like the bin/dev file does to see which testing framework is in use and trigger compilation accordingly.
I hope this was helpful and feel free to leave comments if you know of a better way to do things or if I've missed anything.