Está tendo problemas para entender por que a pseudo classe: contains () nos seletores CSS funciona da maneira que funciona?
-
27-10-2019 - |
Pergunta
Estou usando seletores CSS com Selenium e Cucumber. Quando um localizador não funciona, faço um teste usando o console das Ferramentas para Desenvolvedores do Chrome. Continuo encontrando um comportamento que não entendo (por que ele faz o que está fazendo e não o que eu preciso fazer ...). Por favor, olhe para estes localizadores:
div.view_header ~ div input.my_button
div:contains(My Header Title) ~ div input.my_button
div:contains(My Header Title) ~ div div div input.my_button
No meu DOM, o elemento que corresponde à primeira parte de cada um desses localizadores é o mesmo ...
<div class="view_header foo"> My Header Title </div>
O problema é que apenas os localizadores nº 1 e nº 3 acima realmente correspondem a alguma coisa. Alguém sabe por que isso é verdade. Eu percebo que div:contains(foo)
corresponderá não apenas ao div que realmente contém foo, mas também a todos os divs pais, mas me parece que o resto dos elementos localizadores devem classificá-lo para que funcione.
Estou apenas procurando qualquer insight e, possivelmente, sugestões de uma maneira de ter certeza de que o 'my_button' em que estou clicando está em 'My Header Title' e não em 'my_button' em algum outro lugar da página (e a única maneira fácil de distingui-los é pelo cabeçalho sob o qual estão), ao mesmo tempo que elimina a estrutura DOM aparentemente em excesso no localizador para torná-lo mais provável de ser reutilizável.
<head>
<body class="bp">
<div style="left: -100em; position: absolute; width: 100em;"></div>
<input class="refresh_marker" type="text" value="no" style="display:none">
<div class="container">
<div id="nav_bar">
<div id="user_bar">
<div id="wrapper" style="border-radius: 10px 10px 10px 10px;">
<div class="content">
<div class="page_title"> Title </div>
<div></div>
<a class="change_tracker_link"> </a>
<div class="breadcrumb_trail">
<style type="text/css">
<div id="dialog_no_new_assoc" class="hide" title="No Associations Selected"></div>
<div class="organizer_widget root_organizer" title="WorkflowItem" style="">
<input id="data_classifier" type="hidden" value="Workflow::WorkflowItem">
<input id="data_id" type="hidden" value="34">
<input id="data_getter" type="hidden">
<input id="collection_vertex_id" type="hidden" value="4cb1ecc300fa5f77844b1e87431d0a25390c1c77">
<input id="view-name" type="hidden" value="EnterPaperInformation">
<div class="object organizer">
<div class="clear"></div>
<div class="interior">
<form method="POST" enctype="multipart/form-data">
<input type="hidden" value="4cb1ecc300fa5f77844b1e87431d0a25390c1c77" name="vertex_id">
<input type="submit" value="Save" style="display: none;" name="submit_form">
<div class="organizer_header view_header"> My Header Title </div>
<div class="organizer_widget" title="Citation" style="">
<input id="data_classifier" type="hidden" value="Bibliography::Citation">
<input id="data_id" type="hidden" value="10">
<input id="data_getter" type="hidden" value="citation">
<input id="collection_vertex_id" type="hidden" value="5376dcc81102a5d76bf829513b096be8f67e560d">
<input id="view-name" type="hidden" value="CitationEntrySummary">
<div id="citation" class="object organizer">
<div class="clear"></div>
<div class="interior">
<div id="Citation___id_widget" class="widget_row numeric">
<div id="Citation___title_widget" class="widget_row string">
<div id="Citation___abbreviated_title_widget" class="widget_row string">
<div id="Citation___authors_display_string_widget" class="widget_row string">
<div id="Citation___language_widget" class="widget_row choice">
<div id="Citation___link_widget" class="widget_row link">
<input type="hidden" value="Bibliography::JournalArticle___10" name="check_5376dcc81102a5d76bf829513b096be8f67e560d[]">
<input id="ba_citation" class="my_button" type="button" value="Break Associations" name="break_assoc_5376dcc81102a5d76bf829513b096be8f67e560d">
<div class="clear"></div>
<input type="hidden" value="5376dcc81102a5d76bf829513b096be8f67e560d" name="vertices[]">
</div>
...
Solução
A coisa mais comum que faço quando me encontro com esse tipo de problema é olhar as especificações.
Você poderia escolher um XPath em vez disso? Por métodos internos do Selenium ou JavaScript . Este XPath é igual ao seletor CSS número 2:
//div[contains(text(),'My Header Title')]/following-sibling::div//input[contains(@class,'my_button')]
< PaulEDIT
Depois que seu comentário me mostrou que estamos falando sobre Selenium RC e, portanto, Sizzle, eu cavei mais fundo.
Peguei seu exemplo de HTML, retirei-o dos elementos ocultos e (aparentemente) desnecessários e fiquei com o seguinte:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<script src="sizzle.js" type="text/javascript"></script>
</head>
<body class="bp">
<div class="container">
<div id="nav_bar">
<div id="user_bar">
<div id="wrapper" style="border-radius: 10px 10px 10px 10px;">
<div class="content">
<div class="breadcrumb_trail">
<div class="organizer_widget root_organizer" title="WorkflowItem" style="">
<div class="object organizer">
<div class="interior">
<form method="POST" enctype="multipart/form-data">
<div class="organizer_header view_header"> My Header Title </div>
<div class="organizer_widget" title="Citation" style="">
<div id="citation" class="object organizer">
<div class="clear"></div>
<div class="interior">
<div id="Citation___id_widget" class="widget_row numeric">
<div id="Citation___title_widget" class="widget_row string">
<div id="Citation___abbreviated_title_widget" class="widget_row string">
<div id="Citation___authors_display_string_widget" class="widget_row string">
<div id="Citation___language_widget" class="widget_row choice">
<div id="Citation___link_widget" class="widget_row link">
<input id="ba_citation" class="my_button" type="button" value="Break Associations" name="break_assoc_5376dcc81102a5d76bf829513b096be8f67e560d" />
<div class="clear"></div>
</div></div></div></div></div></div>
</div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</body>
</html>
Baixei o Sizzle mais recente e obtive o versão do Sizzle que é realmente usado pela Selenium no atual lançamento .
Acontece que esses dois são muito diferentes.
Por exemplo, a implementação contains
do Sizzle atual:
return ~( elem.textContent || elem.innerText || getText( elem ) ).indexOf( match[3] );
e a implementação que o Selenium usa:
return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
Tentei ambas as implementações no meu documento de teste, os resultados podem ser vistos aqui (clique para ampliar):
Current Sizzle - combina perfeitamente
Selenium's Sizzle - corresponde a 1 de 4
Os resultados dizem tudo. O Selenium usa uma versão antiga do Sizzle que é de alguma forma imperfeita no manuseio da pseudo-classe :contains()
. A versão atual do Sizzle não sofre com o bug e é capaz de encontrar todos os elementos bem.
Agora, você pode fazer qualquer um destes:
- Arquive um bug do Selenium.
- Use XPath como uma solução alternativa.
- Troque o arquivo
sizzle.js
em seu pacote Selenium.
Outras dicas
#Selenium #Webdriver handle only HTML elements but with using java script executor It's possible to handle #pseudo elements in selenium #webdriver.
Ex: :after , :before etc
String script = "return window.getComputedStyle(document.querySelector('Enter root classname here'),':after / :before').getPropertyValue('content')";
Thread.sleep(3000);
JavascriptExecutor js = (JavascriptExecutor) driver;
String content = (String) js.executeScript(script);
System.out.println(content);