Разрешение Двусмысленности Капибары


Как разрешить двусмысленность в капибаре? По какой-то причине мне нужны ссылки с одинаковыми значениями на странице, но я не могу создать тест, так как я получаю ошибку

Failure/Error: click_link("#tag1")
     Capybara::Ambiguous:
       Ambiguous match, found 2 elements matching link "#tag1"

причина, почему я не могу избежать это из-за дизайна. Я пытаюсь воссоздать страницу twitter с твитами / тегами справа и тегами слева от страницы. Поэтому это будет неизбежно, что одинаковые ссылки на одной странице.

8 88

8 ответов:

мое решение-это

first(:link, link).click

вместо

click_link(link)

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

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

наиболее поддержанный ответ здесь рекомендую использовать first или all вместо find но:

  1. all и first Не ждите, пока элемент с таким локатор появится на странице find не ждать
  2. all(...).first и first не защитит вас от ситуации, что в будущем на странице может появиться другой элемент с таким локатором, и в результате вы можете найти неправильный элемент

так рекомендуется выбрать другой, менее неоднозначный локатор: например, выберите элемент по идентификатору, классу или другому локатору 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 говорит капибара, чтобы найти только точные совпадения, т. е. не найти "подтверждение пароля"

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

click_link(link_name, match: :first)

вы можете найти дополнительную информацию здесь:

http://taimoorchangaizpucitian.wordpress.com/2013/09/06/capybara-click-link-different-cases-and-solutions/

ОТВЕТ:

вы можете попробовать что-то вроде

all('a').select {|elt| elt.text == "#tag1" }.first.click

может быть способ сделать это, который лучше использует доступный синтаксис капибары - что-то вроде all("a[text='#tag1']").first.click но я не могу придумать правильный синтаксис из рук, и я не могу найти соответствующую документацию. Тем не менее, это немного странная ситуация для начала, имея два <a> теги с тем же id,class, и текст. Есть ли шанс, что они дети разные дивы, так как вы могли бы тогда сделать свой findwithin соответствующий сегмент DOM. (Это поможет увидеть немного вашего HTML-код).


старый ответ: (где я думал, что '#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#all. Использование их похоже на то, что вы говорите своим спецификациям, что вы хотите, чтобы они периодически терпели неудачу.

принят ответ предполагает page.first('selector'). Это нежелательно, по крайней мере для спецификаций JS, потому что Node#first использует Node#all.

что сказал:Node#firstбудет подождите, если вы настроите капибару так:

# rails_helper.rb
Capybara.wait_on_first_by_default = true

этот вариант был добавлено в Capybara 2.5.0 и по умолчанию имеет значение false.

как упоминал Андрей, вы должны вместо этого использовать

find('selector', match: :first)

или измените свой селектор. Либо будет работать хорошо, независимо от конфигурации или водитель.

чтобы еще больше усложнить ситуацию, в старых версиях Capybara (или с включенной опцией config),#find С радостью проигнорирует двусмысленность и просто вернет первый соответствующий селектор. Это тоже не очень хорошо, так как это делает ваши спецификации менее явными, и я полагаю, что это больше не поведение по умолчанию. Я опущу детали, потому что они уже обсуждались выше.

больше ресурсы:

из-за этот пост, вы можете исправить это с помощью опции "match":

Capybara.configure do |config|
  config.match = :prefer_exact
end

чтобы избежать неоднозначной ошибки в огурце.

Решение 1

first("#tag1").click

решение 2

Cucumber features/filename.feature --guess