Skip to content

Commit

Permalink
MDL-64979 Behat: Add option to increase timeouts
Browse files Browse the repository at this point in the history
  • Loading branch information
sammarshallou committed Mar 19, 2019
1 parent 8360ef9 commit 0ec20a1
Show file tree
Hide file tree
Showing 22 changed files with 111 additions and 50 deletions.
4 changes: 2 additions & 2 deletions admin/tests/behat/behat_admin.php
Expand Up @@ -58,7 +58,7 @@ public function i_set_the_following_administration_settings_values(TableNode $ta
// 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->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
}

// Search by label.
Expand All @@ -67,7 +67,7 @@ public function i_set_the_following_administration_settings_values(TableNode $ta
$submitsearch = $this->find('css', 'form.adminsearchform input[type=submit]');
$submitsearch->press();

$this->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
$this->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);

// Admin settings does not use the same DOM structure than other moodle forms
// but we also need to use lib/behat/form_field/* to deal with the different moodle form elements.
Expand Down
6 changes: 6 additions & 0 deletions config-dist.php
Expand Up @@ -795,6 +795,12 @@
// Example:
// $CFG->behat_usedeprecated = true;
//
// If you are using a slow machine, it may help to increase the timeouts that Behat uses. The
// following example will increase timeouts by a factor of 3 (using 30 seconds instead of 10
// seconds, for instance).
// Example:
// $CFG->behat_increasetimeout = 3;
//
// Including feature files from directories outside the dirroot is possible if required. The setting
// requires that the running user has executable permissions on all parent directories in the paths.
// Example:
Expand Down
4 changes: 2 additions & 2 deletions course/tests/behat/behat_course.php
Expand Up @@ -346,7 +346,7 @@ public function i_show_section($sectionnumber) {
$showlink->click();

if ($this->running_javascript()) {
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
$this->i_wait_until_section_is_available($sectionnumber);
}
}
Expand Down Expand Up @@ -380,7 +380,7 @@ public function i_hide_section($sectionnumber) {
);

if ($this->running_javascript()) {
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
$this->i_wait_until_section_is_available($sectionnumber);
}
}
Expand Down
6 changes: 3 additions & 3 deletions group/tests/behat/behat_groups.php
Expand Up @@ -67,13 +67,13 @@ public function i_add_user_to_group_members($userfullname, $groupname) {
$script = "Syn.trigger('change', {}, {{ELEMENT}})";
$driver->triggerSynScript($select->getXpath(), $script);
}
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);

// Here we don't need to wait for the AJAX response.
$this->find_button(get_string('adduserstogroup', 'group'))->click();

// Wait for add/remove members page to be loaded.
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);

// Getting the option and selecting it.
$select = $this->find_field('addselect');
Expand All @@ -86,7 +86,7 @@ public function i_add_user_to_group_members($userfullname, $groupname) {
$this->find_button(get_string('add'))->click();

// Wait for the page to load.
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);

// Returning to the main groups page.
$this->find_button(get_string('backtogroups', 'group'))->click();
Expand Down
76 changes: 64 additions & 12 deletions lib/behat/behat_base.php
Expand Up @@ -167,7 +167,7 @@ protected function find_all($selector, $locator, $exception = false, $node = fal

// How much we will be waiting for the element to appear.
if (!$timeout) {
$timeout = self::TIMEOUT;
$timeout = self::get_timeout();
$microsleep = false;
} else {
// Spinning each 0.1 seconds if the timeout was forced as we understand
Expand Down Expand Up @@ -308,13 +308,13 @@ protected function spin($lambda, $args = false, $timeout = false, $exception = f

// Using default timeout which is pretty high.
if (!$timeout) {
$timeout = self::TIMEOUT;
$timeout = self::get_timeout();
}
if ($microsleep) {
// Will sleep 1/10th of a second by default for self::TIMEOUT seconds.
// Will sleep 1/10th of a second by default for self::get_timeout() seconds.
$loops = $timeout * 10;
} else {
// Will sleep for self::TIMEOUT seconds.
// Will sleep for self::get_timeout() seconds.
$loops = $timeout;
}

Expand Down Expand Up @@ -501,7 +501,7 @@ function($context, $args) {
return false;
},
array('selector' => $selector, 'locator' => $locator),
self::EXTENDED_TIMEOUT,
self::get_extended_timeout(),
$exception,
true
);
Expand Down Expand Up @@ -535,7 +535,7 @@ function($context, $args) {
return false;
},
array('selector' => $selector, 'locator' => $locator),
self::EXTENDED_TIMEOUT,
self::get_extended_timeout(),
$exception,
true
);
Expand Down Expand Up @@ -567,7 +567,7 @@ function($context, $args) {
return false;
},
$node,
self::EXTENDED_TIMEOUT,
self::get_extended_timeout(),
$exception,
true
);
Expand Down Expand Up @@ -602,7 +602,7 @@ function($context, $args) {
return false;
},
array($node, $attribute, $attributevalue),
self::EXTENDED_TIMEOUT,
self::get_extended_timeout(),
$exception,
true
);
Expand Down Expand Up @@ -730,7 +730,7 @@ public function wait_for_pending_js() {
public static function wait_for_pending_js_in_session(Session $session) {
// 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++) {
for ($i = 0; $i < self::get_extended_timeout() * 10; $i++) {
$pending = '';
try {
$jscode = trim(preg_replace('/\s+/', ' ', '
Expand Down Expand Up @@ -771,11 +771,13 @@ public static function wait_for_pending_js_in_session(Session $session) {
}

// Timeout waiting for JS to complete. It will be caught and forwarded to behat_hooks::i_look_for_exceptions().
// It is unlikely that Javascript code of a page or an AJAX request needs more than self::EXTENDED_TIMEOUT seconds
// It is unlikely that Javascript code of a page or an AJAX request needs more than get_extended_timeout() seconds
// to be loaded, although when pages contains Javascript errors M.util.js_complete() can not be executed, so the
// number of JS pending code and JS completed code will not match and we will reach this point.
throw new \Exception('Javascript code and/or AJAX requests are not ready after ' . self::EXTENDED_TIMEOUT .
' seconds. There is a Javascript error or the code is extremely slow.');
throw new \Exception('Javascript code and/or AJAX requests are not ready after ' .
self::get_extended_timeout() .
' seconds. There is a Javascript error or the code is extremely slow. ' .
'If you are using a slow machine, consider setting $CFG->behat_increasetimeout.');
}

/**
Expand Down Expand Up @@ -976,4 +978,54 @@ protected function js_trigger_click($node) {
$driver->click($xpath);
}
}

/**
* Gets the required timeout in seconds.
*
* @param int $timeout One of the TIMEOUT constants
* @return int Actual timeout (in seconds)
*/
protected static function get_real_timeout(int $timeout) : int {
global $CFG;
if (!empty($CFG->behat_increasetimeout)) {
return $timeout * $CFG->behat_increasetimeout;
} else {
return $timeout;
}
}

/**
* Gets the default timeout.
*
* The timeout for each Behat step (load page, wait for an element to load...).
*
* @return int Timeout in seconds
*/
public static function get_timeout() : int {
return self::get_real_timeout(6);
}

/**
* Gets the reduced timeout.
*
* A reduced timeout for cases where self::get_timeout() is too much
* and a simple $this->getSession()->getPage()->find() could not
* be enough.
*
* @return int Timeout in seconds
*/
public static function get_reduced_timeout() : int {
return self::get_real_timeout(2);
}

/**
* Gets the extended timeout.
*
* A longer timeout for cases where the normal timeout is not enough.
*
* @return int Timeout in seconds
*/
public static function get_extended_timeout() : int {
return self::get_real_timeout(10);
}
}
2 changes: 1 addition & 1 deletion lib/behat/form_field/behat_form_filemanager.php
Expand Up @@ -62,7 +62,7 @@ class behat_form_filemanager extends behat_form_field {
public function get_value() {

// Wait until DOM and JS is ready.
$this->session->wait(behat_base::TIMEOUT, behat_base::PAGE_READY_JS);
$this->session->wait(behat_base::get_timeout(), behat_base::PAGE_READY_JS);

// Get the label to restrict the files to this single form field.
$fieldlabel = $this->get_field_locator();
Expand Down
2 changes: 1 addition & 1 deletion lib/behat/form_field/behat_form_passwordunmask.php
Expand Up @@ -67,7 +67,7 @@ public function set_value($value) {
$this->field->keyDown(13);
$this->field->keyPress(13);
$this->field->keyUp(13);
$this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
$this->session->wait(behat_base::get_timeout() * 1000, behat_base::PAGE_READY_JS);
}
}
}
2 changes: 1 addition & 1 deletion lib/behat/form_field/behat_form_select.php
Expand Up @@ -94,7 +94,7 @@ public function set_value($value) {
}
}
}
$this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
$this->session->wait(behat_base::get_timeout() * 1000, behat_base::PAGE_READY_JS);
}
}

Expand Down
2 changes: 1 addition & 1 deletion lib/tests/behat/behat_forms.php
Expand Up @@ -126,7 +126,7 @@ protected function expand_all_fields() {
"//a[contains(concat(' ', @class, ' '), ' fheader ') and @aria-expanded = 'false']";

$collapseexpandlink = $this->find('xpath', $expandallxpath . '|' . $expandonlysection,
false, false, self::REDUCED_TIMEOUT);
false, false, behat_base::get_reduced_timeout());
$collapseexpandlink->click();

} catch (ElementNotFoundException $e) {
Expand Down
22 changes: 11 additions & 11 deletions lib/tests/behat/behat_general.php
Expand Up @@ -174,7 +174,7 @@ function($context, $iframename) {
return true;
},
$iframename,
self::EXTENDED_TIMEOUT
behat_base::get_extended_timeout()
);
}

Expand Down Expand Up @@ -271,7 +271,7 @@ public function wait_until_the_page_is_ready() {
return;
}

$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
}

/**
Expand Down Expand Up @@ -604,10 +604,10 @@ public function assert_page_not_contains_text($text) {
"[count(descendant::*[contains(., $xpathliteral)]) = 0]";

// We should wait a while to ensure that the page is not still loading elements.
// Waiting less than self::TIMEOUT as we already waited for the DOM to be ready and
// Waiting less than self::get_timeout() as we already waited for the DOM to be ready and
// all JS to be executed.
try {
$nodes = $this->find_all('xpath', $xpath, false, false, self::REDUCED_TIMEOUT);
$nodes = $this->find_all('xpath', $xpath, false, false, self::get_reduced_timeout());
} catch (ElementNotFoundException $e) {
// All ok.
return;
Expand Down Expand Up @@ -644,7 +644,7 @@ function($context, $args) {
return true;
},
array('nodes' => $nodes, 'text' => $text),
self::REDUCED_TIMEOUT,
behat_base::get_reduced_timeout(),
false,
true
);
Expand Down Expand Up @@ -728,7 +728,7 @@ public function assert_element_not_contains_text($text, $element, $selectortype)
// 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 {
$nodes = $this->find_all('xpath', $xpath, false, $container, self::REDUCED_TIMEOUT);
$nodes = $this->find_all('xpath', $xpath, false, $container, self::get_reduced_timeout());
} catch (ElementNotFoundException $e) {
// All ok.
return;
Expand All @@ -754,7 +754,7 @@ function($context, $args) {
return true;
},
array('nodes' => $nodes, 'text' => $text, 'element' => $element),
self::REDUCED_TIMEOUT,
behat_base::get_reduced_timeout(),
false,
true
);
Expand Down Expand Up @@ -932,7 +932,7 @@ function($context, $args) {
return $context->getSession()->getPage()->findAll($args['selector'], $args['locator']);
},
$params,
self::REDUCED_TIMEOUT,
behat_base::get_reduced_timeout(),
$exception,
false
);
Expand Down Expand Up @@ -1124,7 +1124,7 @@ public function should_not_exist_in_the($element, $selectortype, $containereleme
// Would be better to use a 1 second sleep because the element should not be there,
// but we would need to duplicate the whole find_all() logic to do it, the benefit of
// changing to 1 second sleep is not significant.
$this->find($selector, $locator, false, $containernode, self::REDUCED_TIMEOUT);
$this->find($selector, $locator, false, $containernode, behat_base::get_reduced_timeout());
} catch (ElementNotFoundException $e) {
// It passes.
return;
Expand Down Expand Up @@ -1390,7 +1390,7 @@ function($context, $args) {
return $this->download_file_from_link($link);
},
array('link' => $link),
self::EXTENDED_TIMEOUT,
behat_base::get_extended_timeout(),
$exception
);

Expand Down Expand Up @@ -1433,7 +1433,7 @@ function($context, $args) {
return $this->download_file_from_link($link);
},
array('link' => $link),
self::EXTENDED_TIMEOUT,
behat_base::get_extended_timeout(),
$exception
);

Expand Down
4 changes: 2 additions & 2 deletions lib/tests/behat/behat_hooks.php
Expand Up @@ -489,13 +489,13 @@ public function after_step_javascript(AfterStepScope $scope) {
* @AfterScenario @_switch_window
*/
public function after_scenario_switchwindow(AfterScenarioScope $scope) {
for ($count = 0; $count < self::EXTENDED_TIMEOUT; $count++) {
for ($count = 0; $count < behat_base::get_extended_timeout(); $count++) {
try {
$this->getSession()->restart();
break;
} catch (DriverException $e) {
// Wait for timeout and try again.
sleep(self::TIMEOUT);
sleep(self::get_timeout());
}
}
// If session is not restarted above then it will try to start session before next scenario
Expand Down
2 changes: 1 addition & 1 deletion lib/tests/behat/behat_navigation.php
Expand Up @@ -297,7 +297,7 @@ protected function find_node_in_navigation($nodetext, $parentnodes, $nodetype =
$jscondition = '(document.evaluate("' . $pnode->getXpath() . '", document, null, '.
'XPathResult.ANY_TYPE, null).iterateNext().getAttribute(\'data-loaded\') == "true")';

$this->getSession()->wait(self::EXTENDED_TIMEOUT * 1000, $jscondition);
$this->getSession()->wait(behat_base::get_extended_timeout() * 1000, $jscondition);
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/tests/behat/behat_permissions.php
Expand Up @@ -106,7 +106,7 @@ public function i_fill_the_capabilities_form_with_the_following_permissions($tab
$advancedtoggle->click();

// Wait for the page to load.
$this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS);
$this->getSession()->wait(self::get_timeout() * 1000, self::PAGE_READY_JS);
}
} catch (Exception $e) {
// We already are in advanced mode.
Expand Down
3 changes: 3 additions & 0 deletions lib/upgrade.txt
Expand Up @@ -7,6 +7,9 @@ information provided here is intended especially for developers.
It is recommended that privacy providers using this function call rewrite any long query into a number of separate
calls to add_from_sql for improved performance, and that the new argument is used.
This will allow queries to remain backwards-compatible with older versions of Moodle but will have significantly better performance in version supporting the innerjoin parameter.
* Behat timeout constants behat_base::TIMEOUT, EXTENDED_TIMEOUT, and REDUCED_TIMEOUT will be
deprecated in 3.7. Please instead use the functions behat_base::get_timeout(), get_extended_timeout(),
and get_reduced_timeout(). These allow for timeouts to be increased by a setting in config.php.

=== 3.5.4 ===

Expand Down
Expand Up @@ -100,6 +100,6 @@ public function i_wait_for_all_editpdf_pages_to_load() {
];
$js = implode(' && ', $conditions);

$this->getSession()->wait(self::TIMEOUT * 1000, "({$js})");
$this->getSession()->wait(self::get_timeout() * 1000, "({$js})");
}
}

0 comments on commit 0ec20a1

Please sign in to comment.