mirror of
https://github.com/moodle/moodle.git
synced 2025-08-07 01:46:45 +02:00
Merge branch 'MDL-42625_master' of git://github.com/dmonllao/moodle
This commit is contained in:
commit
ebc77165a4
51 changed files with 991 additions and 335 deletions
|
@ -68,6 +68,7 @@ class behat_deprecated extends behat_base {
|
|||
// Looking for the element DOM node inside the specified row.
|
||||
list($selector, $locator) = $this->transform_selector($selectortype, $element);
|
||||
$elementnode = $this->find($selector, $locator, false, $rownode);
|
||||
$this->ensure_element_is_visible($elementnode);
|
||||
$elementnode->click();
|
||||
}
|
||||
|
||||
|
|
|
@ -69,6 +69,9 @@ class behat_forms extends behat_base {
|
|||
*/
|
||||
public function i_fill_the_moodle_form_with(TableNode $data) {
|
||||
|
||||
// We ensure that all the editors are loaded and we can interact with them.
|
||||
$this->ensure_editors_are_loaded();
|
||||
|
||||
// Expand all fields in case we have.
|
||||
$this->expand_all_fields();
|
||||
|
||||
|
@ -171,31 +174,11 @@ class behat_forms extends behat_base {
|
|||
public function select_option($option, $select) {
|
||||
|
||||
$selectnode = $this->find_field($select);
|
||||
$selectnode->selectOption($option);
|
||||
|
||||
// Adding a click as Selenium requires it to fire some JS events.
|
||||
if ($this->running_javascript()) {
|
||||
|
||||
// In some browsers the selectOption actions can perform a page reload
|
||||
// so we need to ensure the element is still available to continue interacting
|
||||
// with it. We don't wait here.
|
||||
if (!$this->getSession()->getDriver()->find($selectnode->getXpath())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Single select needs an extra click in the option.
|
||||
if (!$selectnode->hasAttribute('multiple')) {
|
||||
|
||||
// Avoid quotes problems.
|
||||
$option = $this->getSession()->getSelectorsHandler()->xpathLiteral($option);
|
||||
$xpath = "//option[(./@value=$option or normalize-space(.)=$option)]";
|
||||
$optionnode = $this->find('xpath', $xpath, false, $selectnode);
|
||||
$optionnode->click();
|
||||
} else {
|
||||
// Multiple ones needs the click in the select.
|
||||
$selectnode->click();
|
||||
}
|
||||
}
|
||||
// We delegate to behat_form_field class, it will
|
||||
// guess the type properly as it is a select tag.
|
||||
$selectformfield = behat_field_manager::get_form_field($selectnode, $this->getSession());
|
||||
$selectformfield->set_value($option);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -225,6 +208,8 @@ class behat_forms extends behat_base {
|
|||
*/
|
||||
public function check_option($option) {
|
||||
|
||||
// We don't delegate to behat_form_checkbox as the
|
||||
// step is explicitly saying I check.
|
||||
$checkboxnode = $this->find_field($option);
|
||||
$checkboxnode->check();
|
||||
}
|
||||
|
@ -238,6 +223,8 @@ class behat_forms extends behat_base {
|
|||
*/
|
||||
public function uncheck_option($option) {
|
||||
|
||||
// We don't delegate to behat_form_checkbox as the
|
||||
// step is explicitly saying I uncheck.
|
||||
$checkboxnode = $this->find_field($option);
|
||||
$checkboxnode->uncheck();
|
||||
}
|
||||
|
|
|
@ -124,7 +124,20 @@ class behat_general extends behat_base {
|
|||
* @param string $iframename
|
||||
*/
|
||||
public function switch_to_iframe($iframename) {
|
||||
$this->getSession()->switchToIFrame($iframename);
|
||||
|
||||
// We spin to give time to the iframe to be loaded.
|
||||
// Using extended timeout as we don't know about which
|
||||
// kind of iframe will be loaded.
|
||||
$this->spin(
|
||||
function($context, $iframename) {
|
||||
$context->getSession()->switchToIFrame($iframename);
|
||||
|
||||
// If no exception we are done.
|
||||
return true;
|
||||
},
|
||||
$iframename,
|
||||
self::EXTENDED_TIMEOUT
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -173,6 +186,7 @@ class behat_general extends behat_base {
|
|||
public function click_link($link) {
|
||||
|
||||
$linknode = $this->find_link($link);
|
||||
$this->ensure_node_is_visible($linknode);
|
||||
$linknode->click();
|
||||
}
|
||||
|
||||
|
@ -202,7 +216,56 @@ class behat_general extends behat_base {
|
|||
throw new DriverException('Waits are disabled in scenarios without Javascript support');
|
||||
}
|
||||
|
||||
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the editors are all completely loaded.
|
||||
*
|
||||
* @Given /^I wait until the editors are loaded$/
|
||||
* @throws DriverException
|
||||
*/
|
||||
public function wait_until_editors_are_loaded() {
|
||||
|
||||
if (!$this->running_javascript()) {
|
||||
throw new DriverException('Editors are not loaded when running without Javascript support');
|
||||
}
|
||||
|
||||
$this->ensure_editors_are_loaded();
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the provided element selector exists in the DOM
|
||||
*
|
||||
* Using the protected method as this method will be usually
|
||||
* called by other methods which are not returning a set of
|
||||
* steps and performs the actions directly, so it would not
|
||||
* be executed if it returns another step.
|
||||
|
||||
* @Given /^I wait until "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" exists$/
|
||||
* @param string $element
|
||||
* @param string $selector
|
||||
* @return void
|
||||
*/
|
||||
public function wait_until_exists($element, $selectortype) {
|
||||
$this->ensure_element_exists($element, $selectortype);
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits until the provided element does not exist in the DOM
|
||||
*
|
||||
* Using the protected method as this method will be usually
|
||||
* called by other methods which are not returning a set of
|
||||
* steps and performs the actions directly, so it would not
|
||||
* be executed if it returns another step.
|
||||
|
||||
* @Given /^I wait until "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>[^"]*)" does not exist$/
|
||||
* @param string $element
|
||||
* @param string $selector
|
||||
* @return void
|
||||
*/
|
||||
public function wait_until_does_not_exists($element, $selectortype) {
|
||||
$this->ensure_element_does_not_exist($element, $selectortype);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -230,6 +293,7 @@ class behat_general extends behat_base {
|
|||
|
||||
// Gets the node based on the requested selector type and locator.
|
||||
$node = $this->get_selected_node($selectortype, $element);
|
||||
$this->ensure_node_is_visible($node);
|
||||
$node->click();
|
||||
}
|
||||
|
||||
|
@ -245,6 +309,7 @@ class behat_general extends behat_base {
|
|||
public function i_click_on_in_the($element, $selectortype, $nodeelement, $nodeselectortype) {
|
||||
|
||||
$node = $this->get_node_in_container($selectortype, $element, $nodeselectortype, $nodeelement);
|
||||
$this->ensure_node_is_visible($node);
|
||||
$node->click();
|
||||
}
|
||||
|
||||
|
@ -298,6 +363,9 @@ class behat_general extends behat_base {
|
|||
/**
|
||||
* Checks, that the specified element is not visible. Only available in tests using Javascript.
|
||||
*
|
||||
* As a "not" method, it's performance is not specially good as we should ensure that the element
|
||||
* have time to appear.
|
||||
*
|
||||
* @Then /^"(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>(?:[^"]|\\")*)" should not be visible$/
|
||||
* @throws ElementNotFoundException
|
||||
* @throws ExpectationException
|
||||
|
@ -381,24 +449,35 @@ class behat_general extends behat_base {
|
|||
$xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
|
||||
"[count(descendant::*[contains(., $xpathliteral)]) = 0]";
|
||||
|
||||
// Wait until it finds the text, otherwise custom exception.
|
||||
try {
|
||||
$nodes = $this->find_all('xpath', $xpath);
|
||||
|
||||
// We also check for the element visibility when running JS tests.
|
||||
if ($this->running_javascript()) {
|
||||
foreach ($nodes as $node) {
|
||||
if ($node->isVisible()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ExpectationException("'{$text}' text was found but was not visible", $this->getSession());
|
||||
}
|
||||
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $text . '" text was not found in the page', $this->getSession());
|
||||
}
|
||||
|
||||
// If we are not running javascript we have enough with the
|
||||
// element existing as we can't check if it is visible.
|
||||
if (!$this->running_javascript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We spin as we don't have enough checking that the element is there, we
|
||||
// should also ensure that the element is visible.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
|
||||
foreach ($args['nodes'] as $node) {
|
||||
if ($node->isVisible()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// If non of the nodes is visible we loop again.
|
||||
throw new ExpectationException('"' . $args['text'] . '" text was found but was not visible', $context->getSession());
|
||||
},
|
||||
array('nodes' => $nodes, 'text' => $text)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -410,16 +489,43 @@ class behat_general extends behat_base {
|
|||
*/
|
||||
public function assert_page_not_contains_text($text) {
|
||||
|
||||
// Delegating the process to assert_page_contains_text.
|
||||
// Looking for all the matching nodes without any other descendant matching the
|
||||
// same xpath (we are using contains(., ....).
|
||||
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
|
||||
$xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
|
||||
"[count(descendant::*[contains(., $xpathliteral)]) = 0]";
|
||||
|
||||
// We should wait a while to ensure that the page is not still loading elements.
|
||||
// Giving preference to the reliability of the results rather than to the performance.
|
||||
try {
|
||||
$this->assert_page_contains_text($text);
|
||||
} catch (ExpectationException $e) {
|
||||
// It should not appear, so this is good.
|
||||
$nodes = $this->find_all('xpath', $xpath);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
// All ok.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the page contains the text this is failing.
|
||||
throw new ExpectationException('"' . $text . '" text was found in the page', $this->getSession());
|
||||
// If we are not running javascript we have enough with the
|
||||
// element existing as we can't check if it is hidden.
|
||||
if (!$this->running_javascript()) {
|
||||
throw new ExpectationException('"' . $text . '" text was found in the page', $this->getSession());
|
||||
}
|
||||
|
||||
// If the element is there we should be sure that it is not visible.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
|
||||
foreach ($args['nodes'] as $node) {
|
||||
if ($node->isVisible()) {
|
||||
throw new ExpectationException('"' . $args['text'] . '" text was found in the page', $context->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
// If non of the found nodes is visible we consider that the text is not visible.
|
||||
return true;
|
||||
},
|
||||
array('nodes' => $nodes, 'text' => $text)
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -446,22 +552,30 @@ class behat_general extends behat_base {
|
|||
// Wait until it finds the text inside the container, otherwise custom exception.
|
||||
try {
|
||||
$nodes = $this->find_all('xpath', $xpath, false, $container);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $text . '" text was not found in the "' . $element . '" element', $this->getSession());
|
||||
}
|
||||
|
||||
// We also check for the element visibility when running JS tests.
|
||||
if ($this->running_javascript()) {
|
||||
foreach ($nodes as $node) {
|
||||
// If we are not running javascript we have enough with the
|
||||
// element existing as we can't check if it is visible.
|
||||
if (!$this->running_javascript()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We also check the element visibility when running JS tests.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
|
||||
foreach ($args['nodes'] as $node) {
|
||||
if ($node->isVisible()) {
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
throw new ExpectationException("'{$text}' text was found in the {$element} element but was not visible", $this->getSession());
|
||||
}
|
||||
|
||||
} catch (ElementNotFoundException $e) {
|
||||
throw new ExpectationException('"' . $text . '" text was not found in the ' . $element . ' element', $this->getSession());
|
||||
}
|
||||
|
||||
throw new ExpectationException('"' . $args['text'] . '" text was found in the "' . $args['element'] . '" element but was not visible', $context->getSession());
|
||||
},
|
||||
array('nodes' => $nodes, 'text' => $text, 'element' => $element)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -476,18 +590,45 @@ class behat_general extends behat_base {
|
|||
*/
|
||||
public function assert_element_not_contains_text($text, $element, $selectortype) {
|
||||
|
||||
// Delegating the process to assert_element_contains_text.
|
||||
// Getting the container where the text should be found.
|
||||
$container = $this->get_selected_node($selectortype, $element);
|
||||
|
||||
// Looking for all the matching nodes without any other descendant matching the
|
||||
// same xpath (we are using contains(., ....).
|
||||
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
|
||||
$xpath = "/descendant-or-self::*[contains(., $xpathliteral)]" .
|
||||
"[count(descendant::*[contains(., $xpathliteral)]) = 0]";
|
||||
|
||||
// We should wait a while to ensure that the page is not still loading elements.
|
||||
// Giving preference to the reliability of the results rather than to the performance.
|
||||
try {
|
||||
$this->assert_element_contains_text($text, $element, $selectortype);
|
||||
} catch (ExpectationException $e) {
|
||||
// It should not appear, so this is good.
|
||||
// We only catch ExpectationException as ElementNotFoundException
|
||||
// will be thrown if the container does not exist.
|
||||
$nodes = $this->find_all('xpath', $xpath, false, $container);
|
||||
} catch (ElementNotFoundException $e) {
|
||||
// All ok.
|
||||
return;
|
||||
}
|
||||
|
||||
// If the element contains the text this is failing.
|
||||
throw new ExpectationException('"' . $text . '" text was found in the ' . $element . ' element', $this->getSession());
|
||||
// If we are not running javascript we have enough with the
|
||||
// element not being found as we can't check if it is visible.
|
||||
if (!$this->running_javascript()) {
|
||||
throw new ExpectationException('"' . $text . '" text was found in the "' . $element . '" element', $this->getSession());
|
||||
}
|
||||
|
||||
// We need to ensure all the found nodes are hidden.
|
||||
$this->spin(
|
||||
function($context, $args) {
|
||||
|
||||
foreach ($args['nodes'] as $node) {
|
||||
if ($node->isVisible()) {
|
||||
throw new ExpectationException('"' . $args['text'] . '" text was found in the "' . $args['element'] . '" element', $context->getSession());
|
||||
}
|
||||
}
|
||||
|
||||
// If all the found nodes are hidden we are happy.
|
||||
return true;
|
||||
},
|
||||
array('nodes' => $nodes, 'text' => $text, 'element' => $element)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -222,42 +222,68 @@ class behat_hooks extends behat_base {
|
|||
}
|
||||
|
||||
/**
|
||||
* Checks that all DOM is ready.
|
||||
* Wait for JS to complete before beginning interacting with the DOM.
|
||||
*
|
||||
* Executed only when running against a real browser.
|
||||
*
|
||||
* @BeforeStep @javascript
|
||||
*/
|
||||
public function before_step_javascript($event) {
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for JS to complete after finishing the step.
|
||||
*
|
||||
* With this we ensure that there are not AJAX calls
|
||||
* still in progress.
|
||||
*
|
||||
* Executed only when running against a real browser.
|
||||
*
|
||||
* @AfterStep @javascript
|
||||
*/
|
||||
public function after_step_javascript($event) {
|
||||
$this->wait_for_pending_js();
|
||||
}
|
||||
|
||||
// If it doesn't have definition or it fails there is no need to check it.
|
||||
if ($event->getResult() != StepEvent::PASSED ||
|
||||
!$event->hasDefinition()) {
|
||||
return;
|
||||
}
|
||||
/**
|
||||
* Waits for all the JS to be loaded.
|
||||
*
|
||||
* @throws NoSuchWindow
|
||||
* @throws UnknownError
|
||||
* @return bool True or false depending whether all the JS is loaded or not.
|
||||
*/
|
||||
protected function wait_for_pending_js() {
|
||||
|
||||
// Wait until the page is ready.
|
||||
// We are already checking that we use a JS browser, this could
|
||||
// change in case we use another JS driver.
|
||||
try {
|
||||
|
||||
// Safari and Internet Explorer requires time between steps,
|
||||
// otherwise Selenium tries to click in the previous page's DOM.
|
||||
if ($this->getSession()->getDriver()->getBrowserName() == 'safari' ||
|
||||
$this->getSession()->getDriver()->getBrowserName() == 'internet explorer') {
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, false);
|
||||
|
||||
} else {
|
||||
// With other browsers we just wait for the DOM ready.
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, '(document.readyState === "complete")');
|
||||
// We don't use behat_base::spin() here as we don't want to end up with an exception
|
||||
// if the page & JSs don't finish loading properly.
|
||||
for ($i = 0; $i < self::EXTENDED_TIMEOUT * 10; $i++) {
|
||||
$pending = '';
|
||||
try {
|
||||
$jscode = 'return ' . self::PAGE_READY_JS . ' ? "" : M.util.pending_js.join(":");';
|
||||
$pending = $this->getSession()->evaluateScript($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?
|
||||
$pending = '';
|
||||
} catch (UnknownError $e) {
|
||||
// Same exception as before, but some combinations of browser + OS reports it as an unknown error
|
||||
// exception.
|
||||
$pending = '';
|
||||
}
|
||||
|
||||
} catch (NoSuchWindow $e) {
|
||||
// If we were interacting with a popup window it will not exists after closing it.
|
||||
} catch (UnknownError $e) {
|
||||
// Custom exception to provide more feedback about possible solutions.
|
||||
$this->throw_unknown_exception($e);
|
||||
// If there are no pending JS we stop waiting.
|
||||
if ($pending === '') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 0.1 seconds.
|
||||
usleep(100000);
|
||||
}
|
||||
|
||||
// Timeout waiting for JS to complete.
|
||||
// TODO MDL-43173 We should fail the scenarios if JS loading times out.
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -76,6 +76,7 @@ class behat_navigation extends behat_base {
|
|||
|
||||
$exception = new ExpectationException('The "' . $nodetext . '" node can not be expanded', $this->getSession());
|
||||
$node = $this->find('xpath', $xpath, $exception);
|
||||
$this->ensure_node_is_visible($node);
|
||||
$node->click();
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,16 @@ class behat_permissions extends behat_base {
|
|||
try {
|
||||
$advancedtoggle = $this->find_button(get_string('showadvanced', 'form'));
|
||||
if ($advancedtoggle) {
|
||||
$this->getSession()->getPage()->pressButton(get_string('showadvanced', 'form'));
|
||||
|
||||
// As we are interacting with a moodle form we wait for the editor to be ready
|
||||
// otherwise we may have problems when setting values on it or clicking on elements
|
||||
// as the position of the elements will change once the editor is loaded.
|
||||
$this->ensure_editors_are_loaded();
|
||||
|
||||
$advancedtoggle->click();
|
||||
|
||||
// Wait for the page to load.
|
||||
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
|
||||
}
|
||||
} catch (Exception $e) {
|
||||
// We already are in advanced mode.
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue