mislav / will_paginate

Pagination library for Rails and other Ruby applications
http://github.com/mislav/will_paginate/wikis
MIT License
5.71k stars 864 forks source link

View spec with nested resource gives “No route matches” #355

Open maxcal opened 10 years ago

maxcal commented 10 years ago

When using will paginate on a view that includes a nested resource it breaks my view specs. The view however works fine when visited by browser or feature specs. It seems that the parent resource (station) is not included when calling url_for. I have been able to isolate it by doing:

before :each  { view.stub(:url_for) }

The RSpec error message

2) stations/measures 
     Failure/Error: render
     ActionView::Template::Error:
       No route matches {:page=>2, :controller=>"stations", :action=>"measures"}
require 'spec_helper'

describe "stations/measures" do

  let!(:station) { build_stubbed(:station) }

  let!(:measures) do
    # page, per_page, total_entries
    WillPaginate::Collection.create(1, 10, 50) do |pager|
      pager.replace([*1..50].map! { build_stubbed(:measure, station: station) })
    end

  end

  before(:each) do
    Measure.stub(:last).and_return(measures.last)
    assign(:station, station)
    assign(:measures, measures)
    stub_user_for_view_test
  end

  subject {
    render
    rendered
  }

  it { should match /Latest measures for #{station.name.capitalize}/ }
  it { should match /Latest measurement recieved at #{measures.last.created_at.strftime("%H:%M")}/ }
  it { should have_selector '.pagination' }

end
<div class="row">
  <h1>Latest measures for <%= @station.name.capitalize %></h1>
  <p>Latest measurement recieved at <%= time_date_hours_seconds(@measures.last.created_at) %></p>
  <%= render :partial => 'measures/table', object: @measures %>

  <%= will_paginate @measures %>
</div>
Senen commented 10 years ago

Did you fix this @maxcal? I'm suffering the same problem.

maxcal commented 10 years ago

@Senen yes, but it was a while ago. Don´t remember exactly how I fixed it but i think it involved using locals in the partial which was paginated:

<%= render partial: 'observations/table',
           locals: { observations: @observations, station: @station }
%>

https://github.com/remote-wind/remote-wind/blob/master/app/views/observations/index.html.erb

<table class="observations small-12 columns">
  <thead>
  <th>Time</th>
  <th>Direction</th>
  <th>Wind Speed</th>
  </thead>
  <tbody>
  <%= render partial: "observations/observation",
             collection: observations,
             locals: { station: station } if observations.any?
  %>
  </tbody>
</table>

https://github.com/remote-wind/remote-wind/blob/master/app/views/observations/_table.html.erb

specs are here: https://github.com/remote-wind/remote-wind/tree/master/spec/views/observations

Senen commented 10 years ago

Thank you @maxcal,

I was having problems because in view specs the method :url_for called from will_paginate view helper in index view does not know how to gernerate the url for paginator. It seems there is some kind of problem with nested resources and routes when invoked from rspec spec views.

This could be a bug. Im not really sure yet.

Anyway i already found a solution that works for me:

I stub the method (:url_for) for index view specs, only for nested resources using will_paginate

allow(view).to receive(:url_for).and_return('#') 

Example of index view spec for a nested resource. I hope helps anyone.

require 'rails_helper'
require 'will_paginate/array'

RSpec.describe "tricks/index", :type => :view do

  context "renders a list of tricks with pagination " do

    before(:each) do
      @sport      = FactoryGirl.create(:sport)  
      @user       = FactoryGirl.create(:user)
      @trick_kind = FactoryGirl.create(:trick_kind, sport: @sport)
      @paginate = 4
      @page = 2
      FactoryGirl.create_list(:trick, @paginate + 1,  sport: @sport, user: @user, trick_kind: @trick_kind )
      @tricks = @sport.tricks.paginate(:page => @page, :per_page => @paginate)
      allow(view).to receive(:url_for).and_return('#')
      render
    end   

    it " should display data" do
      @tricks.each do |trick|   
        assert_select "span.trick_sport_name", :text => trick.sport.name
        assert_select "span.trick_user_nickname", :text => trick.user.nickname
        assert_select "span.trick_sport_name", :text => trick.sport.name
        assert_select "span.trick_votes_count", :text => trick.votes_for.size
      end
    end

    it " should display last page with one element" do    
      assert_select ".trick", :count => 1
      assert_select ".trick_name", :count => 1
      assert_select "iframe", :count => 1
      assert_select ".trick_user_nickname", :count => 1
      assert_select ".trick_votes_count", :count => 1  
    end

    it " should display pagination" do
      assert_select ".pagination", :count => 1
    end

    it " should have to be active the @page item" do
      assert_select "li.active", :count => 1
      assert_select "li.active", :text => @page
    end

  end  

end
maxcal commented 10 years ago

Thats about the same as my specs. I vaguely remember digging into this and the cause seems to be that RSpec view specs are a thin veneer over ActionView::Testcase (or something similar) which doesn't set up the context for url_for properly. That's why feature specs exercising the same view work.