¿Cómo resuelvo la ambigüedad en Capybara? Por alguna razón, necesito enlaces con los mismos valores en una página, pero no puedo crear una prueba porque obtengo 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 aparezcan páginas de enlaces idénticas en la misma página.
ruby-on-rails-3
rspec
capybara
neilmarion
fuente
fuente
Respuestas:
Mi solucion es
en vez de
fuente
first
como se sugirió anteriormente, a menos que sepa absolutamente lo que está haciendo, es probable que dé como resultado especificaciones que se le pasan pero fallan en una compilación de CI o en la máquina de un colega.Tal comportamiento de Capybara es intencional y creo que no debería arreglarse como se sugiere en la mayoría de las otras respuestas.
Las versiones de Capybara anteriores a 2.0 devolvieron el primer elemento en lugar de generar una excepción, pero los mantenedores posteriores de Capybara decidieron que es una mala idea y es mejor aumentarla. Se decidió que en muchas situaciones devolver el primer elemento conduce a devolver no el elemento que el desarrollador quería que se devolviera.
La respuesta más votada aquí recomienda usar
first
o enall
lugar defind
pero:all
yfirst
no espere hasta que el elemento con dicho localizador aparezca en la página, aunquefind
esperaall(...).first
yfirst
no lo protegerá de una situación en la que en el futuro pueda aparecer otro elemento con dicho localizador en la página y, como resultado, puede encontrar un elemento incorrectoPor lo tanto, se recomienda elegir otro localizador menos ambiguo : por ejemplo, seleccione el elemento por id, clase u otro localizador css / xpath para que solo un elemento coincida con él.
Como nota, aquí hay algunos localizadores que generalmente considero útiles a la hora de resolver ambigüedades:
find('ul > li:first-child')
Es más útil que
first('ul > li')
esperar hastali
que aparezca primero en la página.click_link('Create Account', match: :first)
Es mejor que
first(:link, 'Create Account').click
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 dos veces en la página.fill_in('Password', with: 'secret', exact: true)
exact: true
le dice a Capybara que busque solo coincidencias exactas, es decir, que no encuentre "Confirmación de contraseña"fuente
La solución anterior funciona muy bien, pero para los curiosos también puede usar la siguiente sintaxis.
Puedes encontrar más información aquí:
http://taimoorchangaizpucitian.wordpress.com/2013/09/06/capybara-click-link-different-cases-and-solutions/
fuente
NUEVA RESPUESTA:
Puedes probar algo como
Puede haber una forma 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 la sintaxis correcta y no puedo encontrar la documentación adecuada. Dicho esto, es un poco de una situación extraña para empezar, que tiene dos<a>
etiquetas con el mismoid
,class
y texto. ¿Existe alguna posibilidad de que sean hijos de diferentes divs, ya que luego podría hacer sufind
within
segmento apropiado del DOM? (Sería útil ver un poco de su fuente HTML).ANTIGUA RESPUESTA: (donde pensé que '# etiqueta1' significaba que el elemento tenía una
id
"etiqueta1")¿En cuál de los enlaces quieres hacer clic? Si es el primero (o no importa), puede hacer
De lo contrario, puedes hacer
para hacer clic en el segundo.
fuente
find('#tag1')
significa que desea encontrar solo un elemento con idtag1
. Se plantea una excepción ya que hay varios elementos con identificacióntag1
en la páginaall(:xpath, '//a[text()="#tag1"]').first.click
.Puede asegurarse de encontrar el primero utilizando
match
:Pero lo que es más importante, probablemente no desee hacer esto , ya que conducirá a pruebas frágiles que ignoran el olor del código de salida duplicado, lo que a su vez conduce a falsos positivos que siguen funcionando cuando deberían haber fallado, porque eliminó una coincidencia elemento, pero la prueba felizmente encontró el otro.
La mejor opción es usar
within
:Esto asegura que está encontrando el elemento que espera encontrar, mientras sigue aprovechando las capacidades de espera automática y reintento automático de Capybara (que pierde si usa
find('.selector').click
), y deja mucho más claro cuál es la intención.fuente
Para agregar al cuerpo de conocimiento existente aquí:
Para las pruebas de JS, Capybara tiene que mantener dos hilos (uno para RSpec, otro para Rails) y un segundo proceso (el navegador) sincronizados. Lo hace esperando (hasta el tiempo de espera máximo configurado) en la mayoría de los métodos de búsqueda de nodos y comparadores.
Carpincho también tiene métodos que no esperan, principalmente
Node#all
. Usarlos es como decirle a sus especificaciones que le gustaría que fallaran de forma intermitente.La respuesta aceptada sugiere
page.first('selector')
. Esto no es deseable, al menos para las especificaciones de JS, porqueNode#first
usesNode#all
.Dicho esto,
Node#first
se espera si configura capibara, así:Esta opción se agregó en Capybara 2.5.0 y es falsa por defecto.
Como mencionó Andrei, deberías usar
o cambia tu selector. Cualquiera funcionará bien independientemente de la configuración o el controlador.
Para complicar aún más las cosas, en versiones antiguas de Capybara (o con una opción de configuración habilitada),
#find
felizmente ignorará la ambigüedad y simplemente devolverá el primer selector coincidente. Esto tampoco es genial, ya que hace que sus especificaciones sean menos explícitas, lo que imagino que es la razón por la que ya no es el comportamiento predeterminado. Dejaré de lado los detalles porque ya se han discutido anteriormente.Más recursos:
fuente
Debido a esta publicación , puede solucionarlo a través de la opción "coincidir":
fuente
Teniendo en cuenta todas las opciones anteriores, también puede probar esto
Si está usando pepino, puede seguir esto también
Puede pasar el texto como parámetro de los pasos del escenario, que puede ser un paso genérico para reutilizarlo nuevamente.
Algo como
When a user clicks on "text" link
Y en paso definición
When(/^(?:user) clicks on "([^"]*)" (?:link)$/) do |text|
De esta manera, puede reutilizar el mismo paso minimizando las líneas de código y sería fácil escribir nuevos escenarios de pepino.
fuente
Para evitar errores ambiguos en pepino.
Solución 1
Solucion 2
fuente