Merge branch 'MDL-37873_master' of git://github.com/dmonllao/moodle

Conflicts:
	course/tests/behat/add_activities.feature
This commit is contained in:
Damyon Wiese 2013-02-12 14:48:40 +08:00
commit 147f592742
10 changed files with 232 additions and 121 deletions

View File

@ -0,0 +1,29 @@
@tool_behat
Feature: Page contents assertions
In order to write good tests
As a tests writer
I need to check the page contents
@javascript
Scenario: Basic contents assertions
Given I log in as "admin"
And I am on homepage
And I expand "Users" node
And I follow "Groups"
And I press "Create group"
And I fill the moodle form with:
| Group name | I'm the name |
| Group description | I'm the description |
And I press "Save changes"
When I follow "Overview"
And I wait until the page is ready
And I wait "2" seconds
And I hover ".region-content .generaltable td span"
Then I should see "I'm the description"
And I should see "Filter groups by"
And I should not see "Filter groupssss by"
And I should see "Group members" in the ".region-content table th.c1" element
And I should not see "Group membersssss" in the ".region-content table th.c1" element
And I follow "Groups"
And the element "#groupeditform #showcreateorphangroupform" should be enabled
And the element "#groupeditform #showeditgroupsettingsform" should be disabled

View File

@ -0,0 +1,20 @@
@tool_behat @core_form
Feature: Forms manipulation
In order to interact with Moodle
As a user
I need to set forms values
@javascript
Scenario: Basic forms manipulation
Given I log in as "admin"
And I follow "Admin User"
And I follow "Edit profile"
When I fill in "First name" with "Field value"
And I select "Use standard web forms" from "When editing text"
And I check "Unmask"
Then the "First name" field should match "Field value" value
And the "When editing text" select box should contain "Use standard web forms"
And the "Unmask" checkbox should be checked
And I uncheck "Unmask"
And the "Unmask" checkbox should not be checked
And I press "Update profile"

View File

@ -25,6 +25,7 @@ Feature: Add activities to courses
| Introduction | Test database description |
| Required entries | 9 |
| Comments | Yes |
And I turn editing mode off
Then I should not see "Adding a new"
And I follow "Test name"
And I follow "Edit settings"

View File

@ -66,10 +66,6 @@ class behat_course extends behat_base {
*/
public function i_add_to_section_and_i_fill_the_form_with($activity, $section, TableNode $data) {
$activity = $this->fixStepArgument($activity);
$section = $this->fixStepArgument($section);
// The 'I wait until the page is ready' is just in case.
return array(
new Given('I add a "'.$activity.'" to section "'.$section.'"'),
new Given('I fill the moodle form with:', $data),
@ -81,6 +77,7 @@ class behat_course extends behat_base {
* Opens the activity chooser and opens the activity/resource form page.
*
* @Given /^I add a "(?P<activity_or_resource_string>(?:[^"]|\\")*)" to section "(?P<section_number>\d+)"$/
* @throws ElementNotFoundException Thrown by behat_base::find
* @param string $activity
* @param string $section
*/
@ -90,14 +87,14 @@ class behat_course extends behat_base {
$section = $this->fixStepArgument($section);
// Clicks add activity or resource section link.
$sectionxpath = "//*[@id='section-" . $section . "']/*/*/*/div[@class='section-modchooser']/*/*";
$section = $this->getSession()->getPage()->find('xpath', $sectionxpath);
$section->click();
$sectionxpath = "//*[@id='section-" . $section . "']/*/*/*/div[@class='section-modchooser']/span/a";
$sectionnode = $this->find('xpath', $sectionxpath);
$sectionnode->click();
// Clicks the selected activity if it exists.
$activityxpath = ".//label[contains(.,'" . $activity . "')]/input";
$activity = $this->getSession()->getPage()->find('xpath', $activityxpath);
$activity->doubleClick();
$activitynode = $this->find('xpath', $activityxpath);
$activitynode->doubleClick();
}
}

View File

@ -28,6 +28,9 @@
// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
use Behat\Mink\Exception\ExpectationException as ExpectationException,
Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException;
/**
* Steps definitions base class.
*
@ -76,6 +79,89 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
return 0 !== strpos($path, 'http') ? $startUrl . ltrim($path, '/') : $path;
}
/**
* Adapter to Behat\Mink\Element\Element::find() using the spin() method.
*
* @link http://mink.behat.org/#traverse-the-page-selectors
* @param Exception $exception Otherwise we throw expcetion with generic info
* @param string $selector The selector type (css, xpath, named...)
* @param mixed $locator It depends on the $selector, can be the xpath, a name, a css locator...
* @return NodeElement
*/
protected function find($selector, $locator, $exception = false) {
// Generic info.
if (!$exception) {
// With named selectors we can be more specific.
if ($selector == 'named') {
$exceptiontype = $locator[0];
$exceptionlocator = $locator[1];
} else {
$exceptiontype = $selector;
$exceptionlocator = $locator;
}
$exception = new ElementNotFoundException($this->getSession(), $exceptiontype, null, $exceptionlocator);
}
// Waits for the node to appear if it exists, otherwise will timeout and throw the provided exception.
return $this->spin(
function($context, $args) {
return $context->getSession()->getPage()->find($args[0], $args[1]);
},
array($selector, $locator),
self::TIMEOUT,
$exception
);
}
/**
* Finds DOM nodes in the page using named selectors.
*
* The point of using this method instead of Mink ones is the spin
* method of behat_base::find() that looks for the element until it
* is available or it timeouts, this avoids the false failures received
* when selenium tries to execute commands on elements that are not
* ready to be used.
*
* All steps that requires elements to be available before interact with
* them should use one of the find* methods.
*
* The methods calls requires a {'find_' . $elementtype}($locator)
* format, like find_link($locator), find_select($locator),
* find_button($locator)...
*
* @link http://mink.behat.org/#named-selectors
* @throws coding_exception
* @param string $method The name of the called method
* @param mixed $arguments
* @return NodeElement
*/
public function __call($name, $arguments) {
if (substr($name, 0, 5) !== 'find_') {
throw new coding_exception('The "' . $name . '" method does not exist');
}
// Only the named selector identifier.
$cleanname = substr($name, 5);
// All named selectors shares the interface.
if (count($arguments) !== 1) {
throw new coding_exception('The "' . $cleanname . '" named selector needs the locator as it\'s single argument');
}
// 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',
array(
$cleanname,
$this->getSession()->getSelectorsHandler()->xpathLiteral($arguments[0])
)
);
}
/**
* Executes the passed closure until returns true or time outs.
*
@ -91,15 +177,20 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
* - Must return something != false if finishes as expected, this will be the (mixed) value
* returned by spin()
*
* Requires the exception to provide more accurate feedback to tests writers.
* The arguments of the closure are mixed, use $args depending on your needs.
*
* @throws Exception If it timeouts without receiving something != false from the closure
* @param Closure $lambda The function to execute.
* @param Exception $exception The exception to throw in case it time outs.
* @param array $args Arguments to pass to the closure
* You can provide an exception to give more accurate feedback to tests writers, otherwise the
* closure exception will be used, but you must provide an exception if the closure does not throws
* an exception.
*
* @throws Exception If it timeouts without receiving something != false from the closure
* @param Closure $lambda The function to execute.
* @param mixed $args Arguments to pass to the closure
* @param int $timeout Timeout
* @param Exception $exception The exception to throw in case it time outs.
* @return mixed The value returned by the closure
*/
protected function spin($lambda, $exception, $args, $timeout = false) {
protected function spin($lambda, $args = false, $timeout = false, $exception = false) {
// Using default timeout which is pretty high.
if (!$timeout) {
@ -112,11 +203,18 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
try {
// We don't check with !== because most of the time closures will return
// direct Behat methods returns and we are not sure it will be always (bool)false.
// direct Behat methods returns and we are not sure it will be always (bool)false
// if it just runs the behat method without returning anything $return == null.
if ($return = $lambda($this, $args)) {
return $return;
}
} catch(Exception $e) {
// We would use the first closure exception if no exception has been provided.
if (!$exception) {
$exception = $e;
}
// We wait until no exception is thrown or timeout expires.
continue;
}
@ -124,6 +222,11 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext {
sleep(1);
}
// Using coding_exception as is a development issue if no exception has been provided.
if (!$exception) {
$exception = new coding_exception('spin method requires an exception if the closure doesn\'t throw an exception itself');
}
// Throwing exception to the user.
throw $exception;
}

View File

@ -222,7 +222,7 @@ class behat_data_generators extends behat_base {
global $DB;
if (!$id = $DB->get_field('user', 'id', array('username' => $username))) {
throw new Exception('The specified user with username "' . $username . '" does not exists');
throw new Exception('The specified user with username "' . $username . '" does not exist');
}
return $id;
}
@ -237,7 +237,7 @@ class behat_data_generators extends behat_base {
global $DB;
if (!$id = $DB->get_field('role', 'id', array('shortname' => $roleshortname))) {
throw new Exception('The specified role with shortname"' . $roleshortname . '" does not exists');
throw new Exception('The specified role with shortname"' . $roleshortname . '" does not exist');
}
return $id;
@ -258,7 +258,7 @@ class behat_data_generators extends behat_base {
}
if (!$id = $DB->get_field('course_categories', 'id', array('idnumber' => $idnumber))) {
throw new Exception('The specified category with idnumber "' . $idnumber . '" does not exists');
throw new Exception('The specified category with idnumber "' . $idnumber . '" does not exist');
}
return $id;
@ -274,7 +274,7 @@ class behat_data_generators extends behat_base {
global $DB;
if (!$id = $DB->get_field('course', 'id', array('shortname' => $shortname))) {
throw new Exception('The specified course with shortname"' . $shortname . '" does not exists');
throw new Exception('The specified course with shortname"' . $shortname . '" does not exist');
}
return $id;
}
@ -289,7 +289,7 @@ class behat_data_generators extends behat_base {
global $DB;
if (!$id = $DB->get_field('groups', 'id', array('idnumber' => $idnumber))) {
throw new Exception('The specified group with idnumber "' . $idnumber . '" does not exists');
throw new Exception('The specified group with idnumber "' . $idnumber . '" does not exist');
}
return $id;
}
@ -304,7 +304,7 @@ class behat_data_generators extends behat_base {
global $DB;
if (!$id = $DB->get_field('groupings', 'id', array('idnumber' => $idnumber))) {
throw new Exception('The specified grouping with idnumber "' . $idnumber . '" does not exists');
throw new Exception('The specified grouping with idnumber "' . $idnumber . '" does not exist');
}
return $id;
}

View File

@ -48,19 +48,22 @@ class behat_forms extends behat_base {
/**
* Presses button with specified id|name|title|alt|value.
*
* @see Behat\MinkExtension\Context\MinkContext
* @When /^I press "(?P<button_string>(?:[^"]|\\")*)"$/
* @throws ElementNotFoundException Thrown by behat_base::find
*/
public function press_button($button) {
$button = $this->fixStepArgument($button);
$this->getSession()->getPage()->pressButton($button);
// Ensures the button is present.
$buttonnode = $this->find_button($button);
$buttonnode->press();
}
/**
* Fills a moodle form with field/value data.
*
* @throws ElementNotFoundException
* @Given /^I fill the moodle form with:$/
* @throws ElementNotFoundException Thrown by behat_base::find
* @param TableNode $data
*/
public function i_fill_the_moodle_form_with(TableNode $data) {
@ -75,21 +78,8 @@ class behat_forms extends behat_base {
// Removing \\ that escapes " of the steps arguments.
$locator = $this->fixStepArgument($locator);
// Finds the element in the page waiting until it appears (or timeouts)
// otherwise spin() throws exception.
$exception = new ElementNotFoundException(
$this->getSession(), 'form field', 'id|name|label|value', $locator
);
// $context is $this and will be passed to the function by spin().
$args['locator'] = $locator;
// Closure to ensure field($locator) exists.
$fieldnode = $this->spin(
function($context, $args) {
return $context->getSession()->getPage()->findField($args['locator']);
}, $exception, $args
);
// Getting the NodeElement.
$fieldnode = $this->find_field($locator);
// Gets the field type from a parent node.
$field = $this->get_field($fieldnode, $locator);
@ -103,65 +93,66 @@ class behat_forms extends behat_base {
/**
* Fills in form field with specified id|name|label|value.
*
* @see Behat\MinkExtension\Context\MinkContext
* @When /^I fill in "(?P<field_string>(?:[^"]|\\")*)" with "(?P<value_string>(?:[^"]|\\")*)"$/
* @throws ElementNotFoundException Thrown by behat_base::find
*/
public function fill_field($field, $value) {
$field = $this->fixStepArgument($field);
$value = $this->fixStepArgument($value);
$this->getSession()->getPage()->fillField($field, $value);
$fieldnode = $this->find_field($field);
$fieldnode->setValue($value);
}
/**
* Selects option in select field with specified id|name|label|value.
*
* @see Behat\MinkExtension\Context\MinkContext
* @throws ElementNotFoundException
* @When /^I select "(?P<option_string>(?:[^"]|\\")*)" from "(?P<select_string>(?:[^"]|\\")*)"$/
* @throws ElementNotFoundException Thrown by behat_base::find
*/
public function select_option($option, $select) {
$select = $this->fixStepArgument($select);
$option = $this->fixStepArgument($option);
// We add the click event to deal with autosubmit drop down menus.
$selectnode = $this->getSession()->getPage()->findField($select);
if ($selectnode == null) {
throw new ElementNotFoundException(
$this->getSession(), 'form field', 'id|name|label|value', $select
);
}
$selectnode = $this->find_field($select);
$selectnode->selectOption($option);
// Adding a click as Selenium requires it to fire some JS events.
$selectnode->click();
}
/**
* Checks checkbox with specified id|name|label|value.
*
* @see Behat\MinkExtension\Context\MinkContext
* @When /^I check "(?P<option_string>(?:[^"]|\\")*)"$/
* @throws ElementNotFoundException Thrown by behat_base::find
*/
public function check_option($option) {
$option = $this->fixStepArgument($option);
$this->getSession()->getPage()->checkField($option);
$checkboxnode = $this->find_field($option);
$checkboxnode->check();
}
/**
* Unchecks checkbox with specified id|name|label|value.
*
* @see Behat\MinkExtension\Context\MinkContext
* @When /^I uncheck "(?P<option_string>(?:[^"]|\\")*)"$/
* @throws ElementNotFoundException Thrown by behat_base::find
*/
public function uncheck_option($option) {
$option = $this->fixStepArgument($option);
$this->getSession()->getPage()->uncheckField($option);
$checkboxnode = $this->find_field($option);
$checkboxnode->uncheck();
}
/**
* Checks that the form element field have the specified value.
*
* @throws ElementNotFoundException
* @throws ExpectationException
* @Then /^the "(?P<field_string>(?:[^"]|\\")*)" field should match "(?P<value_string>(?:[^"]|\\")*)" value$/
* @throws ExpectationException
* @throws ElementNotFoundException Thrown by behat_base::find
* @param mixed $locator
* @param mixed $value
*/
@ -170,12 +161,7 @@ class behat_forms extends behat_base {
$locator = $this->fixStepArgument($locator);
$value = $this->fixStepArgument($value);
$fieldnode = $this->getSession()->getPage()->findField($locator);
if (null === $fieldnode) {
throw new ElementNotFoundException(
$this->getSession(), 'form field', 'id|name|label|value', $locator
);
}
$fieldnode = $this->find_field($locator);
// Gets the field instance.
$field = $this->get_field($fieldnode, $locator);
@ -192,8 +178,8 @@ class behat_forms extends behat_base {
/**
* Checks, that checkbox with specified in|name|label|value is checked.
*
* @see Behat\MinkExtension\Context\MinkContext
* @Then /^the "(?P<checkbox_string>(?:[^"]|\\")*)" checkbox should be checked$/
* @see Behat\MinkExtension\Context\MinkContext
*/
public function assert_checkbox_checked($checkbox) {
$checkbox = $this->fixStepArgument($checkbox);
@ -203,8 +189,8 @@ class behat_forms extends behat_base {
/**
* Checks, that checkbox with specified in|name|label|value is unchecked.
*
* @see Behat\MinkExtension\Context\MinkContext
* @Then /^the "(?P<checkbox_string>(?:[^"]|\\")*)" checkbox should not be checked$/
* @see Behat\MinkExtension\Context\MinkContext
*/
public function assert_checkbox_not_checked($checkbox) {
$checkbox = $this->fixStepArgument($checkbox);
@ -214,9 +200,9 @@ class behat_forms extends behat_base {
/**
* Checks, that given select box contains the specified option.
*
* @throws ExpectationException
* @throws ElementNotFoundException
* @Then /^the "(?P<select_string>(?:[^"]|\\")*)" select box should contain "(?P<option_string>(?:[^"]|\\")*)"$/
* @throws ExpectationException
* @throws ElementNotFoundException Thrown by behat_base::find
* @param string $select The select element name
* @param string $option The option text/value
*/
@ -225,12 +211,7 @@ class behat_forms extends behat_base {
$select = $this->fixStepArgument($select);
$option = $this->fixStepArgument($option);
$selectnode = $this->getSession()->getPage()->findField($select);
if ($selectnode == null) {
throw new ElementNotFoundException(
$this->getSession(), 'form field', 'id|name|label|value', $select
);
}
$selectnode = $this->find_field($select);
$regex = '/' . preg_quote($option, '/') . '/ui';
if (!preg_match($regex, $selectnode->getText())) {
@ -245,9 +226,9 @@ class behat_forms extends behat_base {
/**
* Checks, that given select box does not contain the specified option.
*
* @throws ExpectationException
* @throws ElementNotFoundException
* @Then /^the "(?P<select_string>(?:[^"]|\\")*)" select box should not contain "(?P<option_string>(?:[^"]|\\")*)"$/
* @throws ExpectationException
* @throws ElementNotFoundException Thrown by behat_base::find
* @param string $select The select element name
* @param string $option The option text/value
*/
@ -256,12 +237,7 @@ class behat_forms extends behat_base {
$select = $this->fixStepArgument($select);
$option = $this->fixStepArgument($option);
$selectnode = $this->getSession()->getPage()->findField($select);
if ($selectnode == null) {
throw new ElementNotFoundException(
$this->getSession(), 'form field', 'id|name|label|value', $select
);
}
$selectnode = $this->find_field($select);
$regex = '/' . preg_quote($option, '/') . '/ui';
if (preg_match($regex, $selectnode->getText())) {

View File

@ -47,7 +47,6 @@ class behat_general extends behat_base {
/**
* Opens Moodle homepage.
*
* @see Behat\MinkExtension\Context\MinkContext
* @Given /^I am on homepage$/
*/
public function i_am_on_homepage() {
@ -57,12 +56,14 @@ class behat_general extends behat_base {
/**
* Clicks link with specified id|title|alt|text.
*
* @see Behat\MinkExtension\Context\MinkContext
* @When /^I follow "(?P<link_string>(?:[^"]|\\")*)"$/
* @throws ElementNotFoundException Thrown by behat_base::find
*/
public function click_link($link) {
$link = $this->fixStepArgument($link);
$this->getSession()->getPage()->clickLink($link);
$linknode = $this->find_link($link);
$linknode->click();
}
/**
@ -87,16 +88,17 @@ class behat_general extends behat_base {
/**
* Mouse over a CSS element.
*
* @throws ExpectationException
* @see Sanpi/Behatch/Context/BrowserContext.php
* @When /^I hover "(?P<element_string>(?:[^"]|\\")*)"$/
* @throws ExpectationException Thrown by behat_base::find
* @param string $element
*/
public function i_hover($element) {
$node = $this->getSession()->getPage()->find('css', $element);
if ($node === null) {
throw new ExpectationException('The hovered element "' . $element . '" was not found anywhere in the page', $this->getSession());
}
$exception = new ExpectationException(
'The hovered element "' . $element . '" was not found anywhere in the page', $this->getSession()
);
$node = $this->find('css', $element, $exception);
$node->mouseOver();
}
@ -125,8 +127,7 @@ class behat_general extends behat_base {
*
* @Then /^I should see "(?P<text_string>(?:[^"]|\\")*)" in the "(?P<element_string>(?:[^"]|\\")*)" element$/
*/
public function assert_element_contains_text($element, $text) {
$element = $this->fixStepArgument($element);
public function assert_element_contains_text($text, $element) {
$this->assertSession()->elementTextContains('css', $element, $this->fixStepArgument($text));
}
@ -135,27 +136,21 @@ class behat_general extends behat_base {
*
* @Then /^I should not see "(?P<text_string>(?:[^"]|\\")*)" in the "(?P<element_string>(?:[^"]|\\")*)" element$/
*/
public function assert_element_not_contains_text($element, $text) {
$element = $this->fixStepArgument($element);
public function assert_element_not_contains_text($text, $element) {
$this->assertSession()->elementTextNotContains('css', $element, $this->fixStepArgument($text));
}
/**
* Checks, that element with given CSS is disabled.
*
* @throws ExpectationException
* @see Sanpi/Behatch/Context/BrowserContext
* @Then /^the element "(?P<element_string>(?:[^"]|\\")*)" should be disabled$/
* @throws ExpectationException Thrown by behat_base::find
* @param string $element
*/
public function the_element_should_be_disabled($element) {
$element = $this->fixStepArgument($element);
$node = $this->getSession()->getPage()->find('css', $element);
if ($node == null) {
throw new ExpectationException('There is no "' . $element . '" element', $this->getSession());
}
$exception = new ExpectationException('There is no "' . $element . '" element', $this->getSession());
$node = $this->find('css', $element, $exception);
if (!$node->hasAttribute('disabled')) {
throw new ExpectationException('The element "' . $element . '" is not disabled', $this->getSession());
@ -165,16 +160,14 @@ class behat_general extends behat_base {
/**
* Checks, that element with given CSS is enabled.
*
* @throws ExpectationException
* @see Sanpi/Behatch/Context/BrowserContext.php
* @Then /^the element "(?P<element_string>(?:[^"]|\\")*)" should be enabled$/
* @throws ExpectationException Thrown by behat_base::find
* @param string $element
*/
public function the_element_should_be_enabled($element) {
$node = $this->getSession()->getPage()->find('css', $element);
if ($node == null) {
throw new ExpectationException('There is no "' . $element . '" element', $this->getSession());
}
$exception = new ExpectationException('There is no "' . $element . '" element', $this->getSession());
$node = $this->find('css', $element, $exception);
if ($node->hasAttribute('disabled')) {
throw new ExpectationException('The element "' . $element . '" is not enabled', $this->getSession());

View File

@ -52,11 +52,6 @@ use Behat\Behat\Event\StepEvent as StepEvent;
*/
class behat_hooks extends behat_base {
/**
* @var string The last visited URL.
*/
private $lasturl = null;
/**
* Gives access to moodle codebase, ensures all is ready and sets up the test lock.
*
@ -162,12 +157,8 @@ class behat_hooks extends behat_base {
return;
}
// Wait until the page is ready if we are in a new URL.
$currenturl = $this->getSession()->getCurrentUrl();
if (is_null($this->lasturl) || $currenturl !== $this->lasturl) {
$this->lasturl = $currenturl;
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
}
// Wait until the page is ready.
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
}
}

View File

@ -40,8 +40,9 @@ class behat_navigation extends behat_base {
/**
* Expands the selected node of the navigation tree that matches the text.
*
* @Given /^I expand "(?P<nodetext_string>(?:[^"]|\\")*)" node$/
*
* @throws ElementNotFoundException Thrown by behat_base::find
* @param string $nodetext
*/
public function i_expand_node($nodetext) {
@ -53,7 +54,7 @@ class behat_navigation extends behat_base {
/descendant::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]
/descendant::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]";
$node = $this->getSession()->getPage()->find('xpath', $xpath);
$node = $this->find('xpath', $xpath);
$node->click();
}