require 'spec_helper' module Ransack module Helpers describe FormHelper do router = ActionDispatch::Routing::RouteSet.new router.draw do resources :people, :notes namespace :admin do resources :comments end end include router.url_helpers # FIXME: figure out a cleaner way to get this behavior before do @controller = ActionView::TestCase::TestController.new @controller.instance_variable_set(:@_routes, router) @controller.class_eval { include router.url_helpers } @controller.view_context_class.class_eval { include router.url_helpers } end describe '#sort_link with default search_key' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people' ) } it { should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ } it { should match /sort_link desc/ } it { should match /Full Name ▼/ } end describe '#sort_url with default search_key' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people' ) } it { should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ } end describe '#sort_link with default search_key defined as symbol' do subject { @controller.view_context .sort_link( Person.ransack({ sorts: ['name desc'] }, search_key: :people_search), :name, controller: 'people' ) } it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ } end describe '#sort_url with default search_key defined as symbol' do subject { @controller.view_context .sort_url( Person.ransack({ sorts: ['name desc'] }, search_key: :people_search), :name, controller: 'people' ) } it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ } end describe '#sort_link desc through association table defined as symbol' do subject { @controller.view_context .sort_link( Person.ransack({ sorts: 'comments_body asc' }), :comments_body, controller: 'people' ) } it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/ } it { should match /sort_link asc/ } it { should match /Body ▲/ } end describe '#sort_url desc through association table defined as symbol' do subject { @controller.view_context .sort_url( Person.ransack({ sorts: 'comments_body asc' }), :comments_body, controller: 'people' ) } it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+desc/ } end describe '#sort_link through association table defined as a string' do subject { @controller.view_context .sort_link( Person.ransack({ sorts: 'comments.body desc' }), 'comments.body', controller: 'people' ) } it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/ } it { should match /sort_link desc/ } it { should match /Comments.body ▼/ } end describe '#sort_url through association table defined as a string' do subject { @controller.view_context .sort_url( Person.ransack({ sorts: 'comments.body desc' }), 'comments.body', controller: 'people' ) } it { should match /people\?q(%5B|\[)s(%5D|\])=comments.body\+asc/ } end describe '#sort_link works even if search params are a blank string' do before { @controller.view_context.params[:q] = '' } specify { expect { @controller.view_context .sort_link( Person.ransack(@controller.view_context.params[:q]), :name, controller: 'people' ) }.not_to raise_error } end describe '#sort_url works even if search params are a blank string' do before { @controller.view_context.params[:q] = '' } specify { expect { @controller.view_context .sort_url( Person.ransack(@controller.view_context.params[:q]), :name, controller: 'people' ) }.not_to raise_error } end describe '#sort_link with search_key defined as a string' do subject { @controller.view_context .sort_link( Person.ransack( { sorts: ['name desc'] }, search_key: 'people_search' ), :name, controller: 'people' ) } it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ } end describe '#sort_link with default_order defined with a string key' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack()], :name, controller: 'people', default_order: 'desc' ) } it { should_not match /default_order/ } end describe '#sort_url with default_order defined with a string key' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack()], :name, controller: 'people', default_order: 'desc' ) } it { should_not match /default_order/ } end describe '#sort_link with multiple search_keys defined as an array' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], :name, [:name, 'email DESC'], controller: 'people' ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } it { should match /sort_link desc/ } it { should match /Full Name ▼/ } end describe '#sort_url with multiple search_keys defined as an array' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], :name, [:name, 'email DESC'], controller: 'people' ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } end describe '#sort_link with multiple search_keys does not break on nil values & ignores them' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc', nil, 'email', nil])], :name, [nil, :name, nil, 'email DESC', nil], controller: 'people' ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } it { should match /sort_link desc/ } it { should match /Full Name ▼/ } end describe '#sort_url with multiple search_keys does not break on nil values & ignores them' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack(sorts: ['name desc', nil, 'email', nil])], :name, [nil, :name, nil, 'email DESC', nil], controller: 'people' ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } end describe '#sort_link with multiple search_keys should allow a label to be specified' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], :name, [:name, 'email DESC'], 'Property Name', controller: 'people' ) } it { should match /Property Name ▼/ } end describe '#sort_link with multiple search_keys should flip multiple fields specified without a direction' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], :name, [:name, :email], controller: 'people' ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } it { should match /sort_link desc/ } it { should match /Full Name ▼/ } end describe '#sort_url with multiple search_keys should flip multiple fields specified without a direction' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack(sorts: ['name desc', 'email asc'])], :name, [:name, :email], controller: 'people' ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+asc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } end describe '#sort_link with multiple search_keys and default_order specified as a string' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack()], :name, [:name, :email], controller: 'people', default_order: 'desc' ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } it { should match /sort_link/ } it { should match /Full Name/ } end describe '#sort_url with multiple search_keys and default_order specified as a string' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack()], :name, [:name, :email], controller: 'people', default_order: 'desc' ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } end describe '#sort_link with multiple search_keys and default_order specified as a symbol' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack()], :name, [:name, :email], controller: 'people', default_order: :desc ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } it { should match /sort_link/ } it { should match /Full Name/ } end describe '#sort_url with multiple search_keys and default_order specified as a symbol' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack], :name, [:name, :email], controller: 'people', default_order: :desc ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } end describe '#sort_link with multiple search_keys should allow multiple default_orders to be specified' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack], :name, [:name, :email], controller: 'people', default_order: { name: 'desc', email: 'asc' } ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/ ) } it { should match /sort_link/ } it { should match /Full Name/ } end describe '#sort_url with multiple search_keys should allow multiple default_orders to be specified' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack], :name, [:name, :email], controller: 'people', default_order: { name: 'desc', email: 'asc' } ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+asc/ ) } end describe '#sort_link with multiple search_keys with multiple default_orders should not override a specified order' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack], :name, [:name, 'email desc'], controller: 'people', default_order: { name: 'desc', email: 'asc' } ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } it { should match /sort_link/ } it { should match /Full Name/ } end describe '#sort_url with multiple search_keys with multiple default_orders should not override a specified order' do subject { @controller.view_context .sort_url( [:main_app, Person.ransack], :name, [:name, 'email desc'], controller: 'people', default_order: { name: 'desc', email: 'asc' } ) } it { should match(/people\?q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=name\+desc&q(%5B|\[)s(%5D|\])(%5B|\[)(%5D|\])=email\+desc/ ) } end describe "#sort_link on polymorphic association should preserve association model name case" do subject { @controller.view_context .sort_link( [:main_app, Note.ransack], :notable_of_Person_type_name, "Notable", controller: 'notes' ) } it { should match /notes\?q(%5B|\[)s(%5D|\])=notable_of_Person_type_name\+asc/ } it { should match /sort_link/ } it { should match /Notable/ } end describe "#sort_url on polymorphic association should preserve association model name case" do subject { @controller.view_context .sort_link( [:main_app, Note.ransack], :notable_of_Person_type_name, "Notable", controller: 'notes' ) } it { should match /notes\?q(%5B|\[)s(%5D|\])=notable_of_Person_type_name\+asc/ } end context 'view has existing parameters' do describe '#sort_link should not remove existing params' do before { @controller.view_context.params[:exist] = 'existing' } subject { @controller.view_context.sort_link( Person.ransack( { sorts: ['name desc'] }, search_key: 'people_search' ), :name, controller: 'people' ) } it { should match /exist\=existing/ } end describe '#sort_url should not remove existing params' do before { @controller.view_context.params[:exist] = 'existing' } subject { @controller.view_context.sort_url( Person.ransack( { sorts: ['name desc'] }, search_key: 'people_search' ), :name, controller: 'people' ) } it { should match /exist\=existing/ } end context 'using a real ActionController::Parameter object' do describe 'with symbol q:, #sort_link should include search params' do subject { @controller.view_context.sort_link(Person.ransack, :name) } let(:params) { ActionController::Parameters.new( { :q => { name_eq: 'TEST' }, controller: 'people' } ) } before { @controller.instance_variable_set(:@params, params) } it { should match( /people\?q(%5B|\[)name_eq(%5D|\])=TEST&q(%5B|\[)s(%5D|\]) =name\+asc/x, ) } end describe 'with symbol q:, #sort_url should include search params' do subject { @controller.view_context.sort_url(Person.ransack, :name) } let(:params) { ActionController::Parameters.new( { :q => { name_eq: 'TEST' }, controller: 'people' } ) } before { @controller.instance_variable_set(:@params, params) } it { should match( /people\?q(%5B|\[)name_eq(%5D|\])=TEST&q(%5B|\[)s(%5D|\]) =name\+asc/x, ) } end describe "with string 'q', #sort_link should include search params" do subject { @controller.view_context.sort_link(Person.ransack, :name) } let(:params) { ActionController::Parameters.new( { 'q' => { name_eq: 'Test2' }, controller: 'people' } ) } before { @controller.instance_variable_set(:@params, params) } it { should match( /people\?q(%5B|\[)name_eq(%5D|\])=Test2&q(%5B|\[)s(%5D|\]) =name\+asc/x, ) } end describe "with string 'q', #sort_url should include search params" do subject { @controller.view_context.sort_url(Person.ransack, :name) } let(:params) { ActionController::Parameters.new( { 'q' => { name_eq: 'Test2' }, controller: 'people' } ) } before { @controller.instance_variable_set(:@params, params) } it { should match( /people\?q(%5B|\[)name_eq(%5D|\])=Test2&q(%5B|\[)s(%5D|\]) =name\+asc/x, ) } end end end describe '#sort_link with hide order indicator set to true' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people', hide_indicator: true ) } it { should match /Full Name/ } it { should_not match /▼|▲/ } end describe '#sort_link with hide order indicator set to false' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people', hide_indicator: false ) } it { should match /Full Name ▼/ } end describe '#sort_link with config set with custom up_arrow' do before do Ransack.configure { |c| c.custom_arrows = { up_arrow: "\u{1F446}" } } end after do Ransack.configure { |c| c.custom_arrows = { up_arrow: "▼" } } end subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people', hide_indicator: false ) } it { should match /Full Name \u{1F446}/ } end describe '#sort_link with config set with custom down_arrow' do before do Ransack.configure { |c| c.custom_arrows = { down_arrow: "\u{1F447}" } } end after do Ransack.configure { |c| c.custom_arrows = { down_arrow: "▲" } } end subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name asc'])], :name, controller: 'people', hide_indicator: false ) } it { should match /Full Name \u{1F447}/ } end describe '#sort_link with config set to hide arrows' do before do Ransack.configure { |c| c.hide_sort_order_indicators = true } end after do Ransack.configure { |c| c.hide_sort_order_indicators = false } end subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people' ) } it { should_not match /▼|▲/ } end describe '#sort_link with config set to show arrows (default setting)' do before do Ransack.configure { |c| c.hide_sort_order_indicators = false } end subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people' ) } it { should match /Full Name ▼/ } end describe '#sort_link with config set to show arrows and a default arrow set' do before do Ransack.configure do |c| c.hide_sort_order_indicators = false c.custom_arrows = { default_arrow: "defaultarrow" } end end after do Ransack.configure do |c| c.custom_arrows = { default_arrow: nil } end end subject { @controller.view_context .sort_link( [:main_app, Person.ransack], :name, controller: 'people' ) } it { should match /Full Name defaultarrow/ } end describe '#sort_link w/config to hide arrows + custom arrow, hides all' do before do Ransack.configure do |c| c.hide_sort_order_indicators = true c.custom_arrows = { down_arrow: 'down', default_arrow: "defaultarrow" } end end after do Ransack.configure do |c| c.hide_sort_order_indicators = false c.custom_arrows = { down_arrow: '▲' } end end subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people' ) } it { should_not match /▼|down|defaultarrow/ } end describe '#sort_link with config set to show arrows + custom arrow' do before do Ransack.configure do |c| c.hide_sort_order_indicators = false c.custom_arrows = { up_arrow: 'up-value' } end end after do Ransack.configure do |c| c.hide_sort_order_indicators = false c.custom_arrows = { up_arrow: '▼' } end end subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people' ) } it { should match /▲|up-value/ } end describe '#sort_link with a block' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, controller: 'people' ) { 'Block label' } } it { should match /Block label ▼/ } end describe '#sort_link with class option' do subject { @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, class: 'people', controller: 'people' ) } it { should match /class="sort_link desc people"/ } it { should_not match /people\?class=people/ } end describe '#sort_link with class option workaround' do it "generates a correct link and prints a deprecation" do expect do link = @controller.view_context .sort_link( [:main_app, Person.ransack(sorts: ['name desc'])], :name, 'name', { controller: 'people' }, class: 'people' ) expect(link).to match(/class="sort_link desc people"/) expect(link).not_to match(/people\?class=people/) end.to output( /Passing two trailing hashes to `sort_link` is deprecated, merge the trailing hashes into a single one\. \(called at #{Regexp.escape(__FILE__)}:/ ).to_stderr end end describe '#search_form_for with default format' do subject { @controller.view_context .search_form_for(Person.ransack) {} } it { should match /action="\/people"/ } end describe '#search_form_for with pdf format' do subject { @controller.view_context .search_form_for(Person.ransack, format: :pdf) {} } it { should match /action="\/people.pdf"/ } end describe '#search_form_for with json format' do subject { @controller.view_context .search_form_for(Person.ransack, format: :json) {} } it { should match /action="\/people.json"/ } end describe '#search_form_for with an array of routes' do subject { @controller.view_context .search_form_for([:admin, Comment.ransack]) {} } it { should match /action="\/admin\/comments"/ } end describe '#search_form_for with custom default search key' do before do Ransack.configure { |c| c.search_key = :example } end subject { @controller.view_context .search_form_for(Person.ransack) { |f| f.text_field :name_eq } } it { should match /example_name_eq/ } end end end end