Капибара не может найти и заполнить первый элемент формы

Похоже, что Capybara не может найти первый элемент формы на странице по отображаемому тексту внутри метки этого элемента формы.

Однако Capybara может найти этот первый элемент формы по значению for этой метки, что кажется странным. Я думаю, что DOM просто еще не загружен, и Capybara может постоянно искать значения for, но не отображаемый текст в labels?

Вот как выглядят мои соответствующие элементы формы:

соответствующие элементы формы

поэтому я должен иметь возможность захватить каждый из этих элементов отображаемым текстом метки, сказав:

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

Вот моя спецификация:

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

Он не находит элемент формы First Name, поэтому не заполняет его. Он отлично находит элемент Last Name и элемент Title, потому что я предполагаю, что к этому времени DOM полностью загружен:

не заполнив имя

Это html:

html

Однако, как ни странно: когда я меняю заполнение Имя с:

fill_in "First Name, with: "foo"

To:

fill_in "contact_first_name, with: "foo"

Теперь это работает, но я не хочу использовать значение for для contact_first_name для захвата этого элемента формы. Я хочу использовать отображаемый текст метки, как это возможно с Title и Last Name.

введите здесь описание изображения

Что мне не хватает? Почему я не могу использовать «Имя» для захвата элемента формы «Имя»?

Обновление: оказывается, если я поставлю sleep 1 прямо перед `fill_in "Имя" с помощью: "foo", то это сработает! Согласно комментариям ниже от @TomWalpole, кажется, что есть библиотека javascript, которая мешает? Ниже приведены все javascripts, которые я включаю:

//= require jquery
//= require jquery.turbolinks
//= require jquery_ujs
//= require turbolinks
//= require bootstrap-sprockets
//= require twitter/typeahead.min
//= require nested_form_fields

person Neil    schedule 21.03.2016    source источник
comment
Какой драйвер вы используете? И какую именно ошибку выдает?   -  person Thomas Walpole    schedule 21.03.2016
comment
@TomWalpole Я пробовал и селен, и вебкит. Ошибка - это просто моя ошибка проверки, что имя не может быть пустым. Это происходит потому, что после того, как капибара отправляет страницу: отправка была недействительной, поскольку капибара не отправила имя.   -  person Neil    schedule 21.03.2016
comment
Итак, это говорит мне о том, что Capybara на самом деле находит элемент и заполняет его, иначе возникнет ошибка ElementNotFound. Наиболее вероятной причиной является какая-то библиотека JS (проверка?), которая увеличивает ввод и запускается после того, как капибара фактически заполнит поле и в конечном итоге очистит то, что капибара вложила в него, вы можете проверить это, поставив sleep 10 перед первым fill_in - - если это так, то нам нужно выяснить, какое изменение любая библиотека вносит на страницу, которую можно обнаружить, и дождаться этого, прежде чем заполнять   -  person Thomas Walpole    schedule 21.03.2016
comment
@TomWalpole вау, хорошо, как вы предложили, я поставил sleep 5 прямо перед выполнением fill_in "First Name", with: "foo", and it worked! could you explain once more why putting sleep 5` перед заполнением первого элемента формы, заставил капибару правильно заполнить первый элемент формы?   -  person Neil    schedule 21.03.2016
comment
Потому что у вас есть библиотека JS, которая добавляет функциональность/поведение к входным данным. Capybara находит элемент и заполняет его до того, как запущенный JS добавит свою функциональность к входным данным. После того, как JS расширил ввод, он сбрасывает его значение в свойство inputs value (в отличие от атрибута value, который был изменен), что в конечном итоге возвращает его к пустому значению. Какие библиотеки вы используете на странице?   -  person Thomas Walpole    schedule 21.03.2016
comment
@TomWalpole Я обновляю дневной вопрос своей библиотекой js внизу вопроса. Спасибо за помощь!   -  person Neil    schedule 21.03.2016
comment
Давайте продолжим обсуждение в чате.   -  person Thomas Walpole    schedule 21.03.2016


Ответы (2)


Capybara на самом деле находит ввод и заполняет его, но затем значение удаляется. Это связано с тем, что у вас есть некоторая библиотека JS, которая добавляет функциональность/поведение к вводу после того, как Capybara заполнит элемент. После того, как JS расширил ввод, он сбрасывает его значение в свойство inputs value (в отличие от атрибута value, который был изменен), что в конечном итоге возвращает его к пустому значению. Засыпание перед первым fill_in является обходным путем, кроме этого, это будет означать выяснение того, какой JS запускается на элементе, и ожидание обнаруживаемых изменений (если они есть) перед вызовом fill_in.

person Thomas Walpole    schedule 21.03.2016
comment
У меня точно такая же проблема с Rails 5 и Turbolinks 5, тестирование с помощью capybara-webkit 1.11. Когда я включаю Turbolinks, fill_in для поля E-Mail на странице сброса пароля больше не работает надежно, что очень прискорбно. - person cseelus; 15.11.2016
comment
@cseelus Вам нужно посмотреть, какие виджеты JS вы прикрепляете к этому полю, и посмотреть, добавляют ли они класс, атрибут данных или любое другое обнаруживаемое изменение в поле, а затем дождаться его существования, прежде чем вызывать fill_in - person Thomas Walpole; 15.11.2016
comment
Спасибо за быстрый ответ. Я просмотрел отдельное обсуждение в чате stackoverflow между вами и автором этого вопроса и прочитал все, что смог найти по этой проблеме. Я занимаюсь этим вопросом уже более двух часов, так как ставить sleep перед fill_in просто не чисто, имхо. Я совершенно уверен, что это Turbolinks, так как я выбросил все остальные вещи JS, кроме jQuery и jQuery UJS, и только когда я выбрасываю сам Turbolinks, fill_in работает надежно, как и ожидалось. - person cseelus; 15.11.2016
comment
Я не прикреплял никаких виджетов к этому текстовому полю (приложение по-прежнему довольно простое и чистое), поэтому, думаю, мне просто нужно захватить элемент по идентификатору, что также ненадежно, но я думаю, что у меня нет никаких другие варианты, когда я хочу сохранить Turbolinks (и capybara-webkit для интеграционного тестирования). - person cseelus; 15.11.2016
comment
@cseelus Может быть, вам стоит открыть новый вопрос и показать, что именно вы делаете. Если вы не добавляете виджеты, ваша проблема не такая, как эта проблема. - person Thomas Walpole; 15.11.2016
comment
Моя проблема заключалась в том, что я использовал fill_in 'E-Mail' в форме сброса пароля, начиная с формы входа. Я пошел туда с click_link 'Forgot password?'. После обширных тестов я понял, что проблема заключалась в том, что Capybara, возможно, в 1/20 случаях заполняла поле электронной почты на странице входа вместо страницы сброса пароля и делала это только при включенном Turbolinks. Простое within('form#reset_password') { fill_in 'E-Mail' … } сделало свое дело (sleep между click_link и fill_in тоже работает, но я стараюсь этого избегать). Спасибо за ваше время и за Capybara, это здорово! - person cseelus; 16.11.2016

Если имеет место медленная загрузка страницы и особенно если задействована какая-либо анимация Javascript и / или CSS, попробуйте увеличить Capybara.default_max_wait_time, например, до 30 секунд (по умолчанию 2 секунды). И fill_in должен начать работать.

fill_in, как и большинство других методов Capybara, является «синхронизированным», т. е. он повторяет поиск поля до максимального времени ожидания по умолчанию, и только если оно не найдено по истечении этого времени, происходит сбой.

См. ознакомительные сведения о Capybara.

person BoraMa    schedule 21.03.2016
comment
К сожалению, проблема не во времени ожидания. Похоже, что Capybara просто не может найти первый элемент в формах по тексту, отображаемому внутри метки. Единственный способ обойти это, который я нашел, - вместо этого отображать значение атрибута for метки. Таким образом, имя будет изменено с: fill_in "First Name", with: "foo" на: fill_in "contact_first_name", with: "foo" - person Neil; 21.03.2016
comment
Понимаю. В любом случае, мне кажется, лучше использовать идентификаторы. Но позвольте мне высказать одну последнюю дикую догадку: будет ли найдено поле, если вы будете искать * Имя? Я понимаю, что звездочка определена в CSS, а не в HTML, но все же... Вы можете распечатать тексты всех меток, используя это: puts page.all('label').map(&:text).inspect в своем тесте. Возможно, вы обнаружите, что Capybara видит метки по-другому. - person BoraMa; 21.03.2016
comment
Спасибо за совет. Он вернулся с этим "[\"First Name\", \"Last Name\"]". Я не думаю, что это помогает мне здесь, но хорошо знать, что он существует. - person Neil; 21.03.2016