user3162553
user3162553

Reputation: 2859

DOM traversal in Capybara testing

Quick summary: why can't capybara find the .admin-edit class?

So, I have built a site where there are published and unpublished articles and only the published articles are seen by guests while admins can see everything. Login is handled through devise and a simple erb expression determines if an article is shown or 'published'.

I list articles on the index action of my articles controller and render a partial to display the articles.

<% if article.published %>
  <dl class="individual-article">
    <dt><%= article.title %> 
      <% if current_user.try(:admin) %>
        | <span class="admin-edit"><%= link_to 'Edit', edit_article_path(article) %></span>
    <% end %><br> 
    <span class="article-tags">
        <%= raw article.tags.map(&:name).map { |t| link_to t, tag_path(t) }.join(', ') %></span>
    </dt>
    <dd><%= truncate(article.body.html_safe, length: 200) %>
        <%= link_to 'more', article_path(article) %>
    </dd>
  </dl>
<% end %>

This works as expected but I cannot test for it correctly. In particular, it returns false on expecting to find 'Edit' if the user is admin.

Here is my sign_in_spec:

require 'rails_helper'

RSpec.describe "SignIns", type: :request do

describe "the sign in path" do
  let(:user)        { FactoryGirl.create(:user)      }
  let(:admin)       { FactoryGirl.create(:admin)     }
  let(:article)     { FactoryGirl.create(:article)   }
  let(:published)   { FactoryGirl.create(:published) }

  it "lets a valid user login and redirects to main page" do
    visit '/users/sign_in' 
    fill_in 'user_email',    :with => admin.email
    fill_in 'user_password', :with => admin.password 
    click_button 'Log in'
    expect(current_path).to eq '/'
    expect(page).to have_css('span.admin-edit')
  end
end  

And here is my article factory:

FactoryGirl.define do
  factory :article do
    title 'Title'
    body  'Content'

  factory :published do 
    published true 
  end
end 

And here is my user factory:

FactoryGirl.define do 

  factory :user do 
    email '[email protected]'
    password 'password'

    factory :admin do 
      admin true 
    end
  end
end

Here is the error:

1) SignIns the sign in path lets a valid user login and redirects to main page
 Failure/Error: expect(page).to have_css('span.admin-edit')
   expected #has_css?("span.admin-edit") to return true, got false
 # ./spec/requests/sign_ins_spec.rb:18:in `block (3 levels) in <top (required)>'

I have tried the following:

  1. Eliminating the extra article if rspec had a problem with multiple classes
  2. Changing have_css to have_selector and selecting the anchor tag
  3. Drawing out the entire DOM root from html body ...
  4. Checking if it was working outside of the spec by manually logging in as user with admin privs -> it does.
  5. Tried deleting unpublished vs published article distinction but still fails.
  6. Tried removing erb condition to check if article is published in view but still fails.
  7. Tried making sure it wasn't loading via ajax (has backup in will_paginate) but fails.

What am I doing wrong?

Edit

It now works if I avoid using the FactoryGirl importing:

@article = Article.create(title: 'Title', body: 'body', published: true)

Instead of

let(:published) { FactoryGirl.create(:published) }

No idea why.

Upvotes: 0

Views: 220

Answers (1)

Peter Alfvin
Peter Alfvin

Reputation: 29389

RSpec lazily assigns let variables, so at the time you display your page, neither the published nor unpublished articles exist. You need to use let! or before or otherwise ensure the objects get created prior to display of your page.

Upvotes: 1

Related Questions