¿Cómo seleccionar el nodo especificado dentro de los conjuntos de nodos de Xpath por índice con Selenium?


91

Estoy escribiendo un caso de prueba de Selenium. Y aquí está la expresión xpath que uso para hacer coincidir todos los botones 'Modificar' dentro de una tabla de datos.

//img[@title='Modify']

Mi pregunta es, ¿cómo puedo visitar los conjuntos de nodos coincidentes por índice? He probado con

//img[@title='Modify'][i]

y

//img[@title='Modify' and position() = i]

Pero ninguno de los dos funciona ... También probé con XPath Checker (una extensión de Firefox). Se han encontrado 13 coincidencias en total, entonces no tengo ni idea de cómo voy a seleccionar una de ellas. ¿ O XPath admite la selección específica de nodos que no están bajo el mismo nodo principal?

Respuestas:


191

Esta es una pregunta frecuente :

//someName[3]

significa : todos los someNameelementos del documento, que son el tercer someNamehijo de su padre; puede haber muchos de estos elementos.

Lo que quieres es exactamente el tercer someNameelemento :

(//someName)[3]

Explicación : []tiene mayor precedencia (prioridad) que //. Recuerde siempre poner las expresiones del tipo //someNameentre paréntesis cuando necesite especificar el nodo N de su lista de nodos seleccionada.


1
¡Muchas gracias! Lo siento, olvidé totalmente las cosas de precedencia ... ¡Lo intenté y funciona!
Kymair Wu

1
@ Kymair-Wu: Me alegro de que esta respuesta te haya sido útil. Aquí en SO la forma de expresar gratitud es aceptando una respuesta (pista: haga clic en la marca de verificación junto a la respuesta). :)
Dimitre Novatchev

@DimitreNovatchev, obtiene puntos por la misma pregunta una y otra vez: p, gracias por las preguntas frecuentes.
Eytoss

2
@Eytoss, de nada. Y sí, obtengo la mayoría de +1 por respuestas relativamente simples, no por las respuestas que creo que son mis mayores logros, probablemente porque todos entienden lo primero y casi nadie entiende lo segundo :)
Dimitre Novatchev

2
@TEHEMPRAH, En realidad vi que en la respuesta no dije "el tercer hijo 'someName' de su padre". Gracias por notar esto. Corregido ahora.
Dimitre Novatchev

14

No hay i en XPath.

O usa números literales: //img[@title='Modify'][1]

O se genera la cadena de expresión de forma dinámica: '//img[@title='Modify']['+i+']'(pero tenga en cuenta que las expresiones XPath dinámicas no funcionan desde dentro XSLT).

¿O XPath admite la selección especificada de nodos que no están bajo el mismo nodo principal?

Si: (//img[@title='Modify'])[13]


Esto //img[@title='Modify'][i]significa "cualquiera <img>con un título de 'Modificar' y un elemento secundario llamado <i>".


Por alguna razón, necesitaba incluir el índice antes de la expresión de atributo. Por ejemplo, para encontrar tds que eran el sexto hijo de a try no tienen contenido vacío://tr/td[6][string-length(text()) > 0]
Samir Aguiar

1
@kopranb Para obtener una explicación, consulte esta respuesta stackoverflow.com/a/1006439/18771
Tomalak

Gracias por explicarnos sobre '// img [@ title =' Modificar '] [' + i + ']' (+1)
DebanjanB

2
//img[@title='Modify'][i]

es la abreviatura de

/descendant-or-self::node()/img[@title='Modify'][i]

por lo tanto, devuelve el i-ésimo nodo bajo el mismo nodo principal.

Usted quiere

/descendant-or-self::img[@title='Modify'][i]

1
Simplemente /descendant::img[@title='Modify'][$index]funcionará bien. También tenga en cuenta esa [i]prueba de predicado para la existencia de iun elemento hijo.

2

No hay ien xpath no es del todo cierto. Todavía puede usar count()para encontrar el índice.

Considere la siguiente página

<html>

	<head>
		<title>HTML Sample table</title>
	</head>

	<style>
	table, td, th {
		border: 1px solid black;
		font-size: 15px;
		font-family: Trebuchet MS, sans-serif;
	}
	table {
		border-collapse: collapse;
		width: 100%;
	}

	th, td {
		text-align: left;
		padding: 8px;
	}

	tr:nth-child(even){background-color: #f2f2f2}

	th {
		background-color: #4CAF50;
		color: white;
	}
	</style>

	<body>
	<table>
		<thead>
			<tr>
				<th>Heading 1</th>
				<th>Heading 2</th>
				<th>Heading 3</th>
				<th>Heading 4</th>
				<th>Heading 5</th>
				<th>Heading 6</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>Data row 1 col 1</td>
				<td>Data row 1 col 2</td>
				<td>Data row 1 col 3</td>
				<td>Data row 1 col 4</td>
				<td>Data row 1 col 5</td>
				<td>Data row 1 col 6</td>
			</tr>
			<tr>
				<td>Data row 2 col 1</td>
				<td>Data row 2 col 2</td>
				<td>Data row 2 col 3</td>
				<td>Data row 2 col 4</td>
				<td>Data row 2 col 5</td>
				<td>Data row 2 col 6</td>
			</tr>
			<tr>
				<td>Data row 3 col 1</td>
				<td>Data row 3 col 2</td>
				<td>Data row 3 col 3</td>
				<td>Data row 3 col 4</td>
				<td>Data row 3 col 5</td>
				<td>Data row 3 col 6</td>
			</tr>
			<tr>
				<td>Data row 4 col 1</td>
				<td>Data row 4 col 2</td>
				<td>Data row 4 col 3</td>
				<td>Data row 4 col 4</td>
				<td>Data row 4 col 5</td>
				<td>Data row 4 col 6</td>
			</tr>
			<tr>
				<td>Data row 5 col 1</td>
				<td>Data row 5 col 2</td>
				<td>Data row 5 col 3</td>
				<td>Data row 5 col 4</td>
				<td>Data row 5 col 5</td>
				<td>Data row 5 col 6</td>
			</tr>
			<tr>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
			</tr>
		</tbody>
	</table>

	</br>

	<table>
		<thead>
			<tr>
				<th>Heading 7</th>
				<th>Heading 8</th>
				<th>Heading 9</th>
				<th>Heading 10</th>
				<th>Heading 11</th>
				<th>Heading 12</th>
			</tr>
		</thead>
		<tbody>
			<tr>
				<td>Data row 1 col 1</td>
				<td>Data row 1 col 2</td>
				<td>Data row 1 col 3</td>
				<td>Data row 1 col 4</td>
				<td>Data row 1 col 5</td>
				<td>Data row 1 col 6</td>
			</tr>
			<tr>
				<td>Data row 2 col 1</td>
				<td>Data row 2 col 2</td>
				<td>Data row 2 col 3</td>
				<td>Data row 2 col 4</td>
				<td>Data row 2 col 5</td>
				<td>Data row 2 col 6</td>
			</tr>
			<tr>
				<td>Data row 3 col 1</td>
				<td>Data row 3 col 2</td>
				<td>Data row 3 col 3</td>
				<td>Data row 3 col 4</td>
				<td>Data row 3 col 5</td>
				<td>Data row 3 col 6</td>
			</tr>
			<tr>
				<td>Data row 4 col 1</td>
				<td>Data row 4 col 2</td>
				<td>Data row 4 col 3</td>
				<td>Data row 4 col 4</td>
				<td>Data row 4 col 5</td>
				<td>Data row 4 col 6</td>
			</tr>
			<tr>
				<td>Data row 5 col 1</td>
				<td>Data row 5 col 2</td>
				<td>Data row 5 col 3</td>
				<td>Data row 5 col 4</td>
				<td>Data row 5 col 5</td>
				<td>Data row 5 col 6</td>
			</tr>
			<tr>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
				<td><button>Modify</button></td>
			</tr>
		</tbody>
	</table>

	</body>
</html>

La página tiene 2 tablas y 6 columnas cada una con nombres de columna únicos y 6 filas con datos variables. La última fila tiene el Modifybotón en ambas tablas.

Suponiendo que el usuario tiene que seleccionar el cuarto Modifybotón de la primera tabla según el encabezado

Usa el xpath //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button

El count()operador resulta útil en situaciones como estas.

Lógica:

  1. Busque el encabezado del Modifybotón usando//th[.='Heading 4']
  2. Encuentre el índice de la columna de encabezado usando count(//tr/th[.='Heading 4']/preceding-sibling::th)+1

Nota: el índice comienza en0

  1. Obtenga las filas para el encabezado correspondiente usando //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]

  2. Obtenga el Modifybotón de la lista de nodos extraídos usando//th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button


1

(// * [@ atributo = 'valor']) [índice] para encontrar el objetivo del elemento mientras encuentra múltiples coincidencias en él


1
¿Puedes explicar un poco más?
abhiarora

0

Aquí está la solución para la variable de índice

Digamos que ha encontrado 5 elementos con el mismo localizador y le gustaría realizar una acción en cada elemento proporcionando un número de índice (aquí, la variable se usa para el índice como "i")

for(int i=1; i<=5; i++)
{
    string xPathWithVariable = "(//div[@class='className'])" + "[" + i + "]";
    driver.FindElement(By.XPath(xPathWithVariable)).Click();
}

Se necesita XPath:

(//div[@class='className'])[1]
(//div[@class='className'])[2]
(//div[@class='className'])[3]
(//div[@class='className'])[4]
(//div[@class='className'])[5]
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.