require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
require_once(__DIR__ . '/../../../lib/behat/behat_field_manager.php');
-use Behat\Behat\Context\Step\Given as Given,
- Behat\Gherkin\Node\TableNode as TableNode,
+use Behat\Gherkin\Node\TableNode as TableNode,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
// YAML decides when is is necessary to wrap strings between single quotes, so not controlled
// values like paths should not be asserted including the key name as they would depend on the
// directories values.
- $this->assertContains($CFG->dirroot . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'behat' . DIRECTORY_SEPARATOR . 'features', $contents);
+ $this->assertContains($CFG->dirroot, $contents);
// Not quoted strings.
$this->assertContains('micarro: /me/lo/robaron', $contents);
- $this->assertContains('class: behat_init_context', $contents);
// YAML uses single quotes to wrap URL strings.
$this->assertContains("base_url: '" . $CFG->behat_wwwroot . "'", $contents);
Scenario: Install language pack
Given I log in as "admin"
And I navigate to "Language packs" node in "Site administration > Language"
- When I set the field "Available language packs" to "English - Pirate (en_ar)"
+ When I set the field "Available language packs" to "en_ar"
And I press "Install selected language pack(s)"
Then I should see "Language pack 'en_ar' was successfully installed"
- And the "Installed language packs" select box should contain "English - Pirate (en_ar)"
+ And the "Installed language packs" select box should contain "en_ar"
And I navigate to "Live logs" node in "Site administration > Reports"
And I should see "The language pack 'en_ar' was installed."
And I log out
Scenario: Try to uninstall language pack
Given I log in as "admin"
And I navigate to "Language packs" node in "Site administration > Language"
- And I set the field "Available language packs" to "English - Pirate (en_ar)"
+ And I set the field "Available language packs" to "en_ar"
And I press "Install selected language pack(s)"
- When I set the field "Installed language packs" to "English - Pirate (en_ar)"
+ When I set the field "Installed language packs" to "en_ar"
And I press "Uninstall selected language pack(s)"
And I press "Continue"
Then I should see "Language pack 'en_ar' was uninstalled"
- And the "Installed language packs" select box should not contain "English - Pirate (en_ar)"
- And the "Available language packs" select box should contain "English - Pirate (en_ar)"
+ And the "Installed language packs" select box should not contain "en_ar"
+ And the "Available language packs" select box should contain "en_ar"
And I navigate to "Live logs" node in "Site administration > Reports"
And I should see "The language pack 'en_ar' was removed."
And I should see "Language pack uninstalled"
Scenario: Try to uninstall English language pack
Given I log in as "admin"
And I navigate to "Language packs" node in "Site administration > Language"
- When I set the field "Installed language packs" to "English (en)"
+ When I set the field "Installed language packs" to "en"
And I press "Uninstall selected language pack(s)"
Then I should see "The English language pack cannot be uninstalled."
And I navigate to "Live logs" node in "Site administration > Reports"
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given;
-use Behat\Behat\Context\Step\When as When;
+use Moodle\BehatExtension\Context\Step\Given as Given;
+use Moodle\BehatExtension\Context\Step\When as When;
/**
* Log in log out steps definitions.
"/descendant::div[@class='restore-course-search']" .
"/descendant::tr[contains(., $existingcourse)]" .
"/descendant::input[@type='radio']");
- $radionode->check();
$radionode->click();
// Pressing the continue button of the restore into an existing course section.
$radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
"/descendant::div[@class='restore-course-search']" .
"/descendant::input[@type='radio']");
- $radionode->check();
$radionode->click();
// Pressing the continue button of the restore into an existing course section.
// Merge without deleting radio option.
$radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='radio'][@name='target'][@value='1']");
- $radionode->check();
$radionode->click();
// Pressing the continue button of the restore merging section.
// Delete contents radio option.
$radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='radio'][@name='target'][@value='0']");
- $radionode->check();
$radionode->click();
// Pressing the continue button of the restore merging section.
return;
}
- $pageoptions = clone $options;
-
$rows = $options->getRows();
$newrows = array();
foreach ($rows as $k => $data) {
$newrows[] = $data;
}
}
- $pageoptions->setRows($newrows);
+ $pageoptions = new TableNode($newrows);
+
return $pageoptions;
}
default:
- paths:
- features: lib/behat/features
- bootstrap: lib/behat/features/bootstrap
- context:
- class: behat_init_context
+ suites:
+ default:
+ paths: { }
+ contexts: { }
extensions:
- Behat\MinkExtension\Extension:
+ Behat\MinkExtension:
base_url: 'http://localhost:8000'
goutte: null
selenium2: null
- Moodle\BehatExtension\Extension:
- features: { }
+ Moodle\BehatExtension:
+ moodledirroot: /Should/Change/To/Moodle/www/dir
steps_definitions: { }
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given;
+use Moodle\BehatExtension\Context\Step\Given as Given;
/**
* Blocks management steps definitions.
// NOTE: no MOODLE_INTERNAL used, this file may be required by behat before including /config.php.
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given;
+use Moodle\BehatExtension\Context\Step\Given as Given;
use Behat\Gherkin\Node\TableNode as TableNode;
/**
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given;
+use Moodle\BehatExtension\Context\Step\Given as Given;
/**
* Steps definitions for cohort actions.
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given,
- Behat\Behat\Context\Step\Then,
+use Moodle\BehatExtension\Context\Step\Given,
+ Moodle\BehatExtension\Context\Step\Then,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
// )
// ),
// 'Mac-Firefox' => array(
+// 'suites' => array (
+// 'default' => array(
+// 'filters' => array(
+// 'tags' => '~@_file_upload'
+// ),
+// ),
+// ),
// 'extensions' => array(
-// 'Behat\MinkExtension\Extension' => array(
+// 'Behat\MinkExtension' => array(
// 'selenium2' => array(
// 'browser' => 'firefox',
// 'capabilities' => array(
// ),
// 'Mac-Safari' => array(
// 'extensions' => array(
-// 'Behat\MinkExtension\Extension' => array(
+// 'Behat\MinkExtension' => array(
// 'selenium2' => array(
// 'browser' => 'safari',
// 'capabilities' => array(
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode,
Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\DriverException as DriverException,
unset($rows[$key]);
}
}
- $table->setRows($rows);
+ $table = new TableNode($rows);
// Adding a forced wait until editors are loaded as otherwise selenium sometimes tries clicks on the
// format field when the editor is being rendered and the click misses the field coordinates.
// The 'Hide' button should be available.
$nohideexception = new ExpectationException('"' . $activityname . '" don\'t have a "' . get_string('hide') . '" icon', $this->getSession());
- $this->find('named', array('link', get_string('hide')), $nohideexception, $activitynode);
+ $this->find('named_partial', array('link', get_string('hide')), $nohideexception, $activitynode);
}
}
// Also 'Show' icon.
$noshowexception = new ExpectationException('"' . $activityname . '" don\'t have a "' . get_string('show') . '" icon', $this->getSession());
- $this->find('named', array('link', get_string('show')), $noshowexception, $activitynode);
+ $this->find('named_partial', array('link', get_string('show')), $noshowexception, $activitynode);
} else {
And I should see the "Course categories and courses" management page
And I click on <sortby> action for "Master cat" in management category listing
And a new page should have loaded since I started watching
- And I start watching to see if a new page loads
And I should see the "Course categories and courses" management page
And I should see category listing <cat1> before <cat2>
And I should see category listing <cat2> before <cat3>
And I should see "Sort by Course time created descending" in the ".course-listing-actions" "css_element"
And I click on <sortby> "link" in the ".course-listing-actions" "css_element"
And a new page should have loaded since I started watching
- And I start watching to see if a new page loads
And I should see the "Course categories and courses" management page
And I should see course listing <course1> before <course2>
And I should see course listing <course2> before <course3>
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode;
/**
require_once(__DIR__ . '/../../../../../../lib/behat/behat_base.php');
use Behat\Gherkin\Node\TableNode as TableNode,
- Behat\Behat\Context\Step\Given as Given,
- Behat\Behat\Context\Step\When as When,
- Behat\Behat\Context\Step\Then as Then,
+ Moodle\BehatExtension\Context\Step\Given as Given,
+ Moodle\BehatExtension\Context\Step\When as When,
+ Moodle\BehatExtension\Context\Step\Then as Then,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
Behat\Mink\Exception\ExpectationException as ExpectationException;
require_once(__DIR__ . '/../../../../../../lib/behat/behat_base.php');
use Behat\Gherkin\Node\TableNode as TableNode,
- Behat\Behat\Context\Step\Given as Given,
- Behat\Behat\Context\Step\When as When,
- Behat\Behat\Context\Step\Then as Then,
+ Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
Behat\Mink\Exception\ExpectationException as ExpectationException;
}
}
+ // Remove empty criterion, as TableNode might contain them to make table rows equal size.
+ $newcriterion = array();
+ foreach ($criterion as $k => $c) {
+ if (!empty($c)) {
+ $newcriterion[$k] = $c;
+ }
+ }
+ $criterion = $newcriterion;
+
// Checking the number of cells.
if (count($criterion) % 2 === 0) {
throw new ExpectationException(
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
use Behat\Gherkin\Node\TableNode as TableNode,
- Behat\Behat\Context\Step\Given as Given,
- Behat\Behat\Context\Step\When as When;
+ Moodle\BehatExtension\Context\Step\Given as Given,
+ Moodle\BehatExtension\Context\Step\When as When;
/**
* Generic grading methods step definitions.
// Shortcut in case we already are in the grading page.
$usergradetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($usergradetext);
- if ($this->getSession()->getPage()->find('named', array('link', $usergradetextliteral))) {
+ if ($this->getSession()->getPage()->find('named_partial', array('link', $usergradetextliteral))) {
return $gradeuserstep;
}
require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given,
- Behat\Behat\Context\Step\Then,
+use Moodle\BehatExtension\Context\Step\Given,
+ Moodle\BehatExtension\Context\Step\Then,
Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode;
class behat_grade extends behat_base {
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Then;
+use Moodle\BehatExtension\Context\Step\Then;
use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
*/
protected function find($selector, $locator, $exception = false, $node = false, $timeout = false) {
+ // Throw exception, so dev knows it is not supported.
+ if ($selector === 'named') {
+ $exception = 'Using the "named" selector is deprecated as of 3.1. '
+ .' Use the "named_partial" or use the "named_exact" selector instead.';
+ throw new ExpectationException($exception, $this->getSession());
+ }
+
// Returns the first match.
$items = $this->find_all($selector, $locator, $exception, $node, $timeout);
return count($items) ? reset($items) : null;
*/
protected function find_all($selector, $locator, $exception = false, $node = false, $timeout = false) {
+ // Throw exception, so dev knows it is not supported.
+ if ($selector === 'named') {
+ $exception = 'Using the "named" selector is deprecated as of 3.1. '
+ .' Use the "named_partial" or use the "named_exact" selector instead.';
+ throw new ExpectationException($exception, $this->getSession());
+ }
+
// Generic info.
if (!$exception) {
// With named selectors we can be more specific.
- if ($selector == 'named') {
+ if (($selector == 'named_exact') || ($selector == 'named_partial')){
$exceptiontype = $locator[0];
$exceptionlocator = $locator[1];
// Redirecting execution to the find method with the specified selector.
// It will detect if it's pointing to an unexisting named selector.
- return $this->find('named',
+ return $this->find('named_partial',
array(
$cleanname,
$this->getSession()->getSelectorsHandler()->xpathLiteral($arguments[0])
$CFG->behat_wwwroot = 'http://itwillnotbeused.com';
}
- $basedir = $CFG->dirroot . DIRECTORY_SEPARATOR . 'lib' . DIRECTORY_SEPARATOR . 'behat';
-
+ // Comments use black color, so failure path is not visible. Using color other then black/white is safer.
+ // https://github.com/Behat/Behat/pull/628.
$config = array(
'default' => array(
- 'paths' => array(
- 'features' => $basedir . DIRECTORY_SEPARATOR . 'features',
- 'bootstrap' => $basedir . DIRECTORY_SEPARATOR . 'features' . DIRECTORY_SEPARATOR . 'bootstrap',
+ 'formatters' => array(
+ 'moodle_progress' => array(
+ 'output_styles' => array(
+ 'comment' => array('magenta'))
+ )
),
- 'context' => array(
- 'class' => 'behat_init_context'
+ 'suites' => array(
+ 'default' => array(
+ 'paths' => $features,
+ 'contexts' => array_keys($stepsdefinitions)
+ )
),
'extensions' => array(
- 'Behat\MinkExtension\Extension' => array(
+ 'Behat\MinkExtension' => array(
'base_url' => $CFG->behat_wwwroot,
'goutte' => null,
'selenium2' => $selenium2wdhost
),
- 'Moodle\BehatExtension\Extension' => array(
- 'formatters' => array(
- 'moodle_progress' => 'Moodle\BehatExtension\Formatter\MoodleProgressFormatter',
- 'moodle_list' => 'Moodle\BehatExtension\Formatter\MoodleListFormatter',
- 'moodle_step_count' => 'Moodle\BehatExtension\Formatter\MoodleStepCountFormatter'
- ),
- 'features' => $features,
+ 'Moodle\BehatExtension' => array(
+ 'moodledirroot' => $CFG->dirroot,
'steps_definitions' => $stepsdefinitions
)
- ),
- 'formatter' => array(
- 'name' => 'moodle_progress'
)
)
);
} else {
// Named selectors uses arrays as locators including the type of named selector.
$locator = array($selectortype, $session->getSelectorsHandler()->xpathLiteral($element));
- $selector = 'named';
+ $selector = 'named_partial';
}
return array($selector, $locator);
public static function register_moodle_selectors(Behat\Mink\Session $session) {
foreach (self::get_moodle_selectors() as $name => $xpath) {
- $session->getSelectorsHandler()->getSelector('named')->registerNamedXpath($name, $xpath);
+ $session->getSelectorsHandler()->getSelector('named_partial')->registerNamedXpath($name, $xpath);
}
}
$this->field->click();
// Trigger the onchange event as triggered when 'checking' the checkbox.
- $this->session->getDriver()->triggerSynScript(
- $this->field->getXPath(),
- "Syn.trigger('change', {}, {{ELEMENT}})"
- );
+ $this->trigger_on_change();
} else if (empty($value) && $this->field->isChecked()) {
$this->field->click();
// Trigger the onchange event as triggered when 'checking' the checkbox.
- $this->session->getDriver()->triggerSynScript(
- $this->field->getXPath(),
- "Syn.trigger('change', {}, {{ELEMENT}})"
- );
+ $this->trigger_on_change();
}
}
return false;
}
+ /**
+ * Trigger on change event.
+ */
+ protected function trigger_on_change() {
+ $this->session->getDriver()->triggerSynScript(
+ $this->field->getXPath(),
+ "Syn.trigger('change', {}, {{ELEMENT}})"
+ );
+ }
}
* @return string The value attribute
*/
public function get_value() {
- return (bool)$this->field->getAttribute('checked');
+ return $this->field->isSelected();
}
/**
public function set_value($value) {
if ($this->running_javascript()) {
- parent::set_value($value);
+ // Check on radio button.
+ $this->field->click();
+
+ // Trigger the onchange event as triggered when 'selecting' the radio.
+ if (!empty($value) && !$this->field->isSelected()) {
+ $this->trigger_on_change();
+ }
} else {
// Goutte does not accept a check nor a click in an input[type=radio].
$this->field->setValue($this->field->getAttribute('value'));
}
}
- /**
- * Returns whether the provided value matches the current value or not.
- *
- * @param string $expectedvalue
- * @return bool
- */
- public function matches($expectedvalue = false) {
- return $this->text_matches($expectedvalue);
- }
}
*/
public function set_value($value) {
- // In some browsers we select an option and it triggers all the
- // autosubmits and works as expected but not in all of them, so we
- // try to catch all the possibilities to make this function work as
- // expected.
-
- // Get the internal id of the element we are going to click.
- // This kind of internal IDs are only available in the selenium wire
- // protocol, so only available using selenium drivers, phantomjs and family.
- if ($this->running_javascript()) {
- $currentelementid = $this->get_internal_field_id();
- }
-
// Is the select multiple?
$multiple = $this->field->hasAttribute('multiple');
-
- // By default, assume the passed value is a non-multiple option.
- $options = array(trim($value));
+ $singleselect = ($this->field->hasClass('singleselect') || $this->field->hasClass('urlselect'));
// Here we select the option(s).
if ($multiple) {
// Split and decode values. Comma separated list of values allowed. With valuable commas escaped with backslash.
- $options = preg_replace('/\\\,/', ',', preg_split('/(?<!\\\),/', $value));
+ $options = preg_replace('/\\\,/', ',', preg_split('/(?<!\\\),/', trim($value)));
// This is a multiple select, let's pass the multiple flag after first option.
$afterfirstoption = false;
foreach ($options as $option) {
$afterfirstoption = true;
}
} else {
- // If value is already set then don't set it again.
- if ($this->field->getValue() == $value) {
- return;
- } else {
- $opt = $this->field->find('named', array(
- 'option', $this->field->getSession()->getSelectorsHandler()->xpathLiteral($value)
- ));
- if ($opt && ($this->field->getValue() == $opt->getValue())) {
- return;
- }
- }
-
- // If not running JS or not a singleselect then use selectOption.
- // For singleselect only click event is enough.
- if (!$this->running_javascript() ||
- !($this->field->hasClass('singleselect') || $this->field->hasClass('urlselect'))) {
-
- // This is a single select, let's pass the last one specified.
- $this->field->selectOption(end($options));
- }
- }
-
- // With JS disabled this is enough and we finish here.
- if (!$this->running_javascript()) {
- return;
- }
-
- // With JS enabled we add more clicks as some selenium
- // drivers requires it to fire JS events.
-
- // In some browsers the selectOption actions can perform a form submit or reload page
- // so we need to ensure the element is still available to continue interacting
- // with it. We don't wait here.
- // getXpath() does not send a query to selenium, so we don't need to wrap it in a try & catch.
- $selectxpath = $this->field->getXpath();
- if (!$this->session->getDriver()->find($selectxpath)) {
- return;
- }
-
- // We also check the selenium internal element id, if it have changed
- // we are dealing with an autosubmit that was already executed, and we don't to
- // execute anything else as the action we wanted was already performed.
- if ($currentelementid != $this->get_internal_field_id()) {
- return;
- }
+ // By default, assume the passed value is a non-multiple option.
+ $this->field->selectOption(trim($value));
+ }
// Wait for all the possible AJAX requests that have been
// already triggered by selectOption() to be finished.
- $this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
-
- // Wrapped in try & catch as the element may disappear if an AJAX request was submitted.
- try {
- $multiple = $this->field->hasAttribute('multiple');
- } catch (Exception $e) {
- // We do not specify any specific Exception type as there are
- // different exceptions that can be thrown by the driver and
- // we can not control them all, also depending on the selenium
- // version the exception type can change.
- return;
- }
-
- // Single select sometimes needs an extra click in the option.
- if (!$multiple) {
-
- // Var $options only contains 1 option.
- $optionxpath = $this->get_option_xpath(end($options), $selectxpath);
-
- // Using the driver direcly because Element methods are messy when dealing
- // with elements inside containers.
- if ($optionnodes = $this->session->getDriver()->find($optionxpath)) {
-
- // Wrapped in a try & catch as we can fall into race conditions
- // and the element may not be there.
- try {
- current($optionnodes)->click();
- } catch (Exception $e) {
- // We continue and return as this means that the element is not there or it is not the same.
- return;
- }
- }
-
- } else {
-
- // Wrapped in a try & catch as we can fall into race conditions
- // and the element may not be there.
- try {
- // Multiple ones needs the click in the select.
- $this->field->click();
- } catch (Exception $e) {
- // We continue and return as this means that the element is not there or it is not the same.
- return;
- }
-
- // We also check that the option(s) are still there. We neither wait.
- foreach ($options as $option) {
- $optionxpath = $this->get_option_xpath($option, $selectxpath);
- if (!$this->session->getDriver()->find($optionxpath)) {
- return;
- }
+ if ($this->running_javascript()) {
+ // Trigger change event as this is needed by some drivers (Phantomjs). Don't do it for
+ // Singleselect as this will cause multiple event fire and lead to race-around condition.
+ $browser = \Moodle\BehatExtension\Driver\MoodleSelenium2Driver::getBrowser();
+ if (!$singleselect && ($browser == 'phantomjs')) {
+ $script = "Syn.trigger('change', {}, {{ELEMENT}})";
+ $this->session->getDriver()->triggerSynScript($this->field->getXpath(), $script);
}
-
- // Wait for all the possible AJAX requests that have been
- // already triggered by clicking on the field to be finished.
$this->session->wait(behat_base::TIMEOUT * 1000, behat_base::PAGE_READY_JS);
-
- // Wrapped in a try & catch as we can fall into race conditions
- // and the element may not be there.
- try {
-
- // Repeating the select(s) as some drivers (chrome that I know) are moving
- // to another option after the general select field click above.
- $afterfirstoption = false;
- foreach ($options as $option) {
- $this->field->selectOption(trim($option), $afterfirstoption);
- $afterfirstoption = true;
- }
- } catch (Exception $e) {
- // We continue and return as this means that the element is not there or it is not the same.
- return;
- }
}
}
// We are dealing with a multi-select.
- // Can pass multiple comma separated, with valuable commas escaped with backslash.
- $expectedarr = array(); // Array of passed text options to test.
-
// Unescape + trim all options and flip it to have the expected values as keys.
$expectedoptions = $this->get_unescaped_options($expectedvalue);
$selectedoptions = array(); // To accumulate found selected options.
- // Selenium getValue() implementation breaks - separates - values having
- // commas within them, so we'll be looking for options with the 'selected' attribute instead.
- if ($this->running_javascript()) {
- // Get all the options in the select and extract their value/text pairs.
- $alloptions = $this->field->findAll('xpath', '//option');
- foreach ($alloptions as $option) {
- // Is it selected?
- if ($option->hasAttribute('selected')) {
- if ($multiple) {
- // If the select is multiple, text commas must be encoded.
- $selectedoptions[] = trim(str_replace(',', '\,', $option->{$method}()));
- } else {
- $selectedoptions[] = trim($option->{$method}());
- }
- }
- }
-
- } else {
- // Goutte does not keep the 'selected' attribute updated, but its getValue() returns
- // the selected elements correctly, also those having commas within them.
-
- // Goutte returns the values as an array or as a string depending
- // on whether multiple options are selected or not.
- $values = $this->field->getValue();
- if (!is_array($values)) {
- $values = array($values);
- }
+ // Driver returns the values as an array or as a string depending
+ // on whether multiple options are selected or not.
+ $values = $this->field->getValue();
+ if (!is_array($values)) {
+ $values = array($values);
+ }
- // Get all the options in the select and extract their value/text pairs.
- $alloptions = $this->field->findAll('xpath', '//option');
- foreach ($alloptions as $option) {
- // Is it selected?
- if (in_array($option->getValue(), $values)) {
- if ($multiple) {
- // If the select is multiple, text commas must be encoded.
- $selectedoptions[] = trim(str_replace(',', '\,', $option->{$method}()));
- } else {
- $selectedoptions[] = trim($option->{$method}());
- }
+ // Get all the options in the select and extract their value/text pairs.
+ $alloptions = $this->field->findAll('xpath', '//option');
+ foreach ($alloptions as $option) {
+ // Is it selected?
+ if (in_array($option->getValue(), $values)) {
+ if ($multiple) {
+ // If the select is multiple, text commas must be encoded.
+ $selectedoptions[] = trim(str_replace(',', '\,', $option->{$method}()));
+ } else {
+ $selectedoptions[] = trim($option->{$method}());
}
}
}
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
use Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
- Behat\Behat\Context\Step\Given as Given,
- Behat\Behat\Context\Step\Then as Then,
+ Moodle\BehatExtension\Context\Step\Given as Given,
+ Moodle\BehatExtension\Context\Step\Then as Then,
Behat\Gherkin\Node\TableNode as TableNode;
/**
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
require_once(__DIR__ . '/../../../lib/behat/behat_field_manager.php');
-use Behat\Behat\Context\Step\Given as Given,
- Behat\Behat\Context\Step\When as When,
- Behat\Behat\Context\Step\Then as Then,
- Behat\Gherkin\Node\TableNode as TableNode,
+use Behat\Gherkin\Node\TableNode as TableNode,
+ Moodle\BehatExtension\Context\Step\Given as Given,
+ Moodle\BehatExtension\Context\Step\When as When,
+ Moodle\BehatExtension\Context\Step\Then as Then,
Behat\Gherkin\Node\PyStringNode as PyStringNode,
- Behat\Mink\Element\NodeElement as NodeElement,
Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
WebDriver\Exception\NoSuchElement as NoSuchElement,
WebDriver\Exception\StaleElementReference as StaleElementReference,
Behat\Gherkin\Node\TableNode as TableNode,
- Behat\Behat\Context\Step\Given as Given;
+ Moodle\BehatExtension\Context\Step\Given as Given;
/**
* Cross component steps definitions.
// 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()->evaluateScript(
+ $this->getSession()->executeScript(
'if (window.name == "") window.name = "' . self::MAIN_WINDOW_NAME . '"');
$this->getSession()->switchToWindow($windowname);
$this->pageloaddetectionrunning = true;
- $session->evaluateScript(
+ $session->executeScript(
'var span = document.createElement("span");
span.setAttribute("data-rel", "' . self::PAGE_LOAD_DETECTION_STRING . '");
span.setAttribute("style", "display: none;");
require_once(__DIR__ . '/../../behat/behat_base.php');
-use Behat\Behat\Event\SuiteEvent as SuiteEvent,
- Behat\Behat\Event\ScenarioEvent as ScenarioEvent,
- Behat\Behat\Event\FeatureEvent as FeatureEvent,
- Behat\Behat\Event\OutlineExampleEvent as OutlineExampleEvent,
- Behat\Behat\Event\StepEvent as StepEvent,
+use Behat\Testwork\Hook\Scope\BeforeSuiteScope,
+ Behat\Testwork\Hook\Scope\AfterSuiteScope,
+ Behat\Behat\Hook\Scope\BeforeFeatureScope,
+ Behat\Behat\Hook\Scope\AfterFeatureScope,
+ Behat\Behat\Hook\Scope\BeforeScenarioScope,
+ Behat\Behat\Hook\Scope\AfterScenarioScope,
+ Behat\Behat\Hook\Scope\BeforeStepScope,
+ Behat\Behat\Hook\Scope\AfterStepScope,
Behat\Mink\Exception\DriverException as DriverException,
WebDriver\Exception\NoSuchWindow as NoSuchWindow,
WebDriver\Exception\UnexpectedAlertOpen as UnexpectedAlertOpen,
* @throws Exception
* @BeforeSuite
*/
- public static function before_suite(SuiteEvent $event) {
+ public static function before_suite(BeforeSuiteScope $scope) {
global $CFG;
// Defined only when the behat CLI command is running, the moodle init setup process will
define('BEHAT_TEST', 1);
define('CLI_SCRIPT', 1);
-
// With BEHAT_TEST we will be using $CFG->behat_* instead of $CFG->dataroot, $CFG->prefix and $CFG->wwwroot.
require_once(__DIR__ . '/../../../config.php');
* @param FeatureEvent $event event fired before feature.
* @BeforeFeature
*/
- public static function before_feature(FeatureEvent $event) {
+ public static function before_feature(BeforeFeatureScope $event) {
if (!defined('BEHAT_FEATURE_TIMING_FILE')) {
return;
}
* @param FeatureEvent $event event fired after feature.
* @AfterFeature
*/
- public static function after_feature(FeatureEvent $event) {
+ public static function after_feature(AfterFeatureScope $event) {
if (!defined('BEHAT_FEATURE_TIMING_FILE')) {
return;
}
* @param SuiteEvent $event event fired after suite.
* @AfterSuite
*/
- public static function after_suite(SuiteEvent $event) {
+ public static function after_suite(AfterSuiteScope $event) {
if (!defined('BEHAT_FEATURE_TIMING_FILE')) {
return;
}
* @throws coding_exception If here we are not using the test database it should be because of a coding error
* @BeforeScenario
*/
- public function before_scenario($event) {
+ public function before_scenario(BeforeScenarioScope $scope) {
global $DB, $SESSION, $CFG;
// As many checks as we can.
* default would be at framework level, which will stop the execution of
* the run.
*
- * @param StepEvent $event event fired before step.
- * @BeforeStep @javascript
+ * @BeforeStep
*/
- public function before_step_javascript(StepEvent $event) {
+ public function before_step_javascript(BeforeStepScope $scope) {
+ self::$currentstepexception = null;
- try {
- $this->wait_for_pending_js();
- self::$currentstepexception = null;
- } catch (Exception $e) {
- self::$currentstepexception = $e;
+ // Only run if JS.
+ if ($this->running_javascript()) {
+ try {
+ $this->wait_for_pending_js();
+ } catch (Exception $e) {
+ self::$currentstepexception = $e;
+ }
}
}
* default would be at framework level, which will stop the execution of
* the run.
*
- * @param StepEvent $event event fired after step.
- * @AfterStep @javascript
+ * @AfterStep
*/
- public function after_step_javascript(StepEvent $event) {
- global $CFG;
+ public function after_step_javascript(AfterStepScope $scope) {
+ global $CFG, $DB;
+
+ // Save the page content if the step failed.
+ if (!empty($CFG->behat_faildump_path) &&
+ $scope->getTestResult()->getResultCode() === Behat\Testwork\Tester\Result\TestResult::FAILED) {
+ $this->take_contentdump($scope);
+ }
+
+ // Abort any open transactions to prevent subsequent tests hanging.
+ // This does the same as abort_all_db_transactions(), but doesn't call error_log() as we don't
+ // want to see a message in the behat output.
+ if (($scope->getTestResult() instanceof \Behat\Behat\Tester\Result\ExecutedStepResult) &&
+ $scope->getTestResult()->hasException()) {
+ if ($DB && $DB->is_transaction_started()) {
+ $DB->force_transaction_rollback();
+ }
+ }
+
+ // Only run if JS.
+ if (!$this->running_javascript()) {
+ return;
+ }
// Save a screenshot if the step failed.
if (!empty($CFG->behat_faildump_path) &&
- $event->getResult() === StepEvent::FAILED) {
- $this->take_screenshot($event);
+ $scope->getTestResult()->getResultCode() === Behat\Testwork\Tester\Result\TestResult::FAILED) {
+ $this->take_screenshot($scope);
}
try {
}
}
- /**
- * Execute any steps required after the step has finished.
- *
- * This includes creating an HTML dump of the content if there was a failure.
- *
- * @param StepEvent $event event fired after step.
- * @AfterStep
- */
- public function after_step(StepEvent $event) {
- global $CFG, $DB;
-
- // Save the page content if the step failed.
- if (!empty($CFG->behat_faildump_path) &&
- $event->getResult() === StepEvent::FAILED) {
- $this->take_contentdump($event);
- }
-
- // Abort any open transactions to prevent subsequent tests hanging.
- // This does the same as abort_all_db_transactions(), but doesn't call error_log() as we don't
- // want to see a message in the behat output.
- if ($event->hasException()) {
- if ($DB && $DB->is_transaction_started()) {
- $DB->force_transaction_rollback();
- }
- }
- }
-
/**
* Executed after scenario having switch window to restart session.
* This is needed to close all extra browser windows and starting
* one browser window.
*
- * @param ScenarioEvent $event event fired after scenario.
+ * @param AfterScenarioScope $event event fired after scenario.
* @AfterScenario @_switch_window
*/
- public function after_scenario_switchwindow(ScenarioEvent $event) {
+ public function after_scenario_switchwindow(AfterScenarioScope $event) {
for ($count = 0; $count < self::EXTENDED_TIMEOUT; $count) {
try {
$this->getSession()->restart();
* Take screenshot when a step fails.
*
* @throws Exception
- * @param StepEvent $event
+ * @param AfterStepScope $scope
*/
- protected function take_screenshot(StepEvent $event) {
+ protected function take_screenshot(AfterStepScope $scope) {
// Goutte can't save screenshots.
if (!$this->running_javascript()) {
return false;
}
- list ($dir, $filename) = $this->get_faildump_filename($event, 'png');
+ list ($dir, $filename) = $this->get_faildump_filename($scope, 'png');
$this->saveScreenshot($filename, $dir);
}
* Take a dump of the page content when a step fails.
*
* @throws Exception
- * @param StepEvent $event
+ * @param AfterStepScope $scope
*/
- protected function take_contentdump(StepEvent $event) {
- list ($dir, $filename) = $this->get_faildump_filename($event, 'html');
+ protected function take_contentdump(AfterStepScope $scope) {
+ list ($dir, $filename) = $this->get_faildump_filename($scope, 'html');
$fh = fopen($dir . DIRECTORY_SEPARATOR . $filename, 'w');
fwrite($fh, $this->getSession()->getPage()->getContent());
*
* This is used for content such as the DOM, and screenshots.
*
- * @param StepEvent $event
+ * @param AfterStepScope $scope
* @param String $filetype The file suffix to use. Limited to 4 chars.
*/
- protected function get_faildump_filename(StepEvent $event, $filetype) {
+ protected function get_faildump_filename(AfterStepScope $scope, $filetype) {
global $CFG;
// All the contentdumps should be in the same parent dir.
// The scenario title + the failed step text.
// We want a i-am-the-scenario-title_i-am-the-failed-step.$filetype format.
- $filename = $event->getStep()->getParent()->getTitle() . '_' . $event->getStep()->getText();
+ $filename = $scope->getFeature()->getTitle() . '_' . $scope->getStep()->getText();
$filename = preg_replace('/([^a-zA-Z0-9\_]+)/', '-', $filename);
- // File name limited to 255 characters. Leaving 4 chars for the file
+ // File name limited to 255 characters. Leaving 5 chars for line number and 4 chars for the file.
// extension as we allow .png for images and .html for DOM contents.
- $filename = substr($filename, 0, 250) . '.' . $filetype;
+ $filename = substr($filename, 0, 245) . '_' . $scope->getStep()->getLine() . '.' . $filetype;
return array($dir, $filename);
}
$pending = '';
try {
$jscode =
- 'if (typeof M === "undefined") {
- if (document.readyState === "complete") {
+ 'return function() {
+ if (typeof M === "undefined") {
+ if (document.readyState === "complete") {
+ return "";
+ } else {
+ return "incomplete";
+ }
+ } else if (' . self::PAGE_READY_JS . ') {
return "";
} else {
- return "incomplete";
+ return M.util.pending_js.join(":");
}
- } else if (' . self::PAGE_READY_JS . ') {
- return "";
- } else {
- return 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.
require_once(__DIR__ . '/../../behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given;
+use Moodle\BehatExtension\Context\Step\Given;
+use Moodle\BehatExtension\Context\Step\When;
use Behat\Mink\Exception\ExpectationException as ExpectationException;
use Behat\Mink\Exception\DriverException as DriverException;
-use Behat\Behat\Context\Step\When as When;
/**
* Steps definitions to navigate through the navigation tree nodes.
if ($parentnodes[0] === $siteadminstr) {
// We don't know if there if Site admin is already expanded so
// don't wait, it is non-JS and we already waited for the DOM.
- if ($siteadminlink = $this->getSession()->getPage()->find('named', array('link', "'" . $siteadminstr . "'"))) {
+ $siteadminlink = $this->getSession()->getPage()->find('named_exact', array('link', "'" . $siteadminstr . "'"));
+ if ($siteadminlink) {
$siteadminlink->click();
}
}
require_once(__DIR__ . '/../../behat/behat_base.php');
use Behat\Mink\Exception\ExpectationException as ExpectationException,
- Behat\Behat\Context\Step\Given as Given,
+ Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode;
/**
* Transformations applicable to TableNode arguments should also
* be applied, adding them in a different method for Behat API restrictions.
*
- * @Transform /^table:(.*)/
+ * @Transform table:Surname,My Surname $NASTYSTRING2
* @param TableNode $tablenode
* @return TableNode The transformed table
*/
- public function tablenode_transformations(TableNode $tablenode) {
+ public function prefixed_tablenode_transformations(TableNode $tablenode) {
+ return $this->tablenode_transformations($tablenode);
+ }
+ /**
+ * Transformations for TableNode arguments.
+ *
+ * Transformations applicable to TableNode arguments should also
+ * be applied, adding them in a different method for Behat API restrictions.
+ *
+ * @Transform table:Surname,$NASTYSTRING1
+ * @param TableNode $tablenode
+ * @return TableNode The transformed table
+ */
+ public function tablenode_transformations(TableNode $tablenode) {
// Walk through all values including the optional headers.
$rows = $tablenode->getRows();
foreach ($rows as $rowkey => $row) {
}
// Return the transformed TableNode.
- $tablenode->setRows($rows);
+ unset($tablenode);
+ $tablenode = new TableNode($rows);
+
return $tablenode;
}
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
require_once(__DIR__ . '/../../../../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given;
-
/**
* Steps definitions related with the editpdf.
*
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given;
+use Moodle\BehatExtension\Context\Step\Given as Given;
/**
* Choice activity definitions.
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
- Behat\Behat\Context\Step\When as When,
+use Moodle\BehatExtension\Context\Step\Given as Given,
+ Moodle\BehatExtension\Context\Step\When as When,
Behat\Gherkin\Node\TableNode as TableNode;
/**
* Database-related steps definitions.
$additem = $this->escape(get_string('add_item', 'feedback'));
$rv[] = new Given("I select \"{$questiontype}\" from the \"{$additem}\" singleselect");
- $newdata = new TableNode();
$rows = $questiondata->getRows();
+ $modifiedrows = array();
foreach ($rows as $row) {
foreach ($row as $key => $value) {
$row[$key] = preg_replace('|\\\\n|', "\n", $value);
}
- $newdata->addRow($row);
+ $modifiedrows[] = $row;
}
+ $newdata = new TableNode($modifiedrows);
+
$rv[] = new Given('I set the following fields to these values:', $newdata);
$saveitem = $this->escape(get_string('save_item', 'feedback'));
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode;
/**
* Forum-related steps definitions.
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode;
/**
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
require_once(__DIR__ . '/../../../../question/tests/behat/behat_question_base.php');
-use Behat\Behat\Context\Step\Given as Given,
- Behat\Gherkin\Node\TableNode as TableNode,
- Behat\Mink\Exception\ExpectationException as ExpectationException;
+use Moodle\BehatExtension\Context\Step\Given as Given,
+ Behat\Gherkin\Node\TableNode as TableNode;
+
+use Behat\Mink\Exception\ExpectationException as ExpectationException;
/**
* Steps definitions related to mod_quiz.
}
$rows = $data->getRows();
array_unshift($rows, $headings);
- $data->setRows($rows);
+ $data = new TableNode($rows);
}
// Add the questions.
require_once(__DIR__ . '/../../../../../../lib/behat/behat_base.php');
require_once(__DIR__ . '/../../../../../../lib/behat/behat_field_manager.php');
-use Behat\Behat\Context\Step\Given as Given,
- Behat\Gherkin\Node\TableNode as TableNode,
+use Behat\Gherkin\Node\TableNode as TableNode,
Behat\Mink\Exception\ElementTextException as ElementTextException;
/**
require_once(__DIR__ . '/../../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode;
/**
require_once(__DIR__ . '/behat_question_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode,
Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
require_once(__DIR__ . '/../../../lib/behat/behat_base.php');
-use Behat\Behat\Context\Step\Given as Given,
+use Moodle\BehatExtension\Context\Step\Given as Given,
Behat\Gherkin\Node\TableNode as TableNode,
Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
* @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(), false);
+ $this->add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement, new TableNode(array()), false);
}
/**
* @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(),
+ $this->add_file_from_repository_to_filemanager($filepath, $repository, $filemanagerelement, new TableNode(array()),
get_string('overwrite', 'repository'));
}
* @param string $filemanagerelement
*/
public function i_upload_file_to_filemanager($filepath, $filemanagerelement) {
- $this->upload_file_to_filemanager($filepath, $filemanagerelement, new TableNode(), false);
+ $this->upload_file_to_filemanager($filepath, $filemanagerelement, new TableNode(array()), false);
}
/**
* @param string $filemanagerelement
*/
public function i_upload_and_overwrite_file_to_filemanager($filepath, $filemanagerelement) {
- $this->upload_file_to_filemanager($filepath, $filemanagerelement, new TableNode(),
+ $this->upload_file_to_filemanager($filepath, $filemanagerelement, new TableNode(array()),
get_string('overwrite', 'repository'));
}