Está tendo problemas para entender por que a pseudo classe: contains () nos seletores CSS funciona da maneira que funciona?

StackOverflow https://stackoverflow.com/questions/9334539

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:

  1. div.view_header ~ div input.my_button

  2. div:contains(My Header Title) ~ div input.my_button

  3. 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"> &nbsp; </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>
                  ...
Foi útil?

Solução

A coisa mais comum que faço quando me encontro com esse tipo de problema é olhar as especificações.

Como você provavelmente sabe, não há nenhum para :contains() na especificação atual e, portanto, você depende de recursos não documentados e não especificados de um navegador / analisador específico. Ele deveria funcionar, mas não funciona - obviamente a implementação não foi concluída. E agora a pseudo classe se foi.

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 Current Sizzle Results

Selenium's Sizzle - corresponde a 1 de 4 Selenium's Sizzle Results


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:

  1. Arquive um bug do Selenium.
  2. Use XPath como uma solução alternativa.
  3. 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);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top