Ответ 1
Мое решение
first(:link, link).click
вместо
click_link(link)
Как разрешить двусмысленность в Capybara? По какой-то причине мне нужны ссылки с одинаковыми значениями на странице, но я не могу создать тест, так как получаю ошибку
Failure/Error: click_link("#tag1")
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching link "#tag1"
Причина, по которой я не могу этого избежать, - это дизайн. Я пытаюсь воссоздать страницу Twitter с твитами/тегами справа и тегами слева от страницы. Поэтому неизбежно, что идентичная страница ссылок появится на одной странице.
Мое решение
first(:link, link).click
вместо
click_link(link)
Такое поведение Капибары преднамеренно, и я считаю, что это не должно быть исправлено, как было предложено в большинстве других ответов.
Версии Capybara до 2.0 вернули первый элемент вместо того, чтобы поднимать исключение, но позже поддерживающие Capybara решили, что это плохая идея, и лучше ее поднять. Было решено, что во многих ситуациях возврат первого элемента приводит к возврату не элемента, который разработчик хотел вернуть.
Наиболее рекомендуемый ответ здесь рекомендуется использовать first
или all
вместо find
, но:
all
и first
не ждите, пока элемент с таким локатором не появится на странице, хотя find
ждетall(...).first
и first
не защитят вас от ситуации, когда в будущем на странице может появиться другой элемент с таким локатором, и в результате вы можете найти неправильный элементИтак, ему рекомендуется выбрать другой, менее двусмысленный локатор: например, выбрать элемент по id, классу или другому локатору css/xpath, чтобы только один элемент будет соответствовать ему.
В качестве примечания здесь приведены некоторые локаторы, которые я обычно считаю полезными при решении двусмысленности:
find('ul > li:first-child')
Это более полезно, чем first('ul > li')
, поскольку он будет ждать, пока на странице не появится li
.
click_link('Create Account', match: :first)
Это лучше, чем first(:link, 'Create Account').click
, поскольку он будет ждать, пока на странице не появится хотя бы одна ссылка "Создать учетную запись". Однако я считаю, что лучше выбрать уникальный локатор, который не отображается на странице дважды.
fill_in('Password', with: 'secret', exact: true)
exact: true
указывает Capybara найти только точные совпадения, т.е. не найти "Подтверждение пароля"
Вышеупомянутое решение отлично работает, но для любопытных вы также можете использовать следующий синтаксис.
click_link(link_name, match: :first)
Здесь вы можете найти более подробную информацию:
НОВЫЙ ОТВЕТ:
Вы можете попробовать что-то вроде
all('a').select {|elt| elt.text == "#tag1" }.first.click
Возможно, есть способ сделать это, чтобы лучше использовать доступный синтаксис Capybara - что-то вроде строк all("a[text='#tag1']").first.click
, но я не могу думать о правильном синтаксисе, и я не могу найти подходящий документация. Сказать, что это немного странная ситуация, с двумя тегами <a>
с теми же id
, class
и текстом. Есть ли вероятность, что они дети разных div, так как вы могли бы сделать свой find
within
соответствующий сегмент DOM. (Это поможет увидеть немного вашего HTML-источника).
OLD ANSWER: (где я думал, что "# tag1" означает, что у элемента был id
"tag1" )
Какую из ссылок вы хотите нажать? Если это первый (или это не имеет значения), вы можете сделать
find('#tag1').click
В противном случае вы можете сделать
all('#tag1')[1].click
чтобы щелкнуть второй.
Вы можете убедиться, что найдете первый, используя match
:
find('.selector', match: :first).click
Но, что важно, , вы, вероятно, не хотите этого делать, поскольку это приведет к хрупким тестам, которые игнорируют запах кода с повторяющимся кодом, что, в свою очередь, ведет до ложных срабатываний, которые продолжают работать, когда они должны были сбой, потому что вы удалили один соответствующий элемент, но тест с радостью нашел другой.
Лучше всего использовать within
:
within('#sidebar') do
find('.selector).click
end
Это гарантирует, что вы найдете элемент, который вы ожидаете найти, но при этом используете возможности автоматического ожидания и автоматического повтора попыток Capybara (которые вы теряете, если используете find('.selector').click
), и это значительно упрощает намерение.
Чтобы добавить к существующему телу знания:
Для тестов JS Capybara должен поддерживать синхронизацию двух потоков (один для RSpec, один для Rails) и второго процесса (браузера). Он делает это, ожидая (до установленного максимального времени ожидания) в большинстве методов сопоставления и node -finding.
Capybara также имеет методы, которые не ждут, прежде всего Node#all
. Использование их походит на то, чтобы сообщить вашим спецификациям, что вы хотите, чтобы они терпели неудачу с перерывами.
Принятый ответ предлагает page.first('selector')
. Это нежелательно, по крайней мере, для спецификаций JS, потому что Node#first
использует Node#all
.
Тем не менее, Node#first
будет ждать, если вы сконфигурируете Capybara так:
# rails_helper.rb
Capybara.wait_on_first_by_default = true
Этот параметр был добавлен в Capybara 2.5.0 и по умолчанию является ложным.
Как упомянул Андрей, вместо этого вы должны использовать
find('selector', match: :first)
или измените свой селектор. Любой из них будет хорошо работать независимо от конфигурации или драйвера.
Чтобы еще больше усложнить ситуацию, в старых версиях Capybara (или с включенной опцией конфигурации) #find
с радостью игнорирует неоднозначность и просто возвращает первый селектор соответствия. Это тоже не здорово, так как это делает ваши спецификации менее явными, что, я думаю, почему больше не является поведением по умолчанию. Я не учитываю специфику, потому что они уже обсуждались выше.
Дополнительные ресурсы:
Благодаря этот пост, вы можете исправить его с помощью опции "match":
Capybara.configure do |config|
config.match = :prefer_exact
end
Чтобы избежать неоднозначной ошибки в огурцах.
Решение 1
first("#tag1").click
Решение 2
Cucumber features/filename.feature --guess