Skip to content

Commit d2704d5

Browse files
committed
Rewrite the chapter about traversing pages
1 parent 51e8bd9 commit d2704d5

1 file changed

Lines changed: 153 additions & 117 deletions

File tree

guides/traversing-pages.rst

Lines changed: 153 additions & 117 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,200 @@
11
Traversing Pages
22
================
33

4-
Now you know how to control the browser itself. But what about traversing
5-
the current page content? Mink talks to its drivers with `XPath selectors`_,
6-
but you also have access to `named selectors`_ and `css selectors`_. Mink
7-
will transform such selectors into XPath queries internally for you.
4+
Most usages of Mink will involve working with the page opened in your browser.
5+
This is done thanks to the powerful Element API. This API allows to traverse
6+
the page (similar to the DOM in Javascript) and to interact with it, which
7+
will be covered in the :doc:`next chapter </guides/manipulating-pages>`.
88

9-
The main class of Mink's selectors engine is ``Behat\Mink\Selector\SelectorsHandler``.
10-
It handles different selector types, which implements ``Behat\Mink\Selector\SelectorInterface``:
9+
DocumentElement and NodeElement
10+
-------------------------------
1111

12-
.. code-block:: php
12+
The Element API consists of 2 main classes. The ``DocumentElement`` instance
13+
represents the page being displayed in the browser, while the ``NodeElement``
14+
class is used to represent any element inside the page. Both class are sharing
15+
a common set of methods to traverse the page (defined in ``TraversableElement``).
1316

14-
$cssSelector = new \Behat\Mink\Selector\CssSelector();
17+
The ``DocumentElement`` instance is accessible through the ``Session::getPage`` method:
1518

16-
// generate XPath query out of CSS:
17-
echo $cssSelector->translateToXPath('h1 > a');
19+
.. code-block:: php
1820
19-
$handler = new \Behat\Mink\Selector\SelectorsHandler();
20-
$handler->registerSelector('css', $cssSelector);
21+
$page = $session->getPage();
2122
22-
// generate XPath query out of CSS:
23-
echo $handler->selectorToXpath('css', 'h1 > a');
23+
// You can now manipulate the page.
2424
25-
When you initialize ``Selector\SelectorsHandler`` it already has `XPath selectors`_,
26-
`named selectors`_ and `css selectors`_ registered in it.
25+
.. note::
2726

28-
You can provide a custom selectors handler as a second argument to your session
29-
instances:
27+
The ``DocumentElement`` instance represents the ``<html>`` node in the
28+
DOM. It is equivalent to ``document.documentElement`` in the Javascript
29+
DOM API.
3030

31-
.. code-block:: php
31+
Traversal Methods
32+
-----------------
3233

33-
$session = new \Behat\Mink\Session($driver,
34-
new \Behat\Mink\Selector\SelectorsHandler()
35-
);
34+
Elements have 2 main traversal methods: ``ElementInterface::findAll`` returns
35+
an array of ``NodeElement`` instances matching the provided :ref:`selector <selectors>`
36+
inside the current element while ``ElementInterface::find`` returns the first
37+
match or ``null`` when there is none.
3638

37-
Mink will use this handler internally in `find* methods`_.
39+
The ``TraversableElement`` class also provides a bunch of shortcut methods
40+
on top of ``find()`` to make it easier to achieve many common use case:
3841

39-
Named Selectors
40-
~~~~~~~~~~~~~~~
42+
``ElementInterface::has``
43+
Checks whether a child element matches the given selector but without
44+
returning it.
4145

42-
Named selectors provide a way to get named XPath queries:
46+
``TraversableElement::findById``
47+
Looks for a child element with the given id.
4348

44-
.. code-block:: php
49+
``TraversableElement::findLink``
50+
Looks for a link with the given text, title, id or ``alt`` attribute
51+
(for images used inside links).
4552

46-
$selector = new \Behat\Mink\Selector\NamedSelector();
47-
$handler = new \Behat\Mink\Selector\SelectorsHandler(array(
48-
'named' => $selector
49-
));
50-
51-
// XPath query to find the fieldset:
52-
$xpath1 = $selector->translateToXPath(
53-
array('fieldset', 'id|legend')
54-
);
55-
$xpath1 = $handler->selectorToXpath('named',
56-
array('fieldset', 'id|legend')
57-
);
58-
59-
// XPath query to find the field:
60-
$xpath2 = $selector->translateToXPath(
61-
array('field', 'id|name|value|label')
62-
);
63-
$xpath2 = $handler->selectorToXpath('named',
64-
array('field', 'id|name|value|label')
65-
);
66-
67-
There's whole lot more named selectors for you to use:
68-
69-
* ``link`` - for searching a link by its href, id, title, img alt or value
70-
* ``button`` - for searching a button by its name, id, value, img alt or
71-
title
72-
* ``link_or_button`` - for searching for both, links and buttons
73-
* ``content`` - for searching a specific page content (text)
74-
* ``select`` - for searching a select field by its id, name or label
75-
* ``checkbox`` - for searching a checkbox by its id, name, or label
76-
* ``radio`` - for searching a radio button by its id, name, or label
77-
* ``file`` - for searching a file input by its id, name, or label
78-
* ``optgroup`` - for searching optgroup by its label
79-
* ``option`` - for searching an option by its content
80-
* ``table`` - for searching a table by its id or caption
81-
82-
CSS Selectors
83-
~~~~~~~~~~~~~
84-
85-
With ``Selector\CssSelector``, you can use CSS expressions to search page
86-
elements:
53+
``TraversableElement::findButton``
54+
Looks for a button with the given text, title, id, ``name`` attribute
55+
or ``alt`` attribute (for images used inside links).
8756

88-
.. code-block:: php
57+
``TraversableElement::findField``
58+
Looks for a field (``input``, ``textarea`` or ``select``) with the given
59+
label, placeholder, id or ``name`` attribute.
8960

90-
$selector = new \Behat\Mink\Selector\CssSelector();
91-
$handler = new \Behat\Mink\Selector\SelectorsHandler(array(
92-
'css' => $selector
93-
));
61+
.. note::
9462

95-
// XPath query to find the link by ID:
96-
$xpath1 = $selector->translateToXPath('a#ID');
97-
$xpath1 = $handler->selectorToXpath('css', 'a#ID');
63+
These shortcuts are returning a single element. If you need to find all
64+
matches, you will need to use ``findAll`` with the :ref:`named selector <named-selector>`.
9865

99-
XPath Selectors
100-
~~~~~~~~~~~~~~~
66+
Nested Traversing
67+
~~~~~~~~~~~~~~~~~
10168

102-
And of course, you can use clean XPath queries:
69+
Every ``find*()`` method will return a ``Behat\Mink\Element\NodeElement`` instance
70+
and ``findAll()`` will return an array of such instances. The fun part is
71+
you can make same old traversing on such elements too:
10372

10473
.. code-block:: php
10574
106-
$xpath = $handler->selectorToXpath('xpath', '//html');
75+
$registerForm = $page->find('css', 'form.register');
10776
108-
It's like a proxy method, which will return the same expression you give
109-
to it. It's used internally in `find* methods`_.
77+
if (null === $registerForm) {
78+
throw new \Exception('The element is not found');
79+
}
11080
111-
``find*`` Methods
112-
~~~~~~~~~~~~~~~~~
81+
// find some field INSIDE form with class="register"
82+
$field = $registerForm->findField('Email');
83+
84+
.. _selectors:
85+
86+
Selectors
87+
---------
11388

114-
So, now we know how to generate XPath queries for specific elements search.
115-
But how we actually make this search? The answer is ``find*`` methods,
116-
available on ``DocumentElement`` object. You can get this object from session:
89+
The ``ElementInterface::find`` and ``ElementInterface::findAll`` methods
90+
support several kinds of selectors to find elements.
91+
92+
CSS Selector
93+
~~~~~~~~~~~~
94+
95+
The ``css`` selector type lets you use CSS expressions to search elements
96+
in the page:
11797

11898
.. code-block:: php
11999
120-
$page = $session->getPage();
121-
$page = $mink->getSession('sahi')->getPage();
100+
$title = $page->find('css', 'h1');
122101
123-
This object provides two very useful traversing methods:
102+
XPath Selector
103+
~~~~~~~~~~~~~~
124104

125-
* ``find()`` - evaluates specific selector on the page content and returns
126-
the last matched element or ``null``:
105+
The ``xpath`` selector type lets you use XPath queries to search elements
106+
in the page:
127107

128-
.. code-block:: php
108+
.. code-block:: php
129109
130-
$fieldElement = $page->find('named',
131-
array('field', 'id|name|value|label')
132-
);
133-
$elementByCss = $page->find('css', 'h3 > a');
110+
$anchorsWithoutUrl = $page->findAll('xpath', '//a[not(@href)]');
134111
135-
* ``findAll()`` - evaluates specific selector on the page content and returns
136-
an array of matched elements:
112+
.. caution::
137113

138-
.. code-block:: php
114+
This selector searches an element inside the current node (which is ``<html>``
115+
for the page object). This means that trying to pass it the XPath of
116+
and element retrieved with ``ElementInterface::getXpath`` will not work
117+
(this query includes the query for the root node). To check whether an
118+
element object still exists in the browser page, use ``ElementInterface::isValid``
119+
instead.
139120

140-
$fieldElements = $page->findAll('named',
141-
array('field', 'id|name|value|label')
142-
);
143-
$elementsByCss = $page->findAll('css', 'h3 > a');
121+
.. _named-selector:
144122

145-
Also, there's a bunch of shortcut methods:
123+
Named Selector
124+
~~~~~~~~~~~~~~
146125

147-
* ``findById()`` - will search for an element by its ID
148-
* ``findLink()`` - will search for a link with ``link`` named selector
149-
* ``findButton()`` - will search for a button with ``button`` named selector
150-
* ``findField()`` - will search for a field with ``field`` named selector
126+
Named selectors provide a set of reusable queries for common needs. For conditions
127+
based on the content of elements, the named selector will try to find an
128+
exact match first. It will then fallback to partial matching in case there
129+
is no result for the exact match. The ``named_exact`` selector type can be
130+
used to force using only exact matching. The ``named_partial`` selector type
131+
can be used to apply partial matching without preferring exact matches.
151132

152-
Nested Traversing
153-
~~~~~~~~~~~~~~~~~
133+
For the named selector type, the second argument of the ``find()`` method
134+
is an array with 2 elements: the name of the query to use and the value to
135+
search with this query:
154136

155-
Every ``find*()`` method will return ``Behat\Mink\Element\NodeElement`` instance
156-
and ``findAll()`` will return an array of such instances. The fun part is
157-
you can make same old traversing on such elements too:
137+
.. code-block:: php
138+
139+
$escapedValue = $session->getSelectorsHandler()->xpathLiteral('Go to top');
140+
141+
$topLink = $page->find('named', array('link', $escapedValue);
142+
143+
.. caution::
144+
145+
The named selector requires escaping the value as XPath literal. Otherwise
146+
the generated XPath query will be invalid.
147+
148+
The following queries are supported by the named selector:
149+
150+
``id``
151+
Searches an element by its id.
152+
``id_or_name``
153+
Searches an element by its id or name.
154+
``link``
155+
Searches a link by its id, title, img alt, rel or text.
156+
``button``
157+
Searches a button by its name, id, text, img alt or title.
158+
``link_or_button``
159+
Searches for both links and buttons.
160+
``content``
161+
Searches a specific page content (text).
162+
``field``
163+
Searches a form field by its id, name, label or placeholder.
164+
``select``
165+
Searches a select field by its id, name or label.
166+
``checkbox``
167+
Searches a checkbox by its id, name, or label.
168+
``radio``
169+
Searches a radio button by its id, name, or label.
170+
``file``
171+
Searches a file input by its id, name, or label.
172+
``optgroup``
173+
Searches an optgroup by its label.
174+
``option``
175+
Searches an option by its content or value.
176+
``fieldset``
177+
Searches a fieldset by its id or legend.
178+
``table``
179+
Searches a table by its id or caption.
180+
181+
Custom Selector
182+
~~~~~~~~~~~~~~~
183+
184+
Mink lets you register your own selector types by implement the ``Behat\Mink\Selector\SelectorInterface``.
185+
It should then be registered in the ``SelectorsHandler`` which is the registry
186+
of available selectors.
187+
188+
The recommended way to register a custom selector is to do it when building
189+
your ``Session``:
158190

159191
.. code-block:: php
160192
161-
$registerForm = $page->find('css', 'form.register');
193+
$selector = new \App\MySelector();
162194
163-
// find some field INSIDE form with class="register"
164-
$field = $registerForm->findField('id|name|value|label');
195+
$handler = new \Behat\Mink\Selector\SelectorsHandler();
196+
$handler->registerSelector('mine', $selector);
197+
198+
$driver = // ...
199+
200+
$session = new \Behat\Mink\Session($driver, $handler);

0 commit comments

Comments
 (0)