Как протестировать элемент Select2 с помощью capybara DSL?

У меня есть элемент Select2 на странице, которая загружает результаты через ajax. Хотелось бы проверить это с помощью capybara/rspec (используя драйвер poltergeist), но так как элемент Select2 фактически запускается как скрытое поле, а затем заполняет результаты <ul> после обработки, ни один из обычных select, fill_in или 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 там, потому что элемент поля поиска не имеет атрибутов метки, идентификатора или имени)

Ответы

Ответ 1

Я создал помощника, который вы можете использовать с Cucumber, он не имеет sleep, поэтому он быстрее, чем методы выше. Это также работает, если вы используете createSearchChoice, предоставленный Select2.

Поместите его в 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.

Ответ 2

Прекратите царапать голову, сделайте это, и она будет работать

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

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

Ответ 3

Для нескольких компонентов 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

Ответ 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

Ответ 5

Для тех, кто борется с Select2 multiple:

Странно я обнаружил, что select_tag с несколькими: true, похоже, стремится генерировать 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();

Ответ 6

Вдохновленный этим ответом и один в 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

Ответ 7

У меня есть два удаленных select2s. Я ищу строку в первой и, согласно найденному результату, второй заполняется. После того, как вы попробовали камень capybara-select2 и все перечисленные здесь решения, мне пришлось придумать собственное решение, потому что ни один из них не работал для поиска. В моем случае то, что я выбираю, не важно, поэтому не пыталось выбрать метку результата с определенным значением. Надеюсь, это поможет кому-то в моей ситуации.

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

Ответ 8

Простой ответ:

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

Ответ 9

Эй, я не уверен, что кто-то по-прежнему заботится, но у меня тоже была эта проблема, и я нашел простой способ справиться с этим.

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

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

Итак, когда у меня возникли проблемы с capybara, которые не могли использовать раскрывающееся меню select2, мое исправление состояло в том, чтобы использовать глобальный push, если не в тестовой среде:

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

Я использовал камень capybara-select2, но кажется, что он больше не поддерживается: (

Ответ 10

Это работает для меня по выпадающему стилю 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 должен быть идентификатором регулярного тега select, из которого сделан select2.

Ответ 11

def select2(id, value)
  find("##{id} ~ span.select2").click
  within ".select2-results" do
    find("li", text: value).click
  end
end

Ответ 12

Здесь решение для более новой версии select2 - в частности, жемчуг select2-rails (4.0.0):

Поместите его в features/support/feature_helpers.rb, а затем добавьте его в свой spec_helper.rb файл с config.include FeatureHelpers в блок RSpec.configure do |config|.

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

Затем используйте fill_in_select2 так же, как вы использовали бы fill_in в Capybara, но включите класс или идентификатор элемента контейнера с соответствующим символом префикса. (например, fill_in_select2 '.actions_list', with: '[email protected]').

Примечание: это выбирает первый элемент в выпадающем меню после ввода текста.

Протестировано в Rails 4.2.5.2, Capybara 2.7.1 и Capybara-Webkit 1.11.1

Ответ 13

Проведя больше на этом, чем хотелось бы, я, наконец, получил его работу, построив ответ Брэндона Макинниса, используя селектор xpath.

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

Ответ 14

Для select2 4.0 в сочетании с capybara-webkit я попробовал решения на основе execute_script, но они не работали, потому что capybara не смог найти полученный элемент li. Я, наконец, остановился на этом простом решении, основанном на send_keys. Поскольку я использую удаленный источник данных, я добавил небольшой сон, чтобы отобразить результаты:

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), чтобы сделать эту работу.