ruby-on-rails-3 - mascota - chiguiro asado
Resolución de ambigüedad del capibara (8)
Debido a esta publicación , puede solucionarlo mediante la opción "coincidencia":
Capybara.configure do |config|
config.match = :prefer_exact
end
¿Cómo resuelvo la ambigüedad en Capibara? Por alguna razón, necesito enlaces con los mismos valores en una página pero no puedo crear una prueba ya que recibo el error
Failure/Error: click_link("#tag1")
Capybara::Ambiguous:
Ambiguous match, found 2 elements matching link "#tag1"
La razón por la que no puedo evitar esto es por el diseño. Estoy intentando recrear la página de Twitter con tweets / etiquetas a la derecha y las etiquetas a la izquierda de la página. Por lo tanto, será inevitable que la página de enlaces idénticos aparezca en la misma página.
La solución anterior funciona muy bien, pero para los curiosos también puede utilizar la siguiente sintaxis.
click_link(link_name, match: :first)
Puedes encontrar más información aquí:
Mi solución es
first(:link, link).click
en lugar de
click_link(link)
NUEVA RESPUESTA:
Puedes intentar algo como
all(''a'').select {|elt| elt.text == "#tag1" }.first.click
Puede haber una manera de hacer esto que haga un mejor uso de la sintaxis de Capybara disponible, algo parecido a all("a[text=''#tag1'']").first.click
pero no puedo pensar en el corregir la sintaxis y no puedo encontrar la documentación adecuada. Para empezar, dice que es una situación un tanto extraña, ya que tiene dos <a>
etiquetas con la misma id
, class
y texto. ¿Hay alguna posibilidad de que sean hijos de diferentes divs, ya que entonces podrías hacer tu find
within
del segmento apropiado del DOM? (Ayudaría ver un poco de su fuente HTML).
ANTIGUA RESPUESTA: (donde pensé que ''# tag1'' significaba que el elemento tenía una id
de "tag1")
¿En cuál de los enlaces quieres hacer clic? Si es el primero (o no importa), puede hacer
find(''#tag1'').click
De lo contrario, puedes hacer
all(''#tag1'')[1].click
hacer clic en el segundo.
Para agregar al cuerpo de conocimiento existente aquí:
Para las pruebas JS, Capybara tiene que mantener dos hilos (uno para RSpec, uno para Rails) y un segundo proceso (el navegador) en sincronización. Lo hace esperando (hasta el tiempo de espera máximo configurado) en la mayoría de los métodos de búsqueda de ganglios y de coincidencia.
El carpincho también tiene métodos que no esperan, principalmente Node#all
. Usarlos es como decirle a tus especificaciones que te gustaría que fallen de manera intermitente.
La respuesta aceptada sugiere page.first(''selector'')
. Esto es indeseable, al menos para las especificaciones JS, porque el Node#first
usa el Node#all
.
Dicho esto, el Node#first
esperará si configura Capybara de la siguiente manera:
# rails_helper.rb
Capybara.wait_on_first_by_default = true
Esta opción se agregó en Capybara 2.5.0 y es falsa por defecto.
Como dijo Andrei, deberías usar
find(''selector'', match: :first)
o cambie su selector. Cualquiera de los dos funcionará bien independientemente de la configuración o el controlador.
Para complicar aún más las cosas, en las versiones anteriores de Capybara (o con una opción de configuración habilitada), #find
ignorará la ambigüedad y simplemente devolverá el primer selector coincidente. Esto tampoco es bueno, ya que hace que sus especificaciones sean menos explícitas, lo que imagino es por qué ya no es el comportamiento predeterminado. Dejaré de lado los detalles porque ya se discutieron más arriba.
Más recursos:
Para evitar un error ambiguo en el pepino.
Solución 1
first("#tag1").click
Solución 2
Cucumber features/filename.feature --guess
Puedes asegurarte de que encuentres el primero usando match
:
find(''.selector'', match: :first).click
Pero lo más importante es que probablemente no desee hacer esto , ya que dará lugar a pruebas frágiles que ignoran el olor del código de salida duplicada, lo que a su vez conduce a falsos positivos que siguen funcionando cuando deberían haber fallado, porque eliminó uno elemento pero la prueba felizmente encontró el otro.
La mejor apuesta es usar within
:
within(''#sidebar'') do
find(''.selector).click
end
Esto asegura que está encontrando el elemento que espera encontrar, mientras aprovecha las capacidades de espera automática y reintento automático de Capybara (que pierde si utiliza find(''.selector'').click
), y lo hace mucho más claro cuál es la intención.
Tal comportamiento de Capybara es intencional y creo que no debería ser corregido como se sugiere en la mayoría de las otras respuestas.
Las versiones de Capybara antes de 2.0 devolvieron el primer elemento en lugar de aumentar la excepción, pero luego los mantenedores de Capybara decidieron que era una mala idea y que es mejor plantearla. Se decidió que, en muchas situaciones, devolver el primer elemento conduce a no devolver el elemento que el desarrollador quería devolver.
La respuesta más votada aquí recomienda usar first
o all
lugar de find
pero:
-
all
yfirst
no espere hasta que el elemento con dicho localizador aparezca en la página, aunquefind
no espera -
all(...).first
yfirst
no lo protegerán de la situación que en el futuro, otro elemento con dicho localizador puede aparecer en la página y, como resultado, puede encontrar un elemento incorrecto
Por lo tanto, se recomienda elegir otro localizador menos ambiguo : por ejemplo, seleccionar elemento por id, clase u otro localizador css / xpath para que solo un elemento coincida.
Como nota aquí hay algunos localizadores que generalmente considero útiles para resolver la ambigüedad:
find(''ul > li:first-child'')
Es más útil que el
first(''ul > li'')
ya que esperará hasta que aparezca el primerli
en la página.click_link(''Create Account'', match: :first)
Es mejor que el
first(:link, ''Create Account'').click
enfirst(:link, ''Create Account'').click
ya que esperará hasta que aparezca al menos un enlace Crear cuenta en la página. Sin embargo, creo que es mejor elegir un localizador único que no aparezca en la página dos veces.fill_in(''Password'', with: ''secret'', exact: true)
exact: true
le dice a Capybara que busque solo coincidencias exactas, es decir, que no encuentre la "Confirmación de contraseña"