Sasha
Sasha

Reputation: 6466

Rails Capybara not seeing fields

I feel like I'm going insane here. Doing a routine capybara test of my Post model. Specifically, making sure the new page redirects to the newly created post after a valid submission. So in my spec/requests folder, I've got this:

describe "Valid post submission --" do

    it "should log in a user and redirect to new post" do

      # Sign in works find, so I won't reproduce it

      # Make a blogpost
      Post.destroy_all
      fill_in :name_here, with: "Post Name Here"
      fill_in :content_here, with: "This is the content of a really short post."
      screenshot_and_open_image
      click_on "Post It"
      screenshot_and_open_image
      puts current_path
      page.should have_selector 'h2', text: "prohibited this post"
      page.should have_selector 'h1', text: "Post Name Here"
      page.should have_selector '.alert', text: "Post was successfully created."
      page.should have_selector 'title', text: full_title('Post Name Here')

    end

The screenshots and puts are there to clarify what's going on. There are basically two different cases. In

  1. I just leave the fields be and test for the default IDs ("post_name" and "post_content"); obviously the above test is for case two:
  2. I change the field ids to "name_here" and "content_here" to test if the label names or anything else is interfering. That's the one the spec of which is above.

In case 2 the post is getting rejected because Capybara can't find either field.

In case 1, only the content would be empty, but the name field would be filled with the content.

The precise error in case 1 is:

2) Posts Invalid post - should refresh with error if content empty
     Screenshot: /Users/myusername/rails/appname/tmp/capybara/screenshot_2013-03-14-22-37-36.634.png
     Failure/Error: fill_in :post_name, with: "Post Name Here"
     Capybara::ElementNotFound:
       cannot fill in, no text field, text area or password field with id, name, or label 'post_name' found
     # (eval):2:in `fill_in'
     # ./spec/requests/posts_spec.rb:43:in `block (3 levels) in <top (required)>'

Case 2 doesn't throw an error about not finding the appropriate content, I believe because the label has that id as well.

This is ridiculous, because I took a snapshot of the HTML durin the test, just before it errors. Here's case 2 -- both cases look 90% the same here, apart from that ID difference:

<form accept-charset="UTF-8" action="/posts" class="new_post" id="new_post" method="post">
 <div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"></div>
 <div class="field">
  <label for="post_name">Name</label>
   <br><input id="name_here" name="post[name]" size="30" type="text">
 </div>
 <div class="field text-area">
  <label for="post_content">Content</label>
  <br><textarea cols="50" id="content_here" name="post[content]" rows="20"></textarea>
 </div>
 <div class="actions btn-group">
  <input class="btn" name="commit" type="submit" value="Post It">
 </div>
</form>

Note the clear existence of the post_content field. In cases where both fail (Case 1), there are both content_here and name_here fields. So the fields are there. And capybara is generally working (this sort of thing works find in other parts of my app). And the problem isn't a conflict with the label name, because putting a different id on the input fields doesn't help capybara find them.

By the way, this all works perfectly in reality.

Any idea at all what's going on here? I'm super frustrated/clueless.

UPDATE -- Following user2172816's advice, I changed my HAML so the HTML looks like this:

<form accept-charset="UTF-8" action="/posts" class="new_post" id="new_post" method="post">
 <div style="margin:0;padding:0;display:inline"><input name="utf8" type="hidden" value="✓"></div>
 <div class="field">
  <label for="post_name">Name</label>
  <br><input id="name" name="post[name]" size="30" type="text">
 </div>
 <div class="field text-area">
  <label for="post_content">Content</label>
  <br><textarea cols="50" id="content" name="post[content]" rows="20"></textarea>
 </div>
 <div class="actions btn-group">
  <input class="btn" name="commit" type="submit" value="Post It">
 </div>
</form>

The test now looks like this:

 # Make a blogpost
  Post.destroy_all
  fill_in "Name", with: "Post Name Here"
  fill_in "Content", with: "This is the content of a really short post."
  page.should have_selector 'h2', text: "prohibited this post"
  page.should have_selector 'h1', text: "Post Name Here"
  page.should have_selector '.alert', text: "Post was successfully created."
  page.should have_selector 'title', text: full_title('Post Name Here')

But it's still erroring (in new parts of the same spec, too!):

  1) Posts Invalid post - should refresh with error if content empty
     Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.072.png
     Failure/Error: fill_in :name, with: "Post Name Here"
     Capybara::ElementNotFound:
       cannot fill in, no text field, text area or password field with id, name, or label 'name' found
     # (eval):2:in `fill_in'
     # ./spec/requests/posts_spec.rb:43:in `block (3 levels) in <top (required)>'

  2) Posts Invalid post - should refresh with error if name empty
     Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.274.png
     Failure/Error: fill_in :name, with: ""
     Capybara::ElementNotFound:
       cannot fill in, no text field, text area or password field with id, name, or label 'name' found
     # (eval):2:in `fill_in'
     # ./spec/requests/posts_spec.rb:33:in `block (3 levels) in <top (required)>'

  3) Posts Valid post submission -- should log in a user and redirect to new post
     Screenshot: /Users/username/rails/appname/tmp/capybara/screenshot_2013-03-14-23-54-38.459.png
     Failure/Error: fill_in :name, with: "Post Name Here"
     Capybara::ElementNotFound:
       cannot fill in, no text field, text area or password field with id, name, or label 'name' found
     # (eval):2:in `fill_in'
     # ./spec/requests/posts_spec.rb:67:in `block (3 levels) in <top (required)>'

Upvotes: 1

Views: 2189

Answers (1)

user2172816
user2172816

Reputation: 1200

The attribute "for" of label should be the "id" of the input element, i.e.

<label for="name_here">Name</label>
   <br><input id="name_here" name="post[name]" size="30" type="text">

And in the spec you should call:

fill_in 'Name', with: "Post Name Here"

Upvotes: 5

Related Questions