diff --git a/Gemfile b/Gemfile index b14f471..6a278f9 100644 --- a/Gemfile +++ b/Gemfile @@ -7,8 +7,9 @@ unless ENV['TRAVIS'] gem 'yard', require: false end -gem 'lotus-utils', github: 'lotus/utils', branch: 'master' # FIXME use a stable branch -gem 'lotus-view', github: 'lotus/view', branch: '0.3.x' +gem 'lotus-utils', github: 'lotus/utils', branch: 'master' # FIXME use a stable branch +gem 'lotus-controller', github: 'lotus/controller', branch: 'master' # FIXME use a stable branch +gem 'lotus-view', github: 'lotus/view', branch: '0.3.x' gem 'simplecov', require: false gem 'coveralls', require: false diff --git a/lib/lotus/helpers.rb b/lib/lotus/helpers.rb index c5ea520..a369d65 100644 --- a/lib/lotus/helpers.rb +++ b/lib/lotus/helpers.rb @@ -2,6 +2,7 @@ require 'lotus/helpers/html_helper' require 'lotus/helpers/escape_helper' require 'lotus/helpers/routing_helper' +require 'lotus/helpers/form_helper' module Lotus # View helpers for Ruby applications @@ -21,6 +22,7 @@ def self.included(base) include Lotus::Helpers::HtmlHelper include Lotus::Helpers::EscapeHelper include Lotus::Helpers::RoutingHelper + include Lotus::Helpers::FormHelper end end end diff --git a/lib/lotus/helpers/form_helper.rb b/lib/lotus/helpers/form_helper.rb new file mode 100644 index 0000000..090edd3 --- /dev/null +++ b/lib/lotus/helpers/form_helper.rb @@ -0,0 +1,186 @@ +require 'lotus/helpers/html_helper/html_builder' +require 'lotus/helpers/html_helper/html_node' +require 'lotus/utils/string' + +module Lotus + module Helpers + module FormHelper + DEFAULT_METHOD = 'POST'.freeze + BROWSER_METHODS = ['GET', 'POST'].freeze + + CHECKED = 'checked'.freeze + SELECTED = 'selected'.freeze + ACCEPT_SEPARATOR = ','.freeze + # ENCTYPE_MULTIPART = 'multipart/form-data'.freeze + + class HtmlNode < ::Lotus::Helpers::HtmlHelper::HtmlNode + def initialize(name, content, attributes, options) + super + @builder = FormBuilder.new(options.fetch(:form_name), options.fetch(:params)) + @verb = options.fetch(:verb, nil) + end + + private + def content + _method_override! + super + end + + def _method_override! + return if @verb.nil? + + verb = @verb + @builder.resolve do + input(type: :hidden, name: :_method, value: verb) + end + end + end + + class FormBuilder < ::Lotus::Helpers::HtmlHelper::HtmlBuilder + self.html_node = ::Lotus::Helpers::FormHelper::HtmlNode + + def initialize(name, params, attributes = {}, &blk) + super() + + @name = name + @params = params + @attributes = attributes + @blk = blk + end + + def options + Hash[form_name: @name, params: @params, verb: @verb] + end + + def to_s + if toplevel? + _method_override! + form(@blk, @attributes) + end + + super + end + + def fields_for(name) + current_name = @name + @name = _input_name(name) + yield + ensure + @name = current_name + end + + def label(content, attributes = {}, &blk) + attributes = { for: _for(content, attributes.delete(:for)) }.merge(attributes) + content = Utils::String.new(content).titleize + + super(content, attributes, &blk) + end + + def color_field(name, attributes = {}) + input _attributes(:color, name, attributes) + end + + def date_field(name, attributes = {}) + input _attributes(:date, name, attributes) + end + + def datetime_field(name, attributes = {}) + input _attributes(:datetime, name, attributes) + end + + def datetime_local_field(name, attributes = {}) + input _attributes(:'datetime-local', name, attributes) + end + + def email_field(name, attributes = {}) + input _attributes(:email, name, attributes) + end + + def hidden_field(name, attributes = {}) + input _attributes(:hidden, name, attributes) + end + + def file_field(name, attributes = {}) + attributes[:accept] = Array(attributes[:accept]).join(ACCEPT_SEPARATOR) if attributes.key?(:accept) + attributes = { type: :file, name: _input_name(name), id: _input_id(name) }.merge(attributes) + + input(attributes) + end + + def text_field(name, attributes = {}) + input _attributes(:text, name, attributes) + end + alias_method :input_text, :text_field + + def radio_button(name, value, attributes = {}) + attributes = { type: :radio, name: _input_name(name), value: value }.merge(attributes) + attributes[:checked] = CHECKED if _value(name) == value + input(attributes) + end + + def select(name, values, attributes = {}) + options = attributes.delete(:options) || {} + attributes = { name: _input_name(name), id: _input_id(name) }.merge(attributes) + + super(attributes) do + values.each do |value, content| + if _value(name) == value + option(content, {value: value, selected: SELECTED}.merge(options)) + else + option(content, {value: value}.merge(options)) + end + end + end + end + + def submit(content, attributes = {}) + attributes = { type: :submit }.merge(attributes) + button(content, attributes) + end + + private + def toplevel? + @attributes.any? + end + + def _method_override! + verb = (@attributes.fetch(:method) { DEFAULT_METHOD }).to_s.upcase + + if BROWSER_METHODS.include?(verb) + @attributes[:method] = verb + else + @attributes[:method] = DEFAULT_METHOD + @verb = verb + end + end + + def _attributes(type, name, attributes) + { type: type, name: _input_name(name), id: _input_id(name), value: _value(name) }.merge(attributes) + end + + def _input_name(name) + "#{ @name }[#{ name }]" + end + + def _input_id(name) + name = _input_name(name).gsub(/\[(?[[[:word:]]\-]*)\]/, '-\k') + Utils::String.new(name).dasherize + end + + def _value(name) + name = _input_name(name).gsub(/\[(?[[:word:]]*)\]/, '.\k') + @params.get(name) + end + + def _for(content, name) + _input_id(name || content) + end + end + + def form_for(name, url, attributes = {}, &blk) + attributes = { action: url, id: "#{ name }-form", method: DEFAULT_METHOD }.merge(attributes) + FormBuilder.new(name, params, attributes, &blk) + end + end + end +end diff --git a/lib/lotus/helpers/html_helper/html_builder.rb b/lib/lotus/helpers/html_helper/html_builder.rb index 17214d8..ce6c942 100644 --- a/lib/lotus/helpers/html_helper/html_builder.rb +++ b/lib/lotus/helpers/html_helper/html_builder.rb @@ -1,4 +1,5 @@ require 'lotus/utils' # RUBY_VERSION >= '2.2' +require 'lotus/utils/class_attribute' require 'lotus/utils/escape' require 'lotus/helpers/html_helper/empty_html_node' require 'lotus/helpers/html_helper/html_node' @@ -149,7 +150,7 @@ class HtmlBuilder CONTENT_TAGS.each do |tag| class_eval %{ def #{ tag }(content = nil, attributes = nil, &blk) - @nodes << HtmlNode.new(:#{ tag }, blk || content, attributes || content) + @nodes << self.class.html_node.new(:#{ tag }, blk || content, attributes || content, options) self end } @@ -164,6 +165,11 @@ def #{ tag }(attributes = nil) } end + include Utils::ClassAttribute + + class_attribute :html_node + self.html_node = ::Lotus::Helpers::HtmlHelper::HtmlNode + # Initialize a new builder # # @return [Lotus::Helpers::HtmlHelper::HtmlBuilder] the builder @@ -174,6 +180,9 @@ def initialize @nodes = [] end + def options + end + # Define a custom tag # # @param name [Symbol,String] the name of the tag @@ -217,7 +226,7 @@ def initialize # # hello # # def tag(name, content = nil, attributes = nil, &blk) - @nodes << HtmlNode.new(name, blk || content, attributes || content) + @nodes << HtmlNode.new(name, blk || content, attributes || content, options) self end diff --git a/lib/lotus/helpers/html_helper/html_node.rb b/lib/lotus/helpers/html_helper/html_node.rb index 28eea35..c1d016f 100644 --- a/lib/lotus/helpers/html_helper/html_node.rb +++ b/lib/lotus/helpers/html_helper/html_node.rb @@ -17,7 +17,7 @@ class HtmlNode < EmptyHtmlNode # @param attributes [Hash,NilClass] the optional tag attributes # # @return [Lotus::Helpers::HtmlHelper::HtmlNode] - def initialize(name, content, attributes) + def initialize(name, content, attributes, options = {}) @builder = HtmlBuilder.new @name = name @content = case content diff --git a/test/fixtures.rb b/test/fixtures.rb index e8c1fb7..f0ea8bb 100644 --- a/test/fixtures.rb +++ b/test/fixtures.rb @@ -1,4 +1,5 @@ require 'lotus/view' +require 'lotus/controller' require 'lotus/helpers/html_helper' require 'lotus/helpers/escape_helper' @@ -195,11 +196,33 @@ def details end end +class FormHelperView + include Lotus::Helpers::FormHelper + attr_reader :params + + def initialize(params) + @params = Lotus::Action::Params.new(params) + end +end + +class DeliveryParams < Lotus::Action::Params + param :delivery do + param :customer_id, type: Integer, presence: true + param :address do + param :street, type: String, presence: true + end + end +end + module FullStack class Routes def self.path(name) "/#{ name }" end + + def self.deliveries + '/deliveries' + end end module Views @@ -214,6 +237,13 @@ def routing_helper_path end end end + + module Deliveries + class New + include TestView + template 'deliveries/new' + end + end end end diff --git a/test/fixtures/templates/albums/new.html.erb b/test/fixtures/templates/albums/new.html.erb new file mode 100644 index 0000000..1cb5582 --- /dev/null +++ b/test/fixtures/templates/albums/new.html.erb @@ -0,0 +1 @@ +<%= album_form %> diff --git a/test/fixtures/templates/deliveries/new.html.erb b/test/fixtures/templates/deliveries/new.html.erb new file mode 100644 index 0000000..176f242 --- /dev/null +++ b/test/fixtures/templates/deliveries/new.html.erb @@ -0,0 +1,23 @@ +<%= + form_for(:delivery, routes.deliveries, class: 'form-horizontal') do + div class: 'form-group' do + label :customer + input_text :customer, class: 'form-control', placeholder: 'Customer', name: nil + + hidden_field :customer_id + end + + fieldset do + legend 'Address' + + fields_for :address do + div class: 'form-group' do + label :street + input_text :street, class: 'form-control', placeholder: 'Street' + end + end + end + + submit 'Create', class: 'btn btn-default' + end +%> diff --git a/test/form_helper_test.rb b/test/form_helper_test.rb new file mode 100644 index 0000000..80c6050 --- /dev/null +++ b/test/form_helper_test.rb @@ -0,0 +1,758 @@ +require 'test_helper' + +describe Lotus::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 + actual.must_equal %(
) + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action, id: 'books').to_s + actual.must_equal %(
) + end + + it "allows to override 'method' attribute (get)" do + actual = view.form_for(:book, action, method: 'get').to_s + actual.must_equal %(
) + end + + it "allows to override 'method' attribute (:get)" do + actual = view.form_for(:book, action, method: :get).to_s + actual.must_equal %(
) + end + + it "allows to override 'method' attribute (GET)" do + actual = view.form_for(:book, action, method: 'GET').to_s + actual.must_equal %(
) + 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 + + actual.must_equal %(
\n\n\n
) + end + end + + it "allows to override 'action' attribute" do + actual = view.form_for(:book, action, action: '/b').to_s + actual.must_equal %(
) + end + + it "allows to specify HTML attributes" do + actual = view.form_for(:book, action, class: 'form-horizonal').to_s + actual.must_equal %(
) + 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 + + actual.must_equal %(
\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 + + actual.must_equal %(
\n\n\n\n\n
) + end + 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 + + # actual.must_include %() + # end + # end + + describe "#color_field" do + it "renders" do + actual = view.form_for(:book, action) do + color_field :cover + end.to_s + + actual.must_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 + + actual.must_include %() + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + color_field :cover, name: 'cover' + end.to_s + + actual.must_include %() + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + color_field :cover, value: '#ffffff' + end.to_s + + actual.must_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 + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { cover: value }] } + let(:value) { '#d3397e' } + + it "renders with value" do + actual = view.form_for(:book, action) do + color_field :cover + end.to_s + + actual.must_include %() + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + color_field :cover, value: '#000000' + end.to_s + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { release_date: value }] } + let(:value) { '2014-06-23' } + + it "renders with value" do + actual = view.form_for(:book, action) do + date_field :release_date + end.to_s + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { published_at: value }] } + let(:value) { '2015-02-19T12:56:31Z' } + + it "renders with value" do + actual = view.form_for(:book, action) do + datetime_field :published_at + end.to_s + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { released_at: value }] } + let(:value) { '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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { publisher_email: value }] } + let(:value) { 'maria@publisher.org' } + + it "renders with value" do + actual = view.form_for(:book, action) do + email_field :publisher_email + end.to_s + + actual.must_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 + + actual.must_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 + + actual.must_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 + + # actual.must_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 + + # actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { image_cover: value }] } + let(:value) { 'image' } + + it "ignores value" do + actual = view.form_for(:book, action) do + file_field :image_cover + end.to_s + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { author_id: value }] } + let(:value) { '1' } + + it "renders with value" do + actual = view.form_for(:book, action) do + hidden_field :author_id + end.to_s + + actual.must_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 + + actual.must_include %() + end + end + end + + describe "#text_field" do + it "renders" do + actual = view.form_for(:book, action) do + text_field :title + end.to_s + + actual.must_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 + + actual.must_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 + + actual.must_include %() + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + text_field :title, value: 'Refactoring' + end.to_s + + actual.must_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 + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { title: value }] } + let(:value) { 'PPoEA' } + + it "renders with value" do + actual = view.form_for(:book, action) do + text_field :title + end.to_s + + actual.must_include %() + end + + it "allows to override 'value' attribute" do + actual = view.form_for(:book, action) do + text_field :title, value: 'DDD' + end.to_s + + actual.must_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 + + actual.must_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 + + actual.must_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 + + actual.must_include %(\n) + end + + describe "with filled params" do + let(:params) { Hash[book: { category: value }] } + let(:value) { '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 + + actual.must_include %(\n) + end + end + end + + describe "#select" do + let(:values) { Hash['it' => 'Italy', 'us' => 'United States'] } + + it "renders" do + actual = view.form_for(:book, action) do + select :store, values + end.to_s + + actual.must_include %() + end + + it "allows to override 'id' attribute" do + actual = view.form_for(:book, action) do + select :store, values, id: 'store' + end.to_s + + actual.must_include %() + end + + it "allows to override 'name' attribute" do + actual = view.form_for(:book, action) do + select :store, values, name: 'store' + end.to_s + + actual.must_include %() + end + + it "allows to specify HTML attributes" do + actual = view.form_for(:book, action) do + select :store, values, class: 'form-control' + end.to_s + + actual.must_include %() + end + + it "allows to specify HTML attributes for options" do + actual = view.form_for(:book, action) do + select :store, values, options: { class: 'form-option' } + end.to_s + + actual.must_include %() + end + + describe "with filled params" do + let(:params) { Hash[book: { store: value }] } + let(:value) { 'it' } + + it "renders with value" do + actual = view.form_for(:book, action) do + select :store, values + end.to_s + + actual.must_include %() + end + end + end +end diff --git a/test/integration/form_helper_test.rb b/test/integration/form_helper_test.rb new file mode 100644 index 0000000..0fce0b3 --- /dev/null +++ b/test/integration/form_helper_test.rb @@ -0,0 +1,27 @@ +require 'test_helper' + +describe 'Form helper' do + describe 'first page load' do + before do + @params = DeliveryParams.new({}) + @actual = FullStack::Views::Deliveries::New.render(format: :html, params: @params) + end + + it 'renders the form' do + @actual.must_include %(\n
\n\n\n\n
\n
\nAddress\n
\n\n\n
\n
\n\n
) + end + end + + describe 'after a failed form submission' do + before do + @params = DeliveryParams.new({ delivery: { address: { street: '5th Ave' }}}) + @params.valid? # trigger validations + + @actual = FullStack::Views::Deliveries::New.render(format: :html, params: @params) + end + + it 'renders the form with previous values' do + @actual.must_include %(
\n
\n\n\n\n
\n
\nAddress\n
\n\n\n
\n
\n\n
) + end + end +end