Comment sélectionner le nœud spécifié dans les ensembles de nœuds Xpath par index avec Selenium?

91

J'écris un testcase Selenium. Et voici l'expression xpath que j'utilise pour faire correspondre tous les boutons «Modifier» dans une table de données.

//img[@title='Modify']

Ma question est la suivante: comment puis-je visiter les ensembles de nœuds correspondants par index? J'ai essayé avec

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

et

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

Mais ni l'un ni l'autre ne fonctionne .. J'ai aussi essayé avec XPath checker (une extension firefox). Il y a au total 13 correspondances trouvées, alors je n'ai aucune idée de comment je vais en sélectionner une. Ou est-ce que XPath prend en charge la sélection spécifiée de nœuds qui ne sont pas sous le même nœud parent?

Kymair Wu
la source

Réponses:

191

Ceci est une FAQ :

//someName[3]

signifie : tous les someNameéléments du document, qui sont le troisième someNameenfant de leur parent - il peut y en avoir beaucoup.

Ce que vous voulez, c'est exactement le 3ème someNameélément :

(//someName)[3]

Explication : le []a une priorité (priorité) plus élevée que //. N'oubliez pas de toujours mettre les expressions du type //someNameentre crochets lorsque vous devez spécifier le Nième nœud de leur liste de nœuds sélectionnée.

Dimitre Novatchev
la source
1
Merci beaucoup! Désolé j'ai totalement oublié les choses précédentes .. J'ai juste essayé et ça marche!
Kymair Wu
1
@ Kymair-Wu: Je suis heureux que cette réponse vous ait été utile. Ici, à SO, la manière d'exprimer sa gratitude est d'accepter une réponse (indice: cliquez sur la coche à côté de la réponse). :)
Dimitre Novatchev
@DimitreNovatchev vous obtenez des points pour la même question encore et encore: p, merci pour la FAQ.
Eytoss
2
@Eytoss, vous êtes les bienvenus. Et oui, j'obtiens le plus de +1 pour des réponses relativement simples - pas pour les réponses qui, à mon avis, sont mes plus grandes réalisations - probablement parce que tout le monde comprend la première et presque personne ne comprend la seconde :)
Dimitre Novatchev
2
@TEHEMPRAH, En fait, j'ai vu que dans la réponse je n'ai pas dit "le 3ème enfant 'someName' de son parent". Merci d'avoir remarqué cela. Corrigé maintenant.
Dimitre Novatchev
14

Il n'y a pas idans XPath.

Soit vous utilisez des nombres littéraux: //img[@title='Modify'][1]

Ou vous créez la chaîne d'expression dynamiquement: '//img[@title='Modify']['+i+']'(mais gardez à l'esprit que les expressions XPath dynamiques ne fonctionnent pas à partir de XSLT).

Ou XPath prend-il en charge la sélection spécifiée de nœuds qui ne sont pas sous le même nœud parent?

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


Cela //img[@title='Modify'][i]signifie "tout <img>avec un titre de" Modifier "et un élément enfant nommé <i>."

Tomalak
la source
Pour une raison quelconque, j'avais besoin d'inclure l'index avant l'expression d'attribut. Par exemple, pour trouver des tds qui étaient le sixième enfant de a tret qui n'ont pas de contenu vide://tr/td[6][string-length(text()) > 0]
Samir Aguiar
1
@kopranb Pour une explication, voir cette réponse stackoverflow.com/a/1006439/18771
Tomalak
Merci d'avoir expliqué "// img [@ title = 'Modify'] ['+ i +'] '(+1)
DebanjanB
2
//img[@title='Modify'][i]

est l'abréviation de

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

par conséquent renvoie le i'ème nœud sous le même nœud parent.

Tu veux

/descendant-or-self::img[@title='Modify'][i]
Nick Jones
la source
1
Cela /descendant::img[@title='Modify'][$index]fonctionnera très bien. Notez également que le [i]prédicat teste l'existence de il'élément enfant.
2

Il n'y a pas idans xpath n'est pas entièrement vrai. Vous pouvez toujours utiliser le count()pour trouver l'index.

Considérez la page suivante

<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 page comporte 2 tables et 6 colonnes chacune avec des noms de colonne uniques et 6 lignes avec des données variables. La dernière ligne a le Modifybouton dans les deux tableaux.

En supposant que l'utilisateur doit sélectionner le 4e Modifybouton du premier tableau en fonction de l'en-tête

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

L' count()opérateur est pratique dans des situations comme celles-ci.

Logique:

  1. Recherchez l'en-tête du Modifybouton à l'aide de//th[.='Heading 4']
  2. Recherchez l'index de la colonne d'en-tête à l'aide de count(//tr/th[.='Heading 4']/preceding-sibling::th)+1

Remarque: l' index commence à0

  1. Obtenez les lignes de l'en-tête correspondant à l'aide de //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]

  2. Obtenez le Modifybouton de la liste de nœuds extraite en utilisant//th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button

Eric Stanley
la source
1

(// * [@ attribute = 'value']) [index] pour trouver la cible de l'élément pendant que vous y trouvez plusieurs correspondances

mahesh
la source
1
Pouvez-vous expliquer un peu plus?
abhiarora
0

Voici la solution pour la variable d'index

Disons que vous avez trouvé 5 éléments avec le même localisateur et que vous souhaitez effectuer une action sur chaque élément en fournissant un numéro d'index (ici, la variable est utilisée pour l'index comme "i")

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

Il faut XPath:

(//div[@class='className'])[1]
(//div[@class='className'])[2]
(//div[@class='className'])[3]
(//div[@class='className'])[4]
(//div[@class='className'])[5]
Srinivas Kassa
la source