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() ...

Bookmarklet: cards Markup Link Bookmarklet

The cards editor has a feature "Cite other card" to create links to other cards in the same deck as mardown links.
If you want to reference a card from a different deck, this bookmarklet might be useful:

javascript:(function () {
  const doAlert = () => { alert("Maybe not a makandra card?") };
  let cardsPathPattern = /(\/[\w-]+\/\d+)-.+/;
  if (window.location.pathname.match(cardsPathPattern)) {
    let currentPath = window.location.pathname.match(cardsPathPattern)[1];
    let title = document.querySelector('h1.note--title')?.textCon...

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:

  1. Use them with an image: <img src='path-to-icon.svg'>
  2. Embed them inline with <svg>$ICON</svg>
  3. Embed them using CSS and background-image: url(path-to-icon.svg) or even background-image: url(data:$ICON).
  4. Build your own icon font.

All of these have drawbacks:

  • Image and background-image do not allow to recolor the image using CSS.
  • Inline-<svg> are unnecessary work for the server and are...

Debug flaky tests with an Unpoly observeDelay

The problem

Unpoly's [up-observe], [up-autosubmit] and [up-validate] as well as their programmatic variants up.observe() and up.autosubmit() are a nightmare for integration tests.

Tests are usually much faster than the configured up.form.config.observeDelay. Therefore, it may happen that you already entered something into the next field before unpoly updates that field with a server response, discarding your changes.

The steps I wait for active ajax requests to complete (if configured) and capybara-lockstep can catch some ...

Git: Switch

tl;dr

git checkout is the swiss army of git commands. If you prefer a semantically more meaningful command for branch related tasks, use git switch instead.

You can use git switch since git 2.23.

Switch Branch

git branch
# * master
#   feature-branch

git switch feature-branch
git branch
#   master
# * feature-branch

git switch -
git branch
# * master
#   feature-branch

Info

For this use case you can of course also use git checkout.

Switch on a Remote Branch

git branch -a
# * m...

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 checkout is the swiss army of git commands. If you prefer a semantically more meaningful command for restoring tasks, use git restore instead.

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...

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...

Updated: Git: removing feature branch on merge

  • Resolve @{-1} to actual branch name. (Happens when merging "-".)

Creating a Rails application in a single file

Greg Molnar has written a neat article about creating a single-file Rails app.
This is not meant for production use but can be useful to try things out, e.g. when hunting down a bug or embedding a Rails app into the tests of a gem.

What you do is basically:

  1. Put everything (gems, application config, database migrations, models, controllers) into a single .ru file, like app.ru.
  2. Run it via rackup app.ru. (Hint: if your file is called config.ru, you can just run `rac...

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....

JavaScript: Testing whether the browser is online or offline

You can use the code below to check whether the browser can make connections to the current site:

await isOnline() // resolves to true or false

The code

The isOnline() function below checks if you can make real requests by re-fetching your site's favicon. If the favicon cannot be downloaded within 6 seconds, it considers your connection to be offline.

async function isOnline({ path, timeout } = {}) {
  if (!navigator.onLine) return false

  path ||= document.querySelect...

Josh McArthur: Fancy Postgres indexes with ActiveRecord

I recently wanted to add a model for address information but also wanted to add a unique index to those fields that is case-insensitive.
The model looked like this:

create_table :shop_locations do |t|
  t.string :street
  t.string :house_number
  t.string :zip_code
  t.string :city
  t.belongs_to :shop
end

But how to solve the uniqueness problem?

Another day, another undocumented Rails feature!

This time, it’s that ActiveRecord::Base.connection.add_index supports an undocumented option to pass a string argument as the v...

Capybara: Most okayest helper to download and inspect files

Testing file download links in an end-to-end test can be painful, especially with Selenium.

The attached download_helpers.rb provides a download_link method for your Capybara tests. It returns a hash describing the download's response:

details = download_link('Download report')
details[:disposition]  # => 'attachment' or 'inline'
details[:filename]     # => 'report.txt'
details[:text]         # => file content as string
details[:content_type] # => 'text/plain'

Features

Compared to [other approaches](...

Flexbox: How to prevent <pre> elements from overflowing

I recently had the problem that embedded code boxes crashed my layout.

It turned out that pres break out of their containers when using them inside a deeper nested flex layout.
For me it was a flex inside a flex item (fleXzibit).

Image

<div class="flex">
  <div class="flex-item">
    ...
    <div class="nested-flex">
      <div class="nested-flex-item">
        <pre>A code example</pre>
      </div>
    </div>
    ...
  </div>
</div>

The reason is that flexbox items default to `mi...

JavaScript: New Features in ES2021

tl;dr

With ES2021 you now can use str.replaceAll(), Promise.any(), logical assignment operators, numeric separators and WeakRef on all major browsers except IE11.

replaceAll

JavaScript's replace(searchValue, replaceValueOrFn) by default replaces only the first match of a given String or RegExp.
When supplying a RegExp as the searchValue argument, you can specify the g ("global") modifier, but you have to remember doing that, hence using replace when you expect global replacement is prone to errors.
When supplying st...

Project maintenance: four levels of code quality

Code quality can be measured in four levels:

  1. (Working code)
  2. Reliable code (minimum)
  3. Readable code (ok for short-lived code)
  4. Changeable code (standard level)

The code quality of a project directly impacts its maintainability.

Generally you should aim for level 3. If the code will stay for less than a few months, it may stay at level 2. Never go below level 1.

0. Working code

You have implemented that feature and it works. Congrats! You have reached level zero, which means three levels of code quality lie ahead.

First, m...

Components: Dynamically growing input field's height to fit content

Sometimes you will need an input field which wraps content and grows in height as soon as content gets longer than the input fields width.
There is no way to get a "normal" string input field to wrap as desired, but there are other ways.

Here is one pretty easy solution to get what you want:

Step 1

Let your input became a text area with one row.

f.text_area(:name, rows: 1, autosize: '') 

Step 2

Include the autosize library in your project

yarn add autosize

And make your...

RSpec: You can super into parent "let" definitions

RSpec's let allows you to super into "outside" definitions, in parent contexts.

Example:

describe '#save' do
  subject { described_class.new(attributes) }
  let(:attributes) { title: 'Example', user: create(:user) }

  it 'saves' do
    expect(subject.save).to eq(true)
  end

  context 'when trying to set a disallowed title' do
    let(:attributes) { super().merge(title: 'Hello') } # <==

    it 'will not save' do
      expect(subject.save).to eq(false)
    end
  end
end

I suggest you don't make a habit of using this regula...

Integrating ESLint

Introduction

To ensure a consistent code style for JavaScript code, we use ESLint. The workflow is similar to integrating rubocop for Ruby code.

1. Adding the gem to an existing code base

You can add the following lines to your package.json under devDependencies:

"devDependencies": {
  "@eslint/js": "x",
  "@stylistic/eslint-plugin": "x",
  "eslint": "x",
  "eslint-plugin-import": "x",
  "globals": "x",
}

...

Understanding Ruby's def keyword

This StackOverflow question about nested function definitions in Ruby imparts a good understanding of Ruby's def.

esbuild: Make your Rails application show build errors

Building application assets with esbuild is the new way to do it, and it's great, especially in combination with Sprockets (or Propshaft on Rails 7).
You might be missing some convenience features, though.

Here we cover one specific issue:
Once you have started your development Rails server and esbuild with the --watch option (if you used jsbundling-rails to set up, you probably use bin/dev), esbuild will recompile your assets upon change, but build errors will only be printed to the terminal. Your application won't complain about them ...

Carrierwave: How to attach files in tests

Attaching files to a field that is handled by Carrierwave uploaders (or maybe any other attachment solution for Rails) in tests allows different approaches. Here is a short summary of the most common methods.

You might also be interested in this card if you see the following error in your test environment:

CarrierWave::FormNotMultipart:
You tried to assign a String or a Pathname to an uploader, for security reasons, this is not allowed.
If this is a file upload, please check that your upload form is multipart encoded.

Factor...

Deployment: Merge consecutive commits without cherry-picking

You want to deploy new features but the latest commits are not ready for production? Then use git merge master~n to skip the n-last commits.

Tip

A big advantage of merging vs. cherry-picking is that cherry-picking will create copies of all picked commits. When you eventually do merge the branch after cherry-picking, you will have duplicate commit messages in your history.

Example

It's time for a production deployment!

git log --pretty=format:"%h - %s" --reverse origin/production..origin/master

0e6ab39f - Feature A
6396...