Consulting. Training. Development.

Testing custom SimpleForm inputs

We’re using SimpleFrom because of its powers in customization. We consider creating custom inputs every time we’re going to write something like this:

1
2
3
<%= f.input :birth_date, order:      [:day, :month, :year],
                         end_year:   Date.today.year - 18,
                         start_year: Date.today.year - 100 %>

It’s worth to have simple input definition in views without need to specify all those options every time. The following looks much better:

1
<%= f.input :birth_date, as: :birth_date %>

Or even:

1
<%= f.input :birth_date %>

But every time implementing custom inputs we should bother about writing tests for them. Let me show the way we write tests for custom SimpleFrom inputs. We’ll use RSpec and put input specs into spec/inputs directory.

Before writing tests we need to implement an example group module that will be included into input specs. To do this you can create spec/support/inputs.rb with the following code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module InputExampleGroup
  extend ActiveSupport::Concern

  include RSpec::Rails::HelperExampleGroup

  def input_for(object, attribute_name, options={})
    helper.simple_form_for object, url: '' do |f|
      f.input attribute_name, options
    end
  end
end

RSpec.configure do |config|
  config.include InputExampleGroup, type: :input, example_group: {
    file_path: config.escaped_path(%w[spec inputs])
  }
end

Now you have input_for test helper method available in your input spec. So it’s time to describe the birth date input:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# spec/inputs/birth_date_input_spec.rb
require 'spec_helper'

describe 'birth_date input' do
  before do
    concat input_for(:foo, :birth_date, as: :birth_date)
  end

  it 'includes blank' do
    assert_select 'option[value=]', count: 3
  end

  it 'has day select first' do
    assert_select 'select:first-of-type[name=?]', 'foo[birth_date(3i)]'
  end

  it "doesn't include current year" do
    assert_select 'option[value=?]', Time.now.year, count: 0
  end
end

We use input_for test helper method here and assert_select method from Rails.

app/inputs/birth_date_input.rb

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class BirthDateInput < SimpleForm::Inputs::DateTimeInput
  def input_type
    'date'
  end

  def input_options
    super.tap do |options|
      options[:include_blank] = true
      options[:order]         = [:day, :month, :year]
      options[:start_year]    = Time.now.year - 100
      options[:end_year]      = Time.now.year - 18
    end
  end
end

Finally we need to configure input mappings in order to use f.input :birth_date instead of f.input :birth_date, as: :birth_date for attributes that have birth_date in their names. This is done modifying SimpleForm configuration in config/initializers/simple_form.rb:

1
2
3
4
5
SimpleForm.setup do |config|
  # ...
  config.input_mappings = { /birth_date/ => :birth_date }
  # ...
end

Using this approach we’re able to clean views using custom SimpleForm inputs and easily write tests for them.

Comments