#-- encoding: UTF-8
#-- copyright
# OpenProject is a project management system.
# Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See doc/COPYRIGHT.rdoc for more details.
#++
require 'spec_helper'
require 'ostruct'
describe TabularFormBuilder do
include Capybara::RSpecMatchers
let(:helper) { ActionView::Base.new }
let(:resource) {
FactoryGirl.build(:user,
firstname: 'JJ',
lastname: 'Abrams',
login: 'lost',
mail: 'jj@lost-mail.com',
failed_login_count: 45)
}
let(:builder) { TabularFormBuilder.new(:user, resource, helper, {}) }
describe '#text_field' do
let(:options) { { title: 'Name', class: 'custom-class' } }
subject(:output) {
builder.text_field :name, options
}
it_behaves_like 'labelled by default'
context 'with single_locale option' do
let(:options) { { single_locale: true } }
let(:resource) { FactoryGirl.build(:custom_field) }
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input:first-child')
end
end
context 'with multi_locale option' do
let(:options) { { multi_locale: true } }
let(:resource) { FactoryGirl.build(:custom_field) }
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-field-container'
before do
allow(Setting).to receive(:available_languages).and_return([:en])
end
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input:first-child')
end
it 'should output select' do
expect(output).to have_selector 'select.locale_selector > option', count: 1
end
it 'should have a link to add a locale' do
expect(output).to be_html_eql(%{
Add
}).at_path('body > a')
end
end
context 'without locale' do
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
context 'with affixes' do
let(:random_id) { 'random_id' }
before do
allow(SecureRandom)
.to receive(:uuid)
.and_return(random_id)
end
context 'with a prefix' do
let(:options) { { title: 'Name', prefix: %{Prefix} } }
it 'should output elements' do
expect(output).to be_html_eql(%{
Prefix
}).within_path('span.form--field-container')
end
it 'includes the prefix hidden in the label' do
expect(output).to be_html_eql(%{
Prefix
}).within_path('label.form--label')
end
end
context 'with a suffix' do
let(:options) { { title: 'Name', suffix: %{Suffix} } }
it 'should output elements' do
expect(output).to be_html_eql(%{
Suffix
}).within_path('span.form--field-container')
end
end
context 'with both prefix and suffix' do
let(:options) {
{
title: 'Name',
prefix: %{PREFIX},
suffix: %{SUFFIX}
}
}
it 'should output elements' do
expect(output).to be_html_eql(%{
PREFIXSUFFIX
}).within_path('span.form--field-container')
end
it 'includes the prefix hidden in the label' do
expect(output).to be_html_eql(%{
PREFIX
}).within_path('label.form--label')
end
end
end
end
describe '#text_area' do
let(:options) { { title: 'Name', class: 'custom-class' } }
subject(:output) {
builder.text_area :name, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-area-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('textarea')
end
end
describe '#select' do
let(:options) { { title: 'Name' } }
let(:html_options) { { class: 'custom-class' } }
subject(:output) {
builder.select :name, ''.html_safe, options, html_options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'select-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('select')
end
end
describe '#collection_select' do
let(:options) { { title: 'Name' } }
let(:html_options) { { class: 'custom-class' } }
subject(:output) {
builder.collection_select :name, [
OpenStruct.new(id: 56, name: 'Diana'),
OpenStruct.new(id: 46, name: 'Ricky'),
OpenStruct.new(id: 33, name: 'Jonas')
], :id, :name, options, html_options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'select-container'
it 'should output element' do
expect(output).to have_selector 'select.custom-class.form--select > option', count: 3
expect(output).to have_selector 'option:first[value="56"]'
expect(output).to have_text 'Jonas'
end
end
describe '#date_select' do
let(:options) { { title: 'Last logged in on' } }
subject(:output) {
builder.date_select :last_login_on, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it 'should output element' do
expect(output).to have_selector 'select', count: 3
expect(output).to have_selector 'select:nth-of-type(2) > option', count: 12
expect(output).to have_selector 'select:last > option', count: 31
end
end
describe '#check_box' do
let(:options) { { title: 'Name', class: 'custom-class' } }
subject(:output) {
builder.check_box :first_login, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'check-box-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input:nth-of-type(2)')
end
end
describe '#collection_check_box' do
let(:options) { {} }
subject(:output) {
builder.collection_check_box :enabled_module_names,
:repositories,
true,
'name',
options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'check-box-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input:nth-of-type(2)')
end
end
describe '#radio_button' do
let(:options) { { title: 'Name', class: 'custom-class' } }
subject(:output) {
builder.radio_button :name, 'John', options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in container'
it_behaves_like 'wrapped in container', 'radio-button-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#number_field' do
let(:options) { { title: 'Bad logins', class: 'custom-class' } }
subject(:output) {
builder.number_field :failed_login_count, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#range_field' do
let(:options) { { title: 'Bad logins', class: 'custom-class' } }
subject(:output) {
builder.range_field :failed_login_count, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'range-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#search_field' do
let(:options) { { title: 'Search name', class: 'custom-class' } }
subject(:output) {
builder.search_field :name, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'search-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#email_field' do
let(:options) { { title: 'Email', class: 'custom-class' } }
subject(:output) {
builder.email_field :mail, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#telephone_field' do
let(:options) { { title: 'Not really email', class: 'custom-class' } }
subject(:output) {
builder.telephone_field :mail, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#password_field' do
let(:options) { { title: 'Not really password', class: 'custom-class' } }
subject(:output) {
builder.password_field :login, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#file_field' do
let(:options) { { title: 'Not really file', class: 'custom-class' } }
subject(:output) {
builder.file_field :name, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'file-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#url_field' do
let(:options) { { title: 'Not really file', class: 'custom-class' } }
subject(:output) {
builder.url_field :name, options
}
it_behaves_like 'labelled by default'
it_behaves_like 'wrapped in field-container by default'
it_behaves_like 'wrapped in container', 'text-field-container'
it 'should output element' do
expect(output).to be_html_eql(%{
}).at_path('input')
end
end
describe '#submit' do
subject(:output) {
Capybara::Node::Simple.new(builder.submit)
}
it_behaves_like 'not labelled'
it_behaves_like 'not wrapped in container'
it_behaves_like 'not wrapped in container', 'submit-container'
it 'should output element' do
expect(output).to have_selector('input[name=commit]')
end
end
describe '#button' do
subject(:output) {
builder.button
}
it_behaves_like 'not labelled'
it_behaves_like 'not wrapped in container'
it_behaves_like 'not wrapped in container', 'button-container'
it 'should output element' do
expect(output).to be_html_eql %{}
end
end
describe '#label' do
subject(:output) { builder.label :name }
it 'should output element' do
expect(output).to be_html_eql %{
}
end
describe 'with existing attributes' do
subject(:output) { builder.label :name, 'Fear', class: 'sharknado', title: 'Fear' }
it 'should keep associated classes' do
expect(output).to be_html_eql %{
}
end
end
describe 'when using it without ActiveModel' do
let(:resource) { OpenStruct.new name: 'Deadpool' }
it 'should fall back to the method name' do
expect(output).to be_html_eql %{
}
end
end
end
# test the label that is generated for various field types
describe 'labels for fields' do
let(:options) { {} }
shared_examples_for 'generated label' do
def expected_label_like(expected_title, expected_classes = 'form--label')
expect(output).to be_html_eql(%{
}).at_path('label')
end
def expected_required_label_like(expected_title, expected_classes = 'form--label')
expect(output).to be_html_eql(%{
}).at_path('label')
end
context 'with a label specified as string' do
let(:text) { 'My own label' }
before do
options[:label] = text
end
it 'uses the label' do
expected_label_like(text)
end
end
context 'with a label specified as symbol' do
let(:text) { :name }
before do
options[:label] = text
end
it 'uses the label' do
expected_label_like(I18n.t(text))
end
end
context 'without ActiveModel and specified label' do
let(:resource) { OpenStruct.new name: 'Deadpool' }
it 'falls back to the I18n name' do
expected_label_like(I18n.t(:name))
end
end
context 'with ActiveModel and without specified label' do
let(:resource) {
FactoryGirl.build_stubbed(:user,
firstname: 'JJ',
lastname: 'Abrams',
login: 'lost',
mail: 'jj@lost-mail.com',
failed_login_count: 45)
}
it 'uses the human attibute name' do
expected_label_like(User.human_attribute_name(:name))
end
context 'with erroneous field' do
before do
resource.errors.add(:name, :invalid)
resource.errors.add(:name, :inclusion)
end
it 'shows an appropriate error label' do
expect(output).to have_selector 'label.-error',
count: 1,
text: 'Name'
end
it 'contains a specific error as a hidden sub-label' do
expect(output).to have_selector 'label.-error p',
count: 1,
text: 'This field is invalid: Name is invalid. ' \
'Name is not set to one of the allowed values.'
end
end
end
context 'when required, with a label specified as symbol' do
let(:text) { :name }
before do
options[:label] = text
options[:required] = true
end
it 'uses the label' do
expected_required_label_like(I18n.t(:name), 'form--label -required')
end
end
end
%w{ text_field
text_area
check_box
password_field }.each do |input_type|
context "for #{input_type}" do
subject(:output) {
builder.send(input_type, :name, options)
}
it_behaves_like 'generated label'
end
end
context 'for select' do
subject(:output) {
builder.select :name, [], options
}
it_behaves_like 'generated label'
end
end
end