2
votes

It appears that Capybara is unable to find the first form element on a page by the displayed text within that form-element's label.

Capybara can however find that first form-element by that label's for value, which seems odd. I'm thinking the DOM just hasn't loaded yet, and Capybara can continuously search for for values but not the displayed text within labels?

Here is what my relevant form elements look like:

relevant form elements

so I should be able to grab each of these elements by the displayed Label text by saying:

fill_in "First Name", with: "foo"
fill_in "Last Name", with: "bar"
fill_in "Title", with: "some title"

Here is my spec:

scenario "with minimum fields while passing validations", js: true do
  visit "/"
  click_link "New Contact"
  fill_in "First Name", with: "foo"
  fill_in "Last Name", with: "bar"
  fill_in "Title", with: "some title"

  click_button "Create Contact"
  expect(page).to have_content "Success."
  expect(page).to have_content "foo bar"
end

It is not finding the First Name form element so it isn't filling it in. It finds the Last Name element and the Title element just fine because I am assuming that the DOM has fully loaded by that time:

not filling in first name

This is the html:

the html

However, oddly enough: when I change filling in the First name from:

fill_in "First Name, with: "foo"

To:

fill_in "contact_first_name, with: "foo"

It works now, but I don't want to use the for value of contact_first_name to grab that form element. I want to use the displayed label text as I am able to with The Title and Last Name.

enter image description here

What am I missing? Why can't I use "First Name" to grab the First Name form element?

Update: It turns out that if I put sleep 1 right before `fill_in "First Name", with: "foo" then it works! According to comments below from @TomWalpole, it seems like there is a javascript library that is getting in the way? Below are all the javascripts I include in:

//= require jquery
//= require jquery.turbolinks
//= require jquery_ujs
//= require turbolinks
//= require bootstrap-sprockets
//= require twitter/typeahead.min
//= require nested_form_fields
2
What driver are you using? And what error exactly do you get?Thomas Walpole
@TomWalpole I've been trying both selenium and webkit. The error is just my validation error that the First Name can't be blank. It is doing this because after capybara submits the page: the submission was invalid since capybara did not submit a first name.Neil
Ok -- so what that tells me is that Capybara is actually finding the element and filling it in, otherwise it would raise an ElementNotFound error. The most likely cause is some kind of JS library (validation?) that's augmenting the input and running after capybara has actually filled the field and ends up clearing what capybara put in it, you can test that by putting a sleep 10 before the first fill_in -- if that is the case then we need to figure out what change whatever library it is makes to the page thats detectable, and wait for that before filling inThomas Walpole
@TomWalpole wow, ok so as you suggested I put sleep 5 right before doing fill_in "First Name", with: "foo", and it worked! could you explain once more why putting sleep 5` before filling in the first form element made capybara properly fill in the first form element?Neil
Because you have some JS library that is adding functionality/behavior to the input. Capybara is finding the element and filling it in before whatever JS is running has added its functionality to the input. Once the JS has augmented the input it is resetting its value to the inputs value property (as opposed to the value attribute which has been changed) which ends up setting it back to blank. What libraries are you using on the page?Thomas Walpole

2 Answers

2
votes

Capybara is actually finding the input and filling it in, but then the value is being removed. This is because you have some JS library that is adding functionality/behavior to the input after the Capybara has filled in the element. Once the JS has augmented the input it is resetting its value to the inputs value property (as opposed to the value attribute which has been changed) which ends up setting it back to blank. Sleeping before the first fill_in is a workaround, other than that it would mean figuring out what JS is being run on the element and waiting for a detectable change (if there are any) before calling fill_in.

0
votes

If slow page loading is the case and especially if some Javascript and / or CSS animation is involved, then try raising the Capybara.default_max_wait_time to e.g. 30 seconds (the default is 2 seconds). And the fill_in should start working.

The fill_in, as well as most other Capybara methods, is "synchronized", i.e. it retries searching for the field up to the default max wait time and only if it is not found after this time, it fails.

See the Capybara readme.