Capybara::ElementNotFound: Unable to find field “E

2019-06-04 08:52发布

问题:

I am following Railstutorial (chapter 9) The test for" delete links " is passed for first time but from the next time it starts giving some kind of errors like:

Failures:

  1) User pages index delete links as an admin user 
     Failure/Error: sign_in admin
     Capybara::ElementNotFound:
       Unable to find field "Email"
     # ./spec/support/utilities.rb:20:in `sign_in'
     # ./spec/requests/user_pages_spec.rb:38:in `block (5 levels) in <top (required)>'

  2) User pages index delete links as an admin user 
     Failure/Error: sign_in admin
     Capybara::ElementNotFound:
       Unable to find field "Email"
     # ./spec/support/utilities.rb:20:in `sign_in'
     # ./spec/requests/user_pages_spec.rb:38:in `block (5 levels) in <top (required)>'

  3) User pages index delete links as an admin user should be able to delete another user
     Failure/Error: sign_in admin
     Capybara::ElementNotFound:
       Unable to find field "Email"
     # ./spec/support/utilities.rb:20:in `sign_in'
     # ./spec/requests/user_pages_spec.rb:38:in `block (5 levels) in <top (required)>'

Finished in 0.81336 seconds
4 examples, 3 failures

Failed examples:

rspec ./spec/requests/user_pages_spec.rb:48 # User pages index delete links as an admin user 
rspec ./spec/requests/user_pages_spec.rb:42 # User pages index delete links as an admin user 
rspec ./spec/requests/user_pages_spec.rb:43 # User pages index delete links as an admin user should be able to delete another user

Randomized with seed 11884

Here is the content of utilities.rb file

include ApplicationHelper

def full_title(page_title)
  base_title = "Welcome to Family & Friends"
  if page_title.empty?
    base_title
  else
    " #{base_title} | #{page_title} "
  end
end

def sign_in(user, options={})
  if options[:no_capybara]
    # Sign in when not using Capybara.
    remember_token = User.new_remember_token
    cookies[:remember_token] = remember_token
    user.update_attribute(:remember_token, User.encrypt(remember_token))
  else
    visit signin_path
    fill_in "Email",    with: user.email
    fill_in "Password", with: user.password
    click_button "Sign in"
  end
end

Part of user_pages_spec.rb file where the error is indicating

describe "delete links" do

      it { should_not have_link('delete') }

      describe "as an admin user" do
        let(:admin) { FactoryGirl.create(:admin) }
        before do
          sign_in admin
          visit users_path
        end

        it { should have_link('delete', href: user_path(User.first)) }
        it "should be able to delete another user" do
          expect do
            click_link('delete', match: :first)
          end.to change(User, :count).by(-1)
        end
        it { should_not have_link('delete', href: user_path(admin)) }
      end
    end
  end

sessions_helper.rb file where definition of sign_in is present

module SessionsHelper

  def sign_in(user)
    remember_token = User.new_remember_token
    cookies.permanent[:remember_token] = remember_token
    user.update_attribute(:remember_token, User.encrypt(remember_token))
    self.current_user = user
  end

  def signed_in?
    !current_user.nil?
  end

  def current_user=(user)
    @current_user = user
  end

  def current_user
    remember_token = User.encrypt(cookies[:remember_token])
    @current_user ||= User.find_by(remember_token: remember_token)
  end

   def current_user?(user)
    user == current_user
  end

   def sign_out
    self.current_user = nil
    cookies.delete(:remember_token)
  end

  def redirect_back_or(default)
    redirect_to(session[:return_to] || default)
    session.delete(:return_to)
  end

  def store_location
    session[:return_to] = request.url if request.get?
  end
end

I have even tried with fill_in "email_id" as suggested in similar posts but not worked in this case

thanks!!

Here is the Content of new.html.erb which html is rendering for signin

<% provide(:title, "Sign in") %>
<h2>Sign in</h2>

<div class="row">
  <div class="span6 offset3">
    <%= form_for(:session, url: sessions_path) do |f| %>

      <%= f.label :email %>
      <%= f.text_field :email %>

      <%= f.label :password %>
      <%= f.password_field :password %>

      <%= f.submit "Sign in", class: "btn btn-large btn-primary" %>
    <% end %>

    <p>New user? <%= link_to "Sign up now!", signup_path %></p>
  </div>
</div>

回答1:

Old question but I ran into this problem as well when working through the tutorial. What's messing up the tests is the state from the previous examples is leaking into the "as an admin user" example.

describe "User Pages" do

  subject { page }

    describe "index" do
      let(:user) { FactoryGirl.create(:user) }

        # This is messing up the lower examples
        before(:each) do
          sign_in user
          visit users_path
        end

To see this in action you can add visit signin_path then save_and_open_page in the before block prior to calling the sign_in helper.

describe "as an admin user" do
  let(:admin) { FactoryGirl.create(:admin) }
    before do
      visit signin_path
      save_and_open_page
      sign_in admin
      visit users_path
    end

When my browser rendered the page I discovered the previously signed in user never got signed out, so visiting the sign in page just redirects you to the profile page of that user and there is no email field on the user profile page. That's why Capybara reports Unable to find field "Email".

To fix it I added a sign_out helper to my utilities.rb file.

def sign_out
  first(:link, "Sign Out").click
end

And I call it before sign_in admin in the example

describe "as an admin user" do
  let(:admin) { FactoryGirl.create(:admin) }
    before do
      sign_out
      sign_in admin
      visit users_path
    end

All my tests passed after this.



回答2:

I also had this problem. Unfortunately, I also could not get the proposed sign_out function to work. Even though save_and_open_page (after installing gem "launchy") showed a "Sign Out" link, the test reported that it could not find one on the page. I ended up solving the test as follows:

  1. I moved the "delete links" block out of the "index" block altogether.
  2. I recreated the 30 test users at the start of my new "delete links" block
  3. I moved the test for absence of delete links on a non-admin users page back up to a new block inside the "pagination" block.

(Of course, two mysteries remain: 1) I have no idea why the code could possibly work the way the book shows it. So why doesn't everyone working through the book have this problem? 2) Why wouldn't my sign_out function find the "Sign Out" link that clearly showed on the page with save_and_open_page. Sometimes you just have to move on to keep making progress. At least it works now.)

Here is the code after making those changes. It passes all the tests now:

  describe "index" do
    let(:user) { FactoryGirl.create(:user) }

    before(:each) do
      sign_in user
      visit users_path
    end

    it { should have_title('All users') }
    it { should have_content('All users') }

    describe "pagination" do

      before(:all) { 30.times { FactoryGirl.create(:user) } }
      after(:all)  { User.delete_all }

      it { should have_selector('div.pagination') }

      it "should list each user" do
        User.paginate(page: 1).each do |user|
          expect(page).to have_selector('li', text: user.name)
        end
      end

      describe "non-admin user should not have delete links" do
        it { should_not have_link('delete', href: user_path(User.first)) }
      end
    end
  end


  # fhj: I could not get this to work as done in the book (i.e. placing this
  # above inside the "index" block). It could not execute sign_in admin
  # because it was already signed in as a non-admin user. I tried the
  # suggestion to create a sign_out helper function, but I could not make
  # that work either. By moving it outside of the block and recreating
  # the 30 test users, I was able to get it to pass the tests.
  # I also created the "non-admin user should not have delete links" block
  # at the end of the "pagination" block above to test for that case properly.
  describe "delete links" do

    before(:all) { 30.times { FactoryGirl.create(:user) } }
    after(:all)  { User.delete_all }

    describe "as an admin user" do
      let(:admin) { FactoryGirl.create(:admin) }
      before do
        sign_in admin
        visit users_path
      end

      it { should have_link('delete', href: user_path(User.first)) }
      it "should be able to delete another user" do
        expect do
          click_link('delete', match: :first)
        end.to change(User, :count).by(-1)
      end
      it { should_not have_link('delete', href: user_path(admin)) }
    end
  end