Geordi 1.2 released

Changes:

  • Remove some old binaries (commands still exist in geordi) and mark others as deprecated
  • Rewrite deploy command to support most deploy scenarios:
    • master to production
    • feature branch to staging
    • master to staging or production to production (plain deploy)
  • Improve Cucumber command (fixes #18):
    • Fix pass-through of unknown options to Cucumber
    • Add --rerun=N option to rerun failed Cucumber tests up to N times. Reboots the test environment between runs, thus will pick up fixes you made durin...

Gemspecs must not list the same gem as both runtime and development dependency

When you're developing a gem, never list the same dependency as both runtime and development dependency in your .gemspec.

So don't do this:

spec.add_dependency 'activesupport'
spec.add_development_dependency 'activesupport', '~> 2.3'

If you do this, your gemspec will not validate and modern versions of Bundler will silently ignore it. This leads to errors like:

Could not find your-gem-0.1.2 in any of the sources

What to do instead

If you want to freeze a different version of a dependency for your t...

SSHKit 1.9.0 failure for Capistrano deploy

SSHKit 1.9.0 might fail with the following error, when trying to deploy a Rail application. Upgrading the gem to version 1.21.0 fixed the issue.

Traceback (most recent call last):
	17: from /home/user/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sshkit-1.9.0/lib/sshkit/runners/parallel.rb:12:in `block (2 levels) in execute'
	16: from /home/user/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sshkit-1.9.0/lib/sshkit/backends/abstract.rb:29:in `run'
	15: from /home/user/.rbenv/versions/2.5.0/lib/ruby/gems/2.5.0/gems/sshkit-1.9....

Migrating to Spreewald

This describes how to migrate an existing cucumber test suite to Spreewald.

  1. Add the gem

  2. Include spreewald into your cucumber environment by putting
    require 'spreewald/web_steps'
    require 'spreewald/email_steps'
    # ...
    or just
    require 'spreewald/all_steps'
    into your support/env.rb.

  3. Look through your step definitions for everything that might be included in Spreewald. Candidates are web_steps, shared_steps, table_steps, `em...

Resolving Element cannot be scrolled into view (Selenium::WebDriver::Error::MoveTargetOutOfBoundsError) on Mavericks

After I upgraded to Mac OS X Mavericks, I regularly got this error message when running Cucumber features with Selenium:

Element cannot be scrolled into view:[object XrayWrapper [object HTMLInputElement]] (Selenium::WebDriver::Error::MoveTargetOutOfBoundsError)

I had the Terminal window running the test on my secondary screen, whereas the Selenium-webdriven Firefox always started on my primary one. Now if I had focused the secondary screen when running the tests, Selenium could not start Firefox and switch to it (probably because t...

makandra/capybara-lockstep

capybara-lockstep can help you with flaky end-to-end tests:

This Ruby gem synchronizes Capybara commands with client-side JavaScript and AJAX requests. This greatly improves the stability of a full-stack integration test suite, even if that suite has timing issues.

Silence specific deprecation warnings in Rails 3+

Sometimes you're getting an ActiveSupport deprecation warning that you cannot or don't want to fix. In these cases, it might be okay to silence some specific warnings. Add this to your initializers, or require it in your tests:

silenced = [
  /Not considered a useful test/,
  /use: should(_not)? have_sent_email/,
] # list of warnings you want to silence

silenced_expr = Regexp.new(silenced.join('|'))

ActiveSupport::Deprecation.behavior = lambda do |msg, stack|
  unless msg =~ silenced_expr
    ActiveSupport::Deprecation::DEFAULT_BEHAVI...

How to silence thin boot messages

Each time thin boots, it prints a boot message :

Thin web server (v1.6.3 codename Protein Powder)
Maximum connections set to 1024
Listening on localhost:36309, CTRL+C to stop

If you are running parallel tests with thin, this will clutter you output. Disable thin logging with these lines:

# e.g. in features/support/thin.rb

require 'thin'
Thin::Logging.silent = true

Note that this disables all logging in tests. Instead, you also might set a different logger with `Thin::Loggi...

Selenium cannot obtain stable Firefox connection

When using geordi for integration tests you might get the following error when trying to run geordi cucumber:

unable to obtain stable firefox connection in 60 seconds (127.0.0.1:7055) (Selenium::WebDriver::Error::WebDriverError)

This means, that the vnc window the tests is talking to has no proper firefox version running. To figure out the issue this might help you:

  • Check if the .firefox-version (e.g. 24.0) is the same as ~/bin/firefoxes/24.0/firefox says in the browser
  • Maybe [rest...

Compare two jQuery objects for equality

Every time you call $(...) jQuery will create a new object. Because of this, comparing two jQuery collections with == will never return true, even when they are wrapping the same native DOM elements:

$('body') == $('body') // false

In order to test if two jQuery objects refer to the same native DOM elements, use is:

var $a = $('body');
var $b = $('body');
$a.is($b); // true

Jasmine equality matcher for jQuery

See [here](/makandra/34925-jasmine-testing-complex-types-for-e...

PostgreSQL's OVERLAPS operator is not fully inclusive

PostgreSQL supports the SQL OVERLAPS operator. You can use it to test if two date ranges overlap:

=> SELECT ('2001-02-16'::date, '2001-12-21'::date) OVERLAPS
          ('2001-12-20'::date, '2002-10-30'::date);

overlaps
--------
true

An important caveat is that the date ranges are defined as start <= time < end. As such the later date is not included in the range:

=> SELECT ('2001-02-16'::date, '2001-12-21'::date) OVERLAPS
          ('2001-12-21'::date, '2002-10-30'::date);

overlaps
--------
false

Also compar...

Testing terminal output with RSpec

When testing Ruby code that prints something to the terminal, you can test that output.
Since RSpec 3.0 there is a very convenient way to do that.

Anything that writes to stdout (like puts or print) can be captured like this:

expect { something }.to output("hello\n").to_stdout

Testing stderr works in a similar fashion:

expect { something }.to output("something went wrogn\n").to_stderr

Hint: Use heredoc to test multi-line output.

expect { something }.to output(<<-MESSAGE.strip_heredoc).to_stdout...

Selenium: Network throttling via Chromedriver

You can throttle the network in your headless chrome via Selenium. This might be useful for debugging issues with flaky integration tests or slow page simulations.

page.driver.browser.network_conditions = {offline: false, latency: 5, download_throughput: 2 * 1024, upload_throughput: 2 * 1024}

The settings will match to the following UI component in Chrome:

Image

Were the values for the default profiles might match the values from this post:

**S...

Rails: Parsing a time in a desired timezone

Sometimes you want to have a time in a given timezone independent from you Rails timezone settings / system timezone. I usually have this use case in tests.

Example

Time.parse('2020-08-09 00:00') will return different results e.g. 2020-08-09 00:00:00 +0200 depending on the Rails timezone settings / system timezone. But in this example we always want to have the given time in UTC because that's what the API returns.

it 'returns a valid API response', vcr: true do
  expect(client.get('/users/1')).to have_attributes(
    name: 'So...

Usage of RSpec's raise_error

Never use raise_error without specifying the Error you expect.

expect { do_a_lot_of_complicated_stuff }.to raise_error

will be green if you make any error in programming. E.g. a simple typo would make the test above green. The block will catch the Spec:: exception and the test will be happy.

Be sure to always have custom errors in your models and raise them in a manner that lets you know what went wrong.

expect { execute_payment! }.to raise_error(PayPal...

How to communicate between processes in Ruby with sockets

In Ruby you can communicate between processes with sockets. This might be helpful in tests that validate parallel executions or custom finalization logic after the garbage collector. Here is an example how such an communication will look like:

require 'socket'
BUFFER_SIZE = 1024

# DGRAM has the advantage that it stops reading the pipe if the next messages starts. In case the message size is larger than the
# BUFFER_SIZE, you need to handle if you are reading another part of the current message or if you already reading the
# next mess...

Katapult 0.3.0 released

Katapult 0.3.0 brings Rails 5 and Ruby 2.5 support with a new design, plus a ton of smaller features, fixes and improvements.

Features

  • Generating a Rails 5.1.4 app on Ruby 2.5.0
  • Dropped asset pipeline in favor of Webpacker
  • The generated application now has a sleek, simple design based on Bootstrap
  • Employing Unpoly
  • New application model DSL shortcut crud for "create a model and a web UI with crud actions"
  • The generated application model is now a transformable e...

Undefined method log for Selenium::WebDriver::Remote::W3C::Bridge

In case your integration tests crash with a message like below, try to upgrade Capybara to a newer version (3.35.3 was good enough). You might encounter this issue when you enabled the w3c option in Selenium.

undefined method `log' for #<Selenium::WebDriver::Remote::W3C::Bridge:0x000055995647ded0>

Your affected code might look similar to this call below and will work after the upgrade again.

Setup your terminal to not scroll when there is new output

When you are scrolling up to investigate a test failure it is super annoying when the terminal scrolls back down whenever the running test outputs another line. Luckily you can disable this behavior:

  • Gnome terminal: *Edit -> Profile preferences -> Scrolling", uncheck Scroll on output
  • Terminator: Right click on terminal screen, Preferences -> Profile -> (for each profile) -> Scrolling, uncheck Scroll on output

Rails 3/4: How to add routes for specs only

If you want to have routes that are only available in tests (e.g. for testing obscure redirects), you can use the with_routing helper -- but that one destroys existing routes which may break a specs that require them to work.

To keep both "regular" and test routes, do this:

class MyApplicationController < ActionController::Base
  def show
    render text: 'Welcome to my application'
  end
end

test_routes = Proc.new do
  get '/my_application' => 'my_application#show'
end
Rails.application.routes.ev...

GitHub Actions: Manually running a workflow

To start a workflow manually it must have a trigger called workflow_dispatch:

---
name: Tests
on:
  push:
    branches:
    - master
  pull_request:
    branches:
    - master
  workflow_dispatch:
    branches:
    - master  

In the Actions tab of your repo you can now select a workflow and press "Run Workflow".

See GitHub documentation for details.

How to check if a file is a human readable text file

Ruby's File class has a handy method binary? which checks whether a file is a binary file. This method might be telling the truth most of the time. But sometimes it doesn't, and that's what causes pain. The method is defined as follows:

# Returns whether or not +file+ is a binary file.  Note that this is
# not guaranteed to be 100% accurate.  It performs a "best guess" based
# on a simple test of the first +File.blksize+ characters.
#
# Example:
#
#   File.binary?('somefile.exe') # => true
#   File.binary?('somefile.txt') # => fal...

The Bark Blog » Testing Rails Model Plugins

Unfortunately, by default plugin tests are pretty bland. They use the plain unit test suite supplied by Ruby, and not any of the extended Rails test framework. This will leave our plugin’s test classes with no access to fixtures, database.yml configuration, or any of those nice class auto-loading features.

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