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

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


person sbeam    schedule 07.10.2012    source источник
comment
Совершенно неприятная проблема   -  person guero64    schedule 23.03.2017


Ответы (17)


Хватит ломать голову, сделай так и все получится

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

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

person Huzaifa Saifuddin    schedule 13.04.2016
comment
Я использую это, и это работает. Но я не думаю, что вы будете тестировать js из select2, поэтому на самом деле вы не тестируете то, что видит конечный пользователь. Но это не всегда может быть проблемой. - person Ozgar; 06.03.2017

Я создал помощник, который вы можете использовать с 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.

person Jankeesvw    schedule 22.06.2014
comment
Оно работает! Даже для мультиселекта select2! Сэр, я у вас в долгу -_- - person xxjjnn; 18.11.2014
comment
Иногда вместо span стоит div, поэтому я использовал find("span").text.empty? ? (find("div", text: value).click) : (find("span", text: value).click) при последней находке - person xxjjnn; 19.11.2014
comment
Вдохновленный этим ответом и ответом на bit.ly/18yhpb3, я придумал то, что кажется надежным для нескольких полей select2 в как одиночный выбор, так и множественный выбор на одной странице: (код перемещен в отдельный ответ для целей форматирования) - person Jeppe Liisberg; 10.03.2015

Вы можете использовать гем capybara-select-2, который сделает всю работу за вас из коробки. Просто передайте search: true в опции помощника, чтобы сделать вызов Ajax:

select2 'Marriage', from: 'Event', search: true
person Hirurg103    schedule 25.10.2018
comment
Мне удалось выполнить задание с помощью игры. Спасибо, что указали на это! - person Agung Setiawan; 23.04.2020
comment
отличное решение. Благодарю. - person Gerson Azevedo; 16.02.2021

Я смог решить эту проблему, используя 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
person sbeam    schedule 07.10.2012
comment
вот вспомогательный метод капибары, который делает это лучше и не привязан к конкретным меткам полей и элементам gist.github.com /3849340 - person sbeam; 07.10.2012
comment
Это должно работать, но я получаю сообщение об ошибке Элемент не доступен для клика в точке (296, 637). Другой элемент получит клик: ‹div id=select2-drop-mask class=select2-drop-mask style=›‹/div› - person lafeber; 16.09.2013
comment
Это не то, что вы хотите, у него сон 2 секунды. - person Jankeesvw; 18.11.2014
comment
да, используйте один из других ответов, это был взлом. Моим решением сейчас было бы не использовать Select2. - person sbeam; 21.03.2016
comment
это также хорошо работает со мной find('#select_field option:first-child').click - person Astm; 15.04.2020

Для нескольких компонентов 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
person lafeber    schedule 18.09.2013
comment
Это привело меня на правильный путь. Я усовершенствовал ваш метод select2, чтобы создать метод запроса и выбора общего назначения, который работает с Select2 3.4 gist.github .com/onyxrev/6970632 - person onyxrev; 14.10.2013

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

Как ни странно, я обнаружил, что 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>

Бесполезно помещать содержимое за пределы диапазона.

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

# 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

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

ХАМЛ используется:

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

js:

$("#some_id").select2();
person xxjjnn    schedule 21.11.2014
comment
Я должен использовать find('#s2id_#{id} a').click чтобы он работал - person gamov; 25.11.2014

Вдохновленный этим ответом и одним из помощника Capybara select2, я придумал то, что кажется надежным для нескольких полей 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
person Jeppe Liisberg    schedule 10.03.2015

Это работает для меня в раскрывающемся стиле 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.

person smoyth    schedule 30.11.2016

У меня есть два удаленных select2. Я ищу строку в первой, и в соответствии с найденным результатом заполняется вторая. Попробовав драгоценный камень 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
person Caner Çakmak    schedule 07.06.2014

https://github.com/goodwill/capybara-select2 с указанным js: true у меня работало нормально . Ваше здоровье

person AndreiMotinga    schedule 07.08.2017
comment
Я рекомендую перейти на более новый гем github.com/Hirurg103/capybara_select2, который поддерживает новейшие версии select2 gem install capybara-select-2 - person akaspick; 29.04.2020

Вот что работает для меня, даже с автозаполнением select2 AJAX:

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

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

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

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

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

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

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

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

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

person Bob Roberts    schedule 09.03.2014

Вот решение для более поздней версии 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.

person Brandon McInnis    schedule 22.03.2017

Потратив на это больше времени, чем хотелось бы, я, наконец, заработал, опираясь на ответ Брэндона Макинниса с помощью селектора 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
person Sean Szurko    schedule 30.04.2017

Для 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), чтобы это заработало.

person Matijs van Zuijlen    schedule 11.05.2017

Этот помощник работал у меня (используя заполнитель):

  # 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'
person Rimian    schedule 16.11.2020