Buscadores/Finders, Ámbito/Scope y Coincidencias/Matches Múltiples

Capybara está construido sobre un conjunto de bloques básicos. Estos bloques básicos consisten en expresiones XPath que se usan para encontrar cosas en la página para a continuación delegar la acción correspondiente en el driver subyacente.

La mayor parte del tiempo usaremos los métodos de alto nivel, pero hay ocasiones en las que debemos recurrir a estos finders para lograr nuestro propósito.

Consideremos esta página web:

[~/sinatra/sinatra-selenium/intro(gh-pages)]$ cat click.html 
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Click Examples</title>
    <script>
      window.onload = function() {
        var myanchor = document.getElementById("myanchor");
        var mydiv = document.getElementById("mydiv");
        var mybutton = document.getElementById("mybutton");

        myanchor.onclick = function() {
          alert("anchor has been clicked");
        };

        mydiv.onclick = function() {
          alert("div has been clicked");
        };

        mybutton.onclick = function() {
          alert("button has been clicked");
        };
      };
    </script>
  </head>
  <body>
    <div id="main">
      <div class="section">
        <a id="myanchor" title="myanchortitle" href="#">Click this Anchor</a>
        <div id="mydiv" title="mydivtitle">Click This Div</div>
        <input id="mybutton" type="button" value="Click This Button" title="mybuttontitle"/>
      </div>
    </div>
  </body>
</html>

Cuando se cliquea en el div

        <div id="mydiv" title="mydivtitle">Click This Div</div>
se emite un mensaje de alerta.

Para automatizar este click haremos uso del método find.

[~/sinatra/sinatra-selenium/intro(gh-pages)]$ cat find_and_click.rb 
visit '/'
find('#mydiv').click
accept_alert
find('#mydiv') retorna un Capybara::Node::Element sobre el cual se invoca la acción.

Un Element representa un elemento de la página. Es posible interactuar con los contenidos de este elelemnto en la misma forma que con un documento:

session = Capybara::Session.new(:rack_test, my_app)

bar = session.find('#bar')              # from Capybara::Node::Finders
bar.select('Baz', :from => 'Quox')      # from Capybara::Node::Actions
Los Element también tienen atributos HTML:

bar.value
bar.text
bar[:title]

Para probar este HTML vamos a usar nuestro script genérico testinglocals.rb:

[~/sinatra/sinatra-selenium/intro(gh-pages)]$ ./testinglocalhtml.rb click.html find_and_click.rb

Casi todo el resto de métodos de busqueda se construyen a partir de este (Véase la clase Capybara::Node::Finders ):

Múltiples Coincidencias/Multiple Matches

Supongamos que estamos comprobando algunas búsquedas de resultados que son añadidos dinámicamente a la página:

[~/sinatra/sinatra-selenium/intro(gh-pages)]$ cat multiple.html 
<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Multiple Match Examples</title>
    <style>
      .result {
        height: 30px;
        width: 100px;
        background-color: green;
        margin-bottom: 10px;
      }
    </style>
  </head>
  <body>
    <div id="main">
      <h1>Search Results</h1>
      <ul class="section">
        <li id="res1" class="result">Match 1</li>
        <li id="res2" class="result">Match 2</li>
        <li id="res3" class="result">Match 3</li>
        <li id="res4" class="result">Match 4</li>
        <li id="res5" class="result">Match 5</li>
      </ul>
    </div>
  </body>
</html>

Una posible solución es iterar sobre los elementos de la clase .result usando el método all:

$ cat multiple.rb
RSpec.configure do |config| # avoid warning
  config.expect_with :rspec do |c|
    c.syntax = [:should, :expect]
  end
end
visit '/'
all('.result').each_with_index do |elem, idx|
   elem.text.should == "Match #{idx + 1}"
end
Cuando se ejecuta
$ ./testinglocalhtml.rb multiple.html multiple.rb p
$
no produce fallos.

Visibilidad

La visibilidad de un elemento afecta a la forma en la que Capybara lo localiza en el DOM. Capybara dispone de un atributo global que se usa para determinar si se prueban o no elementos escondidos (por ejemplo usando propiedades CSS como display: none o visibility: hidden:
Capybara.ignore_hidden_elements = true
el valor por defecto es true.

Ambito

Una opción cuando se intenta encontrar contenido que nos fácil de identificar unívocamente es restríngir la consulta a una sección de la página.

Capybara provee el método within que permite consultas con ámbito.

#within(*find_args) ⇒ Object
#within(a_node) ⇒ Object
Executes the given block within the context of a node. within takes the same options as find, as well as a block. For the duration of the block, any command to Capybara will be handled as though it were scoped to the given element.

within(:xpath, '//div[@id="delivery-address"]') do
  fill_in('Street', :with => '12 Main Street')
end
Just as with find, if multiple elements match the selector given to within, an error will be raised, and just as with find, this behaviour can be controlled through the :match and :exact options.

It is possible to omit the first parameter, in that case, the selector is assumed to be of the type set in Capybara.default_selector.

within('div#delivery-address') do
  fill_in('Street', :with => '12 Main Street')
end
Note that a lot of uses of within can be replaced more succinctly with chaining:

find('div#delivery-address').fill_in('Street', :with => '12 Main Street')
Raises: (Capybara::ElementNotFound) — If the scope can't be found before time expires



Subsecciones
Casiano Rodriguez León 2015-01-07