Nested Spreewald patiently blocks are now patient
In Spreewald 1.10.4+, nested patiently
blocks are now patient.
Here is an example:
patiently do
outer_code
patiently do
inner_code
end
end
On spreewald 1.11.2+ the inner block will wait for the full configured wait time (by default 5 seconds). The outer patiently
block would now be out of time, but it will always be retried at least a second time. This behavior allows with_scope
to be patient, and it must be patient, as explained below.
In versions 1.10.4 - 1.11.1, inner blocks would keep giving the ou...
How to: Solve gem loaded specs mutex
Use bundler > 1.15
to fix Gem::LOADED_SPECS_MUTEX (NameError)
.
Given the following project:
ruby -v
ruby 1.8.7
bundler -v
Bundler version 1.13.7
gem -v
1.8.30
rails -v
Rails 3.2.22.1
Running specs or features resulted in:
uninitialized constant Gem::LOADED_SPECS_MUTEX (NameError)
The previous settings described in Maximum version of Rubygems and Bundler for Ruby 1.8.7 and Rails 2.3 (even the rails version was rails 3.2 and not 2.3) seems not to work here, so I used (also described in the ca...
How to control Chromedriver using curl
Here is how to use Chromedriver without libraries like selenium-webdriver
. This can be useful for debugging.
The following example visits a web page and reads the a headline's text contents.
-
Create a session. You will get a JSON response containing lots of information about your Chrome session, including a
sessionId
. Use that to send any future commands to your chromedriver session.$ curl -XPOST http://localhost:9515/session -d '{"desiredCapabilities":{"browserName":"chrome"}}' {"sessionId":"your-session-id-here","sta...
Katapult 0.4.0 released
Features
- Generating a project README
- Finally: Support for modelling associations between models in your application model! Example:
# lib/katapult/application_model.rb
model 'Cart' do |cart|
cart.belongs_to 'User'
end
crud 'User' do |user|
user.attr :fullname
end
Cucumber: How to avoid VCR errors for unused requests in pending tests
When you have a pending Cucumber step (or feature) that also uses an existing VCR cassette, your pending test may fail at the very end with an error like this:
There are unused HTTP interactions left in the cassette:
- [get ...] => [200 ...]
- [get ...] => [200 ...] (VCR::Errors::UnusedHTTPInteractionError)
The error happens because your VCR is configured to complain about cassettes that contain extra requests which your test did not use. This is often a good configuration.
If you do not want to change your whole test suite...
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...
Capybara: A step for finding images with filename and extension
This cucumber step is useful for testing an image (looking at the src
of the image).
Then(/^I should see the image "([^"]*)"$/) do |filename_with_extension|
expect(page).to have_css("img[src*='#{filename_with_extension}']")
end
Then I should see the image "placeholder.png"
Outline: Read more about how to test a uploaded file here, e.g. file downloads.
Form letters with LibreOffice Writer
This is painful. Consider using Microsoft Office or switching careers. If you need to write < 20 letters consider doing it manually.
So you didn't listen and here it comes:
- Ignore the Mail Merge Wizard. It will crash or destroy your document.
- Export your addresses, recipient names, etc. as a
.ods
spreadsheet (.xls
,.xlsx
,.ods
). Use any columns that work for you, but be consistent. I like to use one column for the address, one column for the salutation line. - Import the spreadsheet as an address book source: *Tools => Add...
Error during Rails 5 upgrade: Environment data not found in the schema
This error is raised because your old database does not have a configured environment yet, which Rails 5 enforces.
If this error occurs while migrating your parallel test databases, make sure to update the parallel_tests
gem first: current versions fix this. If you're still using Cucumber < v3, the latest version of parallel_tests
will be 2.18.0.
HTML: Making browsers wrap long words
By default, browsers will not wrap text at syllable boundaries. Text is wrapped at word boundaries only.
This card explains some options to make browsers wrap inside a long word like "Donaudampfschifffahrt"
.
Option 1: hyphens CSS property (preferred)
Modern browsers can hyphenate natively. Use the hyphens CSS property:
hyphens: auto
There is also hyphens: none
(disable hyphenations even at ­
entities) and hyphens: manual
(hy...
xlsxtream: Streaming & Fast XLSX Spreadsheet Writer for Ruby
When writing XLSX files, there are gems like rubyXL or axlsx. While they do offer features like formatting or graphs, to represent spreadsheet cells, they have to keep several Ruby objects in memory. When writing huge files, both will become slow and consume lots of memory.
Enter Xlsxtream, a Ruby XLSX library with less features (e.g. no individual cell styles) but which does away with the memory issue by streaming ...
chromedriver-helper gem in Gemfile might break you selenium tests (of other projects)
Summary: Don't add chromedriver-helper to the Gemfile
- the executables might break your tests in projects where
chromedriver-helper
is not in the Gemfile - developers with different chrome versions will have problems using the same
chromedriver-helper
version
Background
If you install the chromedriver-helper
gem, but don't have it in you Gemfile, your selenium tests might fail with:
Selenium::WebDriver::Error::WebDriverError: unable to connect to chromedriver 127.0.0.1:9515
The reason is that chromedriver-helper
ov...
Cucumber: Identifying slow steps that drag down your test speed
In most projects I know, Cucumber test suite speed is not an issue. Of course, running 350 features takes its time, but still each test for itself is reasonably fast. There is nothing you can do to fundamentally speed up such a test (of course, you should be using parallel_tests).
However, in projects that go beyond clicking around in a web GUI and checking results, there might be various measures to speed things up. Katapult tests for example could be sped up more than 4 times by re...
Rails + Sidekiq::Web: Configuration for wildcard session cookies
When you're using Sidekiq::Web
to monitor the Sidekiq status AND have your session cookie configured to a wildcard domain like .example.com
, you need to take an additional step to keep your cookies valid.
Issue
Sidekiq::Web
is mounted into your Rails application and will use the Rails session cookie for protection from CSRF attacs. While it somehow figures out the cookie name, it does NOT respect cookie configuration like a custo...
How to install packages from newer Ubuntu releases
We're usually running Ubuntu LTS versions. Sometimes newer hardware requires packages from more recent Ubuntu releases that only come with 6 months of support. If there is really no other way, it's possible to install packages from later Ubuntu releases
Caution: Pay really close attention to what you're doing. Depending on the package, this process may require upgrading a lot of dependencies, possibly breaking the system! You really should not do this unless you've carefully calculated the impact on your system
Preparation
First,...
Capybara: Accessing the parent of an element
If you already selected an element and want to get its parent, you can call find(:xpath, '..')
on it.
To get the grand-parent element, call find(:xpath, '../..')
.
Example
Find a link which contains a twitter icon and check that it links to the correct page:
<a href="http://twitter.com/">
<i class="icon is-twitter"></i>
</a>
link = page.find("a .icon.is-twitter").find(:xpath, '..')
expect(link[:href]).to eq("http://twitter.com/")
About XPath
There is a good overview on XPath syntax on [w3schools](htt...
Spreewald: Content-Disposition not set when testing a download's filename
Precondition
- You are not using javascript tests
- The file is served from a public folder (not via controller)
Problem description
If you deliver files from a public folder it might be that the Content-Disposition
header is not set. That's why the following spreewald step might raise an error:
Then I should get a download with filename "..."
expected: /filename="some.pdf"$/
got: nil (using =~) (RSpec::Expectations::ExpectationNotMetError)
Solution
One solution...
Fixing flaky E2E tests
An end-to-end test (E2E test) is a script that remote-controls a web browser with tools like Selenium WebDriver. This card shows basic techniques for fixing a flaky E2E test suite that sometimes passes and sometimes fails.
Although many examples in this card use Ruby, Cucumber and Selenium, the techniques are applicable to all languages and testing tools.
Why tests are flaky
Your tests probably look like this:
When I click on A
And I click on B
And I click on C
Then I should see effects of C
A test like this works fine...
Async control flow in JavaScript: Promises, Microtasks, async/await
Slides for Henning's talk on Sep 21st 2017.
Understanding sync vs. async control flow
Talking to synchronous (or "blocking") API
print('script start')
html = get('/foo')
print(html)
print('script end')
Script outputs 'script start'
, (long delay), '<html>...</html>'
, 'script end'
.
Talking to asynchronous (or "evented") API
print('script start')
get('foo', done: function(html) {
print(html)
})
print('script end')
Script outputs 'script start'
, 'script end'
, (long ...
Rails: namespacing models with table_name_prefix instead of table_name
When you want to group rails models of a logical context, namespaces are your friend. However, if you have a lot of classes in the same namespace it might be tedious to specify the table name for each class seperately:
class Accounting::Invoice < ApplicationRecord
self.table_name = 'accounting_invoices'
...
end
class Accounting::Payment < ApplicationRecord
self.table_name = 'accounting_payments'
...
end
A replacement for the self.table_name
-assignment is the table_name_prefix
in the module definition:
modu...
How to enable the Thinkpad microphone mute key on Ubuntu 16.04
While the hardware mute button of my Lenovo x230 worked on Ubuntu 14.04 out of the box, it does not on Ubuntu 16.04. It is fairly simple to fix, though.
There is an extensive answer on Ask Ubuntu, but only part of it was required for me. Here is the gist of it.
-
Open a terminal
-
Run
acpi_listen
and press the mute key. You should see something like this:button/f20 F20 00000080 00000000 K
Press
Ctrl
+C
to exit. -
Run
amixer scontrols
. You will see multiple lines, one of which sh...
How to change the class in FactoryBot traits
FactoryBot allows a :class
option to its factory
definitions, to set the class to construct. However, this option is not supported for trait
s.
Most often, you can just define a nested factory instead of a trait, and use the :class
option there.
factory :message do
factory :reply, class: Message::Reply do
# ...
end
end
If you need/want to use traits instead (for example, it might make more sense semantically), you can not use a :class
on a trait
.
In that case, use initialize_with
to define the record's constr...
Dealing with I18n::InvalidPluralizationData errors
When localizing model attributes via I18n you may run into errors like this:
I18n::InvalidPluralizationData: translation data { ... } can not be used with :count => 1. key 'one' is missing.
They seem to appear out of the blue and the error message is more confusing than helpful.
TL;DR A model (e.g. Post
) is lacking an attribute (e.g. thread
) translation.
Fix it by adding a translation for that model's attribute (attributes.post.thread
). The error message reveals the (wrongly) located I18n data (from `attributes.thread...
Rspec: around(:all) and around(:each) hook execution order
Background
-
before(:all)
runs the block once before all of the examples. -
before(:each)
runs the block once before each of your specs.
Summary
-
around(:suite)
does not exist. -
around(:all)
runs afterbefore(:all)
and beforeafter(:all)
. -
around(:each)
runs beforebefore(:each)
and afterafter(:each)
.
As this is not 100% obvious (and not yet documented) it is written down in this card. In RSpec 3 :each
has the alias :example
and :all
the alias :context
.
Example
RSpec.configure do |config|
...