Commit | Line | Data |
---|---|---|
b1bc8cfc | 1 | <?php |
b1bc8cfc DM |
2 | // This file is part of Moodle - http://moodle.org/ |
3 | // | |
4 | // Moodle is free software: you can redistribute it and/or modify | |
5 | // it under the terms of the GNU General Public License as published by | |
6 | // the Free Software Foundation, either version 3 of the License, or | |
7 | // (at your option) any later version. | |
8 | // | |
9 | // Moodle is distributed in the hope that it will be useful, | |
10 | // but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | // GNU General Public License for more details. | |
13 | // | |
14 | // You should have received a copy of the GNU General Public License | |
15 | // along with Moodle. If not, see <http://www.gnu.org/licenses/>. | |
16 | ||
17 | /** | |
18 | * Navigation steps definitions. | |
19 | * | |
20 | * @package core | |
21 | * @category test | |
22 | * @copyright 2012 David Monllaó | |
23 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
24 | */ | |
25 | ||
26 | // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. | |
27 | ||
28 | require_once(__DIR__ . '/../../behat/behat_base.php'); | |
29 | ||
c4509f80 DM |
30 | use Behat\Behat\Context\Step\Given as Given, |
31 | Behat\Mink\Exception\ExpectationException as ExpectationException; | |
32 | ||
b1bc8cfc DM |
33 | /** |
34 | * Steps definitions to navigate through the navigation tree nodes. | |
35 | * | |
36 | * @package core | |
37 | * @category test | |
38 | * @copyright 2012 David Monllaó | |
39 | * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later | |
40 | */ | |
41 | class behat_navigation extends behat_base { | |
42 | ||
817d39b8 SH |
43 | /** |
44 | * Helper function to get a navigation nodes text element given its text from within the navigation block. | |
45 | * | |
46 | * This function finds the node with the given text from within the navigation block. | |
47 | * It checks to make sure the node is visible, and then returns it. | |
48 | * | |
49 | * @param string $text | |
50 | * @param bool $branch Set this true if you're only interested in the node if its a branch. | |
51 | * @param null|bool $collapsed Set this to true or false if you want the node to either be collapsed or not. | |
52 | * If its left as null then we don't worry about it. | |
53 | * @param null|string|Exception|false $exception The exception to throw if the node is not found. | |
54 | * @return \Behat\Mink\Element\NodeElement | |
55 | */ | |
56 | protected function get_node_text_node($text, $branch = false, $collapsed = null, $exception = null) { | |
57 | if ($exception === null) { | |
58 | $exception = new ExpectationException('The "' . $text . '" node could not be found', $this->getSession()); | |
59 | } else if (is_string($exception)) { | |
60 | $exception = new ExpectationException($exception, $this->getSession()); | |
61 | } | |
62 | ||
63 | $nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text); | |
64 | $hasblocktree = "[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]"; | |
65 | $hasbranch = "[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]"; | |
66 | $hascollapsed = "li[contains(concat(' ', normalize-space(@class), ' '), ' collapsed ') or @data-exandable='1']"; | |
487e68b4 | 67 | $notcollapsed = "li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]"; |
817d39b8 SH |
68 | $match = "[normalize-space(.)={$nodetextliteral}]"; |
69 | ||
70 | // Avoid problems with quotes. | |
71 | $isbranch = ($branch) ? $hasbranch : ''; | |
72 | if ($collapsed === true) { | |
73 | $iscollapsed = $hascollapsed; | |
74 | } else if ($collapsed === false) { | |
75 | $iscollapsed = $notcollapsed; | |
76 | } else { | |
77 | $iscollapsed = 'li'; | |
78 | } | |
79 | ||
487e68b4 SH |
80 | // First check root nodes. |
81 | $xpath = "//ul{$hasblocktree}/$hascollapsed/p{$isbranch}/span{$match}|"; | |
82 | // Next search for the node containing the text within a link. | |
83 | $xpath .= "//ul{$hasblocktree}//{$notcollapsed}/ul/{$iscollapsed}/p{$isbranch}/a{$match}|"; | |
84 | // Finally search for the node containing the text within a span. | |
85 | $xpath .= "//ul{$hasblocktree}//{$notcollapsed}/ul/{$iscollapsed}/p{$isbranch}/span{$match}"; | |
817d39b8 SH |
86 | |
87 | $node = $this->find('xpath', $xpath, $exception); | |
88 | $this->ensure_node_is_visible($node); | |
89 | return $node; | |
90 | } | |
91 | ||
92 | /** | |
93 | * Returns true if the navigation node with the given text is expandable. | |
94 | * | |
95 | * @Given /^navigation node "([^"]*)" should be expandable$/ | |
96 | * | |
97 | * @throws ExpectationException | |
98 | * @param string $nodetext | |
99 | * @return bool | |
100 | */ | |
101 | public function navigation_node_should_be_expandable($nodetext) { | |
102 | if (!$this->running_javascript()) { | |
103 | // Nodes are only expandable when JavaScript is enabled. | |
104 | return false; | |
105 | } | |
106 | ||
107 | $node = $this->get_node_text_node($nodetext, true); | |
108 | $node = $node->getParent(); | |
109 | if ($node->hasAttribute('data-expandable') && $node->getAttribute('data-expandable')) { | |
110 | return true; | |
111 | } | |
112 | throw new ExpectationException('The "' . $nodetext . '" node is not expandable', $this->getSession()); | |
113 | } | |
114 | ||
115 | /** | |
116 | * Returns true if the navigation node with the given text is not expandable. | |
117 | * | |
118 | * @Given /^navigation node "([^"]*)" should not be expandable$/ | |
119 | * | |
120 | * @throws ExpectationException | |
121 | * @param string $nodetext | |
122 | * @return bool | |
123 | */ | |
124 | public function navigation_node_should_not_be_expandable($nodetext) { | |
125 | if (!$this->running_javascript()) { | |
126 | // Nodes are only expandable when JavaScript is enabled. | |
127 | return false; | |
128 | } | |
129 | ||
130 | $node = $this->get_node_text_node($nodetext); | |
131 | $node = $node->getParent(); | |
132 | if ($node->hasAttribute('data-expandable') && $node->getAttribute('data-expandable')) { | |
133 | throw new ExpectationException('The "' . $nodetext . '" node is expandable', $this->getSession()); | |
134 | } | |
135 | return true; | |
136 | } | |
137 | ||
b1bc8cfc DM |
138 | /** |
139 | * Expands the selected node of the navigation tree that matches the text. | |
b1bc8cfc | 140 | * @Given /^I expand "(?P<nodetext_string>(?:[^"]|\\")*)" node$/ |
1f9ffbdb | 141 | * |
c4509f80 | 142 | * @throws ExpectationException |
b1bc8cfc | 143 | * @param string $nodetext |
817d39b8 | 144 | * @return bool|void |
b1bc8cfc DM |
145 | */ |
146 | public function i_expand_node($nodetext) { | |
147 | ||
e6d18d9d DM |
148 | // This step is useless with Javascript disabled as Moodle auto expands |
149 | // all of tree's nodes; adding this because of scenarios that shares the | |
150 | // same steps with and without Javascript enabled. | |
151 | if (!$this->running_javascript()) { | |
6dfd8325 MG |
152 | if ($nodetext === get_string('administrationsite')) { |
153 | // Administration menu is not loaded by default any more. Click the link to expand. | |
154 | return new Given('I click on "'.$nodetext.'" "link" in the "'.get_string('administration').'" "block"'); | |
155 | } | |
fb99ef1d | 156 | return true; |
e6d18d9d DM |
157 | } |
158 | ||
817d39b8 SH |
159 | $node = $this->get_node_text_node($nodetext, true, true, 'The "' . $nodetext . '" node can not be expanded'); |
160 | // Check if the node is a link AND a branch. | |
161 | if (strtolower($node->getTagName()) === 'a') { | |
162 | // We just want to expand the node, we don't want to follow it. | |
163 | $node = $node->getParent(); | |
164 | } | |
b1bc8cfc DM |
165 | $node->click(); |
166 | } | |
167 | ||
c4509f80 DM |
168 | /** |
169 | * Collapses the selected node of the navigation tree that matches the text. | |
170 | * | |
171 | * @Given /^I collapse "(?P<nodetext_string>(?:[^"]|\\")*)" node$/ | |
172 | * @throws ExpectationException | |
173 | * @param string $nodetext | |
817d39b8 | 174 | * @return bool|void |
c4509f80 DM |
175 | */ |
176 | public function i_collapse_node($nodetext) { | |
177 | ||
178 | // No collapsible nodes with non-JS browsers. | |
179 | if (!$this->running_javascript()) { | |
fb99ef1d | 180 | return true; |
c4509f80 DM |
181 | } |
182 | ||
817d39b8 SH |
183 | $node = $this->get_node_text_node($nodetext, true, false, 'The "' . $nodetext . '" node can not be collapsed'); |
184 | // Check if the node is a link AND a branch. | |
185 | if (strtolower($node->getTagName()) === 'a') { | |
186 | // We just want to expand the node, we don't want to follow it. | |
187 | $node = $node->getParent(); | |
188 | } | |
c4509f80 DM |
189 | $node->click(); |
190 | } | |
26fb3f5b | 191 | |
26fb3f5b | 192 | /** |
1f631c7c | 193 | * Click link in navigation tree that matches the text in parentnode/s (seperated using greater-than character if more than one) |
a542553d | 194 | * |
26fb3f5b RT |
195 | * @Given /^I navigate to "(?P<nodetext_string>(?:[^"]|\\")*)" node in "(?P<parentnodes_string>(?:[^"]|\\")*)"$/ |
196 | * | |
197 | * @throws ExpectationException | |
198 | * @param string $nodetext navigation node to click. | |
199 | * @param string $parentnodes comma seperated list of parent nodes. | |
a542553d | 200 | * @return void |
26fb3f5b RT |
201 | */ |
202 | public function i_navigate_to_node_in($nodetext, $parentnodes) { | |
a542553d DM |
203 | |
204 | // Site admin is different and needs special treatment. | |
205 | $siteadminstr = get_string('administrationsite'); | |
206 | ||
26fb3f5b | 207 | // Create array of all parentnodes. |
1f631c7c | 208 | $parentnodes = array_map('trim', explode('>', $parentnodes)); |
26fb3f5b RT |
209 | $countparentnode = count($parentnodes); |
210 | ||
a542553d DM |
211 | // If JS is disabled and Site administration is not expanded we |
212 | // should follow it, so all the lower-level nodes are available. | |
213 | if (!$this->running_javascript()) { | |
214 | if ($parentnodes[0] === $siteadminstr) { | |
215 | // We don't know if there if Site admin is already expanded so | |
216 | // don't wait, it is non-JS and we already waited for the DOM. | |
217 | if ($siteadminlink = $this->getSession()->getPage()->find('named', array('link', "'" . $siteadminstr . "'"))) { | |
218 | $siteadminlink->click(); | |
219 | } | |
220 | } | |
221 | } | |
222 | ||
26fb3f5b RT |
223 | // Expand first node, and get it. |
224 | $node = $this->get_top_navigation_node($parentnodes[0]); | |
225 | ||
226 | // Expand parent, sub-parent nodes in navigation if js enabled. | |
227 | if ($node->hasClass('collapsed') || ($node->hasAttribute('data-loaded') && $node->getAttribute('data-loaded') == 0)) { | |
228 | $xpath = "/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]/span"; | |
229 | $nodetoexpand = $node->find('xpath', $xpath); | |
370e88d4 DM |
230 | |
231 | if ($this->running_javascript()) { | |
232 | $this->ensure_node_is_visible($nodetoexpand); | |
233 | $nodetoexpand->click(); | |
a542553d DM |
234 | |
235 | // Site administration node needs to be expanded. | |
236 | if ($parentnodes[0] === $siteadminstr) { | |
237 | $this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS); | |
238 | } | |
370e88d4 | 239 | } |
26fb3f5b RT |
240 | } |
241 | ||
242 | // If sub-parent nodes then get to the last one. | |
243 | if ($countparentnode > 1) { | |
244 | for ($i = 1; $i < $countparentnode; $i++) { | |
245 | $node = $this->get_navigation_node($parentnodes[$i], $node); | |
246 | ||
247 | // Keep expanding all sub-parents if js enabled. | |
248 | if ($this->running_javascript()) { | |
249 | $xpath = "/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]"; | |
250 | if ($node->hasClass('collapsed')) { | |
251 | $nodetoexpand = $node->find('xpath', $xpath); | |
370e88d4 DM |
252 | if ($this->running_javascript()) { |
253 | $this->ensure_node_is_visible($nodetoexpand); | |
254 | $nodetoexpand->click(); | |
255 | } | |
26fb3f5b RT |
256 | } |
257 | } | |
258 | } | |
259 | } | |
260 | ||
261 | // Finally, click on requested node under navigation. | |
262 | $nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($nodetext); | |
263 | $xpath = "/ul/li/p[contains(concat(' ', normalize-space(@class), ' '), ' tree_item ')]" . | |
264 | "/a[normalize-space(.)=" . $nodetextliteral . "]"; | |
265 | $node = $node->find('xpath', $xpath); | |
266 | ||
370e88d4 | 267 | // Throw exception if no node found. |
26fb3f5b | 268 | if (!$node) { |
a542553d DM |
269 | throw new ExpectationException('Navigation node "' . $nodetext . '" not found under "' . |
270 | $parentnodes . '"', $this->getSession()); | |
26fb3f5b | 271 | } |
370e88d4 DM |
272 | |
273 | if ($this->running_javascript()) { | |
274 | $this->ensure_node_is_visible($node); | |
275 | } | |
276 | ||
26fb3f5b RT |
277 | $node->click(); |
278 | } | |
a542553d DM |
279 | |
280 | /** | |
281 | * Helper function to get top navigation node in tree. | |
282 | * | |
283 | * @throws ExpectationException if note not found. | |
284 | * @param string $nodetext name of top navigation node in tree. | |
285 | * @return NodeElement | |
286 | */ | |
287 | protected function get_top_navigation_node($nodetext) { | |
288 | ||
289 | // Avoid problems with quotes. | |
290 | $nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($nodetext); | |
291 | $exception = new ExpectationException('Top navigation node "' . $nodetext . ' not found in "', $this->getSession()); | |
292 | ||
293 | // First find in navigation block. | |
294 | $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]" . | |
295 | "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" . | |
296 | "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" . | |
297 | "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" . | |
298 | "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" . | |
299 | "/span[normalize-space(.)=" . $nodetextliteral ."]]" . | |
300 | "|" . | |
301 | "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" . | |
302 | "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" . | |
303 | "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" . | |
304 | "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" . | |
305 | "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" . | |
306 | "/span[normalize-space(.)=" . $nodetextliteral ."]]" . | |
307 | "|" . | |
308 | "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" . | |
309 | "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" . | |
310 | "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" . | |
311 | "/span[normalize-space(.)=" . $nodetextliteral ."]]"; | |
312 | $node = $this->find('xpath', $xpath, $exception); | |
313 | ||
314 | return $node; | |
315 | } | |
316 | ||
317 | /** | |
318 | * Helper function to get sub-navigation node. | |
319 | * | |
320 | * @throws ExpectationException if note not found. | |
321 | * @param string $nodetext node to find. | |
322 | * @param NodeElement $parentnode parent navigation node. | |
323 | * @return NodeElement. | |
324 | */ | |
325 | protected function get_navigation_node($nodetext, $parentnode = null) { | |
326 | ||
327 | // Avoid problems with quotes. | |
328 | $nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($nodetext); | |
329 | ||
330 | $xpath = "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" . | |
331 | "[child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" . | |
332 | "/child::span[normalize-space(.)=" . $nodetextliteral ."]]"; | |
333 | $node = $parentnode->find('xpath', $xpath); | |
334 | if (!$node) { | |
335 | $xpath = "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" . | |
336 | "[child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" . | |
337 | "/child::a[normalize-space(.)=" . $nodetextliteral ."]]"; | |
338 | $node = $parentnode->find('xpath', $xpath); | |
339 | } | |
340 | ||
341 | if (!$node) { | |
342 | throw new ExpectationException('Sub-navigation node "' . $nodetext . '" not found under "' . | |
343 | $parentnode->getText() . '"', $this->getSession()); | |
344 | } | |
345 | return $node; | |
346 | } | |
b1bc8cfc | 347 | } |