diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index c97f492b696..b36272b1424 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -482,12 +482,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'); } /** @@ -502,7 +535,7 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { } // Check on page to see if it's an app page. Safest way is to look for added JavaScript. - return $this->getSession()->evaluateScript('typeof window.behat') === 'object'; + return $this->evaluate_script('return typeof window.behat') === 'object'; } /** @@ -738,14 +771,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']; } @@ -794,8 +831,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? @@ -1206,4 +1243,70 @@ 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); + } + + /** + * 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/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_app.php b/lib/tests/behat/behat_app.php index af82844ec7d..e474ffc6737 100644 --- a/lib/tests/behat/behat_app.php +++ b/lib/tests/behat/behat_app.php @@ -297,8 +297,7 @@ class behat_app extends behat_base { }, false, 60); // Run the scripts to install Moodle 'pending' checks. - $this->getSession()->executeScript( - file_get_contents(__DIR__ . '/app_behat_runtime.js')); + $this->execute_script(file_get_contents(__DIR__ . '/app_behat_runtime.js')); // Wait until the site login field appears OR the main page. $situation = $this->spin( @@ -373,8 +372,7 @@ class behat_app extends behat_base { */ public function i_press_the_standard_button_in_the_app(string $button) { $this->spin(function($context, $args) use ($button) { - $result = $this->getSession()->evaluateScript('return window.behat.pressStandard("' . - $button . '");'); + $result = $this->evaluate_script("return window.behat.pressStandard('{$button}');"); if ($result !== 'OK') { throw new DriverException('Error pressing standard button - ' . $result); } @@ -391,7 +389,7 @@ class behat_app extends behat_base { */ public function i_close_the_popup_in_the_app() { $this->spin(function($context, $args) { - $result = $this->getSession()->evaluateScript('return window.behat.closePopup();'); + $result = $this->evaluate_script("return window.behat.closePopup();"); if ($result !== 'OK') { throw new DriverException('Error closing popup - ' . $result); } @@ -449,7 +447,7 @@ class behat_app extends behat_base { } else { $nearbit = ''; } - $result = $context->getSession()->evaluateScript('return window.behat.press("' . + $result = $this->evaluate_script('return window.behat.press("' . addslashes_js($text) . '"' . $nearbit .');'); if ($result !== 'OK') { throw new DriverException('Error pressing item - ' . $result); @@ -472,7 +470,7 @@ class behat_app extends behat_base { */ public function i_set_the_field_in_the_app(string $field, string $value) { $this->spin(function($context, $args) use ($field, $value) { - $result = $this->getSession()->evaluateScript('return window.behat.setField("' . + $result = $this->evaluate_script('return window.behat.setField("' . addslashes_js($field) . '", "' . addslashes_js($value) . '");'); if ($result !== 'OK') { throw new DriverException('Error setting field - ' . $result); @@ -494,7 +492,7 @@ class behat_app extends behat_base { */ public function the_header_should_be_in_the_app(string $text) { $result = $this->spin(function($context, $args) { - $result = $this->getSession()->evaluateScript('return window.behat.getHeader();'); + $result = $this->evaluate_script('return window.behat.getHeader();'); if (substr($result, 0, 3) !== 'OK:') { throw new DriverException('Error getting header - ' . $result); } @@ -536,7 +534,7 @@ class behat_app extends behat_base { if (count($names) !== 2) { throw new DriverException('Expected to see 2 tabs open, not ' . count($names)); } - $this->getSession()->getDriver()->executeScript('window.close()'); + $this->execute_script('window.close()'); $this->getSession()->switchToWindow($names[0]); } @@ -548,6 +546,6 @@ class behat_app extends behat_base { * @throws DriverException If the navigator.online mode is not available */ public function i_switch_offline_mode(string $offline) { - $this->getSession()->evaluateScript('appProvider.setForceOffline(' . $offline . ');'); + $this->execute_script('appProvider.setForceOffline(' . $offline . ');'); } } diff --git a/lib/tests/behat/behat_general.php b/lib/tests/behat/behat_general.php index ee6175ae56b..d01ada79b44 100644 --- a/lib/tests/behat/behat_general.php +++ b/lib/tests/behat/behat_general.php @@ -228,8 +228,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); } @@ -258,7 +257,7 @@ class behat_general extends behat_base { $names = $this->getSession()->getWindowNames(); for ($index = 1; $index < count($names); $index ++) { $this->getSession()->switchToWindow($names[$index]); - $this->getSession()->executeScript("window.open('', '_self').close();"); + $this->execute_script("window.open('', '_self').close();"); } $names = $this->getSession()->getWindowNames(); if (count($names) !== 1) { @@ -924,7 +923,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. @@ -1609,11 +1608,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);' + ); } /** @@ -1811,7 +1811,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()); @@ -1843,7 +1843,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 052fe308ae3..a2309e4edca 100644 --- a/lib/tests/behat/behat_navigation.php +++ b/lib/tests/behat/behat_navigation.php @@ -391,7 +391,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; } @@ -757,7 +757,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 @@ -766,9 +766,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) { 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 04cabba1853..9a74a1373e5 100644 --- a/question/type/ddmarker/tests/behat/behat_qtype_ddmarker.php +++ b/question/type/ddmarker/tests/behat/behat_qtype_ddmarker.php @@ -68,8 +68,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; @@ -86,7 +85,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), 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();'); } /**