|
1 | 1 | Traversing Pages |
2 | 2 | ================ |
3 | 3 |
|
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>`. |
8 | 8 |
|
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: |
11 | 18 |
|
12 | 19 | .. code-block:: php |
13 | 20 |
|
14 | | - $cssSelector = new \Behat\Mink\Selector\CssSelector(); |
| 21 | + $page = $session->getPage(); |
15 | 22 |
|
16 | | - // generate XPath query out of CSS: |
17 | | - echo $cssSelector->translateToXPath('h1 > a'); |
| 23 | + // You can now manipulate the page. |
18 | 24 |
|
19 | | - $handler = new \Behat\Mink\Selector\SelectorsHandler(); |
20 | | - $handler->registerSelector('css', $cssSelector); |
| 25 | +.. note:: |
21 | 26 |
|
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. |
24 | 30 |
|
25 | | -When you initialize ``Selector\SelectorsHandler`` it already has `XPath selectors`_, |
26 | | -`named selectors`_ and `css selectors`_ registered in it. |
| 31 | +Traversal Methods |
| 32 | +----------------- |
27 | 33 |
|
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. |
30 | 38 |
|
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: |
32 | 41 |
|
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. |
36 | 45 |
|
37 | | -Mink will use this handler internally in `find* methods`_. |
| 46 | +``TraversableElement::findById`` |
| 47 | + Looks for a child element with the given id. |
38 | 48 |
|
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). |
41 | 52 |
|
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). |
43 | 56 |
|
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:: |
45 | 62 |
|
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: |
87 | 72 |
|
88 | 73 | .. code-block:: php |
89 | 74 |
|
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'); |
94 | 76 |
|
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 | + } |
98 | 80 |
|
99 | | -XPath Selectors |
100 | | -~~~~~~~~~~~~~~~ |
| 81 | + // find some field INSIDE form with class="register" |
| 82 | + $field = $registerForm->findField('Email'); |
| 83 | +
|
| 84 | +.. _selectors: |
101 | 85 |
|
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: |
103 | 97 |
|
104 | 98 | .. code-block:: php |
105 | 99 |
|
106 | | - $xpath = $handler->selectorToXpath('xpath', '//html'); |
| 100 | + $title = $page->find('css', 'h1'); |
107 | 101 |
|
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'); |
110 | 103 |
|
111 | | -``find*`` Methods |
112 | | -~~~~~~~~~~~~~~~~~ |
| 104 | +XPath Selector |
| 105 | +~~~~~~~~~~~~~~ |
113 | 106 |
|
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: |
117 | 109 |
|
118 | 110 | .. code-block:: php |
119 | 111 |
|
120 | | - $page = $session->getPage(); |
121 | | - $page = $mink->getSession('sahi')->getPage(); |
| 112 | + $anchorsWithoutUrl = $page->findAll('xpath', '//a[not(@href)]'); |
122 | 113 |
|
123 | | -This object provides two very useful traversing methods: |
| 114 | +.. caution:: |
124 | 115 |
|
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. |
127 | 122 |
|
128 | | - .. code-block:: php |
| 123 | +.. _named-selector: |
129 | 124 |
|
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 | +~~~~~~~~~~~~~~~ |
137 | 127 |
|
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. |
139 | 134 |
|
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: |
144 | 138 |
|
145 | | -Also, there's a bunch of shortcut methods: |
| 139 | +.. code-block:: php |
146 | 140 |
|
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 | +~~~~~~~~~~~~~~~ |
151 | 185 |
|
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. |
154 | 189 |
|
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``: |
158 | 192 |
|
159 | 193 | .. code-block:: php |
160 | 194 |
|
161 | | - $registerForm = $page->find('css', 'form.register'); |
| 195 | + $selector = new \App\MySelector(); |
162 | 196 |
|
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