MDL-58428 theme: Shift templates ready for Bootstrapbase removal
authorMathew May <mathewm@hotmail.co.nz>
Wed, 16 Jan 2019 04:03:40 +0000 (12:03 +0800)
committerMathew May <mathewm@hotmail.co.nz>
Fri, 22 Feb 2019 07:19:33 +0000 (15:19 +0800)
Move behat override steps from boost into their Moodle core locations &
Move the effected core behat steps into bootstrapbase as override steps.

25 files changed:
admin/tests/behat/behat_admin.php
blocks/tests/behat/behat_blocks.php
course/tests/behat/behat_course.php
grade/tests/behat/behat_grade.php
lib/behat/behat_files.php
lib/tests/behat/behat_action_menu.php
lib/tests/behat/behat_deprecated.php
lib/tests/behat/behat_navigation.php
mod/quiz/tests/behat/behat_mod_quiz.php
repository/tests/behat/behat_filepicker.php
repository/upload/tests/behat/behat_repository_upload.php
theme/boost/tests/behat/behat_theme_boost_behat_blocks.php [deleted file]
theme/boost/tests/behat/behat_theme_boost_behat_navigation.php [deleted file]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_action_menu.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_action_menu.php with 71% similarity]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_admin.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_admin.php with 68% similarity]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_blocks.php [new file with mode: 0644]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_course.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_course.php with 68% similarity]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_deprecated.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_deprecated.php with 59% similarity]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_filepicker.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_filepicker.php with 56% similarity]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_files.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_files.php with 79% similarity]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_grade.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_grade.php with 72% similarity]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_mod_quiz.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_mod_quiz.php with 79% similarity]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_navigation.php [new file with mode: 0644]
theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_repository_upload.php [moved from theme/boost/tests/behat/behat_theme_boost_behat_repository_upload.php with 74% similarity]
theme/bootstrapbase/tests/behat/blacklist.json [new file with mode: 0644]

index 73ce572..b2fd070 100644 (file)
@@ -55,16 +55,12 @@ class behat_admin extends behat_base {
 
         foreach ($data as $label => $value) {
 
-            // We expect admin block to be visible, otherwise go to homepage.
-            if (!$this->getSession()->getPage()->find('css', '.block_settings')) {
-                $this->getSession()->visit($this->locate_path('/'));
-                $this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
-            }
+            $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', [get_string('administrationsite')]);
 
             // Search by label.
-            $searchbox = $this->find_field(get_string('searchinsettings', 'admin'));
+            $searchbox = $this->find_field(get_string('query', 'admin'));
             $searchbox->setValue($label);
-            $submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
+            $submitsearch = $this->find('css', 'form input[type=submit][name=search]');
             $submitsearch->press();
 
             $this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
@@ -78,21 +74,24 @@ class behat_admin extends behat_base {
 
             // Single element settings.
             try {
-                $fieldxpath = "//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]" .
-                    "[@id=//label[contains(normalize-space(.), $label)]/@for or " .
-                    "@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]";
+                $fieldxpath = "//*[self::input | self::textarea | self::select]" .
+                        "[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]" .
+                        "[@id=//label[contains(normalize-space(.), $label)]/@for or " .
+                        "@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]";
                 $fieldnode = $this->find('xpath', $fieldxpath, $exception);
 
-                $formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" .
-                    "/child::div[contains(concat(' ', @class, ' '),  ' form-')]/child::*/parent::div");
+                $formfieldtypenode = $this->find('xpath', $fieldxpath .
+                        "/ancestor::div[contains(concat(' ', @class, ' '), ' form-setting ')]" .
+                        "/child::div[contains(concat(' ', @class, ' '),  ' form-')]/child::*/parent::div");
 
             } catch (ElementNotFoundException $e) {
 
                 // Multi element settings, interacting only the first one.
-                $fieldxpath = "//*[label[normalize-space(.)= $label]|span[normalize-space(.)= $label]]/" .
-                    "ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' form-item ')]" .
-                    "/descendant::div[@class='form-group']/descendant::*[self::input | self::textarea | self::select]" .
-                    "[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]";
+                $fieldxpath = "//*[label[contains(., $label)]|span[contains(., $label)]]" .
+                        "/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' form-item ')]" .
+                        "/descendant::div[contains(concat(' ', @class, ' '), ' form-group ')]" .
+                        "/descendant::*[self::input | self::textarea | self::select]" .
+                        "[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]";
                 $fieldnode = $this->find('xpath', $fieldxpath);
 
                 // It is the same one that contains the type.
@@ -101,6 +100,7 @@ class behat_admin extends behat_base {
 
             // Getting the class which contains the field type.
             $classes = explode(' ', $formfieldtypenode->getAttribute('class'));
+            $type = false;
             foreach ($classes as $class) {
                 if (substr($class, 0, 5) == 'form-') {
                     $type = substr($class, 5);
index b05d84c..5f55ca2 100644 (file)
@@ -46,15 +46,13 @@ class behat_blocks extends behat_base {
      * @param string $blockname
      */
     public function i_add_the_block($blockname) {
-        $this->execute('behat_forms::i_set_the_field_to',
-            array("bui_addblock", $this->escape($blockname))
-        );
+        $addblock = get_string('addblock');
+        $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', $addblock);
 
-        // If we are running without javascript we need to submit the form.
         if (!$this->running_javascript()) {
-            $this->execute('behat_general::i_click_on_in_the',
-                array(get_string('go'), "button", "#add_block", "css_element")
-            );
+            $this->execute('behat_general::i_click_on_in_the', [$blockname, 'link_exact', '#region-main', 'css_element']);
+        } else {
+            $this->execute('behat_general::i_click_on_in_the', [$blockname, 'link_exact', $addblock, 'dialogue']);
         }
     }
 
@@ -108,7 +106,7 @@ class behat_blocks extends behat_base {
         }
 
         $this->execute('behat_general::i_click_on_in_the',
-            array(get_string('actions'), "link", $this->escape($blockname), "block")
+                array("a[data-toggle='dropdown']", "css_element", $this->escape($blockname), "block")
         );
     }
 
@@ -137,7 +135,17 @@ class behat_blocks extends behat_base {
      * @param string $blockname
      */
     public function the_add_block_selector_should_contain_block($blockname) {
-        $this->execute('behat_forms::the_select_box_should_contain', [get_string('addblock'), $blockname]);
+        $addblock = get_string('addblock');
+        $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', $addblock);
+
+        $cancelstr = get_string('cancel');
+        if (!$this->running_javascript()) {
+            $this->execute('behat_general::should_exist_in_the', [$blockname, 'link_exact', '#region-main', 'css_element']);
+            $this->execute('behat_general::i_click_on_in_the', [$cancelstr, 'link_exact', '#region-main', 'css_element']);
+        } else {
+            $this->execute('behat_general::should_exist_in_the', [$blockname, 'link_exact', $addblock, 'dialogue']);
+            $this->execute('behat_general::i_click_on_in_the', [$cancelstr, 'button', $addblock, 'dialogue']);
+        }
     }
 
     /**
@@ -147,6 +155,16 @@ class behat_blocks extends behat_base {
      * @param string $blockname
      */
     public function the_add_block_selector_should_not_contain_block($blockname) {
-        $this->execute('behat_forms::the_select_box_should_not_contain', [get_string('addblock'), $blockname]);
+        $addblock = get_string('addblock');
+        $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', $addblock);
+
+        $cancelstr = get_string('cancel');
+        if (!$this->running_javascript()) {
+            $this->execute('behat_general::should_not_exist_in_the', [$blockname, 'link_exact', '#region-main', 'css_element']);
+            $this->execute('behat_general::i_click_on_in_the', [$cancelstr, 'link_exact', '#region-main', 'css_element']);
+        } else {
+            $this->execute('behat_general::should_not_exist_in_the', [$blockname, 'link_exact', $addblock, 'dialogue']);
+            $this->execute('behat_general::i_click_on_in_the', [$cancelstr, 'button', $addblock, 'dialogue']);
+        }
     }
 }
index 93b8860..ee06c76 100644 (file)
@@ -186,10 +186,11 @@ class behat_course extends behat_base {
             // We are on the frontpage.
             if ($section) {
                 // Section 1 represents the contents on the frontpage.
-                $sectionxpath = "//body[@id='page-site-index']/descendant::div[contains(concat(' ',normalize-space(@class),' '),' sitetopic ')]";
+                $sectionxpath = "//body[@id='page-site-index']" .
+                        "/descendant::div[contains(concat(' ',normalize-space(@class),' '),' sitetopic ')]";
             } else {
                 // Section 0 represents "Site main menu" block.
-                $sectionxpath = "//div[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]";
+                $sectionxpath = "//*[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]";
             }
         } else {
             // We are inside the course.
@@ -201,15 +202,16 @@ class behat_course extends behat_base {
         if ($this->running_javascript()) {
 
             // Clicks add activity or resource section link.
-            $sectionxpath = $sectionxpath . "/descendant::div[@class='section-modchooser']/span/a";
+            $sectionxpath = $sectionxpath . "/descendant::div" .
+                    "[contains(concat(' ', normalize-space(@class) , ' '), ' section-modchooser ')]/span/a";
             $sectionnode = $this->find('xpath', $sectionxpath);
             $sectionnode->click();
 
             // Clicks the selected activity if it exists.
             $activityxpath = "//div[@id='chooseform']/descendant::label" .
-                "/descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' typename ')]" .
-                "[normalize-space(.)=$activityliteral]" .
-                "/parent::label/child::input";
+                    "/descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' typename ')]" .
+                    "[normalize-space(.)=$activityliteral]" .
+                    "/parent::label/child::input";
             $activitynode = $this->find('xpath', $activityxpath);
             $activitynode->doubleClick();
 
@@ -217,8 +219,9 @@ class behat_course extends behat_base {
             // Without Javascript.
 
             // Selecting the option from the select box which contains the option.
-            $selectxpath = $sectionxpath . "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' section_add_menus ')]" .
-                "/descendant::select[option[normalize-space(.)=$activityliteral]]";
+            $selectxpath = $sectionxpath . "/descendant::div" .
+                    "[contains(concat(' ', normalize-space(@class), ' '), ' section_add_menus ')]" .
+                    "/descendant::select[option[normalize-space(.)=$activityliteral]]";
             $selectnode = $this->find('xpath', $selectxpath);
             $selectnode->selectOption($activity);
 
@@ -230,7 +233,6 @@ class behat_course extends behat_base {
 
     }
 
-
     /**
      * Opens a section edit menu if it is not already opened.
      *
@@ -248,7 +250,7 @@ class behat_course extends behat_base {
 
         // If it is already opened we do nothing.
         $xpath = $this->section_exists($sectionnumber);
-        $xpath .= "/descendant::div[contains(@class, 'section-actions')]/descendant::a[contains(@class, 'textmenu')]";
+        $xpath .= "/descendant::div[contains(@class, 'section-actions')]/descendant::a[contains(@data-toggle, 'dropdown')]";
 
         $exception = new ExpectationException('Section "' . $sectionnumber . '" was not found', $this->getSession());
         $menu = $this->find('xpath', $xpath, $exception);
@@ -550,8 +552,8 @@ class behat_course extends behat_base {
         // Edit menu should be visible.
         if ($this->is_course_editor()) {
             $xpath = $sectionxpath .
-                     "/descendant::div[contains(@class, 'section-actions')]" .
-                     "/descendant::a[contains(@class, 'textmenu')]";
+                    "/descendant::div[contains(@class, 'section-actions')]" .
+                    "/descendant::a[contains(@data-toggle, 'dropdown')]";
             if (!$this->getSession()->getPage()->find('xpath', $xpath)) {
                 throw new ExpectationException('The section edit menu is not available', $this->getSession());
             }
@@ -843,15 +845,23 @@ class behat_course extends behat_base {
 
         // If it is already opened we do nothing.
         $activitynode = $this->get_activity_node($activityname);
-        $classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
-        if (!empty($classes['action-menu-shown'])) {
+
+        // Find the menu.
+        $menunode = $activitynode->find('css', 'a[data-toggle=dropdown]');
+        if (!$menunode) {
+            throw new ExpectationException(sprintf('Could not find actions menu for the activity "%s"', $activityname),
+                    $this->getSession());
+        }
+        $expanded = $menunode->getAttribute('aria-expanded');
+        if ($expanded == 'true') {
             return;
         }
 
         $this->execute('behat_course::i_click_on_in_the_activity',
-            array("a[role='menuitem']", "css_element", $this->escape($activityname))
+                array("a[data-toggle='dropdown']", "css_element", $this->escape($activityname))
         );
 
+        $this->actions_menu_should_be_open($activityname);
     }
 
     /**
@@ -869,13 +879,19 @@ class behat_course extends behat_base {
 
         // If it is already closed we do nothing.
         $activitynode = $this->get_activity_node($activityname);
-        $classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
-        if (empty($classes['action-menu-shown'])) {
+        // Find the menu.
+        $menunode = $activitynode->find('css', 'a[data-toggle=dropdown]');
+        if (!$menunode) {
+            throw new ExpectationException(sprintf('Could not find actions menu for the activity "%s"', $activityname),
+                    $this->getSession());
+        }
+        $expanded = $menunode->getAttribute('aria-expanded');
+        if ($expanded != 'true') {
             return;
         }
 
         $this->execute('behat_course::i_click_on_in_the_activity',
-            array("a[role='menuitem']", "css_element", $this->escape($activityname))
+                array("a[data-toggle='dropdown']", "css_element", $this->escape($activityname))
         );
     }
 
@@ -892,10 +908,15 @@ class behat_course extends behat_base {
             throw new DriverException('Activities actions menu not available when Javascript is disabled');
         }
 
-        // If it is already closed we do nothing.
         $activitynode = $this->get_activity_node($activityname);
-        $classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
-        if (empty($classes['action-menu-shown'])) {
+        // Find the menu.
+        $menunode = $activitynode->find('css', 'a[data-toggle=dropdown]');
+        if (!$menunode) {
+            throw new ExpectationException(sprintf('Could not find actions menu for the activity "%s"', $activityname),
+                    $this->getSession());
+        }
+        $expanded = $menunode->getAttribute('aria-expanded');
+        if ($expanded != 'true') {
             throw new ExpectationException(sprintf("The action menu for '%s' is not open", $activityname), $this->getSession());
         }
     }
@@ -1039,18 +1060,18 @@ class behat_course extends behat_base {
 
         // Determine the future new activity xpath from the former one.
         $duplicatedxpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]" .
-            "[contains(., $activityliteral)]/following-sibling::li";
-        $duplicatedactionsmenuxpath = $duplicatedxpath . "/descendant::a[@role='menuitem']";
+                "[contains(., $activityliteral)]/following-sibling::li";
+        $duplicatedactionsmenuxpath = $duplicatedxpath . "/descendant::a[@data-toggle='dropdown']";
 
         if ($this->running_javascript()) {
             // We wait until the AJAX request finishes and the section is visible again.
             $hiddenlightboxxpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]" .
-                "[contains(., $activityliteral)]" .
-                "/ancestor::li[contains(concat(' ', normalize-space(@class), ' '), ' section ')]" .
-                "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]";
+                    "[contains(., $activityliteral)]" .
+                    "/ancestor::li[contains(concat(' ', normalize-space(@class), ' '), ' section ')]" .
+                    "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]";
 
             $this->execute("behat_general::wait_until_exists",
-                array($this->escape($hiddenlightboxxpath), "xpath_element")
+                    array($this->escape($hiddenlightboxxpath), "xpath_element")
             );
 
             // Close the original activity actions menu.
@@ -1059,13 +1080,13 @@ class behat_course extends behat_base {
             // The next sibling of the former activity will be the duplicated one, so we click on it from it's xpath as, at
             // this point, it don't even exists in the DOM (the steps are executed when we return them).
             $this->execute('behat_general::i_click_on',
-                array($this->escape($duplicatedactionsmenuxpath), "xpath_element")
+                    array($this->escape($duplicatedactionsmenuxpath), "xpath_element")
             );
         }
 
         // We force the xpath as otherwise mink tries to interact with the former one.
         $this->execute('behat_general::i_click_on_in_the',
-            array(get_string('editsettings'), "link", $this->escape($duplicatedxpath), "xpath_element")
+                array(get_string('editsettings'), "link", $this->escape($duplicatedxpath), "xpath_element")
         );
 
         $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $data);
@@ -1281,8 +1302,8 @@ class behat_course extends behat_base {
     protected function is_course_editor() {
 
         // We don't need to behat_base::spin() here as all is already loaded.
-        if (!$this->getSession()->getPage()->findButton(get_string('turneditingoff')) &&
-                !$this->getSession()->getPage()->findButton(get_string('turneditingon'))) {
+        if (!$this->getSession()->getPage()->findLink(get_string('turneditingoff')) &&
+                !$this->getSession()->getPage()->findLink(get_string('turneditingon'))) {
             return false;
         }
 
@@ -1842,7 +1863,8 @@ class behat_course extends behat_base {
      * @throws Behat\Mink\Exception\ExpectationException
      */
     protected function user_clicks_on_management_listing_action($listingtype, $listingnode, $action) {
-        $actionsnode = $listingnode->find('xpath', "//*[contains(concat(' ', normalize-space(@class), ' '), '{$listingtype}-item-actions')]");
+        $actionsnode = $listingnode->find('xpath', "//*" .
+                "[contains(concat(' ', normalize-space(@class), ' '), '{$listingtype}-item-actions')]");
         if (!$actionsnode) {
             throw new ExpectationException("Could not find the actions for $listingtype", $this->getSession());
         }
@@ -1851,7 +1873,7 @@ class behat_course extends behat_base {
             throw new ExpectationException("Expected action was not available or not found ($action)", $this->getSession());
         }
         if ($this->running_javascript() && !$actionnode->isVisible()) {
-            $actionsnode->find('css', 'a.toggle-display')->click();
+            $actionsnode->find('css', 'a[data-toggle=dropdown]')->click();
             $actionnode = $actionsnode->find('css', '.action-'.$action);
         }
         $actionnode->click();
@@ -1874,10 +1896,7 @@ class behat_course extends behat_base {
      * @Given /^I navigate to course participants$/
      */
     public function i_navigate_to_course_participants() {
-        $coursestr = behat_context_helper::escape(get_string('courses'));
-        $mycoursestr = behat_context_helper::escape(get_string('mycourses'));
-        $xpath = "//div[contains(@class,'block')]//li[p/*[string(.)=$coursestr or string(.)=$mycoursestr]]";
-        $this->execute('behat_general::i_click_on_in_the', [get_string('participants'), 'link', $xpath, 'xpath_element']);
+        $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', get_string('participants'));
     }
 
     /**
index 12e6789..b17c4cd 100644 (file)
@@ -76,15 +76,18 @@ class behat_grade extends behat_base {
         $gradeitem = behat_context_helper::escape($gradeitem);
 
         if ($this->running_javascript()) {
-            $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]//a[contains(@class,'toggle-display')]";
+            $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]";
             if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
-                $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
+                $this->execute("behat_action_menu::i_open_the_action_menu_in",
+                        array("//tr[contains(.,$gradeitem)]",
+                                "xpath_element"));
             }
         }
 
         $savechanges = get_string('savechanges', 'grades');
         $edit = behat_context_helper::escape(get_string('edit') . '  ');
-        $linkxpath = "//a[./img[starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
+        $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
+                "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
 
         $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
         $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $data);
@@ -128,16 +131,19 @@ class behat_grade extends behat_base {
         $gradeitem = behat_context_helper::escape($gradeitem);
 
         if ($this->running_javascript()) {
-            $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]//a[contains(@class,'toggle-display')]";
+            $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]";
             if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
-                $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
+                $this->execute("behat_action_menu::i_open_the_action_menu_in",
+                        array("//tr[contains(.,$gradeitem)]",
+                                "xpath_element"));
             }
         }
 
         // Going to edit calculation.
         $savechanges = get_string('savechanges', 'grades');
         $edit = behat_context_helper::escape(get_string('editcalculation', 'grades'));
-        $linkxpath = "//a[./img[starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
+        $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
+                "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
         $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
 
         // Mapping names to idnumbers.
@@ -145,11 +151,12 @@ class behat_grade extends behat_base {
         foreach ($datahash as $gradeitem => $idnumber) {
             // This xpath looks for course, categories and items with the provided name.
             // Grrr, we can't equal in categoryitem and courseitem because there is a line jump...
-            $inputxpath ="//input[@class='idnumber'][" .
-                "parent::li[@class='item'][text()='" . $gradeitem . "']" .
-                " or " .
-                "parent::li[@class='categoryitem' or @class='courseitem']/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
-            "]";
+            $inputxpath = "//input[@class='idnumber'][" .
+                    "parent::li[@class='item'][text()='" . $gradeitem . "']" .
+                    " or " .
+                    "parent::li[@class='categoryitem' or @class='courseitem']" .
+                    "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
+                    "]";
             $this->execute('behat_forms::i_set_the_field_with_xpath_to', array($inputxpath, $idnumber));
         }
 
@@ -174,17 +181,18 @@ class behat_grade extends behat_base {
         $gradeitem = behat_context_helper::escape($gradeitem);
 
         if ($this->running_javascript()) {
-            $xpath = "//tr[contains(.,$gradecategorytotal)]//*[contains(@class,'moodle-actionmenu')]" .
-                "//a[contains(@class,'toggle-display')]";
+            $xpath = "//tr[contains(.,$gradecategorytotal)]//*[contains(@class,'moodle-actionmenu')]";
             if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
-                $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
+                $xpath = "//tr[contains(.,$gradecategorytotal)]";
+                $this->execute("behat_action_menu::i_open_the_action_menu_in", array($xpath, "xpath_element"));
             }
         }
 
         // Going to edit calculation.
         $savechanges = get_string('savechanges', 'grades');
         $edit = behat_context_helper::escape(get_string('editcalculation', 'grades'));
-        $linkxpath = "//a[./img[starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
+        $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
+                "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
         $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
 
         // Mapping names to idnumbers.
@@ -193,11 +201,11 @@ class behat_grade extends behat_base {
             // This xpath looks for course, categories and items with the provided name.
             // Grrr, we can't equal in categoryitem and courseitem because there is a line jump...
             $inputxpath = "//input[@class='idnumber'][" .
-                "parent::li[@class='item'][text()='" . $gradeitem . "']" .
-                " | " .
-                "parent::li[@class='categoryitem' | @class='courseitem']" .
-                "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
-            "]";
+                    "parent::li[@class='item'][text()='" . $gradeitem . "']" .
+                    " | " .
+                    "parent::li[@class='categoryitem' | @class='courseitem']" .
+                    "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
+                    "]";
             $this->execute('behat_forms::i_set_the_field_with_xpath_to', array($inputxpath, $idnumber));
         }
 
@@ -221,9 +229,10 @@ class behat_grade extends behat_base {
 
         if ($this->running_javascript()) {
             $gradeitemliteral = behat_context_helper::escape($gradeitem);
-            $xpath = "//tr[contains(.,$gradeitemliteral)]//*[contains(@class,'moodle-actionmenu')]//a[contains(@class,'toggle-display')]";
+            $xpath = "//tr[contains(.,$gradeitemliteral)]//*[contains(@class,'moodle-actionmenu')]";
             if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
-                $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
+                $xpath = "//tr[contains(.,$gradeitemliteral)]";
+                $this->execute("behat_action_menu::i_open_the_action_menu_in", array($xpath, "xpath_element"));
             }
         }
 
@@ -290,11 +299,10 @@ class behat_grade extends behat_base {
      * @param string $gradepath
      */
     public function i_navigate_to_in_the_course_gradebook($gradepath) {
-        // If we are not on one of the gradebook pages already, follow "Grades" link in the navigation block.
+        // If we are not on one of the gradebook pages already, follow "Grades" link in the navigation drawer.
         $xpath = '//div[contains(@class,\'grade-navigation\')]';
         if (!$this->getSession()->getPage()->findAll('xpath', $xpath)) {
-            $this->execute("behat_general::i_click_on_in_the", array(get_string('grades'), 'link',
-                get_string('pluginname', 'block_navigation'), 'block'));
+            $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', get_string('grades'));
         }
 
         $this->select_in_gradebook_tabs($gradepath);
index 43ed54f..d2cb84b 100644 (file)
@@ -58,6 +58,7 @@ class behat_files extends behat_base {
      * @param string $filepickerelement The filepicker form field label
      * @return NodeElement The hidden element node.
      */
+
     protected function get_filepicker_node($filepickerelement) {
 
         // More info about the problem (in case there is a problem).
@@ -66,18 +67,18 @@ class behat_files extends behat_base {
         // If no file picker label is mentioned take the first file picker from the page.
         if (empty($filepickerelement)) {
             $filepickercontainer = $this->find(
-                'xpath',
-                "//*[@data-fieldtype=\"filemanager\"]",
-                $exception
+                    'xpath',
+                    "//*[@data-fieldtype=\"filemanager\"]",
+                    $exception
             );
         } else {
             // Gets the ffilemanager node specified by the locator which contains the filepicker container.
             $filepickerelement = behat_context_helper::escape($filepickerelement);
             $filepickercontainer = $this->find(
-                'xpath',
-                "//input[./@id = //label[normalize-space(.)=$filepickerelement]/@for]" .
-                    '//ancestor::div[@data-fieldtype="filemanager" or @data-fieldtype="filepicker"]',
-                $exception
+                    'xpath',
+                    "//input[./@id = //label[normalize-space(.)=$filepickerelement]/@for]" .
+                    "//ancestor::*[@data-fieldtype = 'filemanager' or @data-fieldtype = 'filepicker']",
+                    $exception
             );
         }
 
index dca01b7..f3055ff 100644 (file)
@@ -49,16 +49,11 @@ class behat_action_menu extends behat_base {
      * @return void
      */
     public function i_open_the_action_menu_in($element, $selectortype) {
-        if (!$this->running_javascript()) {
-            // Action menus automatically expand in a visible list of actions when Javascript is disabled.
-            return;
-        }
         // Gets the node based on the requested selector type and locator.
-        $node = $this->get_node_in_container("css_element", "[role=menuitem][aria-haspopup=true]", $selectortype, $element);
+        $node = $this->get_node_in_container("css_element", "[role=button][aria-haspopup=true]", $selectortype, $element);
 
         // Check if it is not already opened.
-        $menunode = $this->find('css', '[aria-labelledby='.$node->getAttribute('id').']');
-        if ($menunode->getAttribute('aria-hidden') === 'false') {
+        if ($node->getAttribute('aria-expanded') === 'true') {
             return;
         }
 
@@ -73,15 +68,13 @@ class behat_action_menu extends behat_base {
      * @param string $linkstring
      * @return void
      */
-    public function i_choose_in_the_open_action_menu($linkstring) {
+    public function i_choose_in_the_open_action_menu($menuitemstring) {
         if (!$this->running_javascript()) {
             throw new DriverException('Action menu steps are not available with Javascript disabled');
         }
         // Gets the node based on the requested selector type and locator.
-        $node = $this->get_node_in_container("link",
-            $linkstring,
-            "css_element",
-            ".moodle-actionmenu [role=menu][aria-hidden=false]");
+        $menuselector = ".moodle-actionmenu .dropdown.show .dropdown-menu";
+        $node = $this->get_node_in_container("link", $menuitemstring, "css_element", $menuselector);
         $this->ensure_node_is_visible($node);
         $node->click();
     }
index 67b3d8e..c7a693f 100644 (file)
@@ -57,8 +57,6 @@ class behat_deprecated extends behat_base {
     /**
      * Click link in navigation tree that matches the text in parentnode/s (seperated using greater-than character if more than one)
      *
-     * @Given /^I navigate to "(?P<nodetext_string>(?:[^"]|\\")*)" node in "(?P<parentnodes_string>(?:[^"]|\\")*)"$/
-     *
      * @throws ExpectationException
      * @param string $nodetext navigation node to click.
      * @param string $parentnodes comma seperated list of parent nodes.
@@ -78,7 +76,35 @@ class behat_deprecated extends behat_base {
         $this->deprecated_message($alternative);
 
         $parentnodes = array_map('trim', explode('>', $parentnodes));
-        $this->execute('behat_navigation::select_node_in_navigation', array($nodetext, $parentnodes));
+        $nodelist = array_merge($parentnodes, [$nodetext]);
+        $firstnode = array_shift($nodelist);
+
+        if ($firstnode === get_string('administrationsite')) {
+            $this->execute('behat_theme_boost_behat_navigation::i_select_from_flat_navigation_drawer',
+                    array(get_string('administrationsite')));
+            $this->execute('behat_theme_boost_behat_navigation::select_on_administration_page', array($nodelist));
+            return;
+        }
+
+        if ($firstnode === get_string('sitepages')) {
+            if ($nodetext === get_string('calendar', 'calendar')) {
+                $this->execute('behat_theme_boost_behat_navigation::i_select_from_flat_navigation_drawer',
+                        array(($nodetext)));
+            } else {
+                // TODO MDL-57120 other links under "Site pages" are not accessible without navigation block.
+                $this->execute('behat_theme_boost_behat_navigation::select_node_in_navigation',
+                        array($nodetext, $parentnodes));
+            }
+            return;
+        }
+
+        if ($firstnode === get_string('courseadministration')) {
+            // Administration menu is available only on the main course page where settings in Administration
+            // block (original purpose of the step) are available on every course page.
+            $this->execute('behat_theme_boost_behat_navigation::go_to_main_course_page', array());
+        }
+
+        $this->execute('behat_theme_boost_behat_navigation::select_from_administration_menu', array($nodelist));
     }
 
     /**
index fc7444c..eeab30e 100644 (file)
@@ -29,6 +29,7 @@ require_once(__DIR__ . '/../../behat/behat_base.php');
 
 use Behat\Mink\Exception\ExpectationException as ExpectationException;
 use Behat\Mink\Exception\DriverException as DriverException;
+use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
 
 /**
  * Steps definitions to navigate through the navigation tree nodes.
@@ -150,16 +151,16 @@ class behat_navigation extends behat_base {
 
         if ($this->running_javascript()) {
             // The user menu must be expanded when JS is enabled.
-            $xpath = "//div[@class='usermenu']//a[contains(concat(' ', @class, ' '), ' toggle-display ')]";
+            $xpath = "//div[contains(concat(' ', @class, ' '),  ' usermenu ')]//a[contains(concat(' ', @class, ' '), ' dropdown-toggle ')]";
             $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
         }
 
         // Now select the link.
         // The CSS path is always present, with or without JS.
-        $csspath = ".usermenu [data-rel='menu-content']";
+        $csspath = ".usermenu .dropdown-menu";
 
         $this->execute('behat_general::i_click_on_in_the',
-            array($nodetext, "link", $csspath, "css_element")
+                array($nodetext, "link", $csspath, "css_element")
         );
     }
 
@@ -318,29 +319,17 @@ class behat_navigation extends behat_base {
         $exception = new ExpectationException('Top navigation node "' . $nodetext . ' not found in "', $this->getSession());
 
         // First find in navigation block.
-        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]" .
-            "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
-            "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
-            "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
-            "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
-            "[span[normalize-space(.)=" . $nodetextliteral ."] or a[normalize-space(.)=" . $nodetextliteral ."]]]" .
-            "|" .
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
-            "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
-            "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
-            "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
-            "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
-            "/span[normalize-space(.)=" . $nodetextliteral ."]]" .
-            "|" .
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
-            "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
-            "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
-            "/span[normalize-space(.)=" . $nodetextliteral ."]]" .
-            "|" .
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
-            "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
-            "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
-            "/a[normalize-space(.)=" . $nodetextliteral ."]]";
+        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' card-text ')]" .
+                "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
+                "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
+                "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
+                "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
+                "/*[contains(normalize-space(.), " . $nodetextliteral .")]]" .
+                "|" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' card-text ')]/div" .
+                "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
+                "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
+                "/*[contains(normalize-space(.), " . $nodetextliteral .")]]";
 
         $node = $this->find('xpath', $xpath, $exception);
 
@@ -421,13 +410,8 @@ class behat_navigation extends behat_base {
      * @return void
      */
     public function i_navigate_to_in_current_page_administration($nodetext) {
-        $parentnodes = array_map('trim', explode('>', $nodetext));
-        // Find the name of the first category of the administration block tree.
-        $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span';
-        $node = $this->find('xpath', $xpath);
-        array_unshift($parentnodes, $node->getText());
-        $lastnode = array_pop($parentnodes);
-        $this->select_node_in_navigation($lastnode, $parentnodes);
+        $nodelist = array_map('trim', explode('>', $nodetext));
+        $this->select_from_administration_menu($nodelist);
     }
 
     /**
@@ -442,17 +426,15 @@ class behat_navigation extends behat_base {
      * @return void
      */
     public function should_exist_in_current_page_administration($element, $selectortype) {
-        $parentnodes = array_map('trim', explode('>', $element));
-        // Find the name of the first category of the administration block tree.
-        $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span';
-        $node = $this->find('xpath', $xpath);
-        array_unshift($parentnodes, $node->getText());
-        $lastnode = array_pop($parentnodes);
+        $nodes = array_map('trim', explode('>', $element));
+        $nodetext = end($nodes);
 
-        if (!$this->find_node_in_navigation($lastnode, $parentnodes, strtolower($selectortype))) {
-            throw new ExpectationException(ucfirst($selectortype) . ' "' . $element .
-                '" not found in current page administration"', $this->getSession());
-        }
+        // Find administration menu.
+        $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu(true);
+
+        $this->toggle_page_administration_menu($menuxpath);
+        $this->execute('behat_general::should_exist_in_the', [$nodetext, $selectortype, $menuxpath, 'xpath_element']);
+        $this->toggle_page_administration_menu($menuxpath);
     }
 
     /**
@@ -467,17 +449,19 @@ class behat_navigation extends behat_base {
      * @return void
      */
     public function should_not_exist_in_current_page_administration($element, $selectortype) {
-        $parentnodes = array_map('trim', explode('>', $element));
-        // Find the name of the first category of the administration block tree.
-        $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span';
-        $node = $this->find('xpath', $xpath);
-        array_unshift($parentnodes, $node->getText());
-        $lastnode = array_pop($parentnodes);
-
-        if ($this->find_node_in_navigation($lastnode, $parentnodes, strtolower($selectortype))) {
-            throw new ExpectationException(ucfirst($selectortype) . ' "' . $element .
-                '" found in current page administration"', $this->getSession());
+        $nodes = array_map('trim', explode('>', $element));
+        $nodetext = end($nodes);
+
+        // Find administration menu.
+        $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu();
+        if (!$menuxpath) {
+            // Menu not found, exit.
+            return;
         }
+
+        $this->toggle_page_administration_menu($menuxpath);
+        $this->execute('behat_general::should_not_exist_in_the', [$nodetext, $selectortype, $menuxpath, 'xpath_element']);
+        $this->toggle_page_administration_menu($menuxpath);
     }
 
     /**
@@ -490,10 +474,9 @@ class behat_navigation extends behat_base {
      * @return void
      */
     public function i_navigate_to_in_site_administration($nodetext) {
-        $parentnodes = array_map('trim', explode('>', $nodetext));
-        array_unshift($parentnodes, get_string('administrationsite'));
-        $lastnode = array_pop($parentnodes);
-        $this->select_node_in_navigation($lastnode, $parentnodes);
+        $nodelist = array_map('trim', explode('>', $nodetext));
+        $this->i_select_from_flat_navigation_drawer(get_string('administrationsite'));
+        $this->select_on_administration_page($nodelist);
     }
 
     /**
@@ -584,4 +567,209 @@ class behat_navigation extends behat_base {
             $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", [get_string('turneditingon')]);
         }
     }
+
+    /**
+     * Opens the flat navigation drawer if it is not already open
+     *
+     * @When /^I open flat navigation drawer$/
+     * @throws ElementNotFoundException Thrown by behat_base::find
+     */
+    public function i_open_flat_navigation_drawer() {
+        if (!$this->running_javascript()) {
+            // Navigation drawer is always open without JS.
+            return;
+        }
+        $xpath = "//button[contains(@data-action,'toggle-drawer')]";
+        $node = $this->find('xpath', $xpath);
+        $expanded = $node->getAttribute('aria-expanded');
+        if ($expanded === 'false') {
+            $node->click();
+            $this->ensure_node_attribute_is_set($node, 'aria-expanded', 'true');
+            $this->wait_for_pending_js();
+        }
+    }
+
+    /**
+     * Closes the flat navigation drawer if it is open (does nothing if JS disabled)
+     *
+     * @When /^I close flat navigation drawer$/
+     * @throws ElementNotFoundException Thrown by behat_base::find
+     */
+    public function i_close_flat_navigation_drawer() {
+        if (!$this->running_javascript()) {
+            // Navigation drawer can not be closed without JS.
+            return;
+        }
+        $xpath = "//button[contains(@data-action,'toggle-drawer')]";
+        $node = $this->find('xpath', $xpath);
+        $expanded = $node->getAttribute('aria-expanded');
+        if ($expanded === 'true') {
+            $node->click();
+            $this->wait_for_pending_js();
+        }
+    }
+
+    /**
+     * Clicks link with specified id|title|alt|text in the flat navigation drawer.
+     *
+     * @When /^I select "(?P<link_string>(?:[^"]|\\")*)" from flat navigation drawer$/
+     * @throws ElementNotFoundException Thrown by behat_base::find
+     * @param string $link
+     */
+    public function i_select_from_flat_navigation_drawer($link) {
+        $this->i_open_flat_navigation_drawer();
+        $this->execute('behat_general::i_click_on_in_the', [$link, 'link', '#nav-drawer', 'css_element']);
+    }
+
+    /**
+     * If we are not on the course main page, click on the course link in the navbar
+     */
+    protected function go_to_main_course_page() {
+        $url = $this->getSession()->getCurrentUrl();
+        if (!preg_match('|/course/view.php\?id=[\d]+$|', $url)) {
+            $this->find('xpath', '//header//div[@id=\'page-navbar\']//a[contains(@href,\'/course/view.php?id=\')]')->click();
+            $this->execute('behat_general::wait_until_the_page_is_ready');
+        }
+    }
+
+    /**
+     * Finds and clicks a link on the admin page (site administration or course administration)
+     *
+     * @param array $nodelist
+     */
+    protected function select_on_administration_page($nodelist) {
+        $parentnodes = $nodelist;
+        $lastnode = array_pop($parentnodes);
+        $xpath = '//section[@id=\'region-main\']';
+
+        // Check if there is a separate tab for this submenu of the page. If found go to it.
+        if ($parentnodes) {
+            $tabname = behat_context_helper::escape($parentnodes[0]);
+            $tabxpath = '//ul[@role=\'tablist\']/li/a[contains(normalize-space(.), ' . $tabname . ')]';
+            if ($node = $this->getSession()->getPage()->find('xpath', $tabxpath)) {
+                if ($this->running_javascript()) {
+                    // Click on the tab and add 'active' tab to the xpath.
+                    $node->click();
+                    $xpath .= '//div[contains(@class,\'active\')]';
+                } else {
+                    // Add the tab content selector to the xpath.
+                    $tabid = behat_context_helper::escape(ltrim($node->getAttribute('href'), '#'));
+                    $xpath .= '//div[@id = ' . $tabid . ']';
+                }
+                array_shift($parentnodes);
+            }
+        }
+
+        // Find a section with the parent name in it.
+        if ($parentnodes) {
+            // Find the section on the page (links may be repeating in different sections).
+            $section = behat_context_helper::escape($parentnodes[0]);
+            $xpath .= '//div[@class=\'row\' and contains(.,'.$section.')]';
+        }
+
+        // Find a link and click on it.
+        $linkname = behat_context_helper::escape($lastnode);
+        $xpath .= '//a[contains(normalize-space(.), ' . $linkname . ')]';
+        if (!$node = $this->getSession()->getPage()->find('xpath', $xpath)) {
+            throw new ElementNotFoundException($this->getSession(), 'Link "' . join(' > ', $nodelist) . '"" not found on the page');
+        }
+        $node->click();
+        $this->wait_for_pending_js();
+    }
+
+    /**
+     * Locates the administration menu in the <header> element and returns its xpath
+     *
+     * @param bool $mustexist if specified throws an exception if menu is not found
+     * @return null|string
+     */
+    protected function find_header_administration_menu($mustexist = false) {
+        $menuxpath = '//header[@id=\'page-header\']//div[contains(@class,\'moodle-actionmenu\')]';
+        if ($mustexist) {
+            $exception = new ElementNotFoundException($this->getSession(), 'Page header administration menu is not found');
+            $this->find('xpath', $menuxpath, $exception);
+        } else if (!$this->getSession()->getPage()->find('xpath', $menuxpath)) {
+            return null;
+        }
+        return $menuxpath;
+    }
+
+    /**
+     * Locates the administration menu on the page (but not in the header) and returns its xpath
+     *
+     * @param bool $mustexist if specified throws an exception if menu is not found
+     * @return null|string
+     */
+    protected function find_page_administration_menu($mustexist = false) {
+        $menuxpath = '//div[@id=\'region-main-settings-menu\']';
+        if ($mustexist) {
+            $exception = new ElementNotFoundException($this->getSession(), 'Page administration menu is not found');
+            $this->find('xpath', $menuxpath, $exception);
+        } else if (!$this->getSession()->getPage()->find('xpath', $menuxpath)) {
+            return null;
+        }
+        return $menuxpath;
+    }
+
+    /**
+     * Toggles administration menu
+     *
+     * @param string $menuxpath (optional) xpath to the page administration menu if already known
+     */
+    protected function toggle_page_administration_menu($menuxpath = null) {
+        if (!$menuxpath) {
+            $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu();
+        }
+        if ($menuxpath && $this->running_javascript()) {
+            $this->find('xpath', $menuxpath . '//a[@data-toggle=\'dropdown\']')->click();
+            $this->wait_for_pending_js();
+        }
+    }
+
+    /**
+     * Finds a page edit cog and select an item from it
+     *
+     * If the page edit cog is in the page header and the item is not found there, click "More..." link
+     * and find the item on the course/frontpage administration page
+     *
+     * @param array $nodelist
+     * @throws ElementNotFoundException
+     */
+    protected function select_from_administration_menu($nodelist) {
+        // Find administration menu.
+        if ($menuxpath = $this->find_header_administration_menu()) {
+            $isheader = true;
+        } else {
+            $menuxpath = $this->find_page_administration_menu(true);
+            $isheader = false;
+        }
+
+        $this->toggle_page_administration_menu($menuxpath);
+
+        if (!$isheader || count($nodelist) == 1) {
+            $lastnode = end($nodelist);
+            $linkname = behat_context_helper::escape($lastnode);
+            $link = $this->getSession()->getPage()->find('xpath', $menuxpath . '//a[contains(normalize-space(.), ' . $linkname . ')]');
+            if ($link) {
+                $link->click();
+                $this->wait_for_pending_js();
+                return;
+            }
+        }
+
+        if ($isheader) {
+            // Course administration and Front page administration will have subnodes under "More...".
+            $linkname = behat_context_helper::escape(get_string('morenavigationlinks'));
+            $link = $this->getSession()->getPage()->find('xpath', $menuxpath . '//a[contains(normalize-space(.), ' . $linkname . ')]');
+            if ($link) {
+                $link->click();
+                $this->execute('behat_general::wait_until_the_page_is_ready');
+                $this->select_on_administration_page($nodelist);
+                return;
+            }
+        }
+
+        throw new ElementNotFoundException($this->getSession(),
+                'Link "' . join(' > ', $nodelist) . '" not found in the current page edit menu"');
+    }
 }
index 7d5750e..cfa4e45 100644 (file)
@@ -254,15 +254,18 @@ class behat_mod_quiz extends behat_question_base {
         $editquiz = $this->escape(get_string('editquiz', 'quiz'));
         $quizadmin = $this->escape(get_string('pluginadministration', 'quiz'));
         $addaquestion = $this->escape(get_string('addaquestion', 'quiz'));
-        $menuxpath = "//div[contains(@class, ' page-add-actions ')][last()]//a[contains(@class, ' textmenu')]";
-        $itemxpath = "//div[contains(@class, ' page-add-actions ')][last()]//a[contains(@class, ' addquestion ')]";
 
         $this->execute('behat_general::click_link', $quizname);
 
-        $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", $editquiz);
+        $this->execute("behat_navigation::i_navigate_to_in_current_page_administration",
+                $quizadmin . ' > ' . $editquiz);
 
-        $this->execute("behat_general::i_click_on", array($menuxpath, "xpath_element"));
-        $this->execute("behat_general::i_click_on", array($itemxpath, "xpath_element"));
+        if ($this->running_javascript()) {
+            $this->execute("behat_action_menu::i_open_the_action_menu_in", array('.slots', "css_element"));
+            $this->execute("behat_action_menu::i_choose_in_the_open_action_menu", array($addaquestion));
+        } else {
+            $this->execute('behat_general::click_link', $addaquestion);
+        }
 
         $this->finish_adding_question($questiontype, $questiondata);
     }
@@ -296,9 +299,9 @@ class behat_mod_quiz extends behat_question_base {
         }
 
         if ($pageorlast == 'last') {
-            $xpath = "//div[@class = 'last-add-menu']//a[contains(@class, 'textmenu') and contains(., 'Add')]";
+            $xpath = "//div[@class = 'last-add-menu']//a[contains(@data-toggle, 'dropdown') and contains(., 'Add')]";
         } else if (preg_match('~Page (\d+)~', $pageorlast, $matches)) {
-            $xpath = "//li[@id = 'page-{$matches[1]}']//a[contains(@class, 'textmenu') and contains(., 'Add')]";
+            $xpath = "//li[@id = 'page-{$matches[1]}']//a[contains(@data-toggle, 'dropdown') and contains(., 'Add')]";
         } else {
             throw new ExpectationException("The I open the add to quiz menu step must specify either 'Page N' or 'last'.");
         }
@@ -557,7 +560,7 @@ class behat_mod_quiz extends behat_question_base {
         // Split in two checkings to give more feedback in case of exception.
         $exception = new ExpectationException('Question "' . $questionnumber . '" is not in section "' .
                 $sectionheading . '" in the quiz navigation.', $this->getSession());
-        $xpath = "//div[@id = 'mod_quiz_navblock']//*[contains(concat(' ', normalize-space(@class), ' '), ' qnbutton ') and " .
+        $xpath = "//*[@id = 'mod_quiz_navblock']//*[contains(concat(' ', normalize-space(@class), ' '), ' qnbutton ') and " .
                 "contains(., {$questionnumberliteral}) and contains(preceding-sibling::h3[1], {$headingliteral})]";
         $this->find('xpath', $xpath);
     }
index a64b5f6..42c3d5e 100644 (file)
@@ -55,7 +55,8 @@ class behat_filepicker extends behat_files {
         $fieldnode = $this->get_filepicker_node($filemanagerelement);
 
         // Looking for the create folder button inside the specified filemanager.
-        $exception = new ExpectationException('No folders can be created in "'.$filemanagerelement.'" filemanager', $this->getSession());
+        $exception = new ExpectationException('No folders can be created in "'.$filemanagerelement.'" filemanager',
+                $this->getSession());
         $newfolder = $this->find('css', 'div.fp-btn-mkdir a', $exception, $fieldnode);
         $newfolder->click();
 
@@ -83,8 +84,8 @@ class behat_filepicker extends behat_files {
         $fieldnode = $this->get_filepicker_node($filemanagerelement);
 
         $exception = new ExpectationException(
-            'The "'.$foldername.'" folder can not be found in the "'.$filemanagerelement.'" filemanager',
-            $this->getSession()
+                'The "'.$foldername.'" folder can not be found in the "'.$filemanagerelement.'" filemanager',
+                $this->getSession()
         );
 
         $folderliteral = behat_context_helper::escape($foldername);
@@ -94,22 +95,22 @@ class behat_filepicker extends behat_files {
 
             // In the current folder workspace.
             $folder = $this->find(
-                'xpath',
-                "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')]" .
+                    'xpath',
+                    "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')]" .
                     "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-filename ')]" .
                     "[normalize-space(.)=$folderliteral]",
-                $exception,
-                $fieldnode
+                    $exception,
+                    $fieldnode
             );
         } catch (ExpectationException $e) {
 
             // And in the pathbar.
             $folder = $this->find(
-                'xpath',
-                "//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-path-folder-name ')]" .
+                    'xpath',
+                    "//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-path-folder-name ')]" .
                     "[normalize-space(.)=$folderliteral]",
-                $exception,
-                $fieldnode
+                    $exception,
+                    $fieldnode
             );
         }
 
@@ -176,7 +177,6 @@ class behat_filepicker extends behat_files {
         $okbutton->click();
     }
 
-
     /**
      * Makes sure user can see the exact number of elements (files in folders) in the filemanager.
      *
@@ -190,13 +190,14 @@ class behat_filepicker extends behat_files {
 
         // We count .fp-file elements inside a filemanager not being updated.
         $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' filemanager ')]" .
-            "[not(contains(concat(' ', normalize-space(@class), ' '), ' fm-updating '))]" .
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-content ')]" .
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]";
+                "[not(contains(concat(' ', normalize-space(@class), ' '), ' fm-updating '))]" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-content ')]" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]";
 
         $elements = $this->find_all('xpath', $xpath, false, $filemanagernode);
         if (count($elements) != $elementscount) {
-            throw new ExpectationException('Found '.count($elements).' elements in filemanager instead of expected '.$elementscount, $this->getSession());
+            throw new ExpectationException('Found '.count($elements).' elements in filemanager. Expected '.$elementscount,
+                    $this->getSession());
         }
     }
 
@@ -251,7 +252,8 @@ class behat_filepicker extends behat_files {
      * @param string $filemanagerelement
      * @param TableNode $data Data to fill the form in Select file dialogue
      */
-    public function i_add_and_overwrite_file_from_repository_to_filemanager_as($filepath, $repository, $filemanagerelement, TableNode $data) {
+    public function i_add_and_overwrite_file_from_repository_to_filemanager_as($filepath, $repository, $filemanagerelement,
+            TableNode $data) {
         $this->add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement, $data,
                 get_string('overwrite', 'repository'));
     }
index 35d723e..8552067 100644 (file)
@@ -115,13 +115,14 @@ class behat_repository_upload extends behat_files {
         // Ensure all the form is ready.
         $noformexception = new ExpectationException('The upload file form is not ready', $this->getSession());
         $this->find(
-            'xpath',
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' file-picker ')]" .
+                'xpath',
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' container ')]" .
                 "[contains(concat(' ', normalize-space(@class), ' '), ' repository_upload ')]" .
+                "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' file-picker ')]" .
                 "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-content ')]" .
                 "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-upload-form ')]" .
                 "/descendant::form",
-            $noformexception
+                $noformexception
         );
         // After this we have the elements we want to interact with.
 
@@ -173,4 +174,37 @@ class behat_repository_upload extends behat_files {
 
     }
 
+    /**
+     * Try to get the filemanager node specified by the element
+     *
+     * @param string $filepickerelement
+     * @return \Behat\Mink\Element\NodeElement
+     * @throws ExpectationException
+     */
+    protected function get_filepicker_node($filepickerelement) {
+
+        // More info about the problem (in case there is a problem).
+        $exception = new ExpectationException('"' . $filepickerelement . '" filepicker can not be found', $this->getSession());
+
+        // If no file picker label is mentioned take the first file picker from the page.
+        if (empty($filepickerelement)) {
+            $filepickercontainer = $this->find(
+                    'xpath',
+                    "//*[@class=\"form-filemanager\"]",
+                    $exception
+            );
+        } else {
+            // Gets the filemanager node specified by the locator which contains the filepicker container.
+            $filepickerelement = behat_context_helper::escape($filepickerelement);
+            $filepickercontainer = $this->find(
+                    'xpath',
+                    "//input[./@id = //label[normalize-space(.)=$filepickerelement]/@for]" .
+                    "//ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' felement ')]",
+                    $exception
+            );
+        }
+
+        return $filepickercontainer;
+    }
+
 }
diff --git a/theme/boost/tests/behat/behat_theme_boost_behat_blocks.php b/theme/boost/tests/behat/behat_theme_boost_behat_blocks.php
deleted file mode 100644 (file)
index 12ab3f7..0000000
+++ /dev/null
@@ -1,97 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Steps definitions related with blocks.
- *
- * @package   core_block
- * @category  test
- * @copyright 2012 David Monllaó
- * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
-
-require_once(__DIR__ . '/../../../../blocks/tests/behat/behat_blocks.php');
-
-/**
- * Blocks management steps definitions.
- *
- * @package    core_block
- * @category   test
- * @copyright  2012 David Monllaó
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class behat_theme_boost_behat_blocks extends behat_blocks {
-
-    public function i_add_the_block($blockname) {
-        $addblock = get_string('addblock');
-        $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', $addblock);
-
-        if (!$this->running_javascript()) {
-            $this->execute('behat_general::i_click_on_in_the', [$blockname, 'link_exact', '#region-main', 'css_element']);
-        } else {
-            $this->execute('behat_general::i_click_on_in_the', [$blockname, 'link_exact', $addblock, 'dialogue']);
-        }
-    }
-
-    public function i_open_the_blocks_action_menu($blockname) {
-
-        if (!$this->running_javascript()) {
-            // Action menu does not need to be open if Javascript is off.
-            return;
-        }
-
-        // If it is already opened we do nothing.
-        $blocknode = $this->get_text_selector_node('block', $blockname);
-        if ($blocknode->hasClass('action-menu-shown')) {
-            return;
-        }
-
-        $this->execute('behat_general::i_click_on_in_the',
-            array("a[data-toggle='dropdown']", "css_element", $this->escape($blockname), "block")
-        );
-    }
-
-    public function the_add_block_selector_should_contain_block($blockname) {
-        $addblock = get_string('addblock');
-        $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', $addblock);
-
-        $cancelstr = get_string('cancel');
-        if (!$this->running_javascript()) {
-            $this->execute('behat_general::should_exist_in_the', [$blockname, 'link_exact', '#region-main', 'css_element']);
-            $this->execute('behat_general::i_click_on_in_the', [$cancelstr, 'link_exact', '#region-main', 'css_element']);
-        } else {
-            $this->execute('behat_general::should_exist_in_the', [$blockname, 'link_exact', $addblock, 'dialogue']);
-            $this->execute('behat_general::i_click_on_in_the', [$cancelstr, 'button', $addblock, 'dialogue']);
-        }
-    }
-
-    public function the_add_block_selector_should_not_contain_block($blockname) {
-        $addblock = get_string('addblock');
-        $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', $addblock);
-
-        $cancelstr = get_string('cancel');
-        if (!$this->running_javascript()) {
-            $this->execute('behat_general::should_not_exist_in_the', [$blockname, 'link_exact', '#region-main', 'css_element']);
-            $this->execute('behat_general::i_click_on_in_the', [$cancelstr, 'link_exact', '#region-main', 'css_element']);
-        } else {
-            $this->execute('behat_general::should_not_exist_in_the', [$blockname, 'link_exact', $addblock, 'dialogue']);
-            $this->execute('behat_general::i_click_on_in_the', [$cancelstr, 'button', $addblock, 'dialogue']);
-        }
-    }
-
-}
diff --git a/theme/boost/tests/behat/behat_theme_boost_behat_navigation.php b/theme/boost/tests/behat/behat_theme_boost_behat_navigation.php
deleted file mode 100644 (file)
index a0b5290..0000000
+++ /dev/null
@@ -1,323 +0,0 @@
-<?php
-// This file is part of Moodle - http://moodle.org/
-//
-// Moodle is free software: you can redistribute it and/or modify
-// it under the terms of the GNU General Public License as published by
-// the Free Software Foundation, either version 3 of the License, or
-// (at your option) any later version.
-//
-// Moodle is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
-
-/**
- * Navigation steps overrides.
- *
- * @copyright  2016 Damyon Wiese
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
-
-require_once(__DIR__ . '/../../../../lib/tests/behat/behat_navigation.php');
-
-use Behat\Mink\Exception\ExpectationException as ExpectationException;
-use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
-
-/**
- * Steps definitions to navigate through the navigation tree nodes (overrides).
- *
- * @copyright  2016 Damyon Wiese
- * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-class behat_theme_boost_behat_navigation extends behat_navigation {
-
-    public function i_follow_in_the_user_menu($nodetext) {
-
-        if ($this->running_javascript()) {
-            // The user menu must be expanded when JS is enabled.
-            $xpath = "//div[contains(concat(' ', @class, ' '),  ' usermenu ')]//a[contains(concat(' ', @class, ' '), ' dropdown-toggle ')]";
-            $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
-        }
-
-        // Now select the link.
-        // The CSS path is always present, with or without JS.
-        $csspath = ".usermenu .dropdown-menu";
-
-        $this->execute('behat_general::i_click_on_in_the',
-            array($nodetext, "link", $csspath, "css_element")
-        );
-    }
-
-    protected function get_top_navigation_node($nodetext) {
-
-        // Avoid problems with quotes.
-        $nodetextliteral = behat_context_helper::escape($nodetext);
-        $exception = new ExpectationException('Top navigation node "' . $nodetext . ' not found in "', $this->getSession());
-
-        // First find in navigation block.
-        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' card-text ')]" .
-            "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
-            "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
-            "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
-            "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
-            "/*[contains(normalize-space(.), " . $nodetextliteral .")]]" .
-            "|" .
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' card-text ')]/div" .
-            "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
-            "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
-            "/*[contains(normalize-space(.), " . $nodetextliteral .")]]";
-
-        $node = $this->find('xpath', $xpath, $exception);
-
-        return $node;
-    }
-
-    /**
-     * Opens the flat navigation drawer if it is not already open
-     *
-     * @When /^I open flat navigation drawer$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     */
-    public function i_open_flat_navigation_drawer() {
-        if (!$this->running_javascript()) {
-            // Navigation drawer is always open without JS.
-            return;
-        }
-        $xpath = "//button[contains(@data-action,'toggle-drawer')]";
-        $node = $this->find('xpath', $xpath);
-        $expanded = $node->getAttribute('aria-expanded');
-        if ($expanded === 'false') {
-            $node->click();
-            $this->ensure_node_attribute_is_set($node, 'aria-expanded', 'true');
-            $this->wait_for_pending_js();
-        }
-    }
-
-    /**
-     * Closes the flat navigation drawer if it is open (does nothing if JS disabled)
-     *
-     * @When /^I close flat navigation drawer$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     */
-    public function i_close_flat_navigation_drawer() {
-        if (!$this->running_javascript()) {
-            // Navigation drawer can not be closed without JS.
-            return;
-        }
-        $xpath = "//button[contains(@data-action,'toggle-drawer')]";
-        $node = $this->find('xpath', $xpath);
-        $expanded = $node->getAttribute('aria-expanded');
-        if ($expanded === 'true') {
-            $node->click();
-            $this->wait_for_pending_js();
-        }
-    }
-
-    /**
-     * Clicks link with specified id|title|alt|text in the flat navigation drawer.
-     *
-     * @When /^I select "(?P<link_string>(?:[^"]|\\")*)" from flat navigation drawer$/
-     * @throws ElementNotFoundException Thrown by behat_base::find
-     * @param string $link
-     */
-    public function i_select_from_flat_navigation_drawer($link) {
-        $this->i_open_flat_navigation_drawer();
-        $this->execute('behat_general::i_click_on_in_the', [$link, 'link', '#nav-drawer', 'css_element']);
-    }
-
-    /**
-     * If we are not on the course main page, click on the course link in the navbar
-     */
-    protected function go_to_main_course_page() {
-        $url = $this->getSession()->getCurrentUrl();
-        if (!preg_match('|/course/view.php\?id=[\d]+$|', $url)) {
-            $this->find('xpath', '//header//div[@id=\'page-navbar\']//a[contains(@href,\'/course/view.php?id=\')]')->click();
-            $this->execute('behat_general::wait_until_the_page_is_ready');
-        }
-    }
-
-    /**
-     * Finds and clicks a link on the admin page (site administration or course administration)
-     *
-     * @param array $nodelist
-     */
-    protected function select_on_administration_page($nodelist) {
-        $parentnodes = $nodelist;
-        $lastnode = array_pop($parentnodes);
-        $xpath = '//section[@id=\'region-main\']';
-
-        // Check if there is a separate tab for this submenu of the page. If found go to it.
-        if ($parentnodes) {
-            $tabname = behat_context_helper::escape($parentnodes[0]);
-            $tabxpath = '//ul[@role=\'tablist\']/li/a[contains(normalize-space(.), ' . $tabname . ')]';
-            if ($node = $this->getSession()->getPage()->find('xpath', $tabxpath)) {
-                if ($this->running_javascript()) {
-                    // Click on the tab and add 'active' tab to the xpath.
-                    $node->click();
-                    $xpath .= '//div[contains(@class,\'active\')]';
-                } else {
-                    // Add the tab content selector to the xpath.
-                    $tabid = behat_context_helper::escape(ltrim($node->getAttribute('href'), '#'));
-                    $xpath .= '//div[@id = ' . $tabid . ']';
-                }
-                array_shift($parentnodes);
-            }
-        }
-
-        // Find a section with the parent name in it.
-        if ($parentnodes) {
-            // Find the section on the page (links may be repeating in different sections).
-            $section = behat_context_helper::escape($parentnodes[0]);
-            $xpath .= '//div[@class=\'row\' and contains(.,'.$section.')]';
-        }
-
-        // Find a link and click on it.
-        $linkname = behat_context_helper::escape($lastnode);
-        $xpath .= '//a[contains(normalize-space(.), ' . $linkname . ')]';
-        if (!$node = $this->getSession()->getPage()->find('xpath', $xpath)) {
-            throw new ElementNotFoundException($this->getSession(), 'Link "' . join(' > ', $nodelist) . '"" not found on the page');
-        }
-        $node->click();
-        $this->wait_for_pending_js();
-    }
-
-    /**
-     * Locates the administration menu in the <header> element and returns its xpath
-     *
-     * @param bool $mustexist if specified throws an exception if menu is not found
-     * @return null|string
-     */
-    protected function find_header_administration_menu($mustexist = false) {
-        $menuxpath = '//header[@id=\'page-header\']//div[contains(@class,\'moodle-actionmenu\')]';
-        if ($mustexist) {
-            $exception = new ElementNotFoundException($this->getSession(), 'Page header administration menu is not found');
-            $this->find('xpath', $menuxpath, $exception);
-        } else if (!$this->getSession()->getPage()->find('xpath', $menuxpath)) {
-            return null;
-        }
-        return $menuxpath;
-    }
-
-    /**
-     * Locates the administration menu on the page (but not in the header) and returns its xpath
-     *
-     * @param bool $mustexist if specified throws an exception if menu is not found
-     * @return null|string
-     */
-    protected function find_page_administration_menu($mustexist = false) {
-        $menuxpath = '//div[@id=\'region-main-settings-menu\']';
-        if ($mustexist) {
-            $exception = new ElementNotFoundException($this->getSession(), 'Page administration menu is not found');
-            $this->find('xpath', $menuxpath, $exception);
-        } else if (!$this->getSession()->getPage()->find('xpath', $menuxpath)) {
-            return null;
-        }
-        return $menuxpath;
-    }
-
-    /**
-     * Toggles administration menu
-     *
-     * @param string $menuxpath (optional) xpath to the page administration menu if already known
-     */
-    protected function toggle_page_administration_menu($menuxpath = null) {
-        if (!$menuxpath) {
-            $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu();
-        }
-        if ($menuxpath && $this->running_javascript()) {
-            $this->find('xpath', $menuxpath . '//a[@data-toggle=\'dropdown\']')->click();
-            $this->wait_for_pending_js();
-        }
-    }
-
-    /**
-     * Finds a page edit cog and select an item from it
-     *
-     * If the page edit cog is in the page header and the item is not found there, click "More..." link
-     * and find the item on the course/frontpage administration page
-     *
-     * @param array $nodelist
-     * @throws ElementNotFoundException
-     */
-    protected function select_from_administration_menu($nodelist) {
-        // Find administration menu.
-        if ($menuxpath = $this->find_header_administration_menu()) {
-            $isheader = true;
-        } else {
-            $menuxpath = $this->find_page_administration_menu(true);
-            $isheader = false;
-        }
-
-        $this->toggle_page_administration_menu($menuxpath);
-
-        if (!$isheader || count($nodelist) == 1) {
-            $lastnode = end($nodelist);
-            $linkname = behat_context_helper::escape($lastnode);
-            $link = $this->getSession()->getPage()->find('xpath', $menuxpath . '//a[contains(normalize-space(.), ' . $linkname . ')]');
-            if ($link) {
-                $link->click();
-                $this->wait_for_pending_js();
-                return;
-            }
-        }
-
-        if ($isheader) {
-            // Course administration and Front page administration will have subnodes under "More...".
-            $linkname = behat_context_helper::escape(get_string('morenavigationlinks'));
-            $link = $this->getSession()->getPage()->find('xpath', $menuxpath . '//a[contains(normalize-space(.), ' . $linkname . ')]');
-            if ($link) {
-                $link->click();
-                $this->execute('behat_general::wait_until_the_page_is_ready');
-                $this->select_on_administration_page($nodelist);
-                return;
-            }
-        }
-
-        throw new ElementNotFoundException($this->getSession(),
-            'Link "' . join(' > ', $nodelist) . '" not found in the current page edit menu"');
-    }
-
-    public function should_exist_in_current_page_administration($element, $selectortype) {
-        $nodes = array_map('trim', explode('>', $element));
-        $nodetext = end($nodes);
-
-        // Find administration menu.
-        $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu(true);
-
-        $this->toggle_page_administration_menu($menuxpath);
-        $this->execute('behat_general::should_exist_in_the', [$nodetext, $selectortype, $menuxpath, 'xpath_element']);
-        $this->toggle_page_administration_menu($menuxpath);
-    }
-
-    public function should_not_exist_in_current_page_administration($element, $selectortype) {
-        $nodes = array_map('trim', explode('>', $element));
-        $nodetext = end($nodes);
-
-        // Find administration menu.
-        $menuxpath = $this->find_header_administration_menu() ?: $this->find_page_administration_menu();
-        if (!$menuxpath) {
-            // Menu not found, exit.
-            return;
-        }
-
-        $this->toggle_page_administration_menu($menuxpath);
-        $this->execute('behat_general::should_not_exist_in_the', [$nodetext, $selectortype, $menuxpath, 'xpath_element']);
-        $this->toggle_page_administration_menu($menuxpath);
-    }
-
-    public function i_navigate_to_in_current_page_administration($nodetext) {
-        $nodelist = array_map('trim', explode('>', $nodetext));
-        $this->select_from_administration_menu($nodelist);
-    }
-
-    public function i_navigate_to_in_site_administration($nodetext) {
-        $nodelist = array_map('trim', explode('>', $nodetext));
-        $this->i_select_from_flat_navigation_drawer(get_string('administrationsite'));
-        $this->select_on_administration_page($nodelist);
-    }
-}
@@ -17,7 +17,7 @@
 /**
  * Steps definitions to open and close action menus (overrides).
  *
- * @package    core
+ * @package    theme_bootstrapbase
  * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
@@ -30,19 +30,24 @@ require_once(__DIR__ . '/../../../../lib/tests/behat/behat_action_menu.php');
 /**
  * Steps definitions to open and close action menus (overrides).
  *
- * @package    core
+ * @package    theme_bootstrapbase
  * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class behat_theme_boost_behat_action_menu extends behat_action_menu {
+class behat_theme_bootstrapbase_behat_action_menu extends behat_action_menu {
 
     public function i_open_the_action_menu_in($element, $selectortype) {
+        if (!$this->running_javascript()) {
+            // Action menus automatically expand in a visible list of actions when Javascript is disabled.
+            return;
+        }
         // Gets the node based on the requested selector type and locator.
-        $node = $this->get_node_in_container("css_element", "[role=button][aria-haspopup=true]", $selectortype, $element);
+        $node = $this->get_node_in_container("css_element", "[role=menuitem][aria-haspopup=true]", $selectortype, $element);
 
         // Check if it is not already opened.
-        if ($node->getAttribute('aria-expanded') === 'true') {
+        $menunode = $this->find('css', '[aria-labelledby='.$node->getAttribute('id').']');
+        if ($menunode->getAttribute('aria-hidden') === 'false') {
             return;
         }
 
@@ -50,13 +55,15 @@ class behat_theme_boost_behat_action_menu extends behat_action_menu {
         $node->click();
     }
 
-    public function i_choose_in_the_open_action_menu($menuitemstring) {
+    public function i_choose_in_the_open_action_menu($linkstring) {
         if (!$this->running_javascript()) {
             throw new DriverException('Action menu steps are not available with Javascript disabled');
         }
         // Gets the node based on the requested selector type and locator.
-        $menuselector = ".moodle-actionmenu .dropdown.show .dropdown-menu";
-        $node = $this->get_node_in_container("link", $menuitemstring, "css_element", $menuselector);
+        $node = $this->get_node_in_container("link",
+                $linkstring,
+                "css_element",
+                ".moodle-actionmenu [role=menu][aria-hidden=false]");
         $this->ensure_node_is_visible($node);
         $node->click();
     }
@@ -17,6 +17,8 @@
 /**
  * Steps definitions related with administration overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright 2016 Damyon Wiese
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -32,10 +34,12 @@ use Behat\Gherkin\Node\TableNode as TableNode,
 /**
  * Site administration level steps definitions overrides.
  *
- * @copyright 2016 Damyon Wiese
+ * @package    theme_bootstrapbase
+ * @category   test
+ * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class behat_theme_boost_behat_admin extends behat_admin {
+class behat_theme_bootstrapbase_behat_admin extends behat_admin {
 
     public function i_set_the_following_administration_settings_values(TableNode $table) {
 
@@ -45,12 +49,16 @@ class behat_theme_boost_behat_admin extends behat_admin {
 
         foreach ($data as $label => $value) {
 
-            $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', [get_string('administrationsite')]);
+            // We expect admin block to be visible, otherwise go to homepage.
+            if (!$this->getSession()->getPage()->find('css', '.block_settings')) {
+                $this->getSession()->visit($this->locate_path('/'));
+                $this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
+            }
 
             // Search by label.
-            $searchbox = $this->find_field(get_string('query', 'admin'));
+            $searchbox = $this->find_field(get_string('searchinsettings', 'admin'));
             $searchbox->setValue($label);
-            $submitsearch = $this->find('css', 'form input[type=submit][name=search]');
+            $submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
             $submitsearch->press();
 
             $this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
@@ -64,24 +72,21 @@ class behat_theme_boost_behat_admin extends behat_admin {
 
             // Single element settings.
             try {
-                $fieldxpath = "//*[self::input | self::textarea | self::select]" .
-                    "[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]" .
-                    "[@id=//label[contains(normalize-space(.), $label)]/@for or " .
-                    "@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]";
+                $fieldxpath = "//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or " .
+                        "./@type = 'hidden')]" . "[@id=//label[contains(normalize-space(.), $label)]/@for or " .
+                        "@id=//span[contains(normalize-space(.), $label)]/preceding-sibling::label[1]/@for]";
                 $fieldnode = $this->find('xpath', $fieldxpath, $exception);
 
-                $formfieldtypenode = $this->find('xpath', $fieldxpath .
-                    "/ancestor::div[contains(concat(' ', @class, ' '), ' form-setting ')]" .
-                    "/child::div[contains(concat(' ', @class, ' '),  ' form-')]/child::*/parent::div");
+                $formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" .
+                        "/child::div[contains(concat(' ', @class, ' '),  ' form-')]/child::*/parent::div");
 
             } catch (ElementNotFoundException $e) {
 
                 // Multi element settings, interacting only the first one.
-                $fieldxpath = "//*[label[contains(., $label)]|span[contains(., $label)]]" .
-                    "/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' form-item ')]" .
-                    "/descendant::div[contains(concat(' ', @class, ' '), ' form-group ')]" .
-                    "/descendant::*[self::input | self::textarea | self::select]" .
-                    "[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]";
+                $fieldxpath = "//*[label[normalize-space(.)= $label]|span[normalize-space(.)= $label]]/" .
+                        "ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' form-item ')]" .
+                        "/descendant::div[@class='form-group']/descendant::*[self::input | self::textarea | self::select]" .
+                        "[not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]";
                 $fieldnode = $this->find('xpath', $fieldxpath);
 
                 // It is the same one that contains the type.
@@ -90,7 +95,6 @@ class behat_theme_boost_behat_admin extends behat_admin {
 
             // Getting the class which contains the field type.
             $classes = explode(' ', $formfieldtypenode->getAttribute('class'));
-            $type = false;
             foreach ($classes as $class) {
                 if (substr($class, 0, 5) == 'form-') {
                     $type = substr($class, 5);
diff --git a/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_blocks.php b/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_blocks.php
new file mode 100644 (file)
index 0000000..51ca29b
--- /dev/null
@@ -0,0 +1,79 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Steps definitions related with blocks.
+ *
+ * @package    theme_bootstrapbase
+ * @category   test
+ * @copyright 2012 David Monllaó
+ * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+
+require_once(__DIR__ . '/../../../../blocks/tests/behat/behat_blocks.php');
+
+/**
+ * Blocks management steps definitions.
+ *
+ * @package    theme_bootstrapbase
+ * @category   test
+ * @copyright  2012 David Monllaó
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_theme_bootstrapbase_behat_blocks extends behat_blocks {
+
+    public function i_add_the_block($blockname) {
+        $this->execute('behat_forms::i_set_the_field_to',
+                array("bui_addblock", $this->escape($blockname))
+        );
+
+        // If we are running without javascript we need to submit the form.
+        if (!$this->running_javascript()) {
+            $this->execute('behat_general::i_click_on_in_the',
+                    array(get_string('go'), "button", "#add_block", "css_element")
+            );
+        }
+    }
+
+    public function i_open_the_blocks_action_menu($blockname) {
+
+        if (!$this->running_javascript()) {
+            // Action menu does not need to be open if Javascript is off.
+            return;
+        }
+
+        // If it is already opened we do nothing.
+        $blocknode = $this->get_text_selector_node('block', $blockname);
+        if ($blocknode->hasClass('action-menu-shown')) {
+            return;
+        }
+
+        $this->execute('behat_general::i_click_on_in_the',
+                array(get_string('actions'), "link", $this->escape($blockname), "block")
+        );
+    }
+
+    public function the_add_block_selector_should_contain_block($blockname) {
+        $this->execute('behat_forms::the_select_box_should_contain', [get_string('addblock'), $blockname]);
+    }
+
+    public function the_add_block_selector_should_not_contain_block($blockname) {
+        $this->execute('behat_forms::the_select_box_should_not_contain', [get_string('addblock'), $blockname]);
+    }
+
+}
@@ -17,6 +17,8 @@
 /**
  * Behat course-related steps definitions overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -33,10 +35,12 @@ use Behat\Gherkin\Node\TableNode as TableNode,
 /**
  * Course-related steps definitions overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class behat_theme_boost_behat_course extends behat_course {
+class behat_theme_bootstrapbase_behat_course extends behat_course {
 
     public function i_open_actions_menu($activityname) {
 
@@ -46,23 +50,15 @@ class behat_theme_boost_behat_course extends behat_course {
 
         // If it is already opened we do nothing.
         $activitynode = $this->get_activity_node($activityname);
-
-        // Find the menu.
-        $menunode = $activitynode->find('css', 'a[data-toggle=dropdown]');
-        if (!$menunode) {
-            throw new ExpectationException(sprintf('Could not find actions menu for the activity "%s"', $activityname),
-                    $this->getSession());
-        }
-        $expanded = $menunode->getAttribute('aria-expanded');
-        if ($expanded == 'true') {
+        $classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
+        if (!empty($classes['action-menu-shown'])) {
             return;
         }
 
         $this->execute('behat_course::i_click_on_in_the_activity',
-            array("a[data-toggle='dropdown']", "css_element", $this->escape($activityname))
+                array("a[role='menuitem']", "css_element", $this->escape($activityname))
         );
 
-        $this->actions_menu_should_be_open($activityname);
     }
 
     public function i_close_actions_menu($activityname) {
@@ -73,19 +69,13 @@ class behat_theme_boost_behat_course extends behat_course {
 
         // If it is already closed we do nothing.
         $activitynode = $this->get_activity_node($activityname);
-        // Find the menu.
-        $menunode = $activitynode->find('css', 'a[data-toggle=dropdown]');
-        if (!$menunode) {
-            throw new ExpectationException(sprintf('Could not find actions menu for the activity "%s"', $activityname),
-                    $this->getSession());
-        }
-        $expanded = $menunode->getAttribute('aria-expanded');
-        if ($expanded != 'true') {
+        $classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
+        if (empty($classes['action-menu-shown'])) {
             return;
         }
 
         $this->execute('behat_course::i_click_on_in_the_activity',
-            array("a[data-toggle='dropdown']", "css_element", $this->escape($activityname))
+                array("a[role='menuitem']", "css_element", $this->escape($activityname))
         );
     }
 
@@ -95,16 +85,12 @@ class behat_theme_boost_behat_course extends behat_course {
             throw new DriverException('Activities actions menu not available when Javascript is disabled');
         }
 
+        // If it is already closed we do nothing.
         $activitynode = $this->get_activity_node($activityname);
-        // Find the menu.
-        $menunode = $activitynode->find('css', 'a[data-toggle=dropdown]');
-        if (!$menunode) {
-            throw new ExpectationException(sprintf('Could not find actions menu for the activity "%s"', $activityname),
-                    $this->getSession());
-        }
-        $expanded = $menunode->getAttribute('aria-expanded');
-        if ($expanded != 'true') {
-            throw new ExpectationException(sprintf("The action menu for '%s' is not open", $activityname), $this->getSession());
+        $classes = array_flip(explode(' ', $activitynode->getAttribute('class')));
+        if (empty($classes['action-menu-shown'])) {
+            throw new ExpectationException(
+                    sprintf("The action menu for '%s' is not open", $activityname), $this->getSession());
         }
     }
 
@@ -114,11 +100,11 @@ class behat_theme_boost_behat_course extends behat_course {
             // We are on the frontpage.
             if ($section) {
                 // Section 1 represents the contents on the frontpage.
-                $sectionxpath = "//body[@id='page-site-index']" .
-                    "/descendant::div[contains(concat(' ',normalize-space(@class),' '),' sitetopic ')]";
+                $sectionxpath = "//body[@id='page-site-index']/descendant::div[contains(concat(' ',normalize-space(@class),' ')," .
+                        "' sitetopic ')]";
             } else {
                 // Section 0 represents "Site main menu" block.
-                $sectionxpath = "//*[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]";
+                $sectionxpath = "//div[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]";
             }
         } else {
             // We are inside the course.
@@ -130,16 +116,15 @@ class behat_theme_boost_behat_course extends behat_course {
         if ($this->running_javascript()) {
 
             // Clicks add activity or resource section link.
-            $sectionxpath = $sectionxpath . "/descendant::div" .
-                "[contains(concat(' ', normalize-space(@class) , ' '), ' section-modchooser ')]/span/a";
+            $sectionxpath = $sectionxpath . "/descendant::div[@class='section-modchooser']/span/a";
             $sectionnode = $this->find('xpath', $sectionxpath);
             $sectionnode->click();
 
             // Clicks the selected activity if it exists.
             $activityxpath = "//div[@id='chooseform']/descendant::label" .
-                "/descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' typename ')]" .
-                "[normalize-space(.)=$activityliteral]" .
-                "/parent::label/child::input";
+                    "/descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' typename ')]" .
+                    "[normalize-space(.)=$activityliteral]" .
+                    "/parent::label/child::input";
             $activitynode = $this->find('xpath', $activityxpath);
             $activitynode->doubleClick();
 
@@ -147,9 +132,8 @@ class behat_theme_boost_behat_course extends behat_course {
             // Without Javascript.
 
             // Selecting the option from the select box which contains the option.
-            $selectxpath = $sectionxpath . "/descendant::div" .
-                "[contains(concat(' ', normalize-space(@class), ' '), ' section_add_menus ')]" .
-                "/descendant::select[option[normalize-space(.)=$activityliteral]]";
+            $selectxpath = $sectionxpath . "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), " .
+                    "' section_add_menus ')]/descendant::select[option[normalize-space(.)=$activityliteral]]";
             $selectnode = $this->find('xpath', $selectxpath);
             $selectnode->selectOption($activity);
 
@@ -170,18 +154,18 @@ class behat_theme_boost_behat_course extends behat_course {
 
         // Determine the future new activity xpath from the former one.
         $duplicatedxpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]" .
-            "[contains(., $activityliteral)]/following-sibling::li";
-        $duplicatedactionsmenuxpath = $duplicatedxpath . "/descendant::a[@data-toggle='dropdown']";
+                "[contains(., $activityliteral)]/following-sibling::li";
+        $duplicatedactionsmenuxpath = $duplicatedxpath . "/descendant::a[@role='menuitem']";
 
         if ($this->running_javascript()) {
             // We wait until the AJAX request finishes and the section is visible again.
             $hiddenlightboxxpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]" .
-                "[contains(., $activityliteral)]" .
-                "/ancestor::li[contains(concat(' ', normalize-space(@class), ' '), ' section ')]" .
-                "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]";
+                    "[contains(., $activityliteral)]" .
+                    "/ancestor::li[contains(concat(' ', normalize-space(@class), ' '), ' section ')]" .
+                    "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]";
 
             $this->execute("behat_general::wait_until_exists",
-                array($this->escape($hiddenlightboxxpath), "xpath_element")
+                    array($this->escape($hiddenlightboxxpath), "xpath_element")
             );
 
             // Close the original activity actions menu.
@@ -190,13 +174,13 @@ class behat_theme_boost_behat_course extends behat_course {
             // The next sibling of the former activity will be the duplicated one, so we click on it from it's xpath as, at
             // this point, it don't even exists in the DOM (the steps are executed when we return them).
             $this->execute('behat_general::i_click_on',
-                array($this->escape($duplicatedactionsmenuxpath), "xpath_element")
+                    array($this->escape($duplicatedactionsmenuxpath), "xpath_element")
             );
         }
 
         // We force the xpath as otherwise mink tries to interact with the former one.
         $this->execute('behat_general::i_click_on_in_the',
-            array(get_string('editsettings'), "link", $this->escape($duplicatedxpath), "xpath_element")
+                array(get_string('editsettings'), "link", $this->escape($duplicatedxpath), "xpath_element")
         );
 
         $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $data);
@@ -214,7 +198,7 @@ class behat_theme_boost_behat_course extends behat_course {
 
         // If it is already opened we do nothing.
         $xpath = $this->section_exists($sectionnumber);
-        $xpath .= "/descendant::div[contains(@class, 'section-actions')]/descendant::a[contains(@data-toggle, 'dropdown')]";
+        $xpath .= "/descendant::div[contains(@class, 'section-actions')]/descendant::a[contains(@class, 'textmenu')]";
 
         $exception = new ExpectationException('Section "' . $sectionnumber . '" was not found', $this->getSession());
         $menu = $this->find('xpath', $xpath, $exception);
@@ -235,8 +219,8 @@ class behat_theme_boost_behat_course extends behat_course {
         // Edit menu should be visible.
         if ($this->is_course_editor()) {
             $xpath = $sectionxpath .
-                     "/descendant::div[contains(@class, 'section-actions')]" .
-                     "/descendant::a[contains(@data-toggle, 'dropdown')]";
+                    "/descendant::div[contains(@class, 'section-actions')]" .
+                    "/descendant::a[contains(@class, 'textmenu')]";
             if (!$this->getSession()->getPage()->find('xpath', $xpath)) {
                 throw new ExpectationException('The section edit menu is not available', $this->getSession());
             }
@@ -244,8 +228,8 @@ class behat_theme_boost_behat_course extends behat_course {
     }
 
     protected function user_clicks_on_management_listing_action($listingtype, $listingnode, $action) {
-        $actionsnode = $listingnode->find('xpath', "//*" .
-            "[contains(concat(' ', normalize-space(@class), ' '), '{$listingtype}-item-actions')]");
+        $actionsnode = $listingnode->find('xpath',
+                "//*[contains(concat(' ', normalize-space(@class), ' '), '{$listingtype}-item-actions')]");
         if (!$actionsnode) {
             throw new ExpectationException("Could not find the actions for $listingtype", $this->getSession());
         }
@@ -254,7 +238,7 @@ class behat_theme_boost_behat_course extends behat_course {
             throw new ExpectationException("Expected action was not available or not found ($action)", $this->getSession());
         }
         if ($this->running_javascript() && !$actionnode->isVisible()) {
-            $actionsnode->find('css', 'a[data-toggle=dropdown]')->click();
+            $actionsnode->find('css', 'a.toggle-display')->click();
             $actionnode = $actionsnode->find('css', '.action-'.$action);
         }
         $actionnode->click();
@@ -263,8 +247,8 @@ class behat_theme_boost_behat_course extends behat_course {
     protected function is_course_editor() {
 
         // We don't need to behat_base::spin() here as all is already loaded.
-        if (!$this->getSession()->getPage()->findLink(get_string('turneditingoff')) &&
-                !$this->getSession()->getPage()->findLink(get_string('turneditingon'))) {
+        if (!$this->getSession()->getPage()->findButton(get_string('turneditingoff')) &&
+                !$this->getSession()->getPage()->findButton(get_string('turneditingon'))) {
             return false;
         }
 
@@ -272,6 +256,9 @@ class behat_theme_boost_behat_course extends behat_course {
     }
 
     public function i_navigate_to_course_participants() {
-        $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', get_string('participants'));
+        $coursestr = behat_context_helper::escape(get_string('courses'));
+        $mycoursestr = behat_context_helper::escape(get_string('mycourses'));
+        $xpath = "//div[contains(@class,'block')]//li[p/*[string(.)=$coursestr or string(.)=$mycoursestr]]";
+        $this->execute('behat_general::i_click_on_in_the', [get_string('participants'), 'link', $xpath, 'xpath_element']);
     }
 }
@@ -17,6 +17,8 @@
 /**
  * Deprecated steps overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2018 Victor Deniz
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -28,16 +30,18 @@ require_once(__DIR__ . '/../../../../lib/tests/behat/behat_deprecated.php');
 /**
  * Deprecated behat step definitions.
  *
- * @package    theme_boost
+ * @package    theme_bootstrapbase
  * @category   test
  * @copyright  2018 Victor Deniz
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class behat_theme_boost_behat_deprecated extends behat_deprecated {
+class behat_theme_bootstrapbase_behat_deprecated extends behat_deprecated {
 
     /**
      * Click link in navigation tree that matches the text in parentnode/s (seperated using greater-than character if more than one)
      *
+     * @Given /^I navigate to "(?P<nodetext_string>(?:[^"]|\\")*)" node in "(?P<parentnodes_string>(?:[^"]|\\")*)"$/
+     *
      * @throws ExpectationException
      * @param string $nodetext navigation node to click.
      * @param string $parentnodes comma seperated list of parent nodes.
@@ -57,34 +61,6 @@ class behat_theme_boost_behat_deprecated extends behat_deprecated {
         $this->deprecated_message($alternative);
 
         $parentnodes = array_map('trim', explode('>', $parentnodes));
-        $nodelist = array_merge($parentnodes, [$nodetext]);
-        $firstnode = array_shift($nodelist);
-
-        if ($firstnode === get_string('administrationsite')) {
-            $this->execute('behat_theme_boost_behat_navigation::i_select_from_flat_navigation_drawer',
-                    array(get_string('administrationsite')));
-            $this->execute('behat_theme_boost_behat_navigation::select_on_administration_page', array($nodelist));
-            return;
-        }
-
-        if ($firstnode === get_string('sitepages')) {
-            if ($nodetext === get_string('calendar', 'calendar')) {
-                $this->execute('behat_theme_boost_behat_navigation::i_select_from_flat_navigation_drawer',
-                        array(($nodetext)));
-            } else {
-                // TODO MDL-57120 other links under "Site pages" are not accessible without navigation block.
-                $this->execute('behat_theme_boost_behat_navigation::select_node_in_navigation',
-                        array($nodetext, $parentnodes));
-            }
-            return;
-        }
-
-        if ($firstnode === get_string('courseadministration')) {
-            // Administration menu is available only on the main course page where settings in Administration
-            // block (original purpose of the step) are available on every course page.
-            $this->execute('behat_theme_boost_behat_navigation::go_to_main_course_page', array());
-        }
-
-        $this->execute('behat_theme_boost_behat_navigation::select_from_administration_menu', array($nodelist));
+        $this->execute('behat_navigation::select_node_in_navigation', array($nodetext, $parentnodes));
     }
 }
 /**
  * Filemanager and filepicker manipulation steps definitions overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
 // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
 
-require_once(__DIR__ . '/behat_theme_boost_behat_files.php');
+require_once(__DIR__ . '/theme_bootstrapbase_behat_file_helper.php');
+require_once(__DIR__ . '/../../../../repository/tests/behat/behat_filepicker.php');
 
 use Behat\Mink\Exception\ExpectationException as ExpectationException,
     Behat\Gherkin\Node\TableNode as TableNode;
@@ -34,22 +37,13 @@ use Behat\Mink\Exception\ExpectationException as ExpectationException,
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class behat_theme_boost_behat_filepicker extends behat_theme_boost_behat_files {
-    /**
-     * Creates a folder with specified name in the current folder and in the specified filemanager field.
-     *
-     * @Given /^I create "(?P<foldername_string>(?:[^"]|\\")*)" folder in "(?P<filemanager_field_string>(?:[^"]|\\")*)" filemanager$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $foldername
-     * @param string $filemanagerelement
-     */
+class behat_theme_bootstrapbase_behat_filepicker extends behat_theme_bootstrapbase_behat_files {
     public function i_create_folder_in_filemanager($foldername, $filemanagerelement) {
 
         $fieldnode = $this->get_filepicker_node($filemanagerelement);
 
         // Looking for the create folder button inside the specified filemanager.
-        $exception = new ExpectationException('No folders can be created in "'.$filemanagerelement.'" filemanager',
-            $this->getSession());
+        $exception = new ExpectationException('No folders can be created in "'.$filemanagerelement.'" filemanager',$this->getSession());
         $newfolder = $this->find('css', 'div.fp-btn-mkdir a', $exception, $fieldnode);
         $newfolder->click();
 
@@ -64,21 +58,13 @@ class behat_theme_boost_behat_filepicker extends behat_theme_boost_behat_files {
         $buttonnode->click();
     }
 
-    /**
-     * Opens the contents of a filemanager folder. It looks for the folder in the current folder and in the path bar.
-     *
-     * @Given /^I open "(?P<foldername_string>(?:[^"]|\\")*)" folder from "(?P<filemanager_field_string>(?:[^"]|\\")*)" filemanager$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $foldername
-     * @param string $filemanagerelement
-     */
     public function i_open_folder_from_filemanager($foldername, $filemanagerelement) {
 
         $fieldnode = $this->get_filepicker_node($filemanagerelement);
 
         $exception = new ExpectationException(
-            'The "'.$foldername.'" folder can not be found in the "'.$filemanagerelement.'" filemanager',
-            $this->getSession()
+                'The "'.$foldername.'" folder can not be found in the "'.$filemanagerelement.'" filemanager',
+                $this->getSession()
         );
 
         $folderliteral = behat_context_helper::escape($foldername);
@@ -88,22 +74,22 @@ class behat_theme_boost_behat_filepicker extends behat_theme_boost_behat_files {
 
             // In the current folder workspace.
             $folder = $this->find(
-                'xpath',
-                "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')]" .
+                    'xpath',
+                    "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')]" .
                     "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-filename ')]" .
                     "[normalize-space(.)=$folderliteral]",
-                $exception,
-                $fieldnode
+                    $exception,
+                    $fieldnode
             );
         } catch (ExpectationException $e) {
 
             // And in the pathbar.
             $folder = $this->find(
-                'xpath',
-                "//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-path-folder-name ')]" .
+                    'xpath',
+                    "//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-path-folder-name ')]" .
                     "[normalize-space(.)=$folderliteral]",
-                $exception,
-                $fieldnode
+                    $exception,
+                    $fieldnode
             );
         }
 
@@ -111,14 +97,6 @@ class behat_theme_boost_behat_filepicker extends behat_theme_boost_behat_files {
         $folder->click();
     }
 
-    /**
-     * Unzips the specified file from the specified filemanager field. The zip file has to be visible in the current folder.
-     *
-     * @Given /^I unzip "(?P<filename_string>(?:[^"]|\\")*)" file from "(?P<filemanager_field_string>(?:[^"]|\\")*)" filemanager$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $filename
-     * @param string $filemanagerelement
-     */
     public function i_unzip_file_from_filemanager($filename, $filemanagerelement) {
 
         // Open the contextual menu of the filemanager element.
@@ -129,14 +107,6 @@ class behat_theme_boost_behat_filepicker extends behat_theme_boost_behat_files {
         $this->perform_on_element('unzip', $exception);
     }
 
-    /**
-     * Zips the specified folder from the specified filemanager field. The folder has to be in the current folder.
-     *
-     * @Given /^I zip "(?P<filename_string>(?:[^"]|\\")*)" folder from "(?P<filemanager_field_string>(?:[^"]|\\")*)" filemanager$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $foldername
-     * @param string $filemanagerelement
-     */
     public function i_zip_folder_from_filemanager($foldername, $filemanagerelement) {
 
         // Open the contextual menu of the filemanager element.
@@ -147,14 +117,6 @@ class behat_theme_boost_behat_filepicker extends behat_theme_boost_behat_files {
         $this->perform_on_element('zip', $exception);
     }
 
-    /**
-     * Deletes the specified file or folder from the specified filemanager field.
-     *
-     * @Given /^I delete "(?P<file_or_folder_name_string>(?:[^"]|\\")*)" from "(?P<filemanager_field_string>(?:[^"]|\\")*)" filemanager$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $name
-     * @param string $filemanagerelement
-     */
     public function i_delete_file_from_filemanager($name, $filemanagerelement) {
 
         // Open the contextual menu of the filemanager element.
@@ -170,99 +132,39 @@ class behat_theme_boost_behat_filepicker extends behat_theme_boost_behat_files {
         $okbutton->click();
     }
 
-
-    /**
-     * Makes sure user can see the exact number of elements (files in folders) in the filemanager.
-     *
-     * @Then /^I should see "(?P<elementscount_number>\d+)" elements in "(?P<filemanagerelement_string>(?:[^"]|\\")*)" filemanager$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param int $elementscount
-     * @param string $filemanagerelement
-     */
     public function i_should_see_elements_in_filemanager($elementscount, $filemanagerelement) {
         $filemanagernode = $this->get_filepicker_node($filemanagerelement);
 
         // We count .fp-file elements inside a filemanager not being updated.
         $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' filemanager ')]" .
-            "[not(contains(concat(' ', normalize-space(@class), ' '), ' fm-updating '))]" .
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-content ')]" .
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]";
+                "[not(contains(concat(' ', normalize-space(@class), ' '), ' fm-updating '))]" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-content ')]" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]";
 
         $elements = $this->find_all('xpath', $xpath, false, $filemanagernode);
         if (count($elements) != $elementscount) {
-            throw new ExpectationException('Found '.count($elements).' elements in filemanager. Expected '.$elementscount,
-                 $this->getSession());
+            throw new ExpectationException('Found '.count($elements).' elements in filemanager instead of expected '.$elementscount, $this->getSession());
         }
     }
 
-    /**
-     * Picks the file from repository leaving default values in select file dialogue.
-     *
-     * @When /^I add "(?P<filepath_string>(?:[^"]|\\")*)" file from "(?P<repository_string>(?:[^"]|\\")*)" to "(?P<filemanagerelement_string>(?:[^"]|\\")*)" filemanager$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $filepath
-     * @param string $repository
-     * @param string $filemanagerelement
-     */
     public function i_add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement) {
         $this->add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement, new TableNode(array()), false);
     }
 
-    /**
-     * Picks the file from repository leaving default values in select file dialogue and confirming to overwrite an existing file.
-     *
-     * @When /^I add and overwrite "(?P<filepath_string>(?:[^"]|\\")*)" file from "(?P<repository_string>(?:[^"]|\\")*)" to "(?P<filemanagerelement_string>(?:[^"]|\\")*)" filemanager$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $filepath
-     * @param string $repository
-     * @param string $filemanagerelement
-     */
     public function i_add_and_overwrite_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement) {
         $this->add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement, new TableNode(array()),
                 get_string('overwrite', 'repository'));
     }
 
-    /**
-     * Picks the file from repository filling the form in Select file dialogue.
-     *
-     * @When /^I add "(?P<filepath_string>(?:[^"]|\\")*)" file from "(?P<repository_string>(?:[^"]|\\")*)" to "(?P<filemanager_field_string>(?:[^"]|\\")*)" filemanager as:$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $filepath
-     * @param string $repository
-     * @param string $filemanagerelement
-     * @param TableNode $data Data to fill the form in Select file dialogue
-     */
     public function i_add_file_from_repository_to_filemanager_as($filepath, $repository, $filemanagerelement, TableNode $data) {
         $this->add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement, $data, false);
     }
 
-    /**
-     * Picks the file from repository confirming to overwrite an existing file
-     *
-     * @When /^I add and overwrite "(?P<filepath_string>(?:[^"]|\\")*)" file from "(?P<repository_string>(?:[^"]|\\")*)" to "(?P<filemanager_field_string>(?:[^"]|\\")*)" filemanager as:$/
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $filepath
-     * @param string $repository
-     * @param string $filemanagerelement
-     * @param TableNode $data Data to fill the form in Select file dialogue
-     */
-    public function i_add_and_overwrite_file_from_repository_to_filemanager_as($filepath, $repository, $filemanagerelement,
-            TableNode $data) {
+    public function i_add_and_overwrite_file_from_repository_to_filemanager_as($filepath, $repository, $filemanagerelement, TableNode $data) {
         $this->add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement, $data,
                 get_string('overwrite', 'repository'));
     }
 
-    /**
-     * Picks the file from private files repository
-     *
-     * @throws ExpectationException Thrown by behat_base::find
-     * @param string $filepath
-     * @param string $repository
-     * @param string $filemanagerelement
-     * @param TableNode $data Data to fill the form in Select file dialogue
-     * @param false|string $overwriteaction false if we don't expect that file with the same name already exists,
-     *     or button text in overwrite dialogue ("Overwrite", "Rename to ...", "Cancel")
-     */
     protected function add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement, TableNode $data,
             $overwriteaction = false) {
         $filemanagernode = $this->get_filepicker_node($filemanagerelement);
@@ -17,6 +17,8 @@
 /**
  * Files interactions with behat overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -31,10 +33,12 @@ use Behat\Mink\Exception\ExpectationException as ExpectationException,
 /**
  * Files-related actions overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class behat_theme_boost_behat_files extends behat_files {
+class behat_theme_bootstrapbase_behat_files extends behat_files {
 
     protected function get_filepicker_node($filepickerelement) {
 
@@ -44,18 +48,18 @@ class behat_theme_boost_behat_files extends behat_files {
         // If no file picker label is mentioned take the first file picker from the page.
         if (empty($filepickerelement)) {
             $filepickercontainer = $this->find(
-                'xpath',
-                "//*[@data-fieldtype=\"filemanager\"]",
-                $exception
+                    'xpath',
+                    "//*[@data-fieldtype=\"filemanager\"]",
+                    $exception
             );
         } else {
             // Gets the ffilemanager node specified by the locator which contains the filepicker container.
             $filepickerelement = behat_context_helper::escape($filepickerelement);
             $filepickercontainer = $this->find(
-                'xpath',
-                "//input[./@id = //label[normalize-space(.)=$filepickerelement]/@for]" .
-                    "//ancestor::*[@data-fieldtype = 'filemanager' or @data-fieldtype = 'filepicker']",
-                $exception
+                    'xpath',
+                    "//input[./@id = //label[normalize-space(.)=$filepickerelement]/@for]" .
+                    '//ancestor::div[@data-fieldtype="filemanager" or @data-fieldtype="filepicker"]',
+                    $exception
             );
         }
 
@@ -17,6 +17,8 @@
 /**
  * Behat grade related steps definitions overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -27,25 +29,30 @@ require_once(__DIR__ . '/../../../../grade/tests/behat/behat_grade.php');
 
 use Behat\Gherkin\Node\TableNode as TableNode;
 
-class behat_theme_boost_behat_grade extends behat_grade {
+/**
+ * Behat grade overrides.
+ *
+ * @package    theme_bootstrapbase
+ * @category   test
+ * @copyright  2016 Damyon Wiese
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_theme_bootstrapbase_behat_grade extends behat_grade {
 
     public function i_set_the_following_settings_for_grade_item($gradeitem, TableNode $data) {
 
         $gradeitem = behat_context_helper::escape($gradeitem);
 
         if ($this->running_javascript()) {
-            $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]";
+            $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]//a[contains(@class,'toggle-display')]";
             if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
-                $this->execute("behat_action_menu::i_open_the_action_menu_in",
-                    array("//tr[contains(.,$gradeitem)]",
-                    "xpath_element"));
+                $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
             }
         }
 
         $savechanges = get_string('savechanges', 'grades');
         $edit = behat_context_helper::escape(get_string('edit') . '  ');
-        $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
-            "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
+        $linkxpath = "//a[./img[starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
 
         $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
         $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $data);
@@ -57,19 +64,16 @@ class behat_theme_boost_behat_grade extends behat_grade {
         $gradeitem = behat_context_helper::escape($gradeitem);
 
         if ($this->running_javascript()) {
-            $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]";
+            $xpath = "//tr[contains(.,$gradeitem)]//*[contains(@class,'moodle-actionmenu')]//a[contains(@class,'toggle-display')]";
             if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
-                $this->execute("behat_action_menu::i_open_the_action_menu_in",
-                    array("//tr[contains(.,$gradeitem)]",
-                    "xpath_element"));
+                $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
             }
         }
 
         // Going to edit calculation.
         $savechanges = get_string('savechanges', 'grades');
         $edit = behat_context_helper::escape(get_string('editcalculation', 'grades'));
-        $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
-            "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
+        $linkxpath = "//a[./img[starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
         $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
 
         // Mapping names to idnumbers.
@@ -78,11 +82,10 @@ class behat_theme_boost_behat_grade extends behat_grade {
             // This xpath looks for course, categories and items with the provided name.
             // Grrr, we can't equal in categoryitem and courseitem because there is a line jump...
             $inputxpath = "//input[@class='idnumber'][" .
-                "parent::li[@class='item'][text()='" . $gradeitem . "']" .
-                " or " .
-                "parent::li[@class='categoryitem' or @class='courseitem']" .
-                "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
-            "]";
+                    "parent::li[@class='item'][text()='" . $gradeitem . "']" .
+                    " or " .
+                    "parent::li[@class='categoryitem' or @class='courseitem']/parent::ul/parent::li[starts-with(text(),'" .
+                    $gradeitem . "')]]";
             $this->execute('behat_forms::i_set_the_field_with_xpath_to', array($inputxpath, $idnumber));
         }
 
@@ -98,18 +101,17 @@ class behat_theme_boost_behat_grade extends behat_grade {
         $gradeitem = behat_context_helper::escape($gradeitem);
 
         if ($this->running_javascript()) {
-            $xpath = "//tr[contains(.,$gradecategorytotal)]//*[contains(@class,'moodle-actionmenu')]";
+            $xpath = "//tr[contains(.,$gradecategorytotal)]//*[contains(@class,'moodle-actionmenu')]" .
+                    "//a[contains(@class,'toggle-display')]";
             if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
-                $xpath = "//tr[contains(.,$gradecategorytotal)]";
-                $this->execute("behat_action_menu::i_open_the_action_menu_in", array($xpath, "xpath_element"));
+                $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
             }
         }
 
         // Going to edit calculation.
         $savechanges = get_string('savechanges', 'grades');
         $edit = behat_context_helper::escape(get_string('editcalculation', 'grades'));
-        $linkxpath = "//a[./*[contains(concat(' ', normalize-space(@class), ' '), ' icon ') " .
-            "and starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
+        $linkxpath = "//a[./img[starts-with(@title,$edit) and contains(@title,$gradeitem)]]";
         $this->execute("behat_general::i_click_on", array($this->escape($linkxpath), "xpath_element"));
 
         // Mapping names to idnumbers.
@@ -118,11 +120,11 @@ class behat_theme_boost_behat_grade extends behat_grade {
             // This xpath looks for course, categories and items with the provided name.
             // Grrr, we can't equal in categoryitem and courseitem because there is a line jump...
             $inputxpath = "//input[@class='idnumber'][" .
-                "parent::li[@class='item'][text()='" . $gradeitem . "']" .
-                " | " .
-                "parent::li[@class='categoryitem' | @class='courseitem']" .
-                "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
-            "]";
+                    "parent::li[@class='item'][text()='" . $gradeitem . "']" .
+                    " | " .
+                    "parent::li[@class='categoryitem' | @class='courseitem']" .
+                    "/parent::ul/parent::li[starts-with(text(),'" . $gradeitem . "')]" .
+                    "]";
             $this->execute('behat_forms::i_set_the_field_with_xpath_to', array($inputxpath, $idnumber));
         }
 
@@ -138,10 +140,10 @@ class behat_theme_boost_behat_grade extends behat_grade {
 
         if ($this->running_javascript()) {
             $gradeitemliteral = behat_context_helper::escape($gradeitem);
-            $xpath = "//tr[contains(.,$gradeitemliteral)]//*[contains(@class,'moodle-actionmenu')]";
+            $xpath = "//tr[contains(.,$gradeitemliteral)]//*[contains(@class,'moodle-actionmenu')]" .
+                    "//a[contains(@class,'toggle-display')]";
             if ($this->getSession()->getPage()->findAll('xpath', $xpath)) {
-                $xpath = "//tr[contains(.,$gradeitemliteral)]";
-                $this->execute("behat_action_menu::i_open_the_action_menu_in", array($xpath, "xpath_element"));
+                $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
             }
         }
 
@@ -150,10 +152,11 @@ class behat_theme_boost_behat_grade extends behat_grade {
     }
 
     public function i_navigate_to_in_the_course_gradebook($gradepath) {
-        // If we are not on one of the gradebook pages already, follow "Grades" link in the navigation drawer.
+        // If we are not on one of the gradebook pages already, follow "Grades" link in the navigation block.
         $xpath = '//div[contains(@class,\'grade-navigation\')]';
         if (!$this->getSession()->getPage()->findAll('xpath', $xpath)) {
-            $this->execute('behat_navigation::i_select_from_flat_navigation_drawer', get_string('grades'));
+            $this->execute("behat_general::i_click_on_in_the", array(get_string('grades'), 'link',
+                    get_string('pluginname', 'block_navigation'), 'block'));
         }
 
         $this->select_in_gradebook_tabs($gradepath);
@@ -17,6 +17,8 @@
 /**
  * Steps definitions related to mod_quiz overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright 2016 Damyon Wiese
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -32,28 +34,27 @@ use Behat\Mink\Exception\ExpectationException as ExpectationException;
 /**
  * Steps definitions related to mod_quiz overrides.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright 2016 Damyon Wiese
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class behat_theme_boost_behat_mod_quiz extends behat_mod_quiz {
+class behat_theme_bootstrapbase_behat_mod_quiz extends behat_mod_quiz {
 
     public function i_add_question_to_the_quiz_with($questiontype, $quizname, TableNode $questiondata) {
         $quizname = $this->escape($quizname);
         $editquiz = $this->escape(get_string('editquiz', 'quiz'));
         $quizadmin = $this->escape(get_string('pluginadministration', 'quiz'));
         $addaquestion = $this->escape(get_string('addaquestion', 'quiz'));
+        $menuxpath = "//div[contains(@class, ' page-add-actions ')][last()]//a[contains(@class, ' textmenu')]";
+        $itemxpath = "//div[contains(@class, ' page-add-actions ')][last()]//a[contains(@class, ' addquestion ')]";
 
         $this->execute('behat_general::click_link', $quizname);
 
-        $this->execute("behat_navigation::i_navigate_to_in_current_page_administration",
-                $quizadmin . ' > ' . $editquiz);
+        $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", $editquiz);
 
-        if ($this->running_javascript()) {
-            $this->execute("behat_action_menu::i_open_the_action_menu_in", array('.slots', "css_element"));
-            $this->execute("behat_action_menu::i_choose_in_the_open_action_menu", array($addaquestion));
-        } else {
-            $this->execute('behat_general::click_link', $addaquestion);
-        }
+        $this->execute("behat_general::i_click_on", array($menuxpath, "xpath_element"));
+        $this->execute("behat_general::i_click_on", array($itemxpath, "xpath_element"));
 
         $this->finish_adding_question($questiontype, $questiondata);
     }
@@ -67,7 +68,7 @@ class behat_theme_boost_behat_mod_quiz extends behat_mod_quiz {
         // Split in two checkings to give more feedback in case of exception.
         $exception = new ExpectationException('Question "' . $questionnumber . '" is not in section "' .
                 $sectionheading . '" in the quiz navigation.', $this->getSession());
-        $xpath = "//*[@id = 'mod_quiz_navblock']//*[contains(concat(' ', normalize-space(@class), ' '), ' qnbutton ') and " .
+        $xpath = "//div[@id = 'mod_quiz_navblock']//*[contains(concat(' ', normalize-space(@class), ' '), ' qnbutton ') and " .
                 "contains(., {$questionnumberliteral}) and contains(preceding-sibling::h3[1], {$headingliteral})]";
         $this->find('xpath', $xpath);
     }
@@ -79,9 +80,9 @@ class behat_theme_boost_behat_mod_quiz extends behat_mod_quiz {
         }
 
         if ($pageorlast == 'last') {
-            $xpath = "//div[@class = 'last-add-menu']//a[contains(@data-toggle, 'dropdown') and contains(., 'Add')]";
+            $xpath = "//div[@class = 'last-add-menu']//a[contains(@class, 'textmenu') and contains(., 'Add')]";
         } else if (preg_match('~Page (\d+)~', $pageorlast, $matches)) {
-            $xpath = "//li[@id = 'page-{$matches[1]}']//a[contains(@data-toggle, 'dropdown') and contains(., 'Add')]";
+            $xpath = "//li[@id = 'page-{$matches[1]}']//a[contains(@class, 'textmenu') and contains(., 'Add')]";
         } else {
             throw new ExpectationException("The I open the add to quiz menu step must specify either 'Page N' or 'last'.");
         }
diff --git a/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_navigation.php b/theme/bootstrapbase/tests/behat/behat_theme_bootstrapbase_behat_navigation.php
new file mode 100644 (file)
index 0000000..6e7e6a5
--- /dev/null
@@ -0,0 +1,140 @@
+<?php
+// This file is part of Moodle - http://moodle.org/
+//
+// Moodle is free software: you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Moodle is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
+
+/**
+ * Navigation steps overrides.
+ *
+ * @package    theme_bootstrapbase
+ * @category   test
+ * @copyright  2016 Damyon Wiese
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
+
+require_once(__DIR__ . '/../../../../lib/tests/behat/behat_navigation.php');
+
+use Behat\Mink\Exception\ExpectationException as ExpectationException;
+use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
+
+/**
+ * Steps definitions to navigate through the navigation tree nodes (overrides).
+ *
+ * @package    theme_bootstrapbase
+ * @category   test
+ * @copyright  2016 Damyon Wiese
+ * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+class behat_theme_bootstrapbase_behat_navigation extends behat_navigation {
+
+    public function i_follow_in_the_user_menu($nodetext) {
+
+        if ($this->running_javascript()) {
+            // The user menu must be expanded when JS is enabled.
+            $xpath = "//div[@class='usermenu']//a[contains(concat(' ', @class, ' '), ' toggle-display ')]";
+            $this->execute("behat_general::i_click_on", array($this->escape($xpath), "xpath_element"));
+        }
+
+        // Now select the link.
+        // The CSS path is always present, with or without JS.
+        $csspath = ".usermenu [data-rel='menu-content']";
+
+        $this->execute('behat_general::i_click_on_in_the',
+                array($nodetext, "link", $csspath, "css_element")
+        );
+    }
+
+    protected function get_top_navigation_node($nodetext) {
+
+        // Avoid problems with quotes.
+        $nodetextliteral = behat_context_helper::escape($nodetext);
+        $exception = new ExpectationException('Top navigation node "' . $nodetext . ' not found in "', $this->getSession());
+
+        // First find in navigation block.
+        $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]" .
+                "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
+                "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
+                "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
+                "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
+                "[span[normalize-space(.)=" . $nodetextliteral ."] or a[normalize-space(.)=" . $nodetextliteral ."]]]" .
+                "|" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
+                "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
+                "/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
+                "/ul/li[contains(concat(' ', normalize-space(@class), ' '), ' contains_branch ')]" .
+                "[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
+                "/span[normalize-space(.)=" . $nodetextliteral ."]]" .
+                "|" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
+                "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
+                "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
+                "/span[normalize-space(.)=" . $nodetextliteral ."]]" .
+                "|" .
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' content ')]/div" .
+                "/ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
+                "/li[p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
+                "/a[normalize-space(.)=" . $nodetextliteral ."]]";
+
+        $node = $this->find('xpath', $xpath, $exception);
+
+        return $node;
+    }
+
+    public function should_exist_in_current_page_administration($element, $selectortype) {
+        $parentnodes = array_map('trim', explode('>', $element));
+        // Find the name of the first category of the administration block tree.
+        $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span';
+        $node = $this->find('xpath', $xpath);
+        array_unshift($parentnodes, $node->getText());
+        $lastnode = array_pop($parentnodes);
+
+        if (!$this->find_node_in_navigation($lastnode, $parentnodes, strtolower($selectortype))) {
+            throw new ExpectationException(ucfirst($selectortype) . ' "' . $element .
+                    '" not found in current page administration"', $this->getSession());
+        }
+    }
+
+    public function should_not_exist_in_current_page_administration($element, $selectortype) {
+        $parentnodes = array_map('trim', explode('>', $element));
+        // Find the name of the first category of the administration block tree.
+        $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span';
+        $node = $this->find('xpath', $xpath);
+        array_unshift($parentnodes, $node->getText());
+        $lastnode = array_pop($parentnodes);
+
+        if ($this->find_node_in_navigation($lastnode, $parentnodes, strtolower($selectortype))) {
+            throw new ExpectationException(ucfirst($selectortype) . ' "' . $element .
+                    '" found in current page administration"', $this->getSession());
+        }
+    }
+
+    public function i_navigate_to_in_current_page_administration($nodetext) {
+        $parentnodes = array_map('trim', explode('>', $nodetext));
+        // Find the name of the first category of the administration block tree.
+        $xpath = '//div[contains(@class,\'block_settings\')]//div[@id=\'settingsnav\']/ul/li[1]/p[1]/span';
+        $node = $this->find('xpath', $xpath);
+        array_unshift($parentnodes, $node->getText());
+        $lastnode = array_pop($parentnodes);
+        $this->select_node_in_navigation($lastnode, $parentnodes);
+    }
+
+    public function i_navigate_to_in_site_administration($nodetext) {
+        $parentnodes = array_map('trim', explode('>', $nodetext));
+        array_unshift($parentnodes, get_string('administrationsite'));
+        $lastnode = array_pop($parentnodes);
+        $this->select_node_in_navigation($lastnode, $parentnodes);
+    }
+}
@@ -17,6 +17,8 @@
 /**
  * Override definitions for the upload repository type.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
@@ -31,36 +33,12 @@ use Behat\Mink\Exception\ExpectationException as ExpectationException,
 /**
  * Override steps definitions to deal with the upload repository.
  *
+ * @package    theme_bootstrapbase
+ * @category   test
  * @copyright  2016 Damyon Wiese
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class behat_theme_boost_behat_repository_upload extends behat_repository_upload {
-
-    protected function get_filepicker_node($filepickerelement) {
-
-        // More info about the problem (in case there is a problem).
-        $exception = new ExpectationException('"' . $filepickerelement . '" filepicker can not be found', $this->getSession());
-
-        // If no file picker label is mentioned take the first file picker from the page.
-        if (empty($filepickerelement)) {
-            $filepickercontainer = $this->find(
-                'xpath',
-                "//*[@class=\"form-filemanager\"]",
-                $exception
-            );
-        } else {
-            // Gets the ffilemanager node specified by the locator which contains the filepicker container.
-            $filepickerelement = behat_context_helper::escape($filepickerelement);
-            $filepickercontainer = $this->find(
-                'xpath',
-                "//input[./@id = //label[normalize-space(.)=$filepickerelement]/@for]" .
-                    "//ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' felement ')]",
-                $exception
-            );
-        }
-
-        return $filepickercontainer;
-    }
+class behat_theme_bootstrapbase_behat_repository_upload extends behat_repository_upload {
 
     protected function upload_file_to_filemanager($filepath, $filemanagerelement, TableNode $data, $overwriteaction = false) {
         global $CFG;
@@ -73,14 +51,13 @@ class behat_theme_boost_behat_repository_upload extends behat_repository_upload
         // Ensure all the form is ready.
         $noformexception = new ExpectationException('The upload file form is not ready', $this->getSession());
         $this->find(
-            'xpath',
-            "//div[contains(concat(' ', normalize-space(@class), ' '), ' container ')]" .
+                'xpath',
+                "//div[contains(concat(' ', normalize-space(@class), ' '), ' file-picker ')]" .
                 "[contains(concat(' ', normalize-space(@class), ' '), ' repository_upload ')]" .
-                "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' file-picker ')]" .
                 "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-content ')]" .
                 "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-upload-form ')]" .
                 "/descendant::form",
-            $noformexception
+                $noformexception
         );
         // After this we have the elements we want to interact with.
 
diff --git a/theme/bootstrapbase/tests/behat/blacklist.json b/theme/bootstrapbase/tests/behat/blacklist.json
new file mode 100644 (file)
index 0000000..1d0c8d8
--- /dev/null
@@ -0,0 +1,8 @@
+{
+    "features": [
+        "lib/tests/behat/action_menu.feature",
+        "course/tests/behat/activities_edit_with_block_dock.feature",
+        "blocks/tests/behat/hide_blocks.feature",
+        "blocks/tests/behat/move_blocks.feature"
+    ]
+}