Jasmine: Use `throwUnless` for testing-library's `waitFor`
testing-library are widely used testing utilities libraries for javascript dependent frontend testing. The main utilities provided are query methods, user interactions, dom expectations and interacting with components of several frontend frameworks, which allows us to worry less about the details happening in the browser and focus more on user centric tests instead!
Some of the time you will find a necessity to use methods like [waitFor](https://testing-library.com/docs/dom-testing-library/api-async/...
Cucumber: Skipping steps in a scenario outline, based on the current example
In Cucumber, scenario outlines help avoiding tests that are basically the same, except for a few variables (such as different inputs). So far, nothing new.
The problem
Now what if your test should (or should not) do something, like filling in a field only for some tests?
Scenario Outline: ...
When I open the form
And I fill in "Name" with "<name>" # <= we want to do this only occasionally
Then everybody should be happy
Examples:
| name |
| Alice |
| Bob |
You could o...
Faking and testing the network with WebMock
An alternative to this technique is using VCR. VCR allows you to record and replay real HTTP responses, saving you the effort to stub out request/response cycles in close details. If your tests do require close inspection of requests and responses, Webmock is still the way.
WebMock is an alternative to FakeWeb when testing code that uses the network. You sh...
How to use Simplecov to find untested code in a Rails project with RSpec and Cucumber
Simplecov is a code coverage tool. This helps you to find out which parts of your application are not tested.
Integrating this in a rails project with rspec, cucumber and parallel_tests is easy.
-
Add it to your Gemfile and bundle
group :test do gem 'simplecov', require: false end -
Add a
.simplecovfile in your project root:SimpleCov.start 'rails' do # any custom configs like groups and filters can be here at a central place enable_cov...
Limitations you should be aware of when Internet Explorer 9 parses CSS files
Internet Explorer until version 9 has some limitations when parsing CSS files
Summarized, these are:
- Up to 31 CSS files or
<style>tags per page. - Up to 4095 selectors per CSS file.
- Up to 3 nested @import rules
To test the selector limit for a specific browser, check this CSS selector limitation test website.
When you run into this issue, the following links might be helpful to fix the problem. The Idea is to split up the css ...
RSpec: Marking sections in long examples
RSpec examples can get quite long, especially in feature specs. This makes them hard to read & understand. Also, when executing them, it may take seconds to see any progress.
To improve this, I have successfully been using a little "step" helper in my tests. It marks semantic sections, structuring an example while improving documentation. When the test runs, each step prints its message (or a dot, depending on your formatter).
# spec/support/step_helper.rb
module StepHelper
# Use this helper to label groups of related actions in l...
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...
Capistrano: exclude custom bundle groups for production deploy
Capistrano is by default configured to exclude the gems of the groups development and test when deploying to the stages production and staging. Whenever you create custom groups in your Gemfile, make sure to exclude these, if they should not be deployed to the servers. The gems of these groups might not be loaded by rails, however, the deployment process will take longer as the gems will be downloaded and installed to the server.
e.g. to exclude the groups cucumber and deploy, add the following to `config/deploy/production.rb...
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...
Maintaining custom application tasks in Rails
Here are some hints on best practices to maintain your tasks in larger projects.
Rake Tasks vs. Scripts
- The Rails default is using rake tasks for your application tasks. These live in
lib/tasks/*. - In case you want to avoid rake for your tasks and just use plain ruby scripts, consider
lib/scripts/*as folder.
Keeping tasks slim
For readability and testing it's easier to keep your tasks slim. We suggest to use folders inside the tasks or scripts folder.
Example for a task:
The slim task lib/tasks/gitlab.rb:
Threads and processes in a Capybara/Selenium session
TLDR: This card explains which threads and processes interact with each other when you run a Selenium test with Capybara. This will help you understand "impossible" behavior of your tests.
When you run a Rack::Test (non-Javascript) test with Capybara, there is a single process in play. It runs both your test script and the server responding to the user interactions scripted by your test.
A Selenium (Javascript) test has a lot more moving parts:
- One process runs your test script. This is the process you...
How to enable Chromedriver logging
When using Chrome for Selenium tests, the chromedriver binary will be used to control Chrome. To debug problems that stem from Selenium's Chrome and/or Chromedriver, you might want to enable logging for the chromedriver itself. Here is how.
Option 1: Use Selenium::WebDriver::Service
In your test setup, you may already have something like Capybara::Selenium::Driver.new(@app, browser: :chrome, options: ...), especially when passing options like device emulation.
Similar to options, simply add an extra key service and pass an inst...
Chromedriver: Connect local chromedriver with docker
Debugging your integration tests, that run a headless Chrome inside a docker image, is tricky.
In many cases you can connect your Chrome to a remote docker container like docker-selenium, which should be the preferred way when you try to inspect a page within your integration test.
Otherwise you might be able to start your docker container with --net=host and access your local chromedriver in the host address space host.docker.internal.
If both options above don't work for you here is a...
Pitfall: ActiveRecord callbacks: Method call with multiple conditions
In the following example the method update_offices_people_count won't be called when office_id changes, because it gets overwritten by the second line:
after_save :update_offices_people_count, :if => :office_id_changed? # is overwritten …
after_save :update_offices_people_count, :if => :trashed_changed? # … by this line
Instead write:
after_save :update_offices_people_count, :if => :office_people_count_needs_update?
private
def office_people_count_needs_update?
office_id_changed? || trashed_changed?
end
Or...
SAML Single Logout (SLO)
There are two ways a logout in SAML can happen: Service Provider (SP) initiated and Identity Provider (IDP) initiated logout. I'll explain how to implement both flows with devise_saml_authenticatable.
Note
SAML also supports a
SOAPand anArtifactbinding to do this. This guide only refers toPOSTandRedirectbindings.devise_saml_authenticatabledoes not supportSOAPandArtifactbindings.
SP initiated logout (using the Redirect Binding)
When the user clicks on Logout within the app, the app can trigger...
CarrierWave: Default Configuration and Suggested Changes
CarrierWave comes with a set of default configuration options which make sense in most cases. However, you should review these defaults and adjust for your project wherever necessary.
You will also find suggestions on what to change below.
Understanding the default configuration
Here is the current default config for version 2:
config.permissions = 0644
config.directory_permissions = 0755
config.storage_engines = {
:f...
Geordi: How to rerun failed features
Geordi's cucumber command has a --rerun option that reruns failing tests the given number of times. Usage:
geordi cucumber path/to/features --rerun=2
geordi cucumber path/to/features -r2
Background and how to rerun manually
Cucumber will save a file tmp/parallel_cucumber_failures.log containing the filenames and line number of the failed scenarios after a full test run. Normally you can say cucumber -p rerun (rerun is a profile defined by default in config/cucumber.yml) to rerun all failed scenarios.
Here are a few al...
How to turn images into inline attachments in emails
Not all email clients support external images in all situations, e.g. an image within a link. In some cases, a viable workaround is to turn your images into inline attachments.
Note
Rails provides a simple mechanism to achieve this:
This documentation makes it look like you have to care about these attachments in two places. You have to create the attachment in t...
Detect the current Rails environment from JavaScript or CSS
Detecting if a Javascript is running under Selenium WebDriver is super-painful. It's much easier to detect the current Rails environment instead.
You might be better of checking against the name of the current Rails environment. To do this, store the environment name in a data-environment of your <html>. E.g., in your application layout:
<html data-environment=<%= Rails.env %>>
Now you can say in a pi...
Jasmine: Fixing common errors during initialization
Due to the way we setup Jasmine tests in our projects, you may run into various errors when Jasmine boots.
Setting jasmineRequire on undefined
Jasmine 4 may fail with an error like this:
Uncaught TypeError: Cannot set properties of undefined (setting 'jasmineRequire')
This is due to issues in Jasmine's [environment detection](https://github.com/jasmine/jasmine/blob/502cb24bb89212917a3c943b593fd918ffc481cb/lib/jasmine-core/...
Heads up: RSpec-Mocks' #stub_const will define intermediate modules that have not been loaded yet
The issue: You are using stub_const to change a constant value for your test.
stub_const "SomeClass::CONST", 'test'
All of a sudden, tests fail with undefined method 'some_method' for #<SomeClass:0x00000000101433a8>.
The reason
When using stub_const before the Class containing the constant has been loaded, a module is automatically created with the name.
Since RSpec does no autoloading, it will create a SomeClass module by itself. This is arguably a good idea.
As a workaround, use stub_const in your Rails specs li...
Rails: Looking up constants by their name string
TL;DR: Rails ships two methods to convert strings to constants, constantize and safe_constantize. Neither is safe for untrusted user input. Before you call either method you must validate the input string against an allowlist. The only difference between the two methods is that unresolvable constants raise an error with constantize, but return nil with safe_constantize. If you validate the input string against an allowlist, an error should never happen.
Preventing Dangerous Lookups
Suppose an application uses eit...
When loading Yaml contents in Ruby, use the :freeze argument to deep-freeze everything
Ruby methods which load from a Yaml file, like YAML.safe_load or YAML.safe_load_file, support passing freeze: true to deep-freeze the entire contents from the Yaml file.
This is available by default on Ruby 3.0 and newer. On older Rubies, you can install psych 3.2.0 or newer for :freeze support.
As an example, consider the following Yaml file:
---
message:
- hello
- universe
foo:
bar:
baz: "example"
We can now load it as usual, but pass freeze: true.
>> test = YAML.safe_load_file('example.yml', fre...
Gatekeeping: Guide for developer
If your project manager wants to do gatekeeping on a project, as a developer you need to follow the following guidelines (e.g. by using something like this issue checklist template).
In order to reduce the number of rejects we get from clients, we want to review all code written before it goes to the staging server.
Note
This process is tailored to our specific needs and tools at makandra. While it will certainly not apply to all (especially larger teams), we think it...