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
https://github.com/goodwill/capybara-select2 with js: true
especificado funcionó bien para mí. Aclamaciones
def select2(id, value)
find("##{id} ~ span.select2").click
within ".select2-results" do
find("li", text: value).click
end
end