Skip to content

Commit 46ad986

Browse files
committed
Merge pull request #50 from stof/pages
Rewrite the chapter about traversing pages
2 parents 667254d + 9882acb commit 46ad986

1 file changed

Lines changed: 154 additions & 116 deletions

File tree

guides/traversing-pages.rst

Lines changed: 154 additions & 116 deletions
Original file line numberDiff line numberDiff line change
@@ -1,164 +1,202 @@
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+
-------------------------------
11+
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``).
16+
17+
The ``DocumentElement`` instance is accessible through the ``Session::getPage`` method:
1118

1219
.. code-block:: php
1320
14-
$cssSelector = new \Behat\Mink\Selector\CssSelector();
21+
$page = $session->getPage();
1522
16-
// generate XPath query out of CSS:
17-
echo $cssSelector->translateToXPath('h1 > a');
23+
// You can now manipulate the page.
1824
19-
$handler = new \Behat\Mink\Selector\SelectorsHandler();
20-
$handler->registerSelector('css', $cssSelector);
25+
.. note::
2126

22-
// generate XPath query out of CSS:
23-
echo $handler->selectorToXpath('css', 'h1 > a');
27+
The ``DocumentElement`` instance represents the ``<html>`` node in the
28+
DOM. It is equivalent to ``document.documentElement`` in the Javascript
29+
DOM API.
2430

25-
When you initialize ``Selector\SelectorsHandler`` it already has `XPath selectors`_,
26-
`named selectors`_ and `css selectors`_ registered in it.
31+
Traversal Methods
32+
-----------------
2733

28-
You can provide a custom selectors handler as a second argument to your session
29-
instances:
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.
3038

31-
.. code-block:: php
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 cases:
3241

33-
$session = new \Behat\Mink\Session($driver,
34-
new \Behat\Mink\Selector\SelectorsHandler()
35-
);
42+
``ElementInterface::has``
43+
Checks whether a child element matches the given selector but without
44+
returning it.
3645

37-
Mink will use this handler internally in `find* methods`_.
46+
``TraversableElement::findById``
47+
Looks for a child element with the given id.
3848

39-
Named Selectors
40-
~~~~~~~~~~~~~~~
49+
``TraversableElement::findLink``
50+
Looks for a link with the given text, title, id or ``alt`` attribute
51+
(for images used inside links).
4152

42-
Named selectors provide a way to get named XPath queries:
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).
4356

44-
.. 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.
60+
61+
.. note::
4562

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:
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>`.
65+
66+
Nested Traversing
67+
~~~~~~~~~~~~~~~~~
68+
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+
that you can make same old traversing on such elements as well:
8772

8873
.. code-block:: php
8974
90-
$selector = new \Behat\Mink\Selector\CssSelector();
91-
$handler = new \Behat\Mink\Selector\SelectorsHandler(array(
92-
'css' => $selector
93-
));
75+
$registerForm = $page->find('css', 'form.register');
9476
95-
// XPath query to find the link by ID:
96-
$xpath1 = $selector->translateToXPath('a#ID');
97-
$xpath1 = $handler->selectorToXpath('css', 'a#ID');
77+
if (null === $registerForm) {
78+
throw new \Exception('The element is not found');
79+
}
9880
99-
XPath Selectors
100-
~~~~~~~~~~~~~~~
81+
// find some field INSIDE form with class="register"
82+
$field = $registerForm->findField('Email');
83+
84+
.. _selectors:
10185

102-
And of course, you can use clean XPath queries:
86+
Selectors
87+
---------
88+
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 for elements
96+
on the page:
10397

10498
.. code-block:: php
10599
106-
$xpath = $handler->selectorToXpath('xpath', '//html');
100+
$title = $page->find('css', 'h1');
107101
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`_.
102+
$buttonIcon = $page->find('css', '.btn > .icon');
110103
111-
``find*`` Methods
112-
~~~~~~~~~~~~~~~~~
104+
XPath Selector
105+
~~~~~~~~~~~~~~
113106

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:
107+
The ``xpath`` selector type lets you use XPath queries to search for elements
108+
on the page:
117109

118110
.. code-block:: php
119111
120-
$page = $session->getPage();
121-
$page = $mink->getSession('sahi')->getPage();
112+
$anchorsWithoutUrl = $page->findAll('xpath', '//a[not(@href)]');
122113
123-
This object provides two very useful traversing methods:
114+
.. caution::
124115

125-
* ``find()`` - evaluates specific selector on the page content and returns
126-
the last matched element or ``null``:
116+
This selector searches for an element inside the current node (which
117+
is ``<html>`` for the page object). This means that trying to pass it
118+
the XPath of and element retrieved with ``ElementInterface::getXpath``
119+
will not work (this query includes the query for the root node). To check
120+
whether an element object still exists on the browser page, use ``ElementInterface::isValid``
121+
instead.
127122

128-
.. code-block:: php
123+
.. _named-selector:
129124

130-
$fieldElement = $page->find('named',
131-
array('field', 'id|name|value|label')
132-
);
133-
$elementByCss = $page->find('css', 'h3 > a');
134-
135-
* ``findAll()`` - evaluates specific selector on the page content and returns
136-
an array of matched elements:
125+
Named Selectors
126+
~~~~~~~~~~~~~~~
137127

138-
.. code-block:: php
128+
Named selectors provide a set of reusable queries for common needs. For conditions
129+
based on the content of elements, the named selector will try to find an
130+
exact match first. It will then fallback to partial matching in case there
131+
is no result for the exact match. The ``named_exact`` selector type can be
132+
used to force using only exact matching. The ``named_partial`` selector type
133+
can be used to apply partial matching without preferring exact matches.
139134

140-
$fieldElements = $page->findAll('named',
141-
array('field', 'id|name|value|label')
142-
);
143-
$elementsByCss = $page->findAll('css', 'h3 > a');
135+
For the named selector type, the second argument of the ``find()`` method
136+
is an array with 2 elements: the name of the query to use and the value to
137+
search with this query:
144138

145-
Also, there's a bunch of shortcut methods:
139+
.. code-block:: php
146140
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
141+
$escapedValue = $session->getSelectorsHandler()->xpathLiteral('Go to top');
142+
143+
$topLink = $page->find('named', array('link', $escapedValue);
144+
145+
.. caution::
146+
147+
The named selector requires escaping the value as XPath literal. Otherwise
148+
the generated XPath query will be invalid.
149+
150+
The following queries are supported by the named selector:
151+
152+
``id``
153+
Searches for an element by its id.
154+
``id_or_name``
155+
Searches for an element by its id or name.
156+
``link``
157+
Searches for a link by its id, title, img alt, rel or text.
158+
``button``
159+
Searches for a button by its name, id, text, img alt or title.
160+
``link_or_button``
161+
Searches for both links and buttons.
162+
``content``
163+
Searches for a specific page content (text).
164+
``field``
165+
Searches for a form field by its id, name, label or placeholder.
166+
``select``
167+
Searches for a select field by its id, name or label.
168+
``checkbox``
169+
Searches for a checkbox by its id, name, or label.
170+
``radio``
171+
Searches for a radio button by its id, name, or label.
172+
``file``
173+
Searches for a file input by its id, name, or label.
174+
``optgroup``
175+
Searches for an optgroup by its label.
176+
``option``
177+
Searches for an option by its content or value.
178+
``fieldset``
179+
Searches for a fieldset by its id or legend.
180+
``table``
181+
Searches for a table by its id or caption.
182+
183+
Custom Selector
184+
~~~~~~~~~~~~~~~
151185

152-
Nested Traversing
153-
~~~~~~~~~~~~~~~~~
186+
Mink lets you register your own selector types through implementing the ``Behat\Mink\Selector\SelectorInterface``.
187+
It should then be registered in the ``SelectorsHandler`` which is the registry
188+
of available selectors.
154189

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:
190+
The recommended way to register a custom selector is to do it when building
191+
your ``Session``:
158192

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

0 commit comments

Comments
 (0)