¿Cómo obtener el nodo principal en Capybara?

85

Estoy trabajando con muchos complementos de jQuery, que a menudo crean elementos DOM sin id u otras propiedades de identificación, y la única forma de obtenerlos en Capybara (por ejemplo, para hacer clic) es obtener primero a su vecino (otro hijo de su antepasado) . Pero no encontré en ninguna parte, ¿Capybara admite tales cosas, por ejemplo:

find('#some_button').parent.fill_in "Name:", :with => name

?

Sandrew
fuente
También será muy útil para mí, si me dices, ¿Capybara genera clics en elementos con {display: hidden}, y hay alguna manera de encontrar elementos en algún ámbito, donde display! = Hidden?
Sandrew
Esta es una pregunta separada, pero depende del controlador que esté utilizando. Webrat encontrará cosas ocultas felizmente, pero selenium no está tan feliz de hacer clic en elementos que usted no puede ver.
jamuraa

Respuestas:

111

Realmente encontré útil la respuesta de Jamuraa, pero ir a xpath completo me dio una pesadilla de cadena en mi caso, así que felizmente hice uso de la capacidad de concatenar find's en Capybara, lo que me permitió mezclar la selección de CSS y XPath. Su ejemplo se vería así:

find('#some_button').find(:xpath,".//..").fill_in "Name:", :with => name

Actualización de Capybara 2.0 : find(:xpath,".//..")lo más probable es que produzca un Ambiguous matcherror. En ese caso, utilice first(:xpath,".//..")en su lugar.

Pascal Lindelauf
fuente
Gracias por esa idea, ¡nunca hubiera pensado en eso! Estaba haciendo el.parent para un elemento td que encontré con find (: css), pero por alguna razón que no entiendo, el.parent estaba devolviendo # <Capybara :: Document>. el.find (: xpath, ".// ..") por otro lado devuelve # <Capybara :: Element tag = "tr">, que es lo que necesitaba.
Tyler Rick
Otra forma de encontrar de forma recursiva un determinado nodo principal es con el selector de antepasado o propio de xpath. Échale un vistazo a stackoverflow.com/questions/1362466/…
vrish88
25
No es necesario .//..encontrar al padre, solo es ..suficiente, y eso tampoco es ambiguo.
Eamon Nerbonne
ty! Sé que este comentario llega un poco tarde a la fiesta, pero lo condensé en lo siguiente después de leer la edición y el comentario de Eamon: el.first (: xpath, '..')
aschyiel
39

No hay forma de hacer esto con capibara y CSS. Sin embargo, he usado XPath en el pasado para lograr este objetivo, que tiene una forma de obtener el elemento principal y es compatible con Capybara:

find(:xpath, '//*[@id="some_button"]/..').fill_in "Name:", :with => name
Jamuraa
fuente
2
Cuando hago una búsqueda similar de xpath, aparece un error de Nokogiri que dice que la expresión no es válida. Agregué un asterisco frente al corchete abierto en la expresión para corregir la expresión:find(:xpath, '//*[@id="some_button"]/..')
Andrew Ferk
35

Encontré lo siguiente que funciona:

find(:xpath, '..')

Capybara se ha actualizado para admitir esto.

https://github.com/jnicklas/capybara/pull/505

B siete
fuente
1
Me parece que está respondiendo / completando una respuesta anterior aquí ("¿Esto" qué "no estaba funcionando"?). Sería mejor si fuera una respuesta completa e independiente. Así que te recomiendo que lo edites. ;-)
helenov
Puede confirmar. Con la última versión de Capybara 2.14.4, esta es la forma correcta de obtener el nodo principal.
fny
10

Si se encontró con esto tratando de averiguar cómo encontrar cualquier nodo principal (como en el ancestro ) (como se indica en el comentario de @ vrish88 sobre la respuesta de @Pascal Lindelauf):

find('#some_button').find(:xpath, 'ancestor::div[@id="some_div_id"]')
Ben Alavi
fuente
5

Esta respuesta se refiere a cómo manipular un elemento hermano, que es a lo que creo que alude la pregunta original

La hipótesis de tu pregunta funciona con un pequeño ajuste. Si el campo generado dinámicamente se ve así y no tiene una identificación:

<div>
  <input></input>
  <button>Test</button>
</div>

Su consulta sería entonces:

find('button', text: 'Test').find(:xpath, "..").find('input').set('whatever')

Si la entrada generada dinámicamente viene adjunta con un elemento de identificación (tenga cuidado con estos, aunque como en angular, suelen cambiar en función de agregar y eliminar elementos) sería algo como esto:

find('button', text: 'Test').find(:xpath, "..").fill_in('#input_1', with: 'whatever')

Espero que ayude.

T. Slater
fuente
4

Estoy usando un enfoque diferente al encontrar el elemento principal primero usando el texto dentro de este elemento principal:

find("<parent element>", text: "text within your #some_button").fill_in "Name:", with: name

Quizás esto sea útil en una situación similar.

Harm de Wit
fuente
1
Esta es una gran opción. El alcance de las acciones de la interfaz de usuario en una parte de la página que contiene un texto en particular es un caso de uso común para mí.
clemensp
2

Necesitaba encontrar un ancestro con una clase css, aunque era indeterminado si el ancestro objetivo tenía una o más clases css presentes, por lo que no vi una forma de hacer una consulta xpath determinista. Trabajé esto en su lugar:

def find_ancestor_with_class(field, cssClass)
  ancestor = field
  loop do
    ancestor = ancestor.find(:xpath, '..')
    break if ancestor.nil?

    break if ancestor.has_css? cssClass
  end

  ancestor
end

Advertencia: use esto con moderación, podría costarle mucho tiempo en las pruebas, así que asegúrese de que el antepasado esté a solo unos saltos de distancia.

kross
fuente
Aceleración: reemplace su segundo descanso con break if ancestor[:class].split(" ").include?(css_class).
hakunin
@hakunin Tengo curiosidad por saber cuánto de aceleración estás viendo? ¿Significativo?
kross
No se puede probar ahora, pero has_css pareció tardar un segundo, mientras que coincidir con la clase parecía inmediato.
hakunin
Carpincho ahora tiene un ancestor(selector)método incorporado. Ver github.com/teamcapybara/capybara/commit/…
Tyler Rick