Testing Form Wrapper Components in SimpleForm
This test suite validates the wrapper functionality in Simple Form, ensuring proper HTML class handling, error states, and custom wrapper configurations. It covers essential form builder wrapper behaviors and customization options.
Test Coverage Overview
Implementation Analysis
Technical Details
Best Practices Demonstrated
heartcombo/simple_form
test/form_builder/wrapper_test.rb
# frozen_string_literal: true
require 'test_helper'
class WrapperTest < ActionView::TestCase
test 'wrapper does not have error class for attribute without errors' do
with_form_for @user, :active
assert_no_select 'div.field_with_errors'
end
test 'wrapper does not have error class when object is not present' do
with_form_for :project, :name
assert_no_select 'div.field_with_errors'
end
test 'wrapper adds the attribute name class' do
with_form_for @user, :name
assert_select 'div.user_name'
end
test 'wrapper adds the attribute name class for nested forms' do
@user.company = Company.new(1, 'Empresa')
with_concat_form_for @user do |f|
concat(f.simple_fields_for(:company) do |company_form|
concat(company_form.input :name)
end)
end
assert_select 'div.user_company_name'
end
test 'wrapper adds the association name class' do
with_form_for @user, :company
assert_select 'div.user_company'
end
test 'wrapper adds error class for attribute with errors' do
with_form_for @user, :name
assert_select 'div.field_with_errors'
end
test 'wrapper adds error class to input for attribute with errors' do
with_form_for @user, :name, wrapper: custom_wrapper_with_input_error_class
assert_select 'div.field_with_errors'
assert_select 'input.is-invalid'
end
test 'wrapper does not add error class to input when the attribute is valid' do
with_form_for @user, :phone_number, wrapper: custom_wrapper_with_input_error_class
assert_no_select 'div.field_with_errors'
assert_no_select 'input.is-invalid'
end
test 'wrapper adds valid class for present attribute without errors' do
@user.instance_eval { undef errors }
with_form_for @user, :name, wrapper: custom_wrapper_with_input_valid_class
assert_select 'div.field_without_errors'
assert_select 'input.is-valid'
assert_no_select 'div.field_with_errors'
assert_no_select 'input.is-invalid'
end
test 'wrapper does not determine if valid class is needed when it is set to nil' do
@user.instance_eval { undef errors }
with_form_for @user, :name, wrapper: custom_wrapper_with_input_valid_class(valid_class: nil)
assert_no_select 'div.field_without_errors'
end
test 'wrapper adds hint class for attribute with a hint' do
with_form_for @user, :name, hint: 'hint'
assert_select 'div.field_with_hint'
end
test 'wrapper does not have disabled class by default' do
with_form_for @user, :active
assert_no_select 'div.disabled'
end
test 'wrapper has disabled class when input is disabled' do
with_form_for @user, :active, disabled: true
assert_select 'div.disabled'
end
test 'wrapper supports no wrapping when wrapper is false' do
with_form_for @user, :name, wrapper: false
assert_select 'form > label[for=user_name]'
assert_select 'form > input#user_name.string'
end
test 'wrapper supports no wrapping when wrapper tag is false' do
with_form_for @user, :name, wrapper: custom_wrapper_without_top_level
assert_select 'form > label[for=user_name]'
assert_select 'form > input#user_name.string'
end
test 'wrapper wraps tag adds required/optional css classes' do
with_form_for @user, :name
assert_select 'form div.input.required.string'
with_form_for @user, :age, required: false
assert_select 'form div.input.optional.integer'
end
test 'wrapper allows custom options to be given' do
with_form_for @user, :name, wrapper_html: { id: "super_cool", class: 'yay' }
assert_select 'form #super_cool.required.string.yay'
end
test 'wrapper allows tag to be given on demand' do
with_form_for @user, :name, wrapper_tag: :b
assert_select 'form b.required.string'
end
test 'wrapper allows wrapper class to be given on demand' do
with_form_for @user, :name, wrapper_class: :wrapper
assert_select 'form div.wrapper.required.string'
end
test 'wrapper skips additional classes when configured' do
swap SimpleForm, generate_additional_classes_for: %i[input label] do
with_form_for @user, :name, wrapper_class: :wrapper
assert_select 'form div.wrapper'
assert_no_select 'div.required'
assert_no_select 'div.string'
assert_no_select 'div.user_name'
end
end
test 'wrapper does not generate empty css class' do
swap SimpleForm, generate_additional_classes_for: %i[input label] do
swap_wrapper :default, custom_wrapper_without_class do
with_form_for @user, :name
assert_no_select 'div#custom_wrapper_without_class[class]'
end
end
end
# Custom wrapper test
test 'custom wrappers works' do
swap_wrapper do
with_form_for @user, :name, hint: "cool"
assert_select "section.custom_wrapper div.another_wrapper label"
assert_select "section.custom_wrapper div.another_wrapper input.string"
assert_no_select "section.custom_wrapper div.another_wrapper span.omg_error"
assert_select "section.custom_wrapper div.error_wrapper span.omg_error"
assert_select "section.custom_wrapper > div.omg_hint", "cool"
end
end
test 'custom wrappers can be turned off' do
swap_wrapper do
with_form_for @user, :name, another: false
assert_no_select "section.custom_wrapper div.another_wrapper label"
assert_no_select "section.custom_wrapper div.another_wrapper input.string"
assert_select "section.custom_wrapper div.error_wrapper span.omg_error"
end
end
test 'custom wrappers can have additional attributes' do
swap_wrapper :default, custom_wrapper_with_additional_attributes do
with_form_for @user, :name
assert_select "div.custom_wrapper[title='some title'][data-wrapper='test']"
end
end
test 'custom wrappers can have full error message on attributes' do
swap_wrapper :default, custom_wrapper_with_full_error do
with_form_for @user, :name
assert_select 'span.error', "Super User Name! cannot be blank"
end
end
test 'custom wrappers on a form basis' do
swap_wrapper :another do
with_concat_form_for(@user) do |f|
f.input :name
end
assert_no_select "section.custom_wrapper div.another_wrapper label"
assert_no_select "section.custom_wrapper div.another_wrapper input.string"
with_concat_form_for(@user, wrapper: :another) do |f|
f.input :name
end
assert_select "section.custom_wrapper div.another_wrapper label"
assert_select "section.custom_wrapper div.another_wrapper input.string"
end
end
test 'custom wrappers on input basis' do
swap_wrapper :another do
with_form_for @user, :name
assert_no_select "section.custom_wrapper div.another_wrapper label"
assert_no_select "section.custom_wrapper div.another_wrapper input.string"
output_buffer.to_s.replace ""
with_form_for @user, :name, wrapper: :another
assert_select "section.custom_wrapper div.another_wrapper label"
assert_select "section.custom_wrapper div.another_wrapper input.string"
output_buffer.to_s.replace ""
end
with_form_for @user, :name, wrapper: custom_wrapper
assert_select "section.custom_wrapper div.another_wrapper label"
assert_select "section.custom_wrapper div.another_wrapper input.string"
end
test 'access wrappers with indifferent access' do
swap_wrapper :another do
with_form_for @user, :name, wrapper: "another"
assert_select "section.custom_wrapper div.another_wrapper label"
assert_select "section.custom_wrapper div.another_wrapper input.string"
end
end
test 'does not duplicate label classes for different inputs' do
swap_wrapper :default, custom_wrapper_with_label_html_option do
with_concat_form_for(@user) do |f|
concat f.input :name, required: false
concat f.input :email, as: :email, required: true
end
assert_select "label.string.optional.extra-label-class[for='user_name']"
assert_select "label.email.required.extra-label-class[for='user_email']"
assert_no_select "label.string.optional.extra-label-class[for='user_email']"
end
end
test 'raise error when wrapper not found' do
assert_raise SimpleForm::WrapperNotFound do
with_form_for @user, :name, wrapper: :not_found
end
end
test 'uses wrapper for specified in config mapping' do
swap_wrapper :another do
swap SimpleForm, wrapper_mappings: { string: :another } do
with_form_for @user, :name
assert_select "section.custom_wrapper div.another_wrapper label"
assert_select "section.custom_wrapper div.another_wrapper input.string"
end
end
end
test 'uses custom wrapper mapping per form basis' do
swap_wrapper :another do
with_concat_form_for @user, wrapper_mappings: { string: :another } do |f|
concat f.input :name
end
end
assert_select "section.custom_wrapper div.another_wrapper label"
assert_select "section.custom_wrapper div.another_wrapper input.string"
end
test 'simple_fields_form reuses custom wrapper mapping per form basis' do
@user.company = Company.new(1, 'Empresa')
swap_wrapper :another do
with_concat_form_for @user, wrapper_mappings: { string: :another } do |f|
concat(f.simple_fields_for(:company) do |company_form|
concat(company_form.input(:name))
end)
end
end
assert_select "section.custom_wrapper div.another_wrapper label"
assert_select "section.custom_wrapper div.another_wrapper input.string"
end
test "input attributes class will merge with wrapper_options' classes" do
swap_wrapper :default, custom_wrapper_with_input_class do
with_concat_form_for @user do |f|
concat f.input :name, input_html: { class: 'another-class' }
end
end
assert_select "div.custom_wrapper input.string.inline-class.another-class"
end
test "input with data attributes will merge with wrapper_options' data" do
swap_wrapper :default, custom_wrapper_with_input_data_modal do
with_concat_form_for @user do |f|
concat f.input :name, input_html: { data: { modal: 'another-data', target: 'merge-data' } }
end
end
assert_select "input[data-wrapper='data-wrapper'][data-modal='another-data'][data-target='merge-data']"
end
test "input with aria attributes will merge with wrapper_options' aria" do
swap_wrapper :default, custom_wrapper_with_input_aria_modal do
with_concat_form_for @user do |f|
concat f.input :name, input_html: { aria: { modal: 'another-aria', target: 'merge-aria' } }
end
end
assert_select "input[aria-wrapper='aria-wrapper'][aria-modal='another-aria'][aria-target='merge-aria']"
end
test 'input accepts attributes in the DSL' do
swap_wrapper :default, custom_wrapper_with_input_class do
with_concat_form_for @user do |f|
concat f.input :name
end
end
assert_select "div.custom_wrapper input.string.inline-class"
end
test 'label accepts attributes in the DSL' do
swap_wrapper :default, custom_wrapper_with_label_class do
with_concat_form_for @user do |f|
concat f.input :name
end
end
assert_select "div.custom_wrapper label.string.inline-class"
end
test 'label_input accepts attributes in the DSL' do
swap_wrapper :default, custom_wrapper_with_label_input_class do
with_concat_form_for @user do |f|
concat f.input :name
end
end
assert_select "div.custom_wrapper label.string.inline-class"
assert_select "div.custom_wrapper input.string.inline-class"
end
test 'input accepts data attributes in the DSL' do
swap_wrapper :default, custom_wrapper_with_input_attributes do
with_concat_form_for @user do |f|
concat f.input :name
end
end
assert_select "div.custom_wrapper input.string[data-modal=true]"
end
test 'inline wrapper displays when there is content' do
swap_wrapper :default, custom_wrapper_with_wrapped_optional_component do
with_form_for @user, :name, hint: "cannot be blank"
assert_select 'section.custom_wrapper div.no_output_wrapper p.omg_hint', "cannot be blank"
assert_select 'p.omg_hint'
end
end
test 'inline wrapper does not display when there is no content' do
swap_wrapper :default, custom_wrapper_with_wrapped_optional_component do
with_form_for @user, :name
assert_select 'section.custom_wrapper div.no_output_wrapper'
assert_no_select 'p.omg_hint'
end
end
test 'optional wrapper does not display when there is content' do
swap_wrapper :default, custom_wrapper_with_unless_blank do
with_form_for @user, :name, hint: "can't be blank"
assert_select 'section.custom_wrapper div.no_output_wrapper'
assert_select 'div.no_output_wrapper'
assert_select 'p.omg_hint'
end
end
test 'optional wrapper does not display when there is no content' do
swap_wrapper :default, custom_wrapper_with_unless_blank do
with_form_for @user, :name
assert_no_select 'section.custom_wrapper div.no_output_wrapper'
assert_no_select 'div.no_output_wrapper'
assert_no_select 'p.omg_hint'
end
end
end