From 7b24d507388b5ebff5d0ab3998219e2cc29a9764 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Fri, 17 Jan 2020 09:04:59 +0800 Subject: [PATCH 1/3] MDL-67657 behat: Add wrapper for JS execution --- lib/behat/behat_base.php | 85 +++++++++++++++++++++++++++++++++++++++- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index dd224a76f31..019f9fc7c71 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -468,12 +468,45 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { } /** - * Returns whether the scenario is running in a browser that can run Javascript or not. + * Whether Javascript is available in the current Session. * * @return boolean */ protected function running_javascript() { - return get_class($this->getSession()->getDriver()) !== 'Behat\Mink\Driver\GoutteDriver'; + return self::running_javascript_in_session($this->getSession()); + } + + /** + * Require that javascript be available in the current Session. + * + * @throws DriverException + */ + protected function require_javascript() { + return self::require_javascript_in_session($this->getSession()); + } + + /** + * Whether Javascript is available in the specified Session. + * + * @param Session $session + * @return boolean + */ + protected static function running_javascript_in_session(Session $session): bool { + return get_class($session->getDriver()) !== 'Behat\Mink\Driver\GoutteDriver'; + } + + /** + * Require that javascript be available for the specified Session. + * + * @param Session $session + * @throws DriverException + */ + protected static function require_javascript_in_session(Session $session): void { + if (self::running_javascript_in_session($session)) { + return; + } + + throw new DriverException('Javascript is required'); } /** @@ -1137,4 +1170,52 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { public static function get_named_replacements(): array { return []; } + + /** + * Evaluate the supplied script in the current session, returning the result. + * + * @param string $script + * @return mixed + */ + public function evaluate_script(string $script) { + return self::evaluate_script_in_session($this->getSession(), $script); + } + + /** + * Evaluate the supplied script in the specified session, returning the result. + * + * @param Session $session + * @param string $script + * @return mixed + */ + public static function evaluate_script_in_session(Session $session, string $script) { + self::require_javascript_in_session($session); + + return $session->evaluateScript($script); + } + + /** + * Execute the supplied script in the current session. + * + * No result will be returned. + * + * @param string $script + */ + public function execute_script(string $script): void { + self::execute_script_in_session($this->getSession(), $script); + } + + /** + * Excecute the supplied script in the specified session. + * + * No result will be returned. + * + * @param Session $session + * @param string $script + */ + public static function execute_script_in_session(Session $session, string $script): void { + self::require_javascript_in_session($session); + + $session->executeScript($script); + } } From 970ffe04173b8b5129f15782a86137d4911406cf Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Thu, 16 Jan 2020 17:13:28 +0800 Subject: [PATCH 2/3] MDL-67657 behat: Convert existing uses of executeScript --- lib/behat/behat_base.php | 24 +++++++++++-------- lib/behat/form_field/behat_form_editor.php | 18 +++++++------- .../form_field/behat_form_passwordunmask.php | 12 ++++++---- lib/tests/behat/behat_general.php | 20 ++++++++-------- lib/tests/behat/behat_navigation.php | 2 +- .../behat/behat_assignfeedback_editpdf.php | 6 ++--- .../tests/behat/behat_qtype_ddmarker.php | 6 ++--- search/tests/behat/behat_search.php | 2 +- 8 files changed, 49 insertions(+), 41 deletions(-) diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index 019f9fc7c71..c0d84412de8 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -730,14 +730,18 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { // The window inner height will be as specified, which means the available viewport will // actually be smaller if there is a horizontal scrollbar. We assume that horizontal // scrollbars are rare so this doesn't matter. - $offset = $this->getSession()->getDriver()->evaluateScript( - 'return (function() { var before = document.body.style.overflowY;' . - 'document.body.style.overflowY = "scroll";' . - 'var result = {};' . - 'result.x = window.outerWidth - document.body.offsetWidth;' . - 'result.y = window.outerHeight - window.innerHeight;' . - 'document.body.style.overflowY = before;' . - 'return result; })();'); + $js = <<evaluate_script($js); $width += $offset['x']; $height += $offset['y']; } @@ -786,8 +790,8 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { } else { return "incomplete" } - }());')); - $pending = $session->evaluateScript($jscode); + })()')); + $pending = self::evaluate_script_in_session($session, $jscode); } catch (NoSuchWindow $nsw) { // We catch an exception here, in case we just closed the window we were interacting with. // No javascript is running if there is no window right? diff --git a/lib/behat/form_field/behat_form_editor.php b/lib/behat/form_field/behat_form_editor.php index 8f3ff721d5d..3d5df09af66 100644 --- a/lib/behat/form_field/behat_form_editor.php +++ b/lib/behat/form_field/behat_form_editor.php @@ -52,14 +52,16 @@ class behat_form_editor extends behat_form_textarea { if ($this->running_javascript()) { $value = addslashes($value); $js = ' -var editor = Y.one(document.getElementById("'.$editorid.'editable")); -if (editor) { - editor.setHTML("' . $value . '"); -} -editor = Y.one(document.getElementById("'.$editorid.'")); -editor.set("value", "' . $value . '"); +(function() { + var editor = Y.one(document.getElementById("'.$editorid.'editable")); + if (editor) { + editor.setHTML("' . $value . '"); + } + editor = Y.one(document.getElementById("'.$editorid.'")); + editor.set("value", "' . $value . '"); +})(); '; - $this->session->executeScript($js); + behat_base::execute_script_in_session($this->session, $js); } else { parent::set_value($value); } @@ -88,7 +90,7 @@ editor.set("value", "' . $value . '"); r.selectNodeContents(e); s.setSingleRange(r); }()); '; - $this->session->executeScript($js); + behat_base::execute_script_in_session($this->session, $js); } /** diff --git a/lib/behat/form_field/behat_form_passwordunmask.php b/lib/behat/form_field/behat_form_passwordunmask.php index 43cf3720d5f..cfc48c26c74 100644 --- a/lib/behat/form_field/behat_form_passwordunmask.php +++ b/lib/behat/form_field/behat_form_passwordunmask.php @@ -51,12 +51,14 @@ class behat_form_passwordunmask extends behat_form_text { if ($this->running_javascript()) { $id = $this->field->getAttribute('id'); $js = <<session->executeScript($js); + behat_base::execute_script_in_session($this->session, $js); } $this->field->setValue($value); diff --git a/lib/tests/behat/behat_general.php b/lib/tests/behat/behat_general.php index 8df8d3652b2..3ed1a16a10d 100644 --- a/lib/tests/behat/behat_general.php +++ b/lib/tests/behat/behat_general.php @@ -200,8 +200,7 @@ class behat_general extends behat_base { // unnamed window (presumably the main window) to some other named // window, then we first set the main window name to a conventional // value that we can later use this name to switch back. - $this->getSession()->executeScript( - 'if (window.name == "") window.name = "' . self::MAIN_WINDOW_NAME . '"'); + $this->execute_script('if (window.name == "") window.name = "' . self::MAIN_WINDOW_NAME . '"'); $this->getSession()->switchToWindow($windowname); } @@ -872,7 +871,7 @@ class behat_general extends behat_base { return a.compareDocumentPosition(b) & Node.DOCUMENT_POSITION_FOLLOWING; })() EOF; - $ok = $this->getSession()->getDriver()->evaluateScript($js); + $ok = $this->evaluate_script($js); } else { // Using following xpath axe to find it. @@ -1517,11 +1516,12 @@ EOF; $this->pageloaddetectionrunning = true; - $session->executeScript( - 'var span = document.createElement("span"); - span.setAttribute("data-rel", "' . self::PAGE_LOAD_DETECTION_STRING . '"); - span.setAttribute("style", "display: none;"); - document.body.appendChild(span);'); + $this->execute_script( + 'var span = document.createElement("span"); + span.setAttribute("data-rel", "' . self::PAGE_LOAD_DETECTION_STRING . '"); + span.setAttribute("style", "display: none;"); + document.body.appendChild(span);' + ); } /** @@ -1719,7 +1719,7 @@ EOF; $xpath = addslashes_js($element->getXpath()); $script = 'return (function() { return document.activeElement === document.evaluate("' . $xpath . '", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; })(); '; - $targetisfocused = $this->getSession()->evaluateScript($script); + $targetisfocused = $this->evaluate_script($script); if ($not == ' not') { if ($targetisfocused) { throw new ExpectationException("$nodeelement $nodeselectortype is focused", $this->getSession()); @@ -1751,7 +1751,7 @@ EOF; $xpath = addslashes_js($element->getXpath()); $script = 'return (function() { return document.activeElement === document.evaluate("' . $xpath . '", document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue; })(); '; - $targetisfocused = $this->getSession()->evaluateScript($script); + $targetisfocused = $this->evaluate_script($script); if ($not == ' not') { if ($targetisfocused) { throw new ExpectationException("$nodeelement $nodeselectortype is focused", $this->getSession()); diff --git a/lib/tests/behat/behat_navigation.php b/lib/tests/behat/behat_navigation.php index 7856fa761e6..86b17ac4862 100644 --- a/lib/tests/behat/behat_navigation.php +++ b/lib/tests/behat/behat_navigation.php @@ -426,7 +426,7 @@ class behat_navigation extends behat_base { )"; // Adding an extra click we need to show the 'Log in' link. - if (!$this->getSession()->getDriver()->evaluateScript($navbuttonjs)) { + if (!$this->evaluate_script($navbuttonjs)) { return false; } diff --git a/mod/assign/feedback/editpdf/tests/behat/behat_assignfeedback_editpdf.php b/mod/assign/feedback/editpdf/tests/behat/behat_assignfeedback_editpdf.php index fb9f4b2960c..c6c69d686c5 100644 --- a/mod/assign/feedback/editpdf/tests/behat/behat_assignfeedback_editpdf.php +++ b/mod/assign/feedback/editpdf/tests/behat/behat_assignfeedback_editpdf.php @@ -61,21 +61,21 @@ class behat_assignfeedback_editpdf extends behat_base { var event = { clientX: 100, clientY: 250, preventDefault: function() {} }; instance.edit_start(event); }()); '; - $this->getSession()->executeScript($js); + $this->execute_script($js); sleep(1); $js = ' (function() { var instance = M.assignfeedback_editpdf.instance; var event = { clientX: 150, clientY: 275, preventDefault: function() {} }; instance.edit_move(event); }()); '; - $this->getSession()->executeScript($js); + $this->execute_script($js); sleep(1); $js = ' (function() { var instance = M.assignfeedback_editpdf.instance; var event = { clientX: 200, clientY: 300, preventDefault: function() {} }; instance.edit_end(event); }()); '; - $this->getSession()->executeScript($js); + $this->execute_script($js); sleep(1); } diff --git a/question/type/ddmarker/tests/behat/behat_qtype_ddmarker.php b/question/type/ddmarker/tests/behat/behat_qtype_ddmarker.php index 364f48bdbad..d498e435057 100644 --- a/question/type/ddmarker/tests/behat/behat_qtype_ddmarker.php +++ b/question/type/ddmarker/tests/behat/behat_qtype_ddmarker.php @@ -71,8 +71,7 @@ class behat_qtype_ddmarker extends behat_base { // DOM node so that its centre is over the centre of anothe DOM node. // Therefore to make it drag to the specified place, we have to add // a target div. - $session = $this->getSession(); - $session->executeScript(" + $this->execute_script(" (function() { if (document.getElementById('target-{$x}-{$y}')) { return; @@ -90,7 +89,8 @@ class behat_qtype_ddmarker extends behat_base { target.style.setProperty('top', yadjusted + 'px'); target.style.setProperty('width', '1px'); target.style.setProperty('height', '1px'); - }())"); + }())" + ); $generalcontext = behat_context_helper::get('behat_general'); $generalcontext->i_drag_and_i_drop_it_in($this->marker_xpath($marker, $item), diff --git a/search/tests/behat/behat_search.php b/search/tests/behat/behat_search.php index c7ca9478f31..ac11c88cf7a 100644 --- a/search/tests/behat/behat_search.php +++ b/search/tests/behat/behat_search.php @@ -51,7 +51,7 @@ class behat_search extends behat_base { $this->execute('behat_forms::i_set_the_field_to', ['q', $query]); // Submit the form. - $this->getSession()->executeScript('document.querySelector(".search-input-form.expanded").submit();'); + $this->execute_script('return document.querySelector(".search-input-form.expanded").submit();'); } /** From 9ab20032175bedcdc95c8ed2e22ea6138ddc2f69 Mon Sep 17 00:00:00 2001 From: Andrew Nicols Date: Mon, 16 Dec 2019 16:40:52 +0800 Subject: [PATCH 3/3] MDL-67657 behat: Open course with editing using URL This is a huge performance improvement for behat. The current set of steps loads the page It then looks for the Classic/Clean version of the settings menu If it fails to find it looks for the Boost Cog Then it clicks the "Turn editing on" button This can take a substantial period. We do not actually need to run these steps as we are able to jump straight to the URL. We already have access to the sesskey value required to do this. There is not loss in testing functionality because the actual testing of the Turn editing functionality is tested in other places sufficiently. --- lib/behat/behat_base.php | 18 ++++++++++++++++++ lib/tests/behat/behat_navigation.php | 15 ++++++++++++++- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index c0d84412de8..490bf5401a1 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -1222,4 +1222,22 @@ EOF; $session->executeScript($script); } + + /** + * Get the session key for the current session via Javascript. + * + * @return string + */ + public function get_sesskey(): string { + $script = <<evaluate_script($script); + } } diff --git a/lib/tests/behat/behat_navigation.php b/lib/tests/behat/behat_navigation.php index 86b17ac4862..0ec0d98294f 100644 --- a/lib/tests/behat/behat_navigation.php +++ b/lib/tests/behat/behat_navigation.php @@ -780,7 +780,7 @@ class behat_navigation extends behat_base { } /** - * Opens the course homepage with editing mode on. + * Open the course homepage with editing mode enabled. * * @Given /^I am on "(?P(?:[^"]|\\")*)" course homepage with editing mode on$/ * @throws coding_exception @@ -789,9 +789,22 @@ class behat_navigation extends behat_base { */ public function i_am_on_course_homepage_with_editing_mode_on($coursefullname) { global $DB; + $course = $DB->get_record("course", array("fullname" => $coursefullname), 'id', MUST_EXIST); $url = new moodle_url('/course/view.php', ['id' => $course->id]); + + if ($this->running_javascript() && $sesskey = $this->get_sesskey()) { + // Javascript is running so it is possible to grab the session ket and jump straight to editing mode. + $url->param('edit', 1); + $url->param('sesskey', $sesskey); + $this->getSession()->visit($this->locate_path($url->out_as_local_url(false))); + + return; + } + + // Visit the course page. $this->getSession()->visit($this->locate_path($url->out_as_local_url(false))); + try { $this->execute("behat_forms::press_button", get_string('turneditingon')); } catch (Exception $e) {