weekly release 3.7dev
[moodle.git] / theme / boost / tests / behat / behat_theme_boost_behat_navigation.php
1 <?php
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/>.
17 /**
18  * Navigation steps overrides.
19  *
20  * @copyright  2016 Damyon Wiese
21  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22  */
24 // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
26 require_once(__DIR__ . '/../../../../lib/tests/behat/behat_navigation.php');
28 use Behat\Mink\Exception\ExpectationException as ExpectationException;
29 use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
31 /**
32  * Steps definitions to navigate through the navigation tree nodes (overrides).
33  *
34  * @copyright  2016 Damyon Wiese
35  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
36  */
37 class behat_theme_boost_behat_navigation extends behat_navigation {
39     public function i_follow_in_the_user_menu($nodetext) {
41         if ($this->running_javascript()) {
42             // The user menu must be expanded when JS is enabled.
43             $xpath = "//div[contains(concat(' ', @class, ' '),  ' usermenu ')]//a[contains(concat(' ', @class, ' '), ' dropdown-toggle ')]";
44             $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
45         }
47         // Now select the link.
48         // The CSS path is always present, with or without JS.
49         $csspath = ".usermenu .dropdown-menu";
51         $this->execute('behat_general::i_click_on_in_the',
52             array($nodetext, "link", $csspath, "css_element")
53         );
54     }
56     protected function get_top_navigation_node($nodetext) {
58         // Avoid problems with quotes.
59         $nodetextliteral = behat_context_helper::escape($nodetext);
60         $exception = new ExpectationException('Top navigation node "' . $nodetext . ' not found in "', $this->getSession());
62         // First find in navigation block.
63         $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' card-text ')]" .
64             "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
65             "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
66             "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
67             "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
68             "/*[contains(normalize-space(.), " . $nodetextliteral .")]]" .
69             "|" .
70             "//div[contains(concat(' ', normalize-space(@class), ' '), ' card-text ')]/div" .
71             "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
72             "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
73             "/*[contains(normalize-space(.), " . $nodetextliteral .")]]";
75         $node = $this->find('xpath', $xpath, $exception);
77         return $node;
78     }
80     /**
81      * Opens the flat navigation drawer if it is not already open
82      *
83      * @When /^I open flat navigation drawer$/
84      * @throws ElementNotFoundException Thrown by behat_base::find
85      */
86     public function i_open_flat_navigation_drawer() {
87         if (!$this->running_javascript()) {
88             // Navigation drawer is always open without JS.
89             return;
90         }
91         $xpath = "//button[contains(@data-action,'toggle-drawer')]";
92         $node = $this->find('xpath', $xpath);
93         $expanded = $node->getAttribute('aria-expanded');
94         if ($expanded === 'false') {
95             $node->click();
96             $this->ensure_node_attribute_is_set($node, 'aria-expanded', 'true');
97             $this->wait_for_pending_js();
98         }
99     }
101     /**
102      * Closes the flat navigation drawer if it is open (does nothing if JS disabled)
103      *
104      * @When /^I close flat navigation drawer$/
105      * @throws ElementNotFoundException Thrown by behat_base::find
106      */
107     public function i_close_flat_navigation_drawer() {
108         if (!$this->running_javascript()) {
109             // Navigation drawer can not be closed without JS.
110             return;
111         }
112         $xpath = "//button[contains(@data-action,'toggle-drawer')]";
113         $node = $this->find('xpath', $xpath);
114         $expanded = $node->getAttribute('aria-expanded');
115         if ($expanded === 'true') {
116             $node->click();
117             $this->wait_for_pending_js();
118         }
119     }
121     /**
122      * Clicks link with specified id|title|alt|text in the flat navigation drawer.
123      *
124      * @When /^I select "(?P<link_string>(?:[^"]|\\")*)" from flat navigation drawer$/
125      * @throws ElementNotFoundException Thrown by behat_base::find
126      * @param string $link
127      */
128     public function i_select_from_flat_navigation_drawer($link) {
129         $this->i_open_flat_navigation_drawer();
130         $this->execute('behat_general::i_click_on_in_the', [$link, 'link', '#nav-drawer', 'css_element']);
131     }
133     /**
134      * If we are not on the course main page, click on the course link in the navbar
135      */
136     protected function go_to_main_course_page() {
137         $url = $this->getSession()->getCurrentUrl();
138         if (!preg_match('|/course/view.php\?id=[\d]+$|', $url)) {
139             $this->find('xpath', '//header//div[@id=\'page-navbar\']//a[contains(@href,\'/course/view.php?id=\')]')->click();
140             $this->execute('behat_general::wait_until_the_page_is_ready');
141         }
142     }
144     /**
145      * Finds and clicks a link on the admin page (site administration or course administration)
146      *
147      * @param array $nodelist
148      */
149     protected function select_on_administration_page($nodelist) {
150         $parentnodes = $nodelist;
151         $lastnode = array_pop($parentnodes);
152         $xpath = '//section[@id=\'region-main\']';
154         // Check if there is a separate tab for this submenu of the page. If found go to it.
155         if ($parentnodes) {
156             $tabname = behat_context_helper::escape($parentnodes[0]);
157             $tabxpath = '//ul[@role=\'tablist\']/li/a[contains(normalize-space(.), ' . $tabname . ')]';
158             if ($node = $this->getSession()->getPage()->find('xpath', $tabxpath)) {
159                 if ($this->running_javascript()) {
160                     // Click on the tab and add 'active' tab to the xpath.
161                     $node->click();
162                     $xpath .= '//div[contains(@class,\'active\')]';
163                 } else {
164                     // Add the tab content selector to the xpath.
165                     $tabid = behat_context_helper::escape(ltrim($node->getAttribute('href'), '#'));
166                     $xpath .= '//div[@id = ' . $tabid . ']';
167                 }
168                 array_shift($parentnodes);
169             }
170         }
172         // Find a section with the parent name in it.
173         if ($parentnodes) {
174             // Find the section on the page (links may be repeating in different sections).
175             $section = behat_context_helper::escape($parentnodes[0]);
176             $xpath .= '//div[@class=\'row\' and contains(.,'.$section.')]';
177         }
179         // Find a link and click on it.
180         $linkname = behat_context_helper::escape($lastnode);
181         $xpath .= '//a[contains(normalize-space(.), ' . $linkname . ')]';
182         if (!$node = $this->getSession()->getPage()->find('xpath', $xpath)) {
183             throw new ElementNotFoundException($this->getSession(), 'Link "' . join(' > ', $nodelist) . '"" not found on the page');
184         }
185         $node->click();
186         $this->wait_for_pending_js();
187     }
189     /**
190      * Locates the administration menu in the <header> element and returns its xpath
191      *
192      * @param bool $mustexist if specified throws an exception if menu is not found
193      * @return null|string
194      */
195     protected function find_header_administration_menu($mustexist = false) {
196         $menuxpath = '//header[@id=\'page-header\']//div[contains(@class,\'moodle-actionmenu\')]';
197         if ($mustexist) {
198             $exception = new ElementNotFoundException($this->getSession(), 'Page header administration menu is not found');
199             $this->find('xpath', $menuxpath, $exception);
200         } else if (!$this->getSession()->getPage()->find('xpath', $menuxpath)) {
201             return null;
202         }
203         return $menuxpath;
204     }
206     /**
207      * Locates the administration menu on the page (but not in the header) and returns its xpath
208      *
209      * @param bool $mustexist if specified throws an exception if menu is not found
210      * @return null|string
211      */
212     protected function find_page_administration_menu($mustexist = false) {
213         $menuxpath = '//div[@id=\'region-main-settings-menu\']';
214         if ($mustexist) {
215             $exception = new ElementNotFoundException($this->getSession(), 'Page administration menu is not found');
216             $this->find('xpath', $menuxpath, $exception);
217         } else if (!$this->getSession()->getPage()->find('xpath', $menuxpath)) {
218             return null;
219         }
220         return $menuxpath;
221     }
223     /**
224      * Toggles administration menu
225      *
226      * @param string $menuxpath (optional) xpath to the page administration menu if already known
227      */
228     protected function toggle_page_administration_menu($menuxpath = null) {
229         if (!$menuxpath) {
230             $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu();
231         }
232         if ($menuxpath && $this->running_javascript()) {
233             $this->find('xpath', $menuxpath . '//a[@data-toggle=\'dropdown\']')->click();
234             $this->wait_for_pending_js();
235         }
236     }
238     /**
239      * Finds a page edit cog and select an item from it
240      *
241      * If the page edit cog is in the page header and the item is not found there, click "More..." link
242      * and find the item on the course/frontpage administration page
243      *
244      * @param array $nodelist
245      * @throws ElementNotFoundException
246      */
247     protected function select_from_administration_menu($nodelist) {
248         // Find administration menu.
249         if ($menuxpath = $this->find_header_administration_menu()) {
250             $isheader = true;
251         } else {
252             $menuxpath = $this->find_page_administration_menu(true);
253             $isheader = false;
254         }
256         $this->toggle_page_administration_menu($menuxpath);
258         if (!$isheader || count($nodelist) == 1) {
259             $lastnode = end($nodelist);
260             $linkname = behat_context_helper::escape($lastnode);
261             $link = $this->getSession()->getPage()->find('xpath', $menuxpath . '//a[contains(normalize-space(.), ' . $linkname . ')]');
262             if ($link) {
263                 $link->click();
264                 $this->wait_for_pending_js();
265                 return;
266             }
267         }
269         if ($isheader) {
270             // Course administration and Front page administration will have subnodes under "More...".
271             $linkname = behat_context_helper::escape(get_string('morenavigationlinks'));
272             $link = $this->getSession()->getPage()->find('xpath', $menuxpath . '//a[contains(normalize-space(.), ' . $linkname . ')]');
273             if ($link) {
274                 $link->click();
275                 $this->execute('behat_general::wait_until_the_page_is_ready');
276                 $this->select_on_administration_page($nodelist);
277                 return;
278             }
279         }
281         throw new ElementNotFoundException($this->getSession(),
282             'Link "' . join(' > ', $nodelist) . '" not found in the current page edit menu"');
283     }
285     public function should_exist_in_current_page_administration($element, $selectortype) {
286         $nodes = array_map('trim', explode('>', $element));
287         $nodetext = end($nodes);
289         // Find administration menu.
290         $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu(true);
292         $this->toggle_page_administration_menu($menuxpath);
293         $this->execute('behat_general::should_exist_in_the', [$nodetext, $selectortype, $menuxpath, 'xpath_element']);
294         $this->toggle_page_administration_menu($menuxpath);
295     }
297     public function should_not_exist_in_current_page_administration($element, $selectortype) {
298         $nodes = array_map('trim', explode('>', $element));
299         $nodetext = end($nodes);
301         // Find administration menu.
302         $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu();
303         if (!$menuxpath) {
304             // Menu not found, exit.
305             return;
306         }
308         $this->toggle_page_administration_menu($menuxpath);
309         $this->execute('behat_general::should_not_exist_in_the', [$nodetext, $selectortype, $menuxpath, 'xpath_element']);
310         $this->toggle_page_administration_menu($menuxpath);
311     }
313     public function i_navigate_to_in_current_page_administration($nodetext) {
314         $nodelist = array_map('trim', explode('>', $nodetext));
315         $this->select_from_administration_menu($nodelist);
316     }
318     public function i_navigate_to_in_site_administration($nodetext) {
319         $nodelist = array_map('trim', explode('>', $nodetext));
320         $this->i_select_from_flat_navigation_drawer(get_string('administrationsite'));
321         $this->select_on_administration_page($nodelist);
322     }