如何使用 capybara DSL 测试 Select2 元素?

Posted

技术标签:

【中文标题】如何使用 capybara DSL 测试 Select2 元素?【英文标题】:How to test a Select2 element with capybara DSL? 【发布时间】:2012-09-28 02:18:51 【问题描述】:

我在通过 ajax 加载结果的页面上有一个 Select2 元素。想用 capybara/rspec 测试这个(使用 poltergeist 驱动程序),但由于 Select2 元素实际上是作为隐藏字段开始的,然后在处理结果后填充<ul>,没有正常的select,@ 987654324@ 或 choose 助手可以工作。

我现在拥有的是类似的东西

  it "should have a search field for events" do
    click_link "Select an Event"       # this works as expected
    within('.select2-container') do    # works
      find('.select2-search input').set(event.description[0,5])  # won't work!
      find(:xpath, "li[text()='#event.description']").click 
    end
    click_button "Search"
    page.should have_content("Displaying all 2 photos")
  end

上面的第 4 行,显然 capybara 可以找到元素,但值没有改变,并且 Select2 永远不会进行 ajax 调用。 (不能在那里使用普通的fill_in,因为搜索字段元素没有标签、id 或名称属性)

【问题讨论】:

一个非常令人沮丧的问题 【参考方案1】:

别挠头了,这样做会奏效的

select "option_name_here", :from => "Id Of Your select field"

我尝试了每一个模块和每一个该死的东西,最后只用一行简单的代码就做到了,没有任何帮助。

【讨论】:

我正在使用它并且它有效。但我认为您不会从 select2 测试 js,因此您实际上并没有测试最终用户所看到的内容。但这可能并不总是一个问题。【参考方案2】:

我创建了一个可以与 Cucumber 一起使用的助手,它没有 sleep,因此它比上述方法更快。如果您使用 Select2 提供的createSearchChoice,这也有效。

放入features/support/select2_helper.rb

module Select2Helper
  def select2(value, attrs)
    first("#s2id_#attrs[:from]").click
    find(".select2-input").set(value)
    within ".select2-result" do
      find("span", text: value).click
    end
  end
end

World(Select2Helper)

像这样使用它:

Then(/^I select2 "([^"]*)" from "([^"]*)"$/) do |value, select_name|
  select2 value, from: select_name
end

使用 Rails 4.1.1 和 Cucumber 1.3.15 测试。

【讨论】:

有效!即使对于 select2 多选!先生,我欠你的-_- 有时不是span有一个div,所以我在最后一次find时使用了find("span").text.empty? ? (find("div", text: value).click) : (find("span", text: value).click) 受此答案和bit.ly/18yhpb3 的启发,我想出了对于同一页面上的单选和多选的多个 select2 字段来说似乎很健壮的方法:(将代码移动到单独的答案格式化目的)【参考方案3】:

您可以使用capybara-select-2 gem,它将为您完成所有工作。只需将 search: true 传递给辅助选项即可进行 Ajax 调用:

select2 'Marriage', from: 'Event', search: true

【讨论】:

我设法通过游戏完成了我的任务。感谢您指出!【参考方案4】:

我能够通过使用page.execute_script 手动将所需的值和keyup 事件输入搜索字段来解决此问题。

  it "should have a search field for events" do
    click_link "Select an Event"
    page.execute_script("i = $('.select2-container input').first();
                         i.val('#event.description[0,4]').trigger('keyup');");
    sleep 2
    find('.select2-results li:first-child').click
    click_button "Search"
    page.should have_content("Displaying all 2 photos")
  end

【讨论】:

这里有一个 capybara 辅助方法,它可以更好地做到这一点,并且不依赖于特定的字段标签和元素 gist.github.com/3849340 这应该可以,但我收到错误“元素在点 (296, 637) 处不可点击。其他元素将收到点击:" 这不是你想要的,它有 2 秒的睡眠时间。 是的,使用其他答案之一,这是一个黑客。我现在的解决方案是不使用 Select2。 这对我也很有效 find('#select_field option:first-child').click【参考方案5】:

对于您页面上的多个 select2 组件,我创建了以下适用于我的辅助方法:

def select2(id, value)
  page.execute_script %Q
    i = $('#s2id_#id .select2-input');
    i.trigger('keydown').val('#value').trigger('keyup');
  
  sleep 2
  find('div.select2-result-label').click
end

def remove_all_from_select2(id)
  page.execute_script %Q
    $('#s2id_#id .select2-choices a').click();
  
end

def remove_from_select2(id, value)
  page.execute_script %Q
    $('#s2id_#id .select2-choices div:contains("#value")').closest('li').find('a').click();
  
end

【讨论】:

这让我走上了正轨。我改进了您的 select2 方法,以创建与 Select2 3.4 gist.github.com/onyxrev/6970632 一起使用的通用查询和选择方法【参考方案6】:

对于那些在 Select2 multiple中苦苦挣扎的人:

奇怪的是,我发现带有 multiple:true 的 select_tag 似乎热衷于为每个条目生成 html,例如:

<div class="select2-result-label">
  <span class="select2-match"></span>
  value
</div>

而不是

<div class="select2-result-label">
  <span class="select2-match">value</span>
</div>

将内容放在跨度之外毫无帮助。

为了用 capybara 测试它,我有两种方法:

# for most select2s

def select2_select(value, id)
  # This methods requires @javascript in the Scenario
  first("#s2id_#id").click
  find(".select2-input").set(value)
  within ".select2-result" do
    if !find("span").text.empty?
      find("span", text: value).click
    elsif !find("div").text.empty?
      find("div", text: value).click
    else
      fail 'neither span nor div within this select2, did you get the id of the select2 right?'
    end
  end
end


# for select_tag select2s which are multiple:true

def select2_select_multiple(select_these, id)
  # This methods requires @javascript in the Scenario
  [select_these].flatten.each do | value |
    first("#s2id_#id").click
    found = false
    within("#select2-drop") do
      all('li.select2-result').each do | result |
        unless found
          if result.text == value
            result.click
            found = true
          end
        end
      end
    end
  end
end

后者对我来说太老套了,所以我尽可能使用第一个并希望最终替换后者。

使用的 HAML:

= select_tag "some_id", options_for_select(['hello','world']), multiple: true, include_blank: true

js:

$("#some_id").select2();

【讨论】:

我必须使用 find('#s2id_#id a').click 才能工作【参考方案7】:

从这个答案和Capybara select2 helper 的一个答案中得到启发,我想出了对于同一页面上的单选和多选的多个 select2 字段来说似乎很健壮的方法:

def select2(value, attrs)
  s2c = first("#s2id_#attrs[:from]")
  (s2c.first(".select2-choice") || s2c.find(".select2-choices")).click

  find(:xpath, "//body").all("input.select2-input")[-1].set(value)
  page.execute_script(%|$("input.select2-input:visible").keyup();|)
  drop_container = ".select2-results"
  find(:xpath, "//body").all("#drop_container li", text: value)[-1].click
end

【讨论】:

【参考方案8】:

这适用于带有远程数据源的下拉样式 select2:

  def select2(value, from:)
    execute_script("$('##from').select2('open')")
    find(".select2-search__field").set(value)
    find(".select2-results li", text: /#value/).click
  end

from 必须是构建 select2 的常规选择标签的 ID。

【讨论】:

【参考方案9】:

我有两个远程 select2。我在第一个中搜索一个字符串,根据找到的结果,第二个被填充。 在尝试了 capybara-select2 gem 和这里和那里列出的所有解决方案之后,我不得不想出自己的解决方案,因为它们都不能用于搜索。 在我的情况下,我选择的内容并不重要,因此没有尝试选择具有特定值的结果标签。希望这对我的情况有所帮助。

Given(/^I have selected  "(.*?)" category$/) do |arg1|  
  page.find(:css, ".select2-choice.select2-default").click 
  page.find(:css, "#s2id_autogen1_search").set "Dip Boya"
  sleep 2
  page.all(:css, '.select2-result-label')[1].click

end

Given(/^I have selected "(.*?)" variation$/) do |arg1|  
  page.find(:css, ".select2-choice.select2-default").click 
  page.all(:css, '.select2-result-label')[1].click
end

【讨论】:

【参考方案10】:

https://github.com/goodwill/capybara-select2 和 js: true 指定对我来说效果很好。干杯

【讨论】:

我建议迁移到更新的 gem github.com/Hirurg103/capybara_select2,它支持最新版本的 select2 gem install capybara-select-2【参考方案11】:

这对我有用,即使使用 select2 AJAX 自动完成:

find('#user_id').sibling('span.select2').click
find('.select2-search__field').set('ABCDEF')
find('.select2-results li', text: 'ABCDEF').click

【讨论】:

【参考方案12】:

简单回答:

page.find('.select2-drop-active .select2-input').set('foo')
page.find('.select2-drop-active .select2-input').native.send_keys(:return)

【讨论】:

【参考方案13】:

嘿,我不确定是否还有人关心,但我也遇到了这个问题,我找到了一种简单的方法来处理它。

首先,我不会将类添加到每个下拉选择中,而是像这样在全局范围内进行:

    class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput
      def input_html_classes
        super.push('chosen-select')
      end
    end

因此,当我开始遇到 capybara 无法使用 select2 下拉菜单的问题时,我的解决方法是仅在不在测试环境中时才使用全局推送:

    class CollectionSelectInput < SimpleForm::Inputs::CollectionSelectInput
      unless Rails.env.test?
        def input_html_classes
          super.push('chosen-select')
        end
      end
    end

我曾经使用 capybara-select2 gem,但它似乎不再被维护:(

【讨论】:

【参考方案14】:
def select2(id, value)
  find("##id ~ span.select2").click
  within ".select2-results" do
    find("li", text: value).click
  end
end

【讨论】:

【参考方案15】:

这里是 select2 更新版本的解决方案 - 特别是 select2-rails (4.0.0) gem:

将其放入features/support/feature_helpers.rb,然后将其包含在spec_helper.rb 文件中,并在RSpec.configure do |config| 块中使用config.include FeatureHelpers

module FeatureHelpers
  def fill_in_select2(container_selector, with: '')
    page.execute_script %Q
      i = $('#container_selector .select2-search__field');
      i.trigger('keydown').val('#with').trigger('keyup');
    
    sleep 2
    find('.select2-results__option--highlighted').click
  end
end

然后像在 Capybara 中使用 fill_in 一样使用 fill_in_select2,但使用适当的前缀符号包含容器元素的类或 id。 (例如fill_in_select2 '.actions_list', with: 'bob@testmaster1000.com')。

注意:这会选择输入文本后下拉列表中的第一个元素。

在 Rails 4.2.5.2、Capybara 2.7.1 和 Capybara-Webkit 1.11.1 中测试

【讨论】:

【参考方案16】:

在花费了比我想要的更长的时间之后,我终于通过使用 xpath 选择器在 Brandon McInnis 的答案上构建它来工作。

module Select2Helper
  def fill_in_select2(container_selector, with: '')
    page.execute_script %Q
      i = $('#container_selector .select2-search__field');
      i.trigger('keydown').val('#with').trigger('keyup');
    
    sleep 2
    find(:xpath, "//body").all("#container_selector li", text: with)[-1].click
  end
end

【讨论】:

【参考方案17】:

对于 select2 4.0 与 capybara-webkit 的结合,我尝试了基于 execute_script 的解决方案,但它们不起作用,因为 capybara 找不到生成的 'li' 元素。我最终确定了这个基于 send_keys 的简单解决方案。因为我使用的是远程数据源,所以我加了一个小sleep让结果出现:

module Select2Helpers
  def fill_in_select2(field_name, with: '')
    field = find("##field_name")
    parent = field.find(:xpath, './/..')
    ui = parent.find('.select2-search__field')

    ui.send_keys(with)
    sleep 0.5
    ui.send_keys(:enter)
    sleep 0.1
  end
end

请注意,您可能需要更新到最新的 capybara-webkit (1.14.0) 才能完成这项工作。

【讨论】:

【参考方案18】:

这个助手为我工作(使用占位符):

  # Add this helper
  def select2(placeholder, option)
    find("select[data-placeholder='#placeholder'] + .select2").click
    within '.select2-results' do
      find('span', text: option).click
    end
  end

然后这样称呼它:

select2 'Placeholder', 'some option'

【讨论】:

以上是关于如何使用 capybara DSL 测试 Select2 元素?的主要内容,如果未能解决你的问题,请参考以下文章

如何在测试之间重复使用 Capybara 会话?

如何使用 Capybara 测试 OPTIONS 请求?

如何使用 Capybara + Selenium 测试响应代码

如何使用rails cucumber,rspec,capybara在视图(dhtml)中测试动态部分?

如何使用 Capybara Rspec Matchers 测试演示者?

问:如何使用 Turnip、capybara 和 Gherkin 测试 ActionMailer Deliver_later