Disable SimpleCov if you only run a fraction of your tests

Coverage reports are rarely useful if you run only small parts of your test suite.

Just don't not load SimpleCov in this case, and you will see less noise in your test output:

if RSpec.configuration.files_to_run.count > 5
  require "simplecov"
  SimpleCov.start 'rails'
end

See also

Unicode character class escape: Match text by Unicode properties

The linked MDN article is quite informative of a neat feature supported by all major browsers: Unicode character class escape.

You can use it to write regular expressions that work on the full UTF-8 space, not just Latin/ASCII. For example, a password policy matcher might include regular expressions like [A-z] or [0-9], but those do not match e.g. German umlauts or [Eastern Arabic Numerals](https:/...

ActiveRecord: Named bindings in conditions

In Active Record you can use named bindings in where-conditions. This helps you to make your code more readable and reduces repetitions in the binding list.

Example without named bindings

User.where(
  'name = ? OR email = ?',
  params[:query],
  params[:query]
)

Example with named bindings

User.where(
  'name = :query OR email = :query',
  query: params[:query]
)

Capybara will not find links without an href attribute

Capybara will fail to find <a> tags that are missing an href attribute. This will probably happen to you every now and then on JavaScript-heavy applications.

An example would be an AngularJS application where the following HTML actually works. [1]

<a ng-click="hello()">Hello</a>

Capybara will fail to find that link, even though looking it up via the DOM shows it:

>> find_link("Hello")
Capybara::ElementNotFound: Unable to find link "Hello"

>> find("a").text
=> "Hello"

To make find_link and click_link work, ...

Don't define a controller action called #process

Remember that your controller actions share the same method space with private methods defined in ActionController::Base. If your controller behaves in super-weird ways, check that you don't overwrite some internal method with a controller action.

Examples for internal methods:

  • #process
  • #process_action
  • #cookies
  • #params
  • #request
  • #response

Debugging

If you accidentally did overwrite some internal method, you may come across an ArgumentError with the message `wrong number of arguments (given 1, expe...

Updated: Integrating ESLint

Updated the instructions for ESLint to work with version 5.x.

Improve accessibility with [aria-required] in SimpleForm

SimpleForm comes with an option browser_validations which could be used to give fields that have a presence validation the HTML required attribute. We usually turn it off due to difficulties controlling its behavior and appearance. Instead we only mark required fields with an asterisk (*) next to its label. Blind users probably only discover the validation issue after submitting the form due to the now displayed error messages.

A compromise with better acce...

RSpec: Marking sections in long examples

RSpec examples can get quite long, especially in feature specs. This makes them hard to read & understand. Also, when executing them, it may take seconds to see any progress.

To improve this, I have successfully been using a little "step" helper in my tests. It marks semantic sections, structuring an example while improving documentation. When the test runs, each step prints its message (or a dot, depending on your formatter).

# spec/support/step_helper.rb
module StepHelper

  # Use this helper to label groups of related actions in l...

Updated: A simpler default controller implementation

Added:

  • etag { flash.to_h }
  • etag { I18n.locale } (could be left out if all URLs contain a locale fragment, but also doesn't hurt and is a good default)

How to silence Puma for your feature tests

When RSpecs runs the first feature spec, you may see log output like this:

Capybara starting Puma...
* Version 6.5.0, codename: Sky's Version
* Min threads: 0, max threads: 4
* Listening on http://127.0.0.1:39949

You can disable this behavior by tweaking Capybara's Puma server in your spec/support/capybara.rb:

Capybara.server = :puma, { Silent: true }

Note

You don't need to configure this if you're using system tests with modern versions of Rails. They do [exactly the same](https://github.com/rails/rails/blob/ma...

How to use Ubuntu in English, but still show German formats

If you want to have an English Ubuntu UI, but still see dates, money amounts, paper formats, etc. in German formats, you can fine-tune your /etc/default/locale like this:

LANG="en_US.UTF-8"
LC_CTYPE="de_DE.UTF-8"
LC_NUMERIC="de_DE.UTF-8"
LC_TIME="de_DE.UTF-8"
LC_COLLATE="de_DE.UTF-8"
LC_MONETARY="de_DE.UTF-8"
LC_PAPER="de_DE.UTF-8"
LC_NAME="de_DE.UTF-8"
LC_ADDRESS="de_DE.UTF-8"
LC_TELEPHONE="de_DE.UTF-8"
LC_MEASUREMENT="de_DE.UTF-8"
LC_IDENTIFICATION="de_DE.UTF-8"

Make sure you have both en...

Git: rebase dependent feature branch after squash

This card will show you how to use git rebase --onto without confusion.

Use case:

You've got two feature branches (one and two), where two depends on one. Now commits of branch one have changed after you branched two from it (i.e. after code review the commits of branch one are squashed). Thus the commit history of branch one has changed. Branch two's history however didn't change.

Solution:

To make the branches share the same commit history again you will have to rebase and replay (attach) the a...

RSpec: Defining helper methods for an example group

You can define methods in any example group using Ruby's def keyword or define_method method:

describe "example" do
  def sum(a, b)
    a + b
  end

  it "has access to methods defined in its group" do
    expect(sum(3, 4)).to be(7)
  end
end

The helper method is also available to groups nested within that group. The helper method is not available to parent or sibling groups.

Global helpers

To define helpers for all specs (or all specs of a type), [define it in a module](https://rspec.info/features/3-12/rspec-core/help...

How to define a table name prefix for all Rails models in a given namespace

ActiveRecord computes table names of model classes, and results are usually just like you'd expect.
Adding a prefix for all classes in a namespace can feel a odd or broken, but does not have to be. It's actually very easy when done right.

Summary (tl;dr)

Here's the short version: Define a table_name_prefix method in the namespace module, and do not define any table_name_prefix in ActiveRecord classes inside of it. If this sounds familiar, we have [a card about using it already](https://makandracards.com/makandra/47198-rails-namespac...

How to find out what is running on a port on a remote machine

By convention, common protocols use a defined port, like 80 for HTTP or 443 for HTTPS.

You can use nmap to find out what service is running behind a given port, and most often see some details about it. This can be helpful if servers don't offer the services you expect for some ports. If you'd like to see what ports are listing on your local machine, you might want to use ss instead of nmap.

Note that nmap's service discovery may trigger several requests.

Example

When using nmap, adding the -A switch will ...

RSpec Argument Matchers: Expecting non-primitive objects as method invocation arguments

Expecting a primitive value as an argument to a method invocation is easy:

expect(object).to receive(:foo).with('arg1', 'arg2')

This expectation would be met with this call:

object.foo('arg1', 'arg2')

But what if the argument is expected to be an object with a given set of methods? E.g. this class with #first_name and #last_name methods:

class Person

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end
  
  attr_reader :first_name, :last_name
  
end
``...

Updated: How to allow testing beforeunload confirmation dialogs with modern ChromeDrivers

ChromeDriver 135 started following the W3C spec more closely, which broke testing beforeunload confirmations again. To get working confirmations in a testing browser again, unhandled_prompt_behavior needs to be set to the hash { default: :ignore }.

Selenium doesn't currently support setting this option to a hash, so a monkey patch is needed.

Updated: Testing Accessibility using Orca

As it's quite hard to use a screen reader when you have never done so before, I added a guide on how to visually review the spoken text instead.


Debugging Orca's Output

We can visually review everything the screen reader says by hacking together a real-time transcript of Orca's speech.

To do so, run orca in a CLI window telling it to write a temporary debug log (without Braille to reduce noise)

orca --disable braille --debug-file=/tmp/orca-log.txt

Tip: The screen reader will still be audible. You can reduce its volume ...

Managing chrome versions for selenium

Currently we often use geordi to run cucumber and rspec tests. Geordi takes care of installing a matching chromedriver for the installed google-chrome binary. The google-chrome binary is managed by apt.

Another approach is to use the Selenium Manager for installing and using the correct browser versions for you. Here is the setup you need for your integration tests:

Capybara.register_driver :chrome do |app|
  options = Sele...

Webpack(er): Analyze the size of your JavaScript components

We're always striving towards keeping our website's JavaScript as small as possible.

If you're using webpack(er), you can use the webpack-bundle-analyzer plugin to get a good overview, which of your JavaScript modules take up how much space, and where you can optimize.

To use it, add it via npm or yarn

yarn add webpack-bundle-analyzer

Then add this to your environment.js:

// Uncomment this code to show statistics of bundle sizes. Generated file will automatically...

Custom error pages in Rails

Static error pages

To add a few basic styles to the default error pages in Rails, just edit the default templates in public, e.g. public/404.html.

A limitation to these default templates is that they're just static files. You cannot use Haml, Rails helpers or your application layout here. If you need Rails to render your error pages, you need the approach below.

Dynamic error pages

  1. Register your own app as the applicatio...

Don't forget: Automatically remove join records on has_many :through associations

Bad

# Given the following models

class Image < ActiveRecord::Base
  has_many :album_images
  has_many :albums, through: :album_images
end

class Album < ActiveRecord::Base
  has_many :album_images
  has_many :images, through: :album_images
end

# Join model
class AlbumImage < ActiveRecord::Base
  belongs_to :album
  belongs_to :image
end

Destroying a record in this setup will only remove the record itself, and leave orphaned join records behind.

image = Image.last
image.destroy # removes only the `image` record,
   ...

Rails: Handling actions that need to happen after all transactions

In Rails 7.2. the feature ActiveRecord.after_all_transactions_commit was added, for code that may run either inside or outside a transaction (we have a special card for nested transactions in general) and needs to perform work after the state changes have been properly persisted. e.g. sending an email.

Example

def publish_article(article)
  article.update(published: true)

  ActiveRecord.after_all_transactions_commit do
    PublishNotification...

Rails: Testing exceptions with the rescue_responses setting

In Rails 7.2 the new default for config.action_dispatch.show_exceptions is rescuable.

  • :rescuable: It will show a Rails error page in the response only for rescuable exceptions as
    defined by ActionDispatch::ExceptionWrapper.rescue_responses. In the
    event of an unexpected internal server error, the exception that caused
    the error will still be raised within the test so as to provide a useful
    stack trace and a good debugging experience.
  • :all: It wi...