2 // This file is part of Moodle - http://moodle.org/
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.
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.
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/>.
18 * Navigation steps overrides.
20 * @copyright 2016 Damyon Wiese
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
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;
32 * Steps definitions to navigate through the navigation tree nodes (overrides).
34 * @copyright 2016 Damyon Wiese
35 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
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"));
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")
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 .")]]" .
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);
81 * Opens the flat navigation drawer if it is not already open
83 * @When /^I open flat navigation drawer$/
84 * @throws ElementNotFoundException Thrown by behat_base::find
86 public function i_open_flat_navigation_drawer() {
87 if (!$this->running_javascript()) {
88 // Navigation drawer is always open without JS.
91 $xpath = "//button[contains(@data-action,'toggle-drawer')]";
92 $node = $this->find('xpath', $xpath);
93 $expanded = $node->getAttribute('aria-expanded');
94 if ($expanded === 'false') {
96 $this->ensure_node_attribute_is_set($node, 'aria-expanded', 'true');
97 $this->wait_for_pending_js();
102 * Closes the flat navigation drawer if it is open (does nothing if JS disabled)
104 * @When /^I close flat navigation drawer$/
105 * @throws ElementNotFoundException Thrown by behat_base::find
107 public function i_close_flat_navigation_drawer() {
108 if (!$this->running_javascript()) {
109 // Navigation drawer can not be closed without JS.
112 $xpath = "//button[contains(@data-action,'toggle-drawer')]";
113 $node = $this->find('xpath', $xpath);
114 $expanded = $node->getAttribute('aria-expanded');
115 if ($expanded === 'true') {
117 $this->wait_for_pending_js();
122 * Clicks link with specified id|title|alt|text in the flat navigation drawer.
124 * @When /^I select "(?P<link_string>(?:[^"]|\\")*)" from flat navigation drawer$/
125 * @throws ElementNotFoundException Thrown by behat_base::find
126 * @param string $link
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']);
134 * If we are not on the course main page, click on the course link in the navbar
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');
145 * Finds and clicks a link on the admin page (site administration or course administration)
147 * @param array $nodelist
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.
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.
162 $xpath .= '//div[contains(@class,\'active\')]';
164 // Add the tab content selector to the xpath.
165 $tabid = behat_context_helper::escape(ltrim($node->getAttribute('href'), '#'));
166 $xpath .= '//div[@id = ' . $tabid . ']';
168 array_shift($parentnodes);
172 // Find a section with the parent name in it.
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.')]';
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');
186 $this->wait_for_pending_js();
190 * Locates the administration menu in the <header> element and returns its xpath
192 * @param bool $mustexist if specified throws an exception if menu is not found
193 * @return null|string
195 protected function find_header_administration_menu($mustexist = false) {
196 $menuxpath = '//header[@id=\'page-header\']//div[contains(@class,\'moodle-actionmenu\')]';
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)) {
207 * Locates the administration menu on the page (but not in the header) and returns its xpath
209 * @param bool $mustexist if specified throws an exception if menu is not found
210 * @return null|string
212 protected function find_page_administration_menu($mustexist = false) {
213 $menuxpath = '//div[@id=\'region-main-settings-menu\']';
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)) {
224 * Toggles administration menu
226 * @param string $menuxpath (optional) xpath to the page administration menu if already known
228 protected function toggle_page_administration_menu($menuxpath = null) {
230 $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu();
232 if ($menuxpath && $this->running_javascript()) {
233 $this->find('xpath', $menuxpath . '//a[@data-toggle=\'dropdown\']')->click();
234 $this->wait_for_pending_js();
239 * Finds a page edit cog and select an item from it
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
244 * @param array $nodelist
245 * @throws ElementNotFoundException
247 protected function select_from_administration_menu($nodelist) {
248 // Find administration menu.
249 if ($menuxpath = $this->find_header_administration_menu()) {
252 $menuxpath = $this->find_page_administration_menu(true);
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 . ')]');
264 $this->wait_for_pending_js();
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 . ')]');
275 $this->execute('behat_general::wait_until_the_page_is_ready');
276 $this->select_on_administration_page($nodelist);
281 throw new ElementNotFoundException($this->getSession(),
282 'Link "' . join(' > ', $nodelist) . '" not found in the current page edit menu"');
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);
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();
304 // Menu not found, exit.
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);
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);
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);