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

WYSIWYG with Action Text

Rails 6 includes a WYSIWYG editor, Action Text. It works out of the box quite well, but chances are that you want to add some custom functionality. This card contains some tips how to achieve this.

Setup

Basically, follow the guide in the Rails documentation. The automated script may not work with the way webpacker is configured in your project, but it should be easy to fix.

If you don't want the default c...

How to migrate CoffeeScript files from Sprockets to Webpack(er)

If you migrate a Rails application from Sprockets to Webpack(er), you can either transpile your CoffeeScript files to JavaScript or integrate a CoffeeScript compiler to your new process. This checklist can be used to achieve the latter.

  1. If you need to continue exposing your CoffeeScript classes to the global namespace, define them on window directly:
-class @User
+class window.User
  1. Replace Sprocket's require statement with Webpacker's...

Five years of "Today I Learned" from Josh Branchaud

The linked GitHub repository is a bit like our "dev" cards deck, but groomed from a single person (Josh Branchaud). It includes an extensive list of over 900 TILs on many topics that might be interesting for most of us. (e.g. Ruby, Rails, Git, Unix..)

Ruby

Here is an excerpt of all the Ruby TILs that were new to me. I encourage you to take your time to skim over the original list as well!

Migrating from CoffeeScript to ES6

It is quite easy to migrate from CoffeeScript to ES6. You can use decaffeinate to convert your CoffeeScript source to modern JavaScript.

Install decaffeinate globally:

npm install -g decaffeinate

Call decaffeinate on each .coffee file, relaxing some options to get the most modern (and concise) JS:

decaffeinate file.coffee --use-cs2 --loose --optional-chaining --logical-assignment

Tip

If you use Babel and see errors while decaffeinati...

Rails: Flagging all cookies as secure-only to pass a security audit

Why secure-only cookies used to be necessary

Cookies have an optional secure flag. It tells the browser to not send the cookie for a non-https request.

It used to be important to activate the secure flag even on sites that automatically redirect users from http:// to https://. The reason was that most users will only enter a scheme-less domain like makandra.de into their location bar, which will default to `http://m...

A collection of graph and diagram tools

VCR fails if the same request is triggered multiple times

Same requests are recorded only once in vcr. Replaying a test fails, if you trigger the same request multiple times. The error message is somehow confusing, as your cassette contains the request:

An HTTP request has been made that VCR does not know how to handle

If you want to allow to match a request multiple times, you need to configure this explicit with allow_playback_repeats: true. Some exa...

Jasmine: Testing complex types for equality

Jasmine comes with two matchers that test for equality. The first is toBe:

expect(first).toBe(second)

toBe passes when first === second. Unfortunately this is useless for non-primitive values because JavaScript is a horrible language.

However, Jasmine comes with another matcher toEqual:

expect(first).toEqual(second)

This matcher behaves as a human would expect for types like the following:

  • Arrays
  • Objects
  • Nested array/object constructs
  • Regular expressions...

Testing regular expressions visually

Developing complex regular expressions quickly blows my mind. Here are some online regex editors that help you by highlighting matching text and capture groups:

Asset Pipeline Basics

The Rails asset pipeline improves delivery of application assets (javascripts, stylesheets, images, fonts). Here are some basic facts about its inner workings.

No magic

Manifests are the handle on your assets:

app/assets/stylesheets/application.css # use via: stylesheet_link_tag 'application'

The asset pipeline only considers files you explicitly require within your manifest files. The most common directives used in manifests are require some/file and require_tree some/directory. Paths may be **relative to the current director...

jQuery: Work with text nodes and comment nodes

Nearly all jQuery traversal functions ignore elements that are not HTML tags.

To work with other type of nodes (like text, comment or CDATA sections) you need to:

  • Retrieve child nodes contents() (which behaves like children() except that it returns all types of child nodes)
  • Filter manually using either plain Javascript or jQuery's filter() method

Example

Let's write a function that takes a jQuery element and returns an array of all child nodes that are text nodes:

function selectTextNodes($container) {
  retu...

Capybara: evaluate_script might freeze your browser

Capybara gives you two different methods for executing Javascript:

page.evaluate_script("$('input').focus()")
page.execute_script("$('input').focus()")

While you can use both, the first line (with evaluate_script) might freeze your browser window for 10 seconds.

The reason is that evaluate_script will always return a result. The return value will be converted back to Ruby objects, which in case of complex objects (e.g. a jQuery collection) is very expensive.

Because of this we recommend to only use evaluate_script whe...

How to: Specify size of Selenium browser window

Applications often show or hide elements based on viewport dimensions, or may have components that behave differently (like mobile vs desktop navigation menus).
Since you want your integration tests to behave consistently, you want to set a specific size for your tests' browser windows.

Using WebDriver options / Chrome device metrics

For Google Chrome, the preferred way is setting "device metrics". This allows you to configure dimensions larger than your display and enable/disable touch behavior.

Simply use register_driver to set up...

Run Selenium tests in Chrome instead of Firefox

Here is how to switch your Selenium to Chrome:

  1. Make sure you've got a recent version of chromedriver in your $PATH

See also geordi chromedriver_update which is automatically executed before every usage of geordi cucumber.

  1. Register Driver:

Create a file features/support/capybara.rb with the following content for recent version of Capybara:

Capybara.register_driver :selenium do |app|
  Capyb...

Force absolute URLs in views throughout a response

This is more tricky than it should be because url_for, asset_path, etc. all rely on different mechanisms.

Anyway, you can use the attached trait like this:

class ExampleController < ApplicationController
  does 'host_enforcement', :for => 'some_action'
end

Short explanation:

  • asset_host is used for links to stylesheets and javascripts
  • asset_host belongs to ActionController::Base -- changes are persistent and will not be reset after a request
  • rewrite_options is used by the ..._path methods in the views
    ^...

Selector for finding the currently selected option in a <select> tag

Use option:checked to find the currently selected option:

select.querySelector('option:checked')

Yes, :checked, not :selected.

This is the same as finding an option with the { selected: true } property in JavaScript:

select.querySelectorAll('option').find((option) => option.selected)

What about the selected attribute?

Note that option[selected] would only find an <option selected> tag. This may be the selected option right after loading the page, but not once the user switched to a different value. ...

The TCF 2.0 (Tranparency and Consent Framework) standard, and what you should know about it

The Interactive Advertising Bureau (IAB) is a European marketing association which has introduced a standard how advertising can be served to users in line with the General Data Protection Regulation (GDPR). This standard is called the TCF 2.0 (Transparency and Consent Framework). If you want to integrate any kind of advertising into a website, chances are the advertising network will require your website to implement that standard. This is a very brief overview of what this means:

The basic idea in the TCF 2.0 ...

Fixing authentication in legacy applications

Authentication is hard: there are many edge cases, and most users (including yourself) usually only go the "happy path" once and never see the edge cases. If you have rolled your own authentication, or been using older authentication solutions, or resorted to HTTP Basic Authentication, this card will tell you what to do to make your application safe.

Any application that stores sensitive data in the browser

That is: cookies, e.g. by offering a login.

  • Ask the admins to [turn on SSL](https://makandracards.com/makandra/1416-integrate-s...

Middleman configuration for Rails Developers

Middleman is a static page generator that brings many of the goodies that Rails developers are used to.

Out of the box, Middleman brings Haml, Sass, helpers etc. However, it can be configured to do even better. This card is a list of improvement hints for a Rails developer.

Gemfile

Remove tzinfo-data and wdm unless you're on Windows. Add these gems:

gem 'middleman-livereload'
gem 'middleman-sprockets' # Asset pipeline!

gem 'bootstrap-sass' # If you want to use Bootstrap

gem 'byebug'

gem 'capistrano'
gem 'capistrano-mid...

Testing for XSS in Markdown Fields

If you render markdown from user input, an attacker might be able to use this to inject javascript code into the source code of your page.
The linked github page is a collection of common markdown XSS payloads which is handy for writing tests.

Producing arbitrary links:

[Basic](javascript:alert('Basic'))
[Local Storage](javascript:alert(JSON.stringify(localStorage)))
[CaseInsensitive](JaVaScRiPt:alert('CaseInsensitive'))
[URL](javascript://www.google.com%0Aalert('URL'))
[In Quotes]('javascript:alert("InQuotes")')

Using onload...

Improving browser rendering performance

As the web is being used for more and more tasks, expectations rise. Not only should web pages offer rich interaction, they must be responsive in both size and interaction.

This imposes a paradoxon that needs to be solved by building performing applications. It's not enough any more to have your web site do crazy stuff, it is also required to do it crazy fast. This card is intended to give you an introduction to this emerging aspect of web development.

Read this introductory [performance study on Pinterest](http://www.smashingmagazine.com/...

How to set up database_cleaner for Rails with Cucumber and RSpec

Add gem 'database_cleaner' to your Gemfile. Then:

Cucumber & Rails 3+

# features/support/database_cleaner.rb

DatabaseCleaner.clean_with(:deletion) # clean once, now
DatabaseCleaner.strategy = :transaction
Cucumber::Rails::Database.javascript_strategy = :deletion

Cucumber & Rails 2

The latest available cucumber-rails for Rails 2 automatically uses database_cleaner when cucumber/rails/active_record is required -- but only if transactional fixtures are off. To have database_cleaner work correctly:

  1. Add the at...

Implementing social media "like" buttons: Everything you never wanted to know

So you client has asked you to implement a row of buttons to like the URL on Facebook, Twitter and Google+. Here are some things you should know about this.

0. Security considerations

Each "like" button is implemented by including a Javascript on your site. This means you are running fucking remote code on your page. You are giving Facebook, Twitter and Google+ full permission to e. g. copy user cookies. Check with your client if she is cool with that. Also note that if you're site is suggesting security by operating under HTTPS ...