Using Capybara finder methods with arbitrary matching conditions
Capybara has a variety of finder methods like find_button to help you look up DOM elements. There are also matchers like have_field to make expectations during tests.
These methods also have a number of options to influence the lookup. E.g. the :disabled option lets you control whether Capybara will match disabled fields.
If you have a matching condition that cannot be expressed by the existing Capybara opt...
Project management best practices: Technical debt summary
Maintaining larger projects makes it more difficult to balance refactoring and upgrade tasks according to its actual value. Consider to create and periodically maintain a summary, which helps you and your team in the decision which refactoring task should be taken next.
Template
Here is an template on how you might categorize your tasks:
| Technical debt | Estimated Efforts | Visible customer value| Customer value explained| Developer value|Developer value explained|
|-----------------------------|----------------|----------...
Rails: Composing an ETag from multiple records
Rails offers the fresh_when method to automatically compute an ETag from the given record, array of records or scope of records:
class UsersController < ApplicationController
def show
@user = User.find(params[:id])
fresh_when @user
end
def index
@users = User.all.to_a
fresh_when @users
end
end
When your view also displays other records (typically associations), those other records should be included in the ETag. You can do so by passing an array of ETaggable objects to fresh_when.
...
Rails: Your index actions probably want strict_loading
By activating strict_loading you force developers to address n+1 queries by preloading all associations used in the index view. Using an association that is not preloaded will raise an ActiveRecord::StrictLoadingViolationError.
I think it's a good default to activate strict_loading in your controllers' #index actions. This way, when a change introduces an n+1 query, you...
Using partials in Rails views
Rails partials have a lot of "hidden" features and this card describes some non-obvious usages of Rails Partials.
Rendering a basic partial
The most basic way to render a partial:
render partial: 'weather'
This will render a _weather.html.erb file. Notice how all partials need to be prefixed with _.
It's possible to define local variables that are only defined in the partial template.
# _weather.html.erb
<h1>The weather is <%= condition %></h1>
# index.html.erb
render partial: 'weather', locals: { condition: ...
HTTP headers can only transport US-ASCII characters safely
HTTP header values must only contain low-ASCII (7-bit) characters for safe transport. From RFC 7230:
Historically, HTTP has allowed field content with text in the ISO-8859-1 charset [ISO-8859-1], supporting other charsets only through use of [RFC2047] encoding. In practice, most HTTP header field values use only a subset of the US-ASCII charset [USASCII]. Newly defined header fields SHOULD limit their field values to US-ASCII octets.
If you need to transport 8-bit+ characters (e.g...
Carrierwave: Custom file validations inside custom Uploaders
Carrierwave's BaseUploader can have some validations that you can use by overriding a certain method, which's expected name is hard coded. A popular example is extension_allowlist, which returns an array of strings and let's you only upload files that have a filename with an extension that matches an entry in that array. Another useful validation can be size_range, which gives you a little bit of control over how your storage gets polluted.
This is often good enough, but some times you need to validate special cases.
Validations t...
How to reliably center icons vertically with text
vertical-align is hard. Have you ever wanted to vertically center an icon with text? This usually means "vertically align with capital letters", as visually, a text line goes from baseline up to the capital top. That's because text usually starts with a capital letter, plus descenders are far less frequent than ascenders.
In this card we'll vertically center an icon (or any "blockish" inline element, really) with the capital letters of surrounding text. This works well...
Timecop: reset after each test
Timecop is a great gem to set the current time in tests. However, it is easy to introduce flakyness to your test suite when you forget to reset the time after the test.
This might be the case if:
- a test freezes time and a later test does not work for frozen time
- a later test needs the real current date to work correctly
Often you only notice these kinds of errors in rare cases when tests are executed in a particular order.
A way to avoid this is by using block notation (`Timecop.travel(...) ...
Jasmine: Mocking ESM imports
In a Jasmine spec you want to spy on a function that is imported by the code under test. This card explores various methods to achieve this.
Example
We are going to use the same example to demonstrate the different approaches of mocking an imported function.
We have a module 'lib' that exports a function hello():
// lib.js
function hello() {
console.log("hi world")
}
export hello
We have a second module 'client' that exports a function helloTwice(). All this does is call hello() ...
Heads up: network requests `Kernel#open` are not mocked with VCR
We usually rely on VCR and WebMock to prevent any real network connection when running our unit tests.
This is not entirely true: They are both limited to a set of HTTP libraries listed below (as of 2022). Direct calls to Kernel#open or OpenURI#open_uri are not mocked and will trigger real network requests even in tests. This might bite you e.g. in [older versions of CarrierWave](https://github.com/carrierwaveuploader/carrierwave/blob/0.11-stable/lib/carrierwave/upl...
subscript, superscript and line-heights
By default subscript (<sub></sub>) and superscript (<sup></sup>) tags are styled with vertical-align: sub, respectively vertical-align: super by most browsers.
However, without adaptations, this will probably break your line-heights.
A common suggestion is to style those two tags accordingly:
sup, sub {
vertical-align: baseline;
position: relative;
top: -0.4em; /* can be adapted according to preferences */
}
sub {
...
A modern approach to SVG icons
You have some SVG files you want to use as icons on your website. How would you embed them?
Common options are:
- Use them with an image:
<img src='path-to-icon.svg'> - Embed them inline with
<svg>$ICON</svg> - Embed them using CSS and
background-image: url(path-to-icon.svg)or evenbackground-image: url(data:$ICON). - Build your own icon font.
All of these have drawbacks:
- Image and
background-imagedo not allow to recolor the image using CSS. - Inline-
<svg>are unnecessary work for the server and are...
Using path aliases in esbuild
In esbuild, you usually import other files using relative paths:
import './some-related-module'
import `../../utils/some-utility-module`
import `../../../css/some-css.sass`
This is totally fine if you import closely related files, but a bit clunky when you're trying to import some "global" module, like a utility module. When moving a file, your imports also need to change.
To get around this, esbuild support a mechanism first introduced in TypeScript called "path aliases". It works like this:
First, you create a file called `js...
Jasmine: Creating DOM elements efficiently
Jasmine specs for the frontend often need some DOM elements to work with. Because creating them is such a common task, we should have an efficient way to do it.
Let's say I need this HTML structure:
<ul type="square">
<li>item 1</li>
<li>item 2</li>
</ul>
This card compares various approaches to fabricating DOM elements for testing.
Constructing individual elements
While you can use standard DOM functions to individually create and append elements, this is extremely verbose:
let list = document.createElement('...
Jasmine: Cleaning up the DOM after each test
Jasmine specs that work with DOM elements often leave elements in the DOM after they're done. This will leak test-local DOM state to subsequent tests.
For example, this test creates a <spoiler-text> element, runs some expectations, and then forgets to remove it from the DOM:
describe('<spoiler-text>', function() {
it ('hides the secret until clicked', function() {
let element = document.createElement('spoiler-text')
element.secret = 'The butler did it'
document.body.appendChild(element)
...
Careful when using Time objects for generating ETags
You can use ETags to allow clients to use cached responses, if your application would send the same contents as before.
Besides what "actually" defines your response's contents, your application probably also considers "global" conditions, like which user is signed in:
class ApplicationController < ActionController::Base
etag { current_user&.id }
etag { current_user&.updated_at }
end
Under the hood, Rails generates an ETag header value like W/"f14ce3710a2a3187802cadc7e0c8ea99". In doing so, all objects from that etaggers...
Rules of thumb against flaky specs
Here are a few common patterns that will probably lead to flaky specs. If you notice them in your specs, please make sure that you have not introduced a flaky spec.
Using RSpec matchers
One rule of thumb I try to follow in capybara tests is using capybara matchers and not plain rspec matchers.
One example:
visit(some_page)
text_field = find('.textfield')
expect(text_field['value']).to match /pattern/
This can work, but is too brittle and flaky. match will not retry or synchronize the value of text_field.
The equivale...
Git: Restore
tl;dr
git checkoutis the swiss army of git commands. If you prefer a semantically more meaningful command for restoring tasks, usegit restoreinstead.With this command you can ...
- ... do unstaging -
git restore --staged- ... discard staged changes -
git restore --staged --worktree- ... discard unstaged changes -
git restore- ... restore deleted files -
git restore- ... restore historic versions -
git restore --source- ... recreate merge conflicts -
git restore --merge- ... specifiy...
Git commands to discard local changes
Use case
You have uncommited changes (you can always check by using git status), which you want to discard.
Context
Now there are several options to discard these depending on your exact situation.
The headlines will differentiate the cases whether the files are staged or unstaged.
- Staged and unstaged changes
- [Staged changes](https://makandracards.com/makandra/516559-git-commands-to-discard-local-changes#s...
RubyMine: Find and Replace with Regex (Capture Groups and Backreferences)
tl;dr
In RubyMine you can use find and replace with capture groups
(.*?)and backreferences$1(if you have several groups:$[Capture-Group ID]).
Named captures(?<text>.*)are also supported.
Examples
Replace double quotes with single quotes
If you want to replace double quotes with single quotes, replacing every " with a ' is prone to errors. Regular expressions can help you out here.
- Open find and replace
- Activate the regex mode (click on the
.*icon next to the "find" field). - Fill in f...
Careful: `fresh_when last_modified: ...` without an object does not generate an E-Tag
To allow HTTP 304 responses, Rails offers the fresh_when method for controllers.
The most common way is to pass an ActiveRecord instance or scope, and fresh_when will set fitting E-Tag and Last-Modified headers for you. For scopes, an extra query is sent to the database.
fresh_when @users
If you do not want that magic to happen, e.g. because your scope is expens...
JSON APIs: Default design for common features
When you build a JSON API you need to come up with a style to represent attributes, pagination, errors or including associated objects. Instead of reinventing the wheel, you may reuse successful API designs.
JSON API
JSON:API specifies common features found in most JSON APIs, like pagination, ordering and nested resources. The spec looks very similar to how one would build an API with Rails and uses similar patterns. Even if you don't plan on supporting the whole spec, it can still make sense to know how th...
Git: Removing feature branches on merge
When working with feature branches, stale branches pile up over time. It's best to remove them right after merge, locally and on the remote, but it is a little tedious: you need to remember it, and perform the few steps manually each time.
Enter Git hooks. The folks at Liquid Light have built a little post-merge hook that will delete a feature branch on confirmation....