Como selecionar o nó especificado dentro dos conjuntos de nós Xpath por índice com Selenium?

91

Estou escrevendo um caso de teste do Selenium. E aqui está a expressão xpath que uso para corresponder a todos os botões 'Modify' em uma tabela de dados.

//img[@title='Modify']

Minha pergunta é: como posso visitar os conjuntos de nós correspondentes por índice? Eu tentei com

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

e

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

Mas nem funciona .. Eu também tentei com o XPath checker (uma extensão do firefox). Há no total 13 correspondências encontradas, então não tenho a menor idéia de como vou selecionar uma delas. Ou o XPath oferece suporte à seleção especificada de nós que não estão no mesmo nó pai?

Kymair Wu
fonte

Respostas:

191

Este é um FAQ :

//someName[3]

significa : todos os someNameelementos no documento, que são o terceiro someNamefilho de seu pai - pode haver muitos desses elementos.

O que você quer é exatamente o terceiro someNameelemento :

(//someName)[3]

Explicação : o []tem uma precedência (prioridade) maior do que //. Lembre-se sempre de colocar as expressões do tipo //someNameentre colchetes quando precisar especificar o enésimo nó de sua lista de nós selecionada.

Dimitre Novatchev
fonte
1
Muito obrigado! Desculpe, esqueci totalmente as coisas de precedência .. Acabei de experimentar e funciona!
Kymair Wu
1
@ Kymair-Wu: Fico feliz que essa resposta tenha sido útil para você. Aqui no SO a forma de agradecer é aceitando uma resposta (dica: clique na marca de seleção ao lado da resposta). :)
Dimitre Novatchev
@DimitreNovatchev você está recebendo pontos pela mesma pergunta repetidamente: p, obrigado pelo FAQ.
Eytoss
2
@Eytoss, de nada. E sim, estou recebendo mais + 1s para respostas relativamente simples - não para as respostas que acredito serem minhas maiores conquistas - provavelmente porque todo mundo entende a primeira e quase ninguém entende a última :)
Dimitre Novatchev
2
@TEHEMPRAH, Na verdade eu vi que na resposta não disse "o terceiro 'someName' filho de seu pai". Obrigado por perceber isso. Corrigido agora.
Dimitre Novatchev,
14

Não existe ino XPath.

Ou você usa números literais: //img[@title='Modify'][1]

Ou você constrói a string de expressão dinamicamente: '//img[@title='Modify']['+i+']'(mas lembre-se de que as expressões XPath dinâmicas não funcionam dentro do XSLT).

Ou o XPath oferece suporte à seleção especificada de nós que não estão no mesmo nó pai?

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


Isso //img[@title='Modify'][i]significa "qualquer <img>um com o título 'Modificar' e um elemento filho chamado <i>".

Tomalak
fonte
Por algum motivo, precisei incluir o índice antes da expressão do atributo. Por exemplo, para encontrar tds que foram o sexto filho de a tre não têm conteúdo vazio://tr/td[6][string-length(text()) > 0]
Samir Aguiar
1
@kopranb Para obter uma explicação, consulte esta resposta stackoverflow.com/a/1006439/18771
Tomalak
Obrigado por explicar sobre '// img [@ title =' Modify '] [' + i + ']' (+1)
DebanjanB
2
//img[@title='Modify'][i]

é curto para

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

portanto, está retornando o i'ésimo nó sob o mesmo nó pai.

Você quer

/descendant-or-self::img[@title='Modify'][i]
Nick Jones
fonte
1
/descendant::img[@title='Modify'][$index]vai funcionar bem. Observe também que o [i]teste de predicado para a existência de ielemento filho.
2

Não há ino xpath não é totalmente verdade. Você ainda pode usar o count()para localizar o índice.

Considere a seguinte 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>

A página possui 2 tabelas e 6 colunas, cada uma com nomes de coluna exclusivos e 6 linhas com dados variáveis. A última linha contém o Modifybotão em ambas as tabelas.

Supondo que o usuário tenha que selecionar o 4º Modifybotão da primeira tabela com base no título

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

O count()operador é útil em situações como essas.

Lógica:

  1. Encontre o cabeçalho do Modifybotão usando//th[.='Heading 4']
  2. Encontre o índice da coluna do cabeçalho usando count(//tr/th[.='Heading 4']/preceding-sibling::th)+1

Nota: o índice começa em0

  1. Obtenha as linhas para o cabeçalho correspondente usando //th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]

  2. Obtenha o Modifybotão da lista de nós extraída usando//th[.='Heading 4']/ancestor::thead/following-sibling::tbody/tr/td[count(//tr/th[.='Heading 4']/preceding-sibling::th)+1]/button

Eric Stanley
fonte
1

(// * [@ attribute = 'value']) [index] para encontrar o destino do elemento enquanto você encontra várias correspondências nele

mahesh
fonte
1
Você pode explicar um pouco mais?
abhiarora
0

Aqui está a solução para a variável de índice

Digamos que você encontrou 5 elementos com o mesmo localizador e gostaria de realizar uma ação em cada elemento, fornecendo o número do índice (aqui, a variável é usada para o índice como "i")

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

Leva XPath:

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