diff --git a/.rspec b/.rspec new file mode 100644 index 0000000..83e16f8 --- /dev/null +++ b/.rspec @@ -0,0 +1,2 @@ +--color +--require spec_helper diff --git a/.rubocop.yml b/.rubocop.yml index 4e4c583..a76f493 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -5,5 +5,10 @@ inherit_from: Metrics/BlockLength: Exclude: - "test/**/*_test.rb" # Minitest syntax generates this false positive + - 'spec/**/*' Style/MethodMissing: Enabled: false +Style/IndentHeredoc: + Enabled: false +Style/SymbolArray: + Enabled: false diff --git a/.travis.yml b/.travis.yml index b72bae5..9b09564 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ before_install: - gem update --system - rvm @global do gem uninstall bundler -a -x - rvm @global do gem install bundler -v 1.13.7 -script: 'bundle exec rake test:coverage --trace && bundle exec rubocop' +script: 'bundle exec rake spec:coverage --trace && bundle exec rubocop' rvm: - 2.3.3 - 2.4.0 diff --git a/Rakefile b/Rakefile index 9e6597c..91788cf 100644 --- a/Rakefile +++ b/Rakefile @@ -1,17 +1,25 @@ require 'rake' -require 'rake/testtask' require 'bundler/gem_tasks' +require 'rspec/core/rake_task' +require 'rake/testtask' Rake::TestTask.new do |t| t.pattern = 'test/**/*_test.rb' t.libs.push 'test' end -namespace :test do +namespace :spec do + RSpec::Core::RakeTask.new(:unit) do |task| + file_list = FileList['spec/**/*_spec.rb'] + file_list = file_list.exclude("spec/{integration,isolation}/**/*_spec.rb") + + task.pattern = file_list + end + task :coverage do - ENV['COVERALL'] = 'true' - Rake::Task['test'].invoke + ENV['COVERAGE'] = 'true' + Rake::Task['spec:unit'].invoke end end -task default: :test +task default: 'spec:unit' diff --git a/hanami-helpers.gemspec b/hanami-helpers.gemspec index 83513cd..0b6b181 100644 --- a/hanami-helpers.gemspec +++ b/hanami-helpers.gemspec @@ -1,5 +1,7 @@ # coding: utf-8 + lib = File.expand_path('../lib', __FILE__) + $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'hanami/helpers/version' @@ -25,6 +27,6 @@ Gem::Specification.new do |spec| spec.add_development_dependency 'bundler', '~> 1.6' spec.add_development_dependency 'rake', '~> 11' spec.add_development_dependency 'minitest', '~> 5.5' - + spec.add_development_dependency 'rspec', '~> 3.5' spec.add_development_dependency 'dry-struct', '~> 0.1' end diff --git a/spec/hanami/helpers/escape_helper_spec.rb b/spec/hanami/helpers/escape_helper_spec.rb new file mode 100644 index 0000000..c0ead3d --- /dev/null +++ b/spec/hanami/helpers/escape_helper_spec.rb @@ -0,0 +1,64 @@ +RSpec.describe Hanami::Helpers::EscapeHelper do + before do + @view = EscapeView.new + end + + it 'has a private escape html method' do + expect { @view.escape_html }.to raise_error(NoMethodError) + end + + it 'has a private escape html attribute method' do + expect { @view.escape_html_attribute }.to raise_error(NoMethodError) + end + + it 'has a private escape url method' do + expect { @view.escape_url }.to raise_error(NoMethodError) + end + + it 'has a private raw method' do + expect { @view.raw }.to raise_error(NoMethodError) + end + it 'autoscape evil string' do + expect(@view.evil_string).to eq(%(<script>alert('xss')</script>)) + end + + it "don't autoscape safe string" do + expect(@view.good_string).to eq(%(this is a good string)) + end + + it 'autoscape attributes evil string' do + expect(@view.good_attributes_string).to eq(%(link)) + end + + it "don't autoscape attributes safe string" do + expect(@view.evil_attributes_string).to eq(%(link)) + end + + it 'autoscape url evil string' do + expect(@view.good_url_string).to eq(%(http://hanamirb.org)) + end + + it "don't autoscape url evil string" do + expect(@view.evil_url_string).to be_empty + end + + it 'raw string is returned' do + expect(@view.raw_string).to eq(%(
I'm a raw string
)) + end + + it 'raw string is a Hanami::Helpers::Escape::SafeString class' do + expect(@view.raw_string.class).to eq(Hanami::Utils::Escape::SafeString) + end + + it 'html helper alias' do + expect(@view.html_string_alias).to eq(%(this is a good string)) + end + + it 'html attribute helper alias' do + expect(@view.html_attribute_string_alias).to eq(%(link)) + end + + it 'url helper alias' do + expect(@view.url_string_alias).to eq(%(http://hanamirb.org)) + end +end diff --git a/spec/hanami/helpers/form_helper_spec.rb b/spec/hanami/helpers/form_helper_spec.rb new file mode 100644 index 0000000..fb4477f --- /dev/null +++ b/spec/hanami/helpers/form_helper_spec.rb @@ -0,0 +1,2495 @@ +require 'ostruct' + +RSpec.describe Hanami::Helpers::FormHelper do + let(:view) { FormHelperView.new(params) } + let(:params) { Hash[] } + let(:action) { '/books' } + + # + # FORM + # + + describe '#form_for' do + it 'renders' do + actual = view.form_for(:book, action).to_s + expect(actual).to eq(%(
)) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action, id: 'books').to_s + expect(actual).to eq(%(
)) + end + + it "allows to override 'method' attribute (get)" do + actual = view.form_for(:book, action, method: 'get').to_s + expect(actual).to eq(%(
)) + end + + it "allows to override 'method' attribute (:get)" do + actual = view.form_for(:book, action, method: :get).to_s + expect(actual).to eq(%(
)) + end + + it "allows to override 'method' attribute (GET)" do + actual = view.form_for(:book, action, method: 'GET').to_s + expect(actual).to eq(%(
)) + end + + [:patch, :put, :delete].each do |verb| + it "allows to override 'method' attribute (#{verb})" do + actual = view.form_for(:book, action, method: verb) do + text_field :title + end.to_s + + expect(actual).to eq(%(
\n\n\n
)) + end + end + + it "allows to override 'action' attribute" do + actual = view.form_for(:book, action, action: '/b').to_s + expect(actual).to eq(%(
)) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action, class: 'form-horizonal').to_s + expect(actual).to eq(%(
)) + end + + describe 'CSRF protection' do + let(:view) { SessionFormHelperView.new(params, csrf_token) } + let(:csrf_token) { 'abc123' } + + it 'injects hidden field session is enabled' do + actual = view.form_for(:book, action, class: 'form-horizonal') {} + expect(actual.to_s).to eq(%(
\n\n
)) + end + + describe 'with missing token' do + let(:csrf_token) { nil } + + it "doesn't inject hidden field" do + actual = view.form_for(:book, action, class: 'form-horizonal') {} + expect(actual.to_s).to eq(%(
\n\n
)) + end + end + + describe 'with csrf_token on get verb' do + let(:csrf_token) { 'abcd-1234-xyz' } + + it "doesn't inject hidden field" do + actual = view.form_for(:book, action, method: 'GET', class: 'form-horizonal') {} + expect(actual.to_s).to eq(%(
\n\n
)) + end + end + + [:patch, :put, :delete].each do |verb| + it "it injects hidden field when Method Override (#{verb}) is active" do + actual = view.form_for(:book, action, method: verb) do + text_field :title + end.to_s + + expect(actual).to eq(%(
\n\n\n\n
)) + end + end + end + end + + # + # NESTED FIELDS + # + + describe '#fields_for' do + it 'renders' do + actual = view.form_for(:book, action) do + fields_for :categories do + text_field :name + + fields_for :subcategories do + text_field :name + end + + text_field :name2 + end + + text_field :title + end.to_s + + expect(actual).to eq(%(
\n\n\n\n\n
)) + end + + describe 'with filled params' do + let(:params) { Hash[book: { title: 'TDD', categories: { name: 'foo', name2: 'bar', subcategories: { name: 'sub' } } }] } + + it 'renders' do + actual = view.form_for(:book, action) do + fields_for :categories do + text_field :name + + fields_for :subcategories do + text_field :name + end + + text_field :name2 + end + + text_field :title + end.to_s + + expect(actual).to eq(%(
\n\n\n\n\n
)) + end + end + end + + # + # LABEL + # + + describe '#label' do + it 'renders capitalized string' do + actual = view.form_for(:book, action) do + label :free_shipping + end.to_s + + expect(actual).to include(%()) + end + + it 'accepts a string as custom content' do + actual = view.form_for(:book, action) do + label 'Free Shipping', for: :free_shipping + end.to_s + + expect(actual).to include(%()) + end + + it 'accepts a string as explicit "for" attribute' do + actual = view.form_for(:book, action) do + label :free_shipping, for: 'free-shipping' + end.to_s + + expect(actual).to include(%()) + end + end + + # + # BUTTONS + # + + describe '#button' do + it 'renders a button' do + actual = view.form_for(:book, action) do + button "Click me" + end.to_s + + expect(actual).to include(%()) + end + + it 'renders a button with HTML attributes' do + actual = view.form_for(:book, action) do + button "Click me", class: "btn btn-secondary" + end.to_s + + expect(actual).to include(%()) + end + end + + describe '#submit' do + it 'renders a submit button' do + actual = view.form_for(:book, action) do + submit "Create" + end.to_s + + expect(actual).to include(%()) + end + + it 'renders a submit button with HTML attributes' do + actual = view.form_for(:book, action) do + submit "Create", class: "btn btn-primary" + end.to_s + + expect(actual).to include(%()) + end + end + + describe '#image_button' do + it 'renders an image button' do + actual = view.form_for(:book, action) do + image_button "https://hanamirb.org/assets/image_button.png" + end.to_s + + expect(actual).to include(%()) + end + + it 'renders an image button with HTML attributes' do + actual = view.form_for(:book, action) do + image_button "https://hanamirb.org/assets/image_button.png", name: "image", width: "50" + end.to_s + + expect(actual).to include(%()) + end + + it 'prevents XSS attacks' do + actual = view.form_for(:book, action) do + image_button "" + end.to_s + + expect(actual).to include(%()) + end + end + + # + # FIELDSET + # + describe '#fieldset' do + it 'renders a fieldset' do + actual = view.form_for(:book, action) do + fieldset do + legend "Author" + + fields_for :author do + label :name + text_field :name + end + end + end.to_s + + expect(actual).to include(%(
\nAuthor\n\n\n
)) + end + end + + # + # INPUT FIELDS + # + + describe '#check_box' do + it 'renders' do + actual = view.form_for(:book, action) do + check_box :free_shipping + end.to_s + + expect(actual).to include(%(\n)) + end + + it 'allows to pass checked and unchecked value' do + actual = view.form_for(:book, action) do + check_box :free_shipping, checked_value: 'true', unchecked_value: 'false' + end.to_s + + expect(actual).to include(%(\n)) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + check_box :free_shipping, id: 'shipping' + end.to_s + + expect(actual).to include(%(\n)) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + check_box :free_shipping, name: 'book[free]' + end.to_s + + expect(actual).to include(%(\n)) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + check_box :free_shipping, class: 'form-control' + end.to_s + + expect(actual).to include(%(\n)) + end + + it "doesn't render hidden field if 'value' attribute is specified" do + actual = view.form_for(:book, action) do + check_box :free_shipping, value: 'ok' + end.to_s + + expect(actual).not_to include(%()) + expect(actual).to include(%()) + end + + it "renders hidden field if 'value' attribute and 'unchecked_value' option are both specified" do + actual = view.form_for(:book, action) do + check_box :free_shipping, value: 'yes', unchecked_value: 'no' + end.to_s + + expect(actual).to include(%(\n)) + end + + it 'handles multiple checkboxes' do + actual = view.form_for(:book, action) do + check_box :languages, name: 'book[languages][]', value: 'italian' # , id: nil FIXME + check_box :languages, name: 'book[languages][]', value: 'english' # , id: nil FIXME + end.to_s + + expect(actual).to include(%(\n)) + end + + describe 'with filled params' do + let(:params) { Hash[book: { free_shipping: val }] } + + describe 'when the params value equals to check box value' do + let(:val) { '1' } + + it "renders with 'checked' attribute" do + actual = view.form_for(:book, action) do + check_box :free_shipping + end.to_s + + expect(actual).to include(%(\n)) + end + end + + describe 'when the params value equals to the hidden field value' do + let(:val) { '0' } + + it "renders without 'checked' attribute" do + actual = view.form_for(:book, action) do + check_box :free_shipping + end.to_s + + expect(actual).to include(%(\n)) + end + + it "allows to override 'checked' attribute" do + actual = view.form_for(:book, action) do + check_box :free_shipping, checked: 'checked' + end.to_s + + expect(actual).to include(%(\n)) + end + end + + describe 'with a boolean argument' do + let(:val) { true } + + it "renders with 'checked' attribute" do + actual = view.form_for(:book, action) do + check_box :free_shipping + end.to_s + + expect(actual).to include(%(\n)) + end + end + + describe 'when multiple params are present' do + let(:params) { Hash[book: { languages: ['italian'] }] } + + it 'handles multiple checkboxes' do + actual = view.form_for(:book, action) do + check_box :languages, name: 'book[languages][]', value: 'italian' # , id: nil FIXME + check_box :languages, name: 'book[languages][]', value: 'english' # , id: nil FIXME + end.to_s + + expect(actual).to include(%(\n)) + end + end + + describe 'checked_value is boolean' do + let(:params) { Hash[book: { free_shipping: 'true' }] } + + it "renders with 'checked' attribute" do + actual = view.form_for(:book, action) do + check_box :free_shipping, checked_value: true + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'checked_value is boolean' do + let(:params) { Hash[book: { free_shipping: 'true' }] } + + it "renders with 'checked' attribute" do + actual = view.form_for(:book, action) do + check_box :free_shipping, checked_value: true + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe 'automatic values' do + describe 'checkbox' do + describe 'value boolean, helper boolean, values differ' do + let(:values) { Hash[book: OpenStruct.new(free_shipping: false)] } + + it 'renders' do + actual = view.form_for(:book, action, values: values) do + check_box :free_shipping, checked_value: true + end.to_s + + expect(actual).to include(%()) + end + end + end + end + end + + describe '#color_field' do + it 'renders' do + actual = view.form_for(:book, action) do + color_field :cover + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + color_field :cover, id: 'b-cover' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + color_field :cover, name: 'cover' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + color_field :cover, value: '#ffffff' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + color_field :cover, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(cover: val)] } + let(:val) { '#d3397e' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + color_field :cover + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + color_field :cover, value: '#000000' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { cover: val }] } + let(:val) { '#d3397e' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + color_field :cover + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + color_field :cover, value: '#000000' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#date_field' do + it 'renders' do + actual = view.form_for(:book, action) do + date_field :release_date + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + date_field :release_date, id: 'release-date' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + date_field :release_date, name: 'release_date' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + date_field :release_date, value: '2015-02-19' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + date_field :release_date, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(release_date: val)] } + let(:val) { '2014-06-23' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + date_field :release_date + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + date_field :release_date, value: '2015-03-23' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { release_date: val }] } + let(:val) { '2014-06-23' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + date_field :release_date + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + date_field :release_date, value: '2015-03-23' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#datetime_field' do + it 'renders' do + actual = view.form_for(:book, action) do + datetime_field :published_at + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + datetime_field :published_at, id: 'published-timestamp' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + datetime_field :published_at, name: 'book[published][timestamp]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + datetime_field :published_at, value: '2015-02-19T12:50:36Z' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + datetime_field :published_at, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(published_at: val)] } + let(:val) { '2015-02-19T12:56:31Z' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + datetime_field :published_at + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + datetime_field :published_at, value: '2015-02-19T12:50:36Z' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { published_at: val }] } + let(:val) { '2015-02-19T12:56:31Z' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + datetime_field :published_at + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + datetime_field :published_at, value: '2015-02-19T12:50:36Z' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#datetime_local_field' do + it 'renders' do + actual = view.form_for(:book, action) do + datetime_local_field :released_at + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + datetime_local_field :released_at, id: 'local-release-timestamp' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + datetime_local_field :released_at, name: 'book[release-timestamp]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + datetime_local_field :released_at, value: '2015-02-19T14:01:28+01:00' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + datetime_local_field :released_at, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with filled params' do + let(:params) { Hash[book: { released_at: val }] } + let(:val) { '2015-02-19T14:11:19+01:00' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + datetime_local_field :released_at + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + datetime_local_field :released_at, value: '2015-02-19T14:01:28+01:00' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#time_field' do + it 'renders' do + actual = view.form_for(:book, action) do + time_field :release_hour + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + time_field :release_hour, id: 'release-hour' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + time_field :release_hour, name: 'release_hour' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + time_field :release_hour, value: '00:00' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + time_field :release_hour, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(release_hour: val)] } + let(:val) { '18:30' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + time_field :release_hour + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + time_field :release_hour, value: '17:00' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { release_hour: val }] } + let(:val) { '11:30' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + time_field :release_hour + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + time_field :release_hour, value: '8:15' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#month_field' do + it 'renders' do + actual = view.form_for(:book, action) do + month_field :release_month + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + month_field :release_month, id: 'release-month' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + month_field :release_month, name: 'release_month' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + month_field :release_month, value: '2017-03' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + month_field :release_month, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(release_month: val)] } + let(:val) { '2017-03' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + month_field :release_month + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + month_field :release_month, value: '2017-04' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { release_month: val }] } + let(:val) { '2017-10' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + month_field :release_month + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + month_field :release_month, value: '2017-04' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#week_field' do + it 'renders' do + actual = view.form_for(:book, action) do + week_field :release_week + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + week_field :release_week, id: 'release-week' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + week_field :release_week, name: 'release_week' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + week_field :release_week, value: '2017-W10' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + week_field :release_week, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(release_week: val)] } + let(:val) { '2017-W10' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + week_field :release_week + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + week_field :release_week, value: '2017-W31' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { release_week: val }] } + let(:val) { '2017-W44' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + week_field :release_week + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + week_field :release_week, value: '2017-W07' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#email_field' do + it 'renders' do + actual = view.form_for(:book, action) do + email_field :publisher_email + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + email_field :publisher_email, id: 'publisher-email' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + email_field :publisher_email, name: 'book[email]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + email_field :publisher_email, value: 'publisher@example.org' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify 'multiple' attribute" do + actual = view.form_for(:book, action) do + email_field :publisher_email, multiple: true + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + email_field :publisher_email, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(publisher_email: val)] } + let(:val) { 'maria@publisher.org' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + email_field :publisher_email + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + email_field :publisher_email, value: 'publisher@example.org' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { publisher_email: val }] } + let(:val) { 'maria@publisher.org' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + email_field :publisher_email + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + email_field :publisher_email, value: 'publisher@example.org' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#url_field' do + it 'renders' do + actual = view.form_for(:book, action) do + url_field :website + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + url_field :website, id: 'website' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + url_field :website, name: 'book[url]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + url_field :website, value: 'http://example.org' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify 'multiple' attribute" do + actual = view.form_for(:book, action) do + url_field :website, multiple: true + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + url_field :website, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(website: val)] } + let(:val) { 'http://publisher.org' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + url_field :website + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + url_field :website, value: 'https://www.example.org' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { website: val }] } + let(:val) { 'http://publisher.org' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + url_field :website + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + url_field :website, value: 'http://example.org' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with escape url' do + let(:values) { Hash[book: Book.new(website: val)] } + let(:val) { %("onclick=javascript:alert('xss')) } + + it 'renders with automatic value' do + actual = view.form_for(:book, action, values: values) do + url_field :website + end.to_s + + expect(actual).to include(%()) + end + + it 'renders with explicit value' do + actual = view.form_for(:book, action, values: values) do + url_field :website, value: val + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#tel_field' do + it 'renders' do + actual = view.form_for(:book, action) do + tel_field :publisher_telephone + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + tel_field :publisher_telephone, id: 'publisher-telephone' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + tel_field :publisher_telephone, name: 'book[telephone]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + tel_field :publisher_telephone, value: 'publisher@example.org' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify 'multiple' attribute" do + actual = view.form_for(:book, action) do + tel_field :publisher_telephone, multiple: true + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + tel_field :publisher_telephone, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(publisher_telephone: val)] } + let(:val) { 'maria@publisher.org' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + tel_field :publisher_telephone + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + tel_field :publisher_telephone, value: 'publisher@example.org' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { publisher_telephone: val }] } + let(:val) { 'maria@publisher.org' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + tel_field :publisher_telephone + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + tel_field :publisher_telephone, value: 'publisher@example.org' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#file_field' do + it 'renders' do + actual = view.form_for(:book, action) do + file_field :image_cover + end.to_s + + expect(actual).to include(%()) + end + + it "sets 'enctype' attribute to the form" + # it "sets 'enctype' attribute to the form" do + # actual = view.form_for(:book, action) do + # file_field :image_cover + # end.to_s + + # expect(actual).to include(%(
)) + # end + + it "sets 'enctype' attribute to the form when there are nested fields" + # it "sets 'enctype' attribute to the form when there are nested fields" do + # actual = view.form_for(:book, action) do + # fields_for :images do + # file_field :cover + # end + # end.to_s + + # expect(actual).to include(%()) + # end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + file_field :image_cover, id: 'book-cover' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + file_field :image_cover, name: 'book[cover]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify 'multiple' attribute" do + actual = view.form_for(:book, action) do + file_field :image_cover, multiple: true + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify single value for 'accept' attribute" do + actual = view.form_for(:book, action) do + file_field :image_cover, accept: 'application/pdf' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify multiple values for 'accept' attribute" do + actual = view.form_for(:book, action) do + file_field :image_cover, accept: 'image/png,image/jpg' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify multiple values (array) for 'accept' attribute" do + actual = view.form_for(:book, action) do + file_field :image_cover, accept: ['image/png', 'image/jpg'] + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(image_cover: val)] } + let(:val) { 'image' } + + it 'ignores value' do + actual = view.form_for(:book, action, values: values) do + file_field :image_cover + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { image_cover: val }] } + let(:val) { 'image' } + + it 'ignores value' do + actual = view.form_for(:book, action) do + file_field :image_cover + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#hidden_field' do + it 'renders' do + actual = view.form_for(:book, action) do + hidden_field :author_id + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + hidden_field :author_id, id: 'author-id' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + hidden_field :author_id, name: 'book[author]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + hidden_field :author_id, value: '23' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + hidden_field :author_id, class: 'form-details' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(author_id: val)] } + let(:val) { '1' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + hidden_field :author_id + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + hidden_field :author_id, value: '23' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { author_id: val }] } + let(:val) { '1' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + hidden_field :author_id + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + hidden_field :author_id, value: '23' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#number_field' do + it 'renders the element' do + actual = view.form_for(:book, action) do + number_field :percent_read + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + number_field :percent_read, id: 'percent-read' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override the 'name' attribute" do + actual = view.form_for(:book, action) do + number_field :percent_read, name: 'book[read]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override the 'value' attribute" do + actual = view.form_for(:book, action) do + number_field :percent_read, value: '99' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + number_field :percent_read, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify a 'min' attribute" do + actual = view.form_for(:book, action) do + number_field :percent_read, min: 0 + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify a 'max' attribute" do + actual = view.form_for(:book, action) do + number_field :percent_read, max: 100 + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify a 'step' attribute" do + actual = view.form_for(:book, action) do + number_field :percent_read, step: 5 + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(percent_read: val)] } + let(:val) { 95 } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + number_field :percent_read + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + number_field :percent_read, value: 50 + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'without values' do + let(:book) { Book.new(title: val) } + let(:val) { '"DDD" Book' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + text_field :title + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + text_field :title, value: book.title + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { percent_read: val }] } + let(:val) { 95 } + + it 'renders with value' do + actual = view.form_for(:book, action) do + number_field :percent_read + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + number_field :percent_read, value: 50 + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#range_field' do + it 'renders the element' do + actual = view.form_for(:book, action) do + range_field :discount_percentage + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + range_field :discount_percentage, id: 'discount-percentage' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override the 'name' attribute" do + actual = view.form_for(:book, action) do + range_field :discount_percentage, name: 'book[read]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override the 'value' attribute" do + actual = view.form_for(:book, action) do + range_field :discount_percentage, value: '99' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + range_field :discount_percentage, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify a 'min' attribute" do + actual = view.form_for(:book, action) do + range_field :discount_percentage, min: 0 + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify a 'max' attribute" do + actual = view.form_for(:book, action) do + range_field :discount_percentage, max: 100 + end.to_s + + expect(actual).to include(%()) + end + + it "allows to specify a 'step' attribute" do + actual = view.form_for(:book, action) do + range_field :discount_percentage, step: 5 + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(discount_percentage: val)] } + let(:val) { 95 } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + range_field :discount_percentage + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + range_field :discount_percentage, value: 50 + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'without values' do + let(:book) { Book.new(title: val) } + let(:val) { '"DDD" Book' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + text_field :title + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + text_field :title, value: book.title + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { discount_percentage: val }] } + let(:val) { 95 } + + it 'renders with value' do + actual = view.form_for(:book, action) do + range_field :discount_percentage + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + range_field :discount_percentage, value: 50 + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#text_area' do + it 'renders the element' do + actual = view.form_for(:book, action) do + text_area :description + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + text_area :description, nil, id: 'desc' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + text_area :description, nil, name: 'book[desc]' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + text_area :description, nil, class: 'form-control', cols: '5' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to omit content' do + actual = view.form_for(:book, action) do + text_area :description, class: 'form-control', cols: '5' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to omit content, by accepting Hash serializable options' do + options = Hanami::Utils::Hash.new(class: 'form-control', cols: 5) + + actual = view.form_for(:book, action) do + text_area :description, options + end.to_s + + expect(actual).to include(%()) + end + + describe 'set content explicitly' do + let(:content) { 'A short description of the book' } + + it 'allows to set content' do + actual = view.form_for(:book, action) do + text_area :description, content + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(description: val)] } + let(:val) { 'A short description of the book' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + text_area :description + end.to_s + + expect(actual).to include(%()) + end + + it 'renders with value, when only attributes are specified' do + actual = view.form_for(:book, action, values: values) do + text_area :description, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to override value' do + actual = view.form_for(:book, action, values: values) do + text_area :description, 'Just a simple description' + end.to_s + + expect(actual).to include(%()) + end + + it 'forces blank value' do + actual = view.form_for(:book, action, values: values) do + text_area :description, '' + end.to_s + + expect(actual).to include(%()) + end + + it 'forces blank value, when also attributes are specified' do + actual = view.form_for(:book, action, values: values) do + text_area :description, '', class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { description: val }] } + let(:val) { 'A short description of the book' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + text_area :description + end.to_s + + expect(actual).to include(%()) + end + + it 'renders with value, when only attributes are specified' do + actual = view.form_for(:book, action) do + text_area :description, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to override value' do + actual = view.form_for(:book, action) do + text_area :description, 'Just a simple description' + end.to_s + + expect(actual).to include(%()) + end + + it 'forces blank value' do + actual = view.form_for(:book, action) do + text_area :description, '' + end.to_s + + expect(actual).to include(%()) + end + + it 'forces blank value, when also attributes are specified' do + actual = view.form_for(:book, action) do + text_area :description, '', class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#text_field' do + it 'renders' do + actual = view.form_for(:book, action) do + text_field :title + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + text_field :title, id: 'book-short-title' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + text_field :title, name: 'book[short_title]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + text_field :title, value: 'Refactoring' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + text_field :title, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(title: val)] } + let(:val) { 'PPoEA' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + text_field :title + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + text_field :title, value: 'DDD' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { title: val }] } + let(:val) { 'PPoEA' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + text_field :title + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + text_field :title, value: 'DDD' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#search_field' do + it 'renders' do + actual = view.form_for(:book, action) do + search_field :search_title + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + search_field :search_title, id: 'book-short-title' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + search_field :search_title, name: 'book[short_title]' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + search_field :search_title, value: 'Refactoring' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + search_field :search_title, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(search_title: val)] } + let(:val) { 'PPoEA' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + search_field :search_title + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action, values: values) do + search_field :search_title, value: 'DDD' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { search_title: val }] } + let(:val) { 'PPoEA' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + search_field :search_title + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + search_field :search_title, value: 'DDD' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#password_field' do + it 'renders' do + actual = view.form_for(:signup, action) do + password_field :password + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:signup, action) do + password_field :password, id: 'signup-pass' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:signup, action) do + password_field :password, name: 'password' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:signup, action) do + password_field :password, value: 'topsecret' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:signup, action) do + password_field :password, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[signup: Signup.new(password: val)] } + let(:val) { 'secret' } + + it 'ignores value' do + actual = view.form_for(:signup, action, values: values) do + password_field :password + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:signup, action, values: values) do + password_field :password, value: '123' + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[signup: { password: val }] } + let(:val) { 'secret' } + + it 'ignores value' do + actual = view.form_for(:signup, action) do + password_field :password + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:signup, action) do + password_field :password, value: '123' + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#radio_button' do + it 'renders' do + actual = view.form_for(:book, action) do + radio_button :category, 'Fiction' + radio_button :category, 'Non-Fiction' + end.to_s + + expect(actual).to include(%(\n)) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + radio_button :category, 'Fiction', name: 'category_name' + radio_button :category, 'Non-Fiction', name: 'category_name' + end.to_s + + expect(actual).to include(%(\n)) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + radio_button :category, 'Fiction', class: 'form-control' + radio_button :category, 'Non-Fiction', class: 'radio-button' + end.to_s + + expect(actual).to include(%(\n)) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(category: val)] } + let(:val) { 'Non-Fiction' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + radio_button :category, 'Fiction' + radio_button :category, 'Non-Fiction' + end.to_s + + expect(actual).to include(%(\n)) + end + end + + describe 'with filled params' do + describe 'string value' do + let(:params) { Hash[book: { category: val }] } + let(:val) { 'Non-Fiction' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + radio_button :category, 'Fiction' + radio_button :category, 'Non-Fiction' + end.to_s + + expect(actual).to include(%(\n)) + end + end + + describe 'decimal value' do + let(:params) { Hash[book: { price: val }] } + let(:val) { '20.0' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + radio_button :price, 10.0 + radio_button :price, 20.0 + end.to_s + + expect(actual).to include(%(\n)) + end + end + end + end + + describe '#select' do + let(:option_values) { Hash['Italy' => 'it', 'United States' => 'us'] } + + it 'renders' do + actual = view.form_for(:book, action) do + select :store, option_values + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + select :store, option_values, id: 'store' + end.to_s + + expect(actual).to include(%()) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + select :store, option_values, name: 'store' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + select :store, option_values, class: 'form-control' + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to specify HTML attributes for options' do + actual = view.form_for(:book, action) do + select :store, option_values, options: { class: 'form-option' } + end.to_s + + expect(actual).to include(%()) + end + + describe "with option 'multiple'" do + it 'renders' do + actual = view.form_for(:book, action) do + select :store, option_values, multiple: true + end.to_s + + expect(actual).to include(%()) + end + + it 'allows to select values' do + actual = view.form_for(:book, action) do + select :store, option_values, multiple: true, options: { selected: %w(it us) } + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with values an structured Array of values' do + let(:option_values) { [%w(Italy it), ['United States', 'us']] } + + it 'renders' do + actual = view.form_for(:book, action) do + select :store, option_values + end.to_s + + expect(actual).to include(%()) + end + + describe 'and filled params' do + let(:params) { Hash[book: { store: val }] } + let(:val) { 'it' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + select :store, option_values + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe 'with values an Array of objects' do + let(:values) { [Store.new('it', 'Italy'), Store.new('us', 'United States')] } + + it 'renders' do + actual = view.form_for(:book, action) do + select :store, option_values + end.to_s + + expect(actual).to include(%()) + end + + describe 'and filled params' do + let(:params) { Hash[book: { store: val }] } + let(:val) { 'it' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + select :store, option_values + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(store: val)] } + let(:val) { 'it' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + select :store, option_values + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + let(:params) { Hash[book: { store: val }] } + let(:val) { 'it' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + select :store, option_values + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with prompt option' do + it 'allows string' do + actual = view.form_for(:book, action) do + select :store, option_values, options: { prompt: 'Select a store' } + end.to_s + + expect(actual).to include(%()) + end + + it 'allows blank string' do + actual = view.form_for(:book, action) do + select :store, option_values, options: { prompt: '' } + end.to_s + + expect(actual).to include(%()) + end + + describe 'with values' do + let(:values) { Hash[book: Book.new(store: val)] } + let(:val) { 'it' } + + it 'renders with value' do + actual = view.form_for(:book, action, values: values) do + select :store, option_values, options: { prompt: 'Select a store' } + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'with filled params' do + describe 'string values' do + let(:params) { Hash[book: { store: val }] } + let(:val) { 'it' } + + it 'renders with value' do + actual = view.form_for(:book, action) do + select :store, option_values, options: { prompt: 'Select a store' } + end.to_s + + expect(actual).to include(%()) + end + end + + describe 'integer values' do + let(:values) { Hash['Brave new world' => 1, 'Solaris' => 2] } + let(:params) { Hash[bookshelf: { book: val }] } + let(:val) { '1' } + + it 'renders' do + actual = view.form_for(:bookshelf, action) do + select :book, values + end.to_s + + expect(actual).to include(%()) + end + end + end + end + + describe 'with selected attribute' do + let(:params) { Hash[book: { store: val }] } + let(:val) { 'it' } + + it 'sets the selected attribute' do + actual = view.form_for(:book, action) do + select :store, option_values, options: { selected: val } + end.to_s + + expect(actual).to include(%()) + end + end + end + + describe '#datalist' do + let(:values) { ['Italy', 'United States'] } + + it 'renders' do + actual = view.form_for(:book, action) do + datalist :store, values, 'books' + end.to_s + + expect(actual).to include(%(\n\n\n\n)) + end + + it "just allows to override 'id' attribute of the text input" do + actual = view.form_for(:book, action) do + datalist :store, values, 'books', id: 'store' + end.to_s + + expect(actual).to include(%(\n\n\n\n)) + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + datalist :store, values, 'books', name: 'store' + end.to_s + + expect(actual).to include(%(\n\n\n\n)) + end + + it 'allows to specify HTML attributes' do + actual = view.form_for(:book, action) do + datalist :store, values, 'books', class: 'form-control' + end.to_s + + expect(actual).to include(%(\n\n\n\n)) + end + + it 'allows to specify HTML attributes for options' do + actual = view.form_for(:book, action) do + datalist :store, values, 'books', options: { class: 'form-option' } + end.to_s + + expect(actual).to include(%(\n\n\n\n)) + end + + it 'allows to specify HTML attributes for datalist' do + actual = view.form_for(:book, action) do + datalist :store, values, 'books', datalist: { class: 'form-option' } + end.to_s + + expect(actual).to include(%(\n\n\n\n)) + end + + describe 'with a Hash of values' do + let(:values) { Hash['Italy' => 'it', 'United States' => 'us'] } + + it 'renders' do + actual = view.form_for(:book, action) do + datalist :store, values, 'books' + end.to_s + + expect(actual).to include(%(\n\n\n\n)) + end + end + end +end diff --git a/spec/hanami/helpers/html_helper/html_builder_spec.rb b/spec/hanami/helpers/html_helper/html_builder_spec.rb new file mode 100644 index 0000000..778d47f --- /dev/null +++ b/spec/hanami/helpers/html_helper/html_builder_spec.rb @@ -0,0 +1,293 @@ +RSpec.describe Hanami::Helpers::HtmlHelper::HtmlBuilder do + before do + @builder = Hanami::Helpers::HtmlHelper::HtmlBuilder.new + end + + ############################################################################## + # TAGS # + ############################################################################## + + describe 'unknown tag' do + it 'generates it' do + result = @builder.tag(:custom, 'Foo', id: 'content').to_s + expect(result).to eq(%(Foo)) + end + end + + describe '' do + it 'generates a link' do + result = @builder.a('Hanami', href: 'http://hanamirb.org').to_s + expect(result).to eq(%(Hanami)) + end + + it 'generates a link with target' do + result = @builder.a('Hanami', href: 'http://hanamirb.org', target: '_blank').to_s + expect(result).to eq(%(Hanami)) + end + + it 'generates a link with image' do + result = @builder.a('Hanami', href: 'http://hanamirb.org') do + img(src: '/images/logo.png') + end.to_s + + expect(result).to eq(%(\n\n)) + end + end + + describe '' do + it 'generates an abbreviation' do + result = @builder.abbr('MVC', title: 'Model View Controller').to_s + expect(result).to eq(%(MVC)) + end + end + + describe '' do + it 'generates an address' do + content = Hanami::Utils::Escape::SafeString.new( + <<-CONTENT +Mozilla Foundation
+1981 Landings Drive
+Building K
+Mountain View, CA 94043-0801
+USA +CONTENT + ) + + result = @builder.address(content).to_s + expect(result).to eq(%(
#{content}
)) + end + end + + describe ')) + end + + it 'generates a script tag with javascript code' do + result = @builder.script { Hanami::Utils::Escape::SafeString.new(%(alert("hello"))) }.to_s + expect(result).to eq(%()) + end + end + + describe '