Issue #105 Migrate from Minitest to RSpec (#106)

* Create .rspec

* Update .rubocop.yml

* Update .travis.yml

* Update hanami-helpers.gemspec

* Update Rakefile

* Create escape_helper_spec.rb

* Create escape_helper_spec.rb

* Create escape_helper_spec.rb

* Delete escape_helper_spec.rb

* Create fixtures.rb

* Create version_spec.rb

* Update escape_helper_spec.rb

* Create html_helper_spec.rb

* create form_helper_spec in progress

* Create link_to_helper_spec.rb

* Create routing_helper_spec.rb

* Create number_formatting_helper_spec.rb

* Update escape_helper_spec.rb

* Create html_helper_spec

* Create number_formatter_helper_spec

* Create routing_helper_spec

* Rename number_formatter_helper_spec to number_formatter_helper_spec.rb

* Rename html_helper_spec to html_helper_spec.rb

* Rename routing_helper_spec to routing_helper_spec.rb

* Create form_helper_spec.rb

* Create link_to_helper_spec.rb

* Update .travis.yml

* Update fixtures.rb

* Create new.html.erb

* Add files via upload

* Create spec_helper.rb

* Create html_builder_spec.rb

* Update html_helper_spec.rb

* Update escape_helper_spec.rb

* Update form_helper_spec.rb

* Update html_helper_spec.rb

* Update escape_helper_spec.rb

* Update version_spec.rb

* Update

* Update form_helper_spec.rb

* Update html_helper_spec.rb

* Update number_formatting_helper_spec.rb

* clear trailing whitespace

* Update spec_helper.rb

* Update html_helper_spec.rb

* Update hanami-helpers.gemspec

* Update escape_helper_spec.rb

* fix indentation issues

* Update hanami-helpers.gemspec

* Update Rakefile

* Update form_helper_spec.rb

* Update form_helper_spec.rb

* Fix to skip errors about array and indent

* Fix to clear CI issue

This should fix this issue:

test/fixtures.rb:58:5: W: Lint/AmbiguousBlockAssociation: Parenthesize the param html to make sure that the block will be associated with the + method call. (https://github.com/bbatsov/ruby-style-guide#syntax)
    html { div 'Hello' } + html { div 'Hanami' }
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

* Update form_helper_spec.rb

* Update fixtures.rb

* Update fixtures.rb

* Update form_helper_spec.rb

* Update fixtures.rb
This commit is contained in:
morrme 2017-04-11 07:23:34 -05:00 committed by Luca Guidi
parent ed852dd633
commit 3425b08a2f
33 changed files with 4184 additions and 8 deletions

2
.rspec Normal file
View File

@ -0,0 +1,2 @@
--color
--require spec_helper

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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(%(<a title='foo'>link</a>))
end
it "don't autoscape attributes safe string" do
expect(@view.evil_attributes_string).to eq(%(<a title='&lt;script&gt;alert&#x28;&#x27;xss&#x27;&#x29;&lt;&#x2f;script&gt;'>link</a>))
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(%(<div>I'm a raw string</div>))
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(%(<a title='foo'>link</a>))
end
it 'url helper alias' do
expect(@view.url_string_alias).to eq(%(http://hanamirb.org))
end
end

File diff suppressed because it is too large Load Diff

View File

@ -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(%(<custom id="content">Foo</custom>))
end
end
describe '<a>' do
it 'generates a link' do
result = @builder.a('Hanami', href: 'http://hanamirb.org').to_s
expect(result).to eq(%(<a href="http://hanamirb.org">Hanami</a>))
end
it 'generates a link with target' do
result = @builder.a('Hanami', href: 'http://hanamirb.org', target: '_blank').to_s
expect(result).to eq(%(<a href="http://hanamirb.org" target="_blank">Hanami</a>))
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(%(<a href="http://hanamirb.org">\n<img src="/images/logo.png">\n</a>))
end
end
describe '<abbr>' do
it 'generates an abbreviation' do
result = @builder.abbr('MVC', title: 'Model View Controller').to_s
expect(result).to eq(%(<abbr title="Model View Controller">MVC</abbr>))
end
end
describe '<addr>' do
it 'generates an address' do
content = Hanami::Utils::Escape::SafeString.new(
<<-CONTENT
Mozilla Foundation<br>
1981 Landings Drive<br>
Building K<br>
Mountain View, CA 94043-0801<br>
USA
CONTENT
)
result = @builder.address(content).to_s
expect(result).to eq(%(<address>#{content}</address>))
end
end
describe '<script>' do
it 'generates a script tag with a link to a javascript' do
result = @builder.script(src: '/assets/application.js').to_s
expect(result).to eq(%(<script src="/assets/application.js"></script>))
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(%(<script>\nalert("hello")\n</script>))
end
end
describe '<template>' do
it 'generates a template tag' do
result = @builder.template(id: 'product') do
div 'Computer'
end.to_s
expect(result).to eq(%(<template id="product">\n<div>Computer</div>\n</template>))
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(%(<script>\nalert("hello")\n</script>))
end
end
describe '<title>' do
it 'generates a title' do
result = @builder.title('Welcome to Foo').to_s
expect(result).to eq(%(<title>Welcome to Foo</title>))
end
end
describe '<dialog>' do
it 'generates a dialog' do
result = @builder.dialog('Greetings, one and all!').to_s
expect(result).to eq(%(<dialog>Greetings, one and all!</dialog>))
end
end
describe '<hgroup>' do
it 'generates a hgroup' do
result = @builder.hgroup do
h1 "Hello"
end.to_s
expect(result).to eq(%(<hgroup>\n<h1>Hello</h1>\n</hgroup>))
end
end
describe '<rtc>' do
it 'generates a rtc' do
result = @builder.rtc("Rome").to_s
expect(result).to eq(%(<rtc>Rome</rtc>))
end
end
describe '<slot>' do
it 'generates a slot' do
result = @builder.slot("Need description").to_s
expect(result).to eq(%(<slot>Need description</slot>))
end
end
describe '<var>' do
it 'generates a var' do
result = @builder.var("x").to_s
expect(result).to eq(%(<var>x</var>))
end
end
##############################################################################
# EMPTY TAGS #
##############################################################################
describe 'empty tag' do
it 'generates it' do
result = @builder.empty_tag(:xr, id: 'foo').to_s
expect(result).to eq(%(<xr id="foo">))
end
end
describe '<img>' do
it 'generates an image' do
result = @builder.img(src: '/images/logo.png', alt: 'Hanami logo').to_s
expect(result).to eq(%(<img src="/images/logo.png" alt="Hanami logo">))
end
it 'generates an image with size' do
result = @builder.img(src: '/images/logo.png', height: 128, width: 128).to_s
expect(result).to eq(%(<img src="/images/logo.png" height="128" width="128">))
end
end
describe '<link>' do
it 'generates a link to a stylesheet' do
result = @builder.link(href: '/assets/application.css', rel: 'stylesheet').to_s
expect(result).to eq(%(<link href="/assets/application.css" rel="stylesheet">))
end
end
describe '<meta>' do
it 'generates HTML4 content type' do
# RUBY_VERSION >= 2.2
# result = @builder.meta('http-equiv': 'Content-Type', content: 'text/html; charset=utf-8').to_s
result = @builder.meta(:'http-equiv' => 'Content-Type', content: 'text/html; charset=utf-8').to_s
expect(result).to eq(%(<meta http-equiv="Content-Type" content="text/html; charset=utf-8">))
end
it 'generates HTML5 content type' do
result = @builder.meta(charset: 'utf-8').to_s
expect(result).to eq(%(<meta charset="utf-8">))
end
it 'generates a page refresh' do
# RUBY_VERSION >= 2.2
# result = @builder.meta('http-equiv': 'refresh', content: '23;url=http://hanamirb.org').to_s
result = @builder.meta(:'http-equiv' => 'refresh', content: '23;url=http://hanamirb.org').to_s
expect(result).to eq(%(<meta http-equiv="refresh" content="23;url=http://hanamirb.org">))
end
end
##############################################################################
# FRAGMENTS
##############################################################################
describe 'fragment' do
it 'generates a html fragment' do
result = @builder.fragment do
span 'Hello'
span 'Hanami'
end.to_s
expect(result).to eq(%(<span>Hello</span>\n<span>Hanami</span>))
end
end
##############################################################################
# ATTRIBUTES #
##############################################################################
describe 'attributes' do
it 'handles no attribute list' do
result = @builder.input.to_s
expect(result).to eq('<input>')
end
it 'handles empty attribute list' do
result = @builder.input({}).to_s
expect(result).to eq('<input>')
end
it 'handles nil attribute list' do
result = @builder.input(nil).to_s
expect(result).to eq('<input>')
end
it 'does not render boolean attribute when its value is false' do
result = @builder.input(required: false).to_s
expect(result).to eq('<input>')
end
it 'does not render boolean attribute when its value is nil' do
result = @builder.input(required: nil).to_s
expect(result).to eq('<input>')
end
it 'does render boolean attribute when its value is true' do
result = @builder.input(required: true).to_s
expect(result).to eq('<input required="required">')
end
it 'does render boolean attribute when its value is trueish' do
result = @builder.input(required: 'yes').to_s
expect(result).to eq('<input required="required">')
end
it 'also handles strings for detection of boolean attributes' do
result = @builder.input('required' => true).to_s
expect(result).to eq('<input required="required">')
end
it 'renders multiple attributes' do
result = @builder.input('required' => true, 'value' => 'Title "book"', 'something' => 'bar').to_s
expect(result).to eq('<input required="required" value="Title "book"" something="bar">')
end
end
##############################################################################
# TEXT
##############################################################################
describe 'plain text' do
it 'renders plain text' do
result = @builder.text('Foo').to_s
expect(result).to eq('Foo')
end
it 'accepts any object that respond to #to_s' do
result = @builder.text(23).to_s
expect(result).to eq('23')
end
it 'renders plain text inside a tag' do
result = @builder.p do
span('Foo')
text('Bar')
end.to_s
expect(result).to eq(%(<p>\n<span>Foo</span>\nBar\n</p>))
end
it 'ignores block' do
result = @builder.text('Foo') { p 'Bar' }.to_s
expect(result).to eq('Foo')
end
it 'allows concatenation with raw string' do
result = @builder.p do
span('Foo') +
'Bar'
end.to_s
expect(result).to eq(%(<p>\n<span>Foo</span>\nBar\n</p>))
end
it 'escapes HTML inside' do
result = @builder.text(%(<p>Foo</p>)).to_s
expect(result).to eq('&lt;p&gt;Foo&lt;&#x2F;p&gt;')
end
end
end

View File

@ -0,0 +1,107 @@
RSpec.describe Hanami::Helpers::HtmlHelper do
before do
@view = HtmlView.new
end
it 'has a private html builder' do
expect { @view.html }.to raise_error(NoMethodError)
end
it 'returns an empty tag' do
expect(@view.empty_div.to_s).to eq(%(<div></div>))
end
it 'returns a tag with string content' do
expect(@view.div_with_string_content.to_s).to eq(%(<div>hello world</div>))
end
it 'returns a tag with block content as string' do
expect(@view.div_with_block_content_as_string.to_s).to eq(%(<div>\nhola\n</div>))
end
it 'returns a tag with block content as tag helper' do
expect(@view.div_with_block_content_with_tag_helper.to_s).to eq(%(<div><p>inner</p></div>))
end
it 'returns a tag with block content with nested calls' do
expect(@view.div_with_block_content_with_nested_calls.to_s).to eq(%(<div>\n<span>hello</span>\n</div>))
end
it 'returns a tag with block content with multiple nested calls' do
expect(@view.div_with_block_content_and_multiple_nested_calls.to_s).to eq(%(<form action="/users" method="POST">\n<div>\n<label for="user-first-name">First name</label>\n<input type="text" id="user-first-name" name="user[first_name]" value="L">\n</div>\n<input type="submit" value="Save changes">\n</form>))
end
it 'returns a concatenation of multiple divs' do
expect(@view.concatenation_of_multiple_divs.to_s).to eq(%(<div>Hello</div>\n<div>Hanami</div>))
end
it 'returns a concatenation of multiple fragments' do
expect(@view.concatenation_of_multiple_fragments.to_s).to eq(%(<div>Hello</div>\n<div>Hanami</div>))
end
it 'returns a concatenation of fragment and div' do
expect(@view.concatenation_of_fragment_and_div.to_s).to eq(%(<div>Hello</div>\n<div>Hanami</div>))
end
it 'returns a fragment with block content as string' do
expect(@view.fragment_with_block_content.to_s).to eq(%(<div>Hello</div>\n<div>Hanami</div>))
end
it 'returns a tag with attribute' do
expect(@view.div_with_attr.to_s).to eq(%(<div id="container"></div>))
end
it 'returns a tag with data attribute' do
expect(@view.div_with_data_attr.to_s).to eq(%(<div data-where="up"></div>))
end
it 'returns a tag with attributes' do
expect(@view.div_with_attrs.to_s).to eq(%(<div id="content" class="filled"></div>))
end
it 'returns a tag with string content and attributes' do
expect(@view.div_with_string_content_and_attrs.to_s).to eq(%(<div id="greeting" class="blink">ciao</div>))
end
it 'returns a tag with block content as string and attributes' do
expect(@view.div_with_block_content_as_string_and_attrs.to_s).to eq(%(<div id="sidebar" class="blue">\nbonjour\n</div>))
end
it 'returns a custom tag' do
expect(@view.custom_tag.to_s).to eq(%(<custom id="next">Foo</custom>))
end
it 'returns a custom empty tag' do
expect(@view.custom_empty_tag.to_s).to eq(%(<xr id="next">))
end
it 'autoescapes string contents' do
expect(@view.evil_string_content.to_s).to eq(%(<div>&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</div>))
end
it 'autoescapes block contents' do
expect(@view.evil_block_content.to_s).to eq(%(<div>\n&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;\n</div>))
end
it 'autoescapes nested helpers contents' do
expect(@view.evil_tag_helper.to_s).to eq(%(<div><p>&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</p></div>))
end
it 'autoescapes nested blocks' do
expect(@view.evil_nested_block_content.to_s).to eq(%(<div>\n<p>&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</p>\n</div>))
end
describe 'with link_to helper' do
before do
@view = HtmlAndLinkTo.new
end
it 'returns two links in div' do
expect(@view.two_links_to_in_div.to_s).to eq(%(<div>\n<a href=\"/comments\">Comments</a>\n<a href=\"/posts\">Posts</a>\n</div>))
end
it 'returns span and link in div' do
expect(@view.span_and_link_to_in_div.to_s).to eq(%(<div>\n<span>hello</span>\n<a href=\"/comments\">Comments</a>\n</div>))
end
end
end

View File

@ -0,0 +1,53 @@
RSpec.describe Hanami::Helpers::LinkToHelper do
before do
@view = LinkTo.new
end
it 'returns a link to posts' do
expect(@view.link_to_posts.to_s).to eq(%(<a href="/posts/">Posts</a>))
end
it 'returns a link to a post' do
expect(@view.link_to_post.to_s).to eq(%(<a href="/post/1">Post</a>))
end
it 'returns a link with a class' do
expect(@view.link_to_with_class.to_s).to eq(%(<a class="first" href="/posts/">Post</a>))
end
it 'returns a link with id' do
expect(@view.link_to_with_id.to_s).to eq(%(<a id="posts__link" href="/posts/">Post</a>))
end
it 'returns a link relative link' do
expect(@view.link_to_relative_posts.to_s).to eq(%(<a href="posts">Posts</a>))
end
it 'returns a link with html content' do
expect(@view.link_to_with_html_content.to_s).to eq(%(<a href="/posts/">\n<strong>Post</strong>\n</a>))
end
it 'returns a link with html content, id and class' do
expect(@view.link_to_with_html_content_id_and_class.to_s).to eq(%(<a id="posts__link" class="first" href="/posts/">\n<strong>Post</strong>\n</a>))
end
it 'raises an exception link with content and html content' do
expect { @view.link_to_with_content_and_html_content }.to raise_error(ArgumentError)
end
it 'raises an exception when link with content, html content, id and class' do
expect { @view.link_to_with_content_html_content_id_and_class }.to raise_error(ArgumentError)
end
it 'raises an exception when have not arguments' do
expect { @view.link_to_without_args }.to raise_error(ArgumentError)
end
it 'raises an exception when have not arguments and empty block' do
expect { @view.link_to_without_args_and_empty_block }.to raise_error(ArgumentError)
end
it 'raises an exception when have only content' do
expect { @view.link_to_with_only_content }.to raise_error(ArgumentError)
end
end

View File

@ -0,0 +1,85 @@
RSpec.describe Hanami::Helpers::NumberFormattingHelper do
before do
@view = NumbersView.new
end
it 'returns string representation of one' do
expect(@view.single_digit).to eq('1')
end
it 'returns string representation of one thousand' do
expect(@view.thousand_digits).to eq('1,000')
end
it 'returns string representation of one million' do
expect(@view.million_digits).to eq('1,000,000')
end
it 'returns string representation of point one' do
expect(@view.zero_point_one).to eq('0.1')
end
it 'returns string representation of 5 thousand and 2 point 007' do
expect(@view.mixed_digits).to eq('5,002.007')
end
it 'formats precision to 2dp by default' do
expect(@view.precision_default_format).to eq('3.14')
end
it 'returns string formatted to 4dp' do
expect(@view.precision_format).to eq('3.1416')
end
it 'returns string padded with zeros' do
expect(@view.precision_higher_than_numbers_precision).to eq('3.1400')
end
it 'returns string with no decimal part' do
expect(@view.zero_precision).to eq('3')
end
it 'returns string with "." delimiter and "," separator' do
expect(@view.euro_format).to eq('1.234,12')
end
it 'raises TypeError when nil is passed' do
expect { @view.pass_nil }.to raise_error(TypeError)
end
it 'raises a TypeError when a class name is passed' do
expect { @view.pass_class_name }.to raise_error(TypeError)
end
it 'raises a TypeError when a string cannot be coerced into a float' do
expect { @view.pass_string }.to raise_error(TypeError)
end
it 'returns string when passed a string that represent an integer' do
expect(@view.pass_non_numeric_integer).to eq('1')
end
it 'returns string when passed a string that represent a float' do
expect(@view.pass_non_numeric_float).to eq('1.0')
end
it 'returns string when passed BigDecimal' do
expect(@view.big_decimal).to eq('0.0001')
end
it 'returns string when passed Complex' do
expect(@view.complex).to eq('1.0')
end
it 'returns string when passed a Rational' do
expect(@view.rational).to eq('1.0')
end
it 'returns infinity representation' do
expect(@view.infinity).to eq('Infinity')
end
it 'returns NaN representation' do
expect(@view.nan).to eq('NaN')
end
end

View File

@ -0,0 +1,25 @@
RSpec.describe Hanami::Helpers::RoutingHelper do
before do
@template = Hanami::View::Template.new('test/fixtures/templates/full_stack/dashboard/index.html.erb', 'utf-8')
end
describe 'when routing constant is defined' do
before do
@view = FullStack::Views::Dashboard::Index.new(@template, {})
end
it 'has access to routing helper' do
expect(@view.routing_helper_path).to eq(%(/dashboard))
end
end
describe 'when routing constant is not defined' do
before do
@view = ViewWithoutRoutingHelper.new(@template, {})
end
it 'raises an exception when trying to access routing helper' do
expect { @view.routing_helper_path }.to raise_error(NameError)
end
end
end

View File

@ -0,0 +1,5 @@
RSpec.describe Hanami::Helpers::VERSION do
it 'exposes version' do
expect(Hanami::Helpers::VERSION).to eq('1.0.0.beta2')
end
end

View File

@ -0,0 +1,15 @@
RSpec.describe 'Escape helper' do
before do
@user = User.new('MG', 'http://freud.org', %(<span>hello</span>))
@actual = Users::Show.render(format: :html, user: @user)
end
it 'renders the title' do
expect(@actual).to match(%(<h1>#{@user.name}</h1>))
end
it 'renders the details' do
expect(@actual).to match(%(<div id="details">\n<ul>\n<li>\n<a href="#{@user.website}" title="#{@user.name}'s website">website</a>\n</li>\n<li>#{@user.snippet}</li>\n</ul>\n</div>))
end
end

View File

@ -0,0 +1,124 @@
RSpec.describe 'Form helper' do
before do
Hanami::View.load!
end
describe 'form with huge ERB block' do
before do
@params = Hanami::Action::BaseParams.new({})
@session = Session.new(_csrf_token: 'l23')
@actual = FullStack::Views::Sessions::New.render(format: :html, params: @params, session: @session)
end
it 'renders the form' do
expect(@actual).to include(%(<form action="/sessions" method="POST" accept-charset="utf-8" id="session-form" class="form-horizontal">\n<input type="hidden" name="_csrf_token" value="#{@session[:_csrf_token]}">\n<div class="form-group">\n<label for="session-email">Email</label>\n<input type="email" name="session[email]" id="session-email" value="" class="form-control" placeholder="Email address">\n</div>\n<div class="form-group">\n<label for="session-password">Password</label>\n<input type="password" name="session[password]" id="session-password" value="" class="form-control">\n</div>\n<input type="hidden" name="session[remember][me]" value="0">\n<input type="checkbox" name="session[remember][me]" id="session-remember-me" value="1">\n<button type="submit" class="btn btn-default">Sign in</button>\n</form>))
end
end
describe 'form to create a new resource' do
describe 'first page load' do
before do
@params = DeliveryParams.new({})
@session = Session.new(_csrf_token: 'm15')
@actual = FullStack::Views::Deliveries::New.render(format: :html, params: @params, session: @session)
end
it 'renders the form' do
expect(@actual).to include(%(<form action="/deliveries" method="POST" accept-charset="utf-8" id="delivery-form" class="form-horizontal">\n<input type="hidden" name="_csrf_token" value="#{@session[:_csrf_token]}">\n<div class="form-group">\n<label for="delivery-customer">Customer</label>\n<input type="text" name="" id="delivery-customer" value="" class="form-control" placeholder="Customer">\n<input type="hidden" name="delivery[customer_id]" id="delivery-customer-id" value="">\n</div>\n<fieldset>\n<legend>Address</legend>\n<div class="form-group">\n<label for="delivery-address-street">Street</label>\n<input type="text" name="delivery[address][street]" id="delivery-address-street" value="" class="form-control" placeholder="Street">\n</div>\n</fieldset>\n<button type="submit" class="btn btn-default">Create</button>\n</form>))
end
end
describe 'after a failed form submission' do
before do
@params = DeliveryParams.new(delivery: { address: { street: '5th Ave' } })
@session = Session.new(_csrf_token: 'm15')
@actual = FullStack::Views::Deliveries::New.render(format: :html, params: @params, session: @session)
end
it 'renders the form with previous values' do
expect(@actual).to include(%(<form action="/deliveries" method="POST" accept-charset="utf-8" id="delivery-form" class="form-horizontal">\n<input type="hidden" name="_csrf_token" value="#{@session[:_csrf_token]}">\n<div class="form-group">\n<label for="delivery-customer">Customer</label>\n<input type="text" name="" id="delivery-customer" value="" class="form-control" placeholder="Customer">\n<input type="hidden" name="delivery[customer_id]" id="delivery-customer-id" value="">\n</div>\n<fieldset>\n<legend>Address</legend>\n<div class="form-group">\n<label for="delivery-address-street">Street</label>\n<input type="text" name="delivery[address][street]" id="delivery-address-street" value="5th Ave" class="form-control" placeholder="Street">\n</div>\n</fieldset>\n<button type="submit" class="btn btn-default">Create</button>\n</form>))
end
end
end
describe 'form to update a resource' do
describe 'first page load' do
before do
@address = Address.new(street: '5th Ave')
@delivery = Delivery.new(id: 1, customer_id: 23, address: @address)
@params = DeliveryParams.new({})
@session = Session.new(_csrf_token: 's14')
@actual = FullStack::Views::Deliveries::Edit.render(format: :html, delivery: @delivery, params: @params, session: @session)
end
it 'renders the form' do
expect(@actual).to include(%(<form action="/deliveries/#{@delivery.id}" method="POST" accept-charset="utf-8" id="delivery-form" class="form-horizontal">\n<input type="hidden" name="_method" value="PATCH">\n<input type="hidden" name="_csrf_token" value="#{@session[:_csrf_token]}">\n<div class="form-group">\n<label for="delivery-customer">Customer</label>\n<input type="text" name="" id="delivery-customer" value="" class="form-control" placeholder="Customer">\n<input type="hidden" name="delivery[customer_id]" id="delivery-customer-id" value="#{@delivery.customer_id}">\n</div>\n<fieldset>\n<legend>Address</legend>\n<div class="form-group">\n<label for="delivery-address-street">Street</label>\n<input type="text" name="delivery[address][street]" id="delivery-address-street" value="#{@address.street}" class="form-control" placeholder="Street">\n</div>\n</fieldset>\n<button type="submit" class="btn btn-default">Update</button>\n</form>))
end
end
describe 'after a failed submission' do
before do
@address = Address.new(street: '5th Ave')
@delivery = Delivery.new(id: 1, customer_id: 23, address: @address)
@params = DeliveryParams.new(delivery: { address: { street: 'Mulholland Drive' } })
@session = Session.new(_csrf_token: 's14')
@actual = FullStack::Views::Deliveries::Edit.render(format: :html, delivery: @delivery, params: @params, session: @session)
end
it 'renders the form' do
expect(@actual).to include(%(<form action="/deliveries/#{@delivery.id}" method="POST" accept-charset="utf-8" id="delivery-form" class="form-horizontal">\n<input type="hidden" name="_method" value="PATCH">\n<input type="hidden" name="_csrf_token" value="#{@session[:_csrf_token]}">\n<div class="form-group">\n<label for="delivery-customer">Customer</label>\n<input type="text" name="" id="delivery-customer" value="" class="form-control" placeholder="Customer">\n<input type="hidden" name="delivery[customer_id]" id="delivery-customer-id" value="#{@delivery.customer_id}">\n</div>\n<fieldset>\n<legend>Address</legend>\n<div class="form-group">\n<label for="delivery-address-street">Street</label>\n<input type="text" name="delivery[address][street]" id="delivery-address-street" value="#{@params[:delivery][:address][:street]}" class="form-control" placeholder="Street">\n</div>\n</fieldset>\n<button type="submit" class="btn btn-default">Update</button>\n</form>))
end
end
describe 'after a failed submission with blank values' do
before do
@address = Address.new(street: '5th Ave')
@delivery = Delivery.new(id: 1, customer_id: 23, address: @address)
@params = DeliveryParams.new(delivery: { address: { street: '' } })
@session = Session.new(_csrf_token: 's14')
@actual = FullStack::Views::Deliveries::Edit.render(format: :html, delivery: @delivery, params: @params, session: @session)
end
it 'renders the form' do
expect(@actual).to include(%(<form action="/deliveries/#{@delivery.id}" method="POST" accept-charset="utf-8" id="delivery-form" class="form-horizontal">\n<input type="hidden" name="_method" value="PATCH">\n<input type="hidden" name="_csrf_token" value="#{@session[:_csrf_token]}">\n<div class="form-group">\n<label for="delivery-customer">Customer</label>\n<input type="text" name="" id="delivery-customer" value="" class="form-control" placeholder="Customer">\n<input type="hidden" name="delivery[customer_id]" id="delivery-customer-id" value="#{@delivery.customer_id}">\n</div>\n<fieldset>\n<legend>Address</legend>\n<div class="form-group">\n<label for="delivery-address-street">Street</label>\n<input type="text" name="delivery[address][street]" id="delivery-address-street" value="" class="form-control" placeholder="Street">\n</div>\n</fieldset>\n<button type="submit" class="btn btn-default">Update</button>\n</form>))
end
end
end
describe 'form with nested structures' do
describe 'first page load' do
before do
@address1 = Address.new(street: '5th Ave')
@address2 = Address.new(street: '4th Ave')
@bill = Bill.new(id: 1, addresses: [@address1, @address2])
@params = BillParams.new({})
@session = Session.new(_csrf_token: 's14')
@actual = FullStack::Views::Bills::Edit.render(format: :html, bill: @bill, params: @params, session: @session)
end
it 'renders the form' do
expect(@actual).to include(%(<form action="/bills/#{@bill.id}" method="POST" accept-charset="utf-8" id="bill-form" class="form-horizontal">\n<input type="hidden" name="_method" value="PATCH">\n<input type="hidden" name="_csrf_token" value="#{@session[:_csrf_token]}">\n<fieldset>\n<legend>Addresses</legend>\n<div class="form-group">\n<label for="bill-addresses-0-street">Street</label>\n<input type="text" name="bill[addresses][][street]" id="bill-addresses-0-street" value="#{@address1.street}" class="form-control" placeholder="Street" data-funky="id-0">\n</div>\n<div class="form-group">\n<label for="bill-addresses-1-street">Street</label>\n<input type="text" name="bill[addresses][][street]" id="bill-addresses-1-street" value="#{@address2.street}" class="form-control" placeholder="Street" data-funky="id-1">\n</div>\n<label for="bill-ensure-names">Ensure names</label>\n</fieldset>\n<button type="submit" class="btn btn-default">Update</button>\n</form>\n))
end
end
describe 'after a failed submission' do
before do
@address1 = Address.new(street: '5th Ave')
@address2 = Address.new(street: '4th Ave')
@bill = Bill.new(id: 1, addresses: [@address1, @address2])
@params = BillParams.new(bill: { addresses: [{ street: 'Mulholland Drive' }, { street: 'Quaint Edge' }] })
@session = Session.new(_csrf_token: 's14')
@actual = FullStack::Views::Bills::Edit.render(format: :html, bill: @bill, params: @params, session: @session)
end
it 'renders the form' do
expect(@actual).to include(%(<form action="/bills/#{@bill.id}" method="POST" accept-charset="utf-8" id="bill-form" class="form-horizontal">\n<input type="hidden" name="_method" value="PATCH">\n<input type="hidden" name="_csrf_token" value="#{@session[:_csrf_token]}">\n<fieldset>\n<legend>Addresses</legend>\n<div class="form-group">\n<label for="bill-addresses-0-street">Street</label>\n<input type="text" name="bill[addresses][][street]" id="bill-addresses-0-street" value="#{@params[:bill][:addresses][0][:street]}" class="form-control" placeholder="Street" data-funky="id-0">\n</div>\n<div class="form-group">\n<label for="bill-addresses-1-street">Street</label>\n<input type="text" name="bill[addresses][][street]" id="bill-addresses-1-street" value="#{@params[:bill][:addresses][1][:street]}" class="form-control" placeholder="Street" data-funky="id-1">\n</div>\n<label for="bill-ensure-names">Ensure names</label>\n</fieldset>\n<button type="submit" class="btn btn-default">Update</button>\n</form>\n))
end
end
end
end

View File

@ -0,0 +1,16 @@
RSpec.describe 'Html helper' do
before do
@book = Book.new(title: 'The Work of Art in the Age of Mechanical Reproduction')
@actual = Books::Show.render(format: :html, book: @book)
end
it 'renders the generated html' do
expect(@actual).to match("<div>\n<h1>The Work of Art in the Age of Mechanical Reproduction</h1>\n</div>")
end
it 'raises an error when referencing an unknown local variable' do
expect do
Books::Error.render(format: :html, book: @book)
end.to raise_error(NoMethodError)
end
end

View File

@ -0,0 +1,42 @@
Rspec.describe 'Escape helper' do
before do
@user = LinkTo.new
@actual = LinkTo::Index.render(format: :html)
end
it 'renders the title' do
expect(@actual).to match(%(<a href="/">Home</a>))
end
it 'renders relative link' do
expect(@actual).to match(%(<a href="relative">Relative</a>))
end
it 'renders link using html content' do
expect(@actual).to match(%(<a href="/">\n<p>Home with html content</p>\n</a>))
end
it 'renders link using html content, id and class' do
expect(@actual).to match(%(<a id="home__link" class="first" href="/">\n<p>Home with html content, id and class</p>\n</a>))
end
it 'renders link using content' do
expect(@actual).to match(%(<a href="http://external.com">External</a>))
end
it 'renders link using html content' do
expect(@actual).to match(%(<a href="http://external.com">\n<strong>External with html content</strong>\n</a>))
end
it 'escapes content' do
expect(@actual).to match(%(<a href="/">&lt;script&gt;alert(&apos;xss&apos;)&lt;&#x2F;script&gt;</a>))
end
it 'escapes raw block content' do
expect(@actual).to match(%(<a href="/">\n&lt;script&gt;alert(&apos;xss2&apos;)&lt;&#x2F;script&gt;\n</a>))
end
it 'escapes html builder block content' do
expect(@actual).to match(%(<a href="/">\n<p>&lt;script&gt;alert(&apos;xss3&apos;)&lt;&#x2F;script&gt;</p>\n</a>))
end
end

View File

@ -0,0 +1,9 @@
RSpec.describe 'Number formatting helper' do
before do
@rendered = FullStack::Views::Cart::Show.render(format: :html, total: 1234.56)
end
it 'formats number' do
expect(@rendered).to include('1,234.56')
end
end

View File

@ -0,0 +1,9 @@
Rspec.describe 'Routing helper' do
before do
@actual = FullStack::Views::Dashboard::Index.render(format: :html)
end
it 'uses helper' do
expect(@actual).to include(%(/dashboard))
end
end

36
spec/spec_helper.rb Normal file
View File

@ -0,0 +1,36 @@
if ENV['COVERALL']
require 'coveralls'
Coveralls.wear!
end
require 'hanami/utils'
RSpec.configure do |config|
config.expect_with :rspec do |expectations|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
end
config.mock_with :rspec do |mocks|
mocks.verify_partial_doubles = true
end
config.shared_context_metadata_behavior = :apply_to_host_groups
config.filter_run_when_matching :focus
config.disable_monkey_patching!
config.warnings = true
config.default_formatter = 'doc' if config.files_to_run.one?
config.profile_examples = 10
config.order = :random
Kernel.srand config.seed
end
$LOAD_PATH.unshift 'lib'
require 'hanami/helpers'
require_relative './support/fixtures'
Hanami::View.load!

699
spec/support/fixtures.rb Normal file
View File

@ -0,0 +1,699 @@
require 'hanami/view'
require 'hanami/controller'
require 'hanami/helpers/html_helper'
require 'hanami/helpers/escape_helper'
require 'dry/struct'
module Types
include Dry::Types.module
end
Store = Struct.new(:code, :label) do
def to_ary
[label, code]
end
end
class Signup < Dry::Struct
attribute :password, Types::String.optional
end
class HtmlView
include Hanami::Helpers::HtmlHelper
def empty_div
html.div
end
def div_with_string_content
html.div('hello world')
end
def div_with_block_content_as_string
html.div { 'hola' }
end
def div_with_block_content_with_tag_helper
html.div(html.p('inner'))
end
def div_with_block_content_with_nested_calls
html.div do
span 'hello'
end
end
def div_with_block_content_and_multiple_nested_calls
html.form(action: '/users', method: 'POST') do
div do
label 'First name', for: 'user-first-name'
input type: 'text', id: 'user-first-name', name: 'user[first_name]', value: 'L'
end
input type: 'submit', value: 'Save changes'
end
end
def concatenation_of_multiple_fragments
hello = html { div 'Hello' }
hanami = html { div 'Hanami' }
hello + hanami
end
def concatenation_of_multiple_divs
html.div('Hello') + html.div('Hanami')
end
def concatenation_of_fragment_and_div
html { div 'Hello' } + html.div('Hanami')
end
def fragment_with_block_content
html do
div 'Hello'
div 'Hanami'
end
end
def div_with_attr
html.div(id: 'container')
end
# RUBY_VERSION >= '2.2'
# def div_with_data_attr
# html.div('data-where': 'up')
# end
def div_with_data_attr
html.div(:'data-where' => 'up')
end
def div_with_attrs
html.div(id: 'content', class: 'filled')
end
def div_with_string_content_and_attrs
html.div('ciao', id: 'greeting', class: 'blink')
end
def div_with_block_content_as_string_and_attrs
html.div(id: 'sidebar', class: 'blue') { 'bonjour' }
end
def custom_tag
html.tag(:custom, 'Foo', id: 'next')
end
def custom_empty_tag
html.empty_tag(:xr, id: 'next')
end
def evil_string_content
html.div("<script>alert('xss')</script>")
end
def evil_block_content
html.div { "<script>alert('xss')</script>" }
end
def evil_tag_helper
html.div(html.p("<script>alert('xss')</script>"))
end
def evil_nested_block_content
html.div do
p "<script>alert('xss')</script>"
end
end
end
class NumbersView
include Hanami::Helpers::NumberFormattingHelper
def single_digit
format_number 1
end
def thousand_digits
format_number 1_000
end
def million_digits
format_number 1_000_000
end
def zero_point_one
format_number 0.1
end
def precision_default_format
format_number 3.141592
end
def precision_format
format_number 3.141592, precision: 4
end
def precision_higher_than_numbers_precision
format_number 3.14, precision: 4
end
def zero_precision
format_number 3.14, precision: 0
end
def mixed_digits
format_number 5002.007, precision: 3
end
def euro_format
format_number 1234.12, delimiter: '.', separator: ','
end
def pass_nil
format_number nil
end
def pass_class_name
format_number Object
end
def pass_string
format_number 'string'
end
def pass_non_numeric_integer
format_number '1'
end
def pass_non_numeric_float
format_number '1.0'
end
def big_decimal
format_number BigDecimal.new('0.0001'), precision: 4
end
def complex
format_number Complex(1)
end
def rational
format_number Rational(1)
end
def string
format_number Rational(1)
end
def infinity
format_number Float::INFINITY
end
def nan
format_number 0.0 / 0
end
end
class EscapeView
include Hanami::Helpers::EscapeHelper
def good_string
escape_html 'this is a good string'
end
def evil_string
escape_html "<script>alert('xss')</script>"
end
def good_attributes_string
"<a title='#{escape_html_attribute('foo')}'>link</a>"
end
def evil_attributes_string
"<a title='#{escape_html_attribute('<script>alert(\'xss\')</script>')}'>link</a>"
end
def good_url_string
escape_url 'http://hanamirb.org'
end
def evil_url_string
escape_url "javascript:alert('xss')"
end
def raw_string
raw "<div>I'm a raw string</div>"
end
def html_string_alias
h 'this is a good string'
end
def html_attribute_string_alias
"<a title='#{ha('foo')}'>link</a>"
end
def url_string_alias
hu 'http://hanamirb.org'
end
end
class Book < Dry::Struct
constructor_type :weak
attribute :title, Types::String.optional
attribute :search_title, Types::String.optional
attribute :description, Types::String.optional
attribute :author_id, Types::Form::Int.optional
attribute :category, Types::String.optional
attribute :cover, Types::String.optional
attribute :image_cover, Types::String.optional
attribute :percent_read, Types::Form::Int.optional
attribute :discount_percentage, Types::Form::Int.optional
attribute :published_at, Types::String.optional
attribute :website, Types::String.optional
attribute :publisher_email, Types::String.optional
attribute :publisher_telephone, Types::String.optional
attribute :release_date, Types::Form::Date.optional
attribute :release_hour, Types::String.optional
attribute :release_week, Types::String.optional
attribute :release_month, Types::Form::Date.optional
attribute :store, Types::String.optional
end
User = Struct.new(:name, :website, :snippet)
module TestView
def self.included(view)
view.class_eval do
include Hanami::View
include Hanami::Helpers
root __dir__ + '/fixtures/templates'
end
end
end
module Books
class Show
include TestView
def title_widget
html.div do
h1 book.title
end
end
end
class Error
include TestView
def error_widget
html.div do
unknown_local_variable
end
end
def render
error_widget.to_s
end
end
end
module Users
class Show
include TestView
def title
html.h1(user.name)
end
def details
html.div(id: 'details') do
ul do
li do
a('website', href: hu(user.website), title: "#{ha(user.name)}'s website")
end
li raw(user.snippet)
end
end
end
end
end
class FormHelperView
include Hanami::Helpers::FormHelper
attr_reader :params
def initialize(params)
@params = _build_params(params)
end
private
def _build_params(params)
parameters = params.to_h
# Randomly use Hanami::Action::BaseParams or the given raw Hash in order to
# simulate Hash usage during the test setup unit tests in Hanami projects.
if parameters.respond_to?(:dig)
[true, false].sample ? Hanami::Action::BaseParams.new(parameters) : parameters
else
Hanami::Action::BaseParams.new(parameters)
end
end
end
class SessionFormHelperView < FormHelperView
def initialize(params, csrf_token)
super(params)
@csrf_token = csrf_token
end
def session
{ _csrf_token: @csrf_token }
end
end
class Address
attr_reader :street
def initialize(attributes = {})
@street = attributes[:street]
end
end
class Delivery
attr_reader :id, :customer_id, :address
def initialize(attributes = {})
@id = attributes[:id]
@customer_id = attributes[:customer_id]
@address = attributes[:address]
end
end
class Bill
attr_reader :id, :addresses
def initialize(attributes = {})
@id = attributes[:id]
@addresses = attributes[:addresses]
end
end
class DeliveryParams < Hanami::Action::Params
params do
required(:delivery).schema do
required(:customer_id, :int).filled
required(:address).schema do
required(:street, :string).filled
end
end
end
end
class BillParams < Hanami::Action::Params
params do
required(:bill).schema do
required(:addresses).each do
schema do
required(:street, :string).filled
end
end
end
end
end
class Session
def initialize(values)
@values = values.to_h
end
def [](key)
@values[key]
end
end
module FullStack
class Routes
def path(name)
_escape "/#{name}"
end
def sessions_path
_escape '/sessions'
end
def deliveries_path
_escape '/deliveries'
end
def delivery_path(attrs = {})
_escape "/deliveries/#{attrs.fetch(:id)}"
end
def bill_path(attrs = {})
_escape "/bills/#{attrs.fetch(:id)}"
end
private
def _escape(string)
Hanami::Utils::Escape::SafeString.new(string)
end
end
def self.routes
Routes.new
end
module Views
module Dashboard
class Index
include TestView
root __dir__ + '/fixtures/templates/full_stack'
template 'dashboard/index'
def routing_helper_path
routes.path(:dashboard)
end
end
end
module Sessions
class New
include TestView
template 'sessions/new'
end
end
module Cart
class Show
include TestView
template 'cart/show'
def total
format_number locals[:total]
end
end
end
module Deliveries
class New
include TestView
template 'deliveries/new'
def form
Form.new(:delivery, routes.deliveries_path)
end
def submit_label
'Create'
end
end
class Edit
include TestView
template 'deliveries/edit'
def form
Form.new(:delivery,
routes.delivery_path(id: delivery.id),
{ delivery: delivery }, method: :patch)
end
def submit_label
'Update'
end
end
end
module Bills
class Edit
include TestView
template 'bills/edit'
def form
Form.new(:bill, routes.bill_path(id: bill.id), { bill: bill }, method: :patch)
end
def submit_label
'Update'
end
end
end
end
end
class ViewWithoutRoutingHelper
include TestView
root __dir__ + '/fixtures/templates/full_stack'
template 'dashboard/index'
def routing_helper_path
routes.path(:dashboard)
end
end
class LinkTo
include Hanami::Helpers::LinkToHelper
class Index
include TestView
def link_to_home
link_to('Home', '/')
end
def link_to_relative
link_to('Relative', 'relative')
end
def link_to_home_with_html_content
link_to('/') do
p 'Home with html content'
end
end
def link_to_home_with_html_content_id_and_class
link_to('/', id: 'home__link', class: 'first') do
p 'Home with html content, id and class'
end
end
def link_to_external_url_with_content
link_to('External', 'http://external.com')
end
def link_to_external_url_with_html_content
link_to('http://external.com') do
strong 'External with html content'
end
end
def link_to_xss_content
link_to(%(<script>alert('xss')</script>), '/')
end
def link_to_xss_raw_content_block
link_to('/') do
%(<script>alert('xss2')</script>)
end
end
def link_to_xss_html_builder_content_block
link_to('/') do
p %(<script>alert('xss3')</script>)
end
end
end
class Routes
def self.path(name, id = nil)
"/#{name}" << "/#{id}"
end
end
def routes
Routes
end
def link_to_relative_posts
link_to('Posts', 'posts')
end
def link_to_posts
link_to('Posts', routes.path(:posts))
end
def link_to_post
link_to('Post', routes.path(:post, 1))
end
def link_to_with_class
link_to('Post', routes.path(:posts), class: 'first')
end
def link_to_with_id
link_to('Post', routes.path(:posts), id: 'posts__link')
end
def link_to_with_html_content
link_to(routes.path(:posts)) do
strong 'Post'
end
end
def link_to_with_content_and_html_content
link_to('Post', routes.path(:posts)) do
strong 'Post'
end
end
def link_to_with_html_content_id_and_class
link_to(routes.path(:posts), id: 'posts__link', class: 'first') do
strong 'Post'
end
end
def link_to_without_args
link_to
end
def link_to_without_args_and_empty_block
link_to do
# this block was left intentionally blank ;)
end
end
def link_to_with_only_content
link_to 'Post'
end
def link_to_with_content_html_content_id_and_class
link_to('Post', routes.path(:posts), id: 'posts__link', class: 'first') do
strong 'Post'
end
end
end
class HtmlAndLinkTo
include Hanami::Helpers::HtmlHelper
include Hanami::Helpers::LinkToHelper
def two_links_to_in_div
html.div do
link_to('Comments', '/comments') +
link_to('Posts', '/posts')
end
end
def span_and_link_to_in_div
html.div do
span('hello') +
link_to('Comments', '/comments')
end
end
end

View File

@ -0,0 +1 @@
<%= album_form %>

View File

@ -0,0 +1,18 @@
<%=
form_for(form, class: 'form-horizontal') do
fieldset do
legend 'Addresses'
fields_for_collection :addresses do |i|
div class: 'form-group' do
label :street
input_text :street, class: 'form-control', placeholder: 'Street', 'data-funky': "id-#{i}"
end
end
label :ensure_names
end
submit submit_label, class: 'btn btn-default'
end
%>

View File

@ -0,0 +1,3 @@
<div id="content">
<%= title_widget %>
</div>

View File

@ -0,0 +1,23 @@
<%=
form_for(form, 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 submit_label, class: 'btn btn-default'
end
%>

View File

@ -0,0 +1 @@
<%= render partial: 'test/fixtures/templates/deliveries/form' %>

View File

@ -0,0 +1 @@
<%= render partial: 'test/fixtures/templates/deliveries/form' %>

View File

@ -0,0 +1 @@
<%= total %>

View File

@ -0,0 +1 @@
<%= routes.path(:dashboard) %>

View File

@ -0,0 +1,9 @@
<%= link_to_home %>
<%= link_to_relative %>
<%= link_to_home_with_html_content %>
<%= link_to_home_with_html_content_id_and_class %>
<%= link_to_external_url_with_content %>
<%= link_to_external_url_with_html_content %>
<%= link_to_xss_content %>
<%= link_to_xss_raw_content_block %>
<%= link_to_xss_html_builder_content_block %>

View File

@ -0,0 +1,19 @@
<%=
form_for :session, routes.sessions_path, class: 'form-horizontal' do
div class: 'form-group' do
label :email
email_field :email, class: 'form-control', placeholder: 'Email address'
end
div class: 'form-group' do
label :password
password_field :password, class: 'form-control'
end
fields_for :remember do
check_box :me
end
submit 'Sign in', class: 'btn btn-default'
end
%>

View File

@ -0,0 +1,2 @@
<%= title %>
<%= details %>

View File

@ -55,7 +55,10 @@ class HtmlView
end
def concatenation_of_multiple_fragments
html { div 'Hello' } + html { div 'Hanami' }
hello = html { div 'Hello' }
hanami = html { div 'Hanami' }
hello + hanami
end
def concatenation_of_multiple_divs