ruby-on-rails capybara capybara-webkit jquery-select2

ruby on rails - ¿Cómo probar un elemento Select2 con capybara DSL?



ruby-on-rails capybara-webkit (17)

Tengo un elemento Select2 en una página que carga resultados a través de ajax. Me gustaría probar esto con capybara / rspec (usando el controlador poltergeist), pero dado que el elemento Select2 realmente comienza como un campo oculto, y luego llena un <ul> cuando se procesan los resultados, ninguno de los select normales, fill_in o choose Los ayudantes trabajarán.

Lo que tengo ahora es algo como

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

En la línea 4 anterior, al parecer, Capybara puede encontrar el elemento, pero el valor no se modifica y Select2 nunca realiza su llamada ajax. (No se puede usar fill_in normal allí porque el elemento del campo de búsqueda no tiene atributos de etiqueta, ID o nombre)


Aquí hay una solución para una versión más reciente de select2, específicamente la gema select2-rails (4.0.0):

Colóquelo en features/support/feature_helpers.rb , e spec_helper.rb en su archivo spec_helper.rb con config.include FeatureHelpers en su RSpec.configure do |config| bloquear.

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

Luego use fill_in_select2 tal como lo haría con fill_in en Capybara, pero incluya la clase o el id de un elemento contenedor con el símbolo de prefijo apropiado. (por ejemplo, fill_in_select2 ''.actions_list'', with: ''[email protected]'' ).

Nota : esto selecciona el primer elemento en el menú desplegable después de ingresar el texto.

Probado en Rails 4.2.5.2, Capybara 2.7.1 y Capybara-Webkit 1.11.1


Creé un ayudante que puedes usar con Cucumber, no tiene sleep por lo que es más rápido que los métodos anteriores. Esto también funciona si utiliza el createSearchChoice proporcionado por Select2.

Póngalo en 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)

Úsalo así:

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

Probado con rieles 4.1.1 y pepino 1.3.15.


Deja de rascarte la cabeza, haz esto y funcionará.

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

Probé cada módulo y cada maldita cosa que pude y, finalmente, lo hice sin ningún tipo de ayuda y cosas por una simple línea de código.


Después de gastar más tiempo en esto de lo que me gustaría, finalmente lo puse funcionando basándose en la respuesta de Brandon McInnis usando un selector 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


Esto es lo que funciona para mí, incluso con el autocompletado AJAX de select2:

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


Esto me funciona en un estilo desplegable select2 con una fuente de datos remota:

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

from debe ser el ID de la etiqueta de selección normal de la que se crea select2.


Inspirado en esta respuesta y uno en Capybara select2 helper, se me ocurrió lo que parece sólido para múltiples campos select2 tanto de selección única como de selección múltiple en la misma página:

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


Oye, no estoy seguro de que a nadie le importe, pero también tuve este problema y encontré una forma fácil de resolverlo.

En primer lugar, no agrego la clase a cada selección desplegable, sino que lo hago globalmente de esta manera:

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

Entonces, cuando comencé a tener problemas con Capybara al no poder usar el menú desplegable de Select2, mi solución consistía en usar solo el impulso global si no estaba en un entorno de prueba:

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

Solía ​​usar la gema capybara-select2 pero parece que ya no se mantiene :(


Para aquellos que luchan con Select2 múltiple :

Curiosamente, encontré que select_tag with multiple: true parece estar interesado en generar html para cada entrada como:

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

más bien que

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

Inútilmente poner los contenidos fuera del lapso.

Para probarlo con capibara tengo dos métodos:

# 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

Este último es demasiado intrincado para mi gusto, por lo que uso el primero siempre que sea posible y espero reemplazar este último eventualmente.

HAML utilizado:

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

js

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


Para select2 4.0 en combinación con capybara-webkit, probé las soluciones basadas en execute_script pero no funcionaron porque capybara no pudo encontrar el elemento ''li'' resultante. Finalmente me decidí por esta solución simple basada en send_keys. Debido a que uso una fuente de datos remota, agregué una pequeña suspensión para permitir que aparezcan los resultados:

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

Tenga en cuenta que es posible que deba actualizar a la última versión de capybara-webkit (1.14.0) para que esto funcione.


Para varios componentes select2 en su página, he creado los siguientes métodos de ayuda que funcionan para mí:

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


Pude resolver esto utilizando page.execute_script para hackear manualmente el valor necesario y el evento keyup en el campo de búsqueda.

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


Puede usar la gema capybara-select-2, que hará todo el trabajo por usted nuestra caja. Simplemente pase la search: true a las opciones de ayuda para realizar una llamada Ajax:

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


Respuesta simple:

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


Tengo dos select2s remotos. Busco una cadena en la primera y de acuerdo con el resultado encontrado, la segunda se llena. Después de probar capybara-select2 gema y todas las soluciones enumeradas aquí y allá, tuve que encontrar mi propia solución porque ninguna de ellas funcionaba para buscar. En mi caso, lo que selecciono no es importante, por lo que no intenté seleccionar una etiqueta de resultado con un valor específico. Espero que esto ayude a alguien en mi situación.

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



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