MDL-39635 behat: XPath cleanups

- Escaping steps arguments redirected to other steps
- Adding normalized-space() in all contains() assertions
- General xpaths review
- Convering provided xpath text strings to xpath literals
  to avoid problems with arguments containing both single
  quotes and double quotes
This commit is contained in:
David Monllao 2013-06-21 13:48:36 +08:00 committed by David Monllao
parent dedb973878
commit 3897608152
34 changed files with 211 additions and 155 deletions

View File

@ -72,8 +72,12 @@ class behat_admin extends behat_base {
// 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.
$exception = new ElementNotFoundException($this->getSession(), '"' . $label . '" administration setting ');
// The argument should be converted to an xpath literal.
$label = $this->getSession()->getSelectorsHandler()->xpathLiteral($label);
$fieldxpath = "//*[self::input | self::textarea | self::select][not(./@type = 'submit' or ./@type = 'image' or ./@type = 'hidden')]" .
"[@id=//label[contains(normalize-space(string(.)), '" . $label . "')]/@for]";
"[@id=//label[contains(normalize-space(.), $label)]/@for]";
$fieldnode = $this->find('xpath', $fieldxpath, $exception);
$formfieldtypenode = $this->find('xpath', $fieldxpath . "/ancestor::div[@class='form-setting']" .
"/child::div[contains(concat(' ', @class, ' '), ' form-')]/child::*/parent::div");

View File

@ -15,7 +15,7 @@ Feature: Display extended course names
And I should not see "C_shortname Course fullname"
Scenario: Courses list with extended course names
Given I click on "Courses" "link" in the "//div[@id='settingsnav']//descendant::li[contains(concat(' ', @class, ' '), ' type_setting ')][not(contains(., 'Site administration'))][contains(., 'Appearance')]" "xpath_element"
Given I click on "Courses" "link" in the "//div[@id='settingsnav']/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' type_setting ')][not(contains(., 'Site administration'))][contains(., 'Appearance')]" "xpath_element"
And I check "Display extended course names"
When I press "Save changes"
And I am on homepage

View File

@ -37,7 +37,7 @@ Feature: Page contents assertions
And I follow "Course 1"
When I click on "Move this to the dock" "button" in the ".block_settings" "css_element"
Then I should not see "Question bank"
And I click on "//div[@id='dock']/descendant::*[contains(., 'Administration')]/h2" "xpath_element"
And I click on "//div[@id='dock']/descendant::h2[normalize-space(.)='Administration']" "xpath_element"
@javascript
Scenario: Locators inside specific DOM nodes using XPath
@ -45,5 +45,5 @@ Feature: Page contents assertions
| fullname | shortname | category |
| Course 1 | C1 | 0 |
And I log in as "admin"
When I click on "Move this to the dock" "button" in the "//*[contains(concat(' ', normalize-space(@class), ' '), ' block_settings ')]" "xpath_element"
When I click on "Move this to the dock" "button" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' block_settings ')]" "xpath_element"
Then I should not see "Turn editing on"

View File

@ -30,17 +30,17 @@ Feature: Set up contextual data for tests
Then I should see "Course 1"
And I should see "Course 2"
And I should see "Course 3"
When I go to the courses management page
And I go to the courses management page
And I follow "Cat 1"
Then I should see "Cat 2"
And I should see "Cat 2"
And I should see "Cat 3"
When I follow "Cat 3"
Then I should see "Course 1"
And I follow "Cat 3"
And I should see "Course 1"
And I should see "Course 2"
When I select "Cat 2" from "Course categories:"
Then I should see "No courses in this category"
When I select "Miscellaneous" from "Course categories:"
Then I should see "Course 3"
And I select "Cat 1 / Cat 2" from "Course categories:"
And I should see "No courses in this category"
And I select "Miscellaneous" from "Course categories:"
And I should see "Course 3"
@javascript
Scenario: Add a bunch of groups and groupings

View File

@ -50,8 +50,8 @@ class behat_auth extends behat_base {
return array(new Given('I am on homepage'),
new Given('I follow "' . get_string('login') . '"'),
new Given('I fill in "' . get_string('username') . '" with "'.$username.'"'),
new Given('I fill in "' . get_string('password') . '" with "'.$username.'"'),
new Given('I fill in "' . get_string('username') . '" with "' . $this->escape($username) . '"'),
new Given('I fill in "' . get_string('password') . '" with "'. $this->escape($username) . '"'),
new Given('I press "' . get_string('login') . '"')
);
}

View File

@ -31,7 +31,7 @@ Feature: Backup Moodle courses
And I should not see "Section 3"
And I press "Continue"
And I click on "Continue" "button" in the ".bcs-current-course" "css_element"
And I click on "//div[contains(concat(' ', @class, ' '), ' fitem ')][contains(., 'Include calendar events')]/descendant::img" "xpath_element"
And I click on "setting_root_logs" "checkbox" in the "//div[contains(@class, 'fitem')][contains(., 'Include course logs')]" "xpath_element"
And "//div[contains(concat(' ', normalize-space(@class), ' '), ' fitem ')][contains(., 'Include calendar events')]/descendant::img" "xpath_element" should exists
And I check "Include course logs"
And I press "Cancel"
And I click on "Cancel" "button" in the ".confirmation-dialogue" "css_element"

View File

@ -105,15 +105,16 @@ class behat_backup extends behat_base {
// Click the course link.
$this->find_link($tocourse)->click();
// Click the backup link.
// Click the import link.
$this->find_link(get_string('import'))->click();
// Select the course.
$exception = new ExpectationException('"' . $fromcourse . '" course not found in the list of courses to import from', $this->getSession());
$fromcourse = str_replace("'", "\'", $fromcourse);
$xpath = "//div[contains(concat(' ', @class, ' '), ' ics-results ')]" .
"/descendant::tr[contains(., '" . $fromcourse . "')]" .
// The argument should be converted to an xpath literal.
$fromcourse = $this->getSession()->getSelectorsHandler()->xpathLiteral($fromcourse);
$xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' ics-results ')]" .
"/descendant::tr[contains(., $fromcourse)]" .
"/descendant::input[@type='radio']";
$radionode = $this->find('xpath', $xpath, $exception);
$radionode->check();
@ -150,17 +151,19 @@ class behat_backup extends behat_base {
// Confirm restore.
$this->select_backup($backupfilename);
// The argument should be converted to an xpath literal.
$existingcourse = $this->getSession()->getSelectorsHandler()->xpathLiteral($existingcourse);
// Selecting the specified course (we can not call behat_forms::select_radio here as is in another behat subcontext).
$existingcourse = str_replace("'", "\'", $existingcourse);
$radionode = $this->find('xpath', "//div[contains(@class, 'bcs-existing-course')]" .
$radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
"/descendant::div[@class='restore-course-search']" .
"/descendant::tr[contains(., '" . $existingcourse . "')]" .
"/descendant::tr[contains(., $existingcourse)]" .
"/descendant::input[@type='radio']");
$radionode->check();
$radionode->click();
// Pressing the continue button of the restore into an existing course section.
$continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-existing-course')]" .
$continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
"/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@ -182,14 +185,14 @@ class behat_backup extends behat_base {
$this->select_backup($backupfilename);
// The first category in the list.
$radionode = $this->find('xpath', "//div[contains(@class, 'bcs-new-course')]" .
$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.
$continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-new-course')]" .
$continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
"/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@ -211,13 +214,13 @@ class behat_backup extends behat_base {
$this->select_backup($backupfilename);
// Merge without deleting radio option.
$radionode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
$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.
$continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
$continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@ -239,13 +242,13 @@ class behat_backup extends behat_base {
$this->select_backup($backupfilename);
// Delete contents radio option.
$radionode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
$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.
$continuenode = $this->find('xpath', "//div[contains(@class, 'bcs-current-course')]" .
$continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
"/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
$continuenode->click();
$this->wait();
@ -265,7 +268,11 @@ class behat_backup extends behat_base {
// Using xpath as there are other restore links before this one.
$exception = new ExpectationException('The "' . $backupfilename . '" backup file can not be found in this page', $this->getSession());
$xpath = "//tr[contains(., '" . $backupfilename . "')]/descendant::a[contains(., '" . get_string('restore') . "')]";
// The argument should be converted to an xpath literal.
$backupfilename = $this->getSession()->getSelectorsHandler()->xpathLiteral($backupfilename);
$xpath = "//tr[contains(., $backupfilename)]/descendant::a[contains(., '" . get_string('restore') . "')]";
$restorelink = $this->find('xpath', $xpath, $exception);
$restorelink->click();
@ -341,6 +348,10 @@ class behat_backup extends behat_base {
*/
protected function wait($timeout = false) {
if (!$this->running_javascript()) {
return;
}
if (!$timeout) {
$timeout = self::TIMEOUT;
}

View File

@ -91,8 +91,11 @@ class behat_block_comments extends behat_base {
$exception = new ElementNotFoundException($this->getSession(), '"' . $comment . '" comment ');
$commentxpath = "//div[contains(concat(' ', @class, ' '), ' block_comments ')]" .
"/descendant::div[@class='comment-message'][contains(., '" . $comment . "')]";
// Using xpath liternal to avoid possible problems with comments containing quotes.
$commentliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($comment);
$commentxpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' block_comments ')]" .
"/descendant::div[@class='comment-message'][contains(., $commentliteral)]";
$commentnode = $this->find('xpath', $commentxpath, $exception);
// Click on delete icon.
@ -101,7 +104,7 @@ class behat_block_comments extends behat_base {
$deleteicon->click();
// Yes confirm.
$confirmnode = $this->find('xpath', "//div[@class='comment-delete-confirm']/descendant::a[contains(., 'Yes')]");
$confirmnode = $this->find('xpath', "//div[@class='comment-delete-confirm']/descendant::a[contains(., '" . get_string('yes') . "')]");
$confirmnode->click();
// Wait for the AJAX request.

View File

@ -46,7 +46,7 @@ class behat_blocks extends behat_base {
* @param string $blockname
*/
public function i_add_the_block($blockname) {
$steps = new Given('I select "' . $blockname . '" from "bui_addblock"');
$steps = new Given('I select "' . $this->escape($blockname) . '" from "bui_addblock"');
// If we are running without javascript we need to submit the form.
if (!$this->running_javascript()) {

View File

@ -32,4 +32,4 @@ Feature: Add and configure blocks throughout the site
And I press "Save changes"
And I follow "Course 1"
# The first block matching the pattern should be top-left block
And I should see "Comments" in the "//*[@id='region-pre']/descendant::div[contains(concat(' ', @class, ' '), ' block ')]" "xpath_element"
And I should see "Comments" in the "//*[@id='region-pre']/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' block ')]" "xpath_element"

View File

@ -54,7 +54,7 @@ class behat_cohort extends behat_base {
$userid = $DB->get_field('user', 'id', array('username' => $username));
$steps = array(
new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "//table[@id=\'cohorts\']//tr[contains(., \'' . $cohortidnumber . '\')]" "xpath_element"'),
new Given('I click on "' . get_string('assign', 'cohort') . '" "link" in the "' . $this->escape($cohortidnumber) . '" table row'),
new Given('I select "' . $userid . '" from "' . get_string('potusers', 'cohort') . '"'),
new Given('I press "' . get_string('add') . '"'),
new Given('I press "' . get_string('backtocohorts', 'cohort') . '"')

View File

@ -32,11 +32,11 @@ Feature: Upload users to a cohort
And I press "Upload users"
And I press "Continue"
And I follow "Cohorts"
And I click on "Assign" "link" in the "//table[@id='cohorts']//tr[contains(., 'Cohort 1')]" "xpath_element"
And I click on "Assign" "link" in the "Cohort 1" table row
Then the "Current users" select box should contain "Tom Jones (tomjones@example.com)"
And the "Current users" select box should contain "Bob Jones (bobjones@example.com)"
And I press "Back to cohorts"
And I click on "Assign" "link" in the "//table[@id='cohorts']//tr[contains(., 'Cohort 2')]" "xpath_element"
And I click on "Assign" "link" in the "Cohort 2" table row
And the "Current users" select box should contain "Mary Smith (marysmith@example.com)"
And the "Current users" select box should contain "Alice Smith (alicesmith@example.com)"
And I am on homepage

View File

@ -50,12 +50,13 @@ class behat_completion extends behat_base {
public function user_has_completed_activity($userfullname, $activityname) {
// Will throw an exception if the element can not be hovered.
$titleliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($userfullname . ", " . $activityname . ": Completed");
$xpath = "//table[@id='completion-progress']" .
"/descendant::img[contains(@title, '" . $userfullname . ", " . $activityname . ": Completed')]";
"/descendant::img[contains(@title, $titleliteral)]";
return array(
new Given('I go to the current course activity completion report'),
new Given('I hover "' . $xpath . '" "xpath_element"')
new Given('I hover "' . $this->escape($xpath) . '" "xpath_element"')
);
}
@ -68,11 +69,13 @@ class behat_completion extends behat_base {
*/
public function user_has_not_completed_activity($userfullname, $activityname) {
// Will throw an exception if the element can not be hovered.
$titleliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($userfullname . ", " . $activityname . ": Not completed");
$xpath = "//table[@id='completion-progress']" .
"/descendant::img[contains(@title, '" . $userfullname . ", " . $activityname . ": Not completed')]";
"/descendant::img[contains(@title, $titleliteral)]";
return array(
new Given('I go to the current course activity completion report'),
new Given('I hover "' . $xpath . '" "xpath_element"')
new Given('I hover "' . $this->escape($xpath) . '" "xpath_element"')
);
return $steps;
@ -89,9 +92,9 @@ class behat_completion extends behat_base {
// Expand reports node if we can't see the link.
try {
$this->find('xpath', "//*[@id='settingsnav']" .
$this->find('xpath', "//div[@id='settingsnav']" .
"/descendant::li" .
"/descendant::li[not(contains(@class,'collapsed'))]" .
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]" .
"/descendant::p[contains(., '" . get_string('pluginname', 'report_progress') . "')]");
} catch (ElementNotFoundException $e) {
$steps[] = new Given('I expand "' . get_string('reports') . '" node');

View File

@ -27,21 +27,21 @@ Feature: Toggle activities groups mode from the course page
| Force group mode | No |
When I press "Save changes"
Then "No groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groupn')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groupn')]" "xpath_element" should exists
And I click on "No groups (Click to change)" "link" in the "Test forum name" activity
And I wait "3" seconds
And "Separate groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groups')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groups')]" "xpath_element" should exists
And I reload the page
And "Separate groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groups')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groups')]" "xpath_element" should exists
And I click on "Separate groups (Click to change)" "link" in the "Test forum name" activity
And I wait "3" seconds
And "Visible groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groupv')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groupv')]" "xpath_element" should exists
And I reload the page
And "Visible groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groupv')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groupv')]" "xpath_element" should exists
And I click on "Visible groups (Click to change)" "link" in the "Test forum name" activity
And "No groups (Click to change)" "link" should exists
And ".//a//img[contains(@src, 'groupn')]" "xpath_element" should exists
And "//a/child::img[contains(@src, 'groupn')]" "xpath_element" should exists

View File

@ -26,7 +26,7 @@ Feature: Indent items on the course page
When I indent right "Test glossary name" activity
Then "#section-1 li.glossary div.mod-indent-1" "css_element" should exists
And I indent right "Test glossary name" activity
And "//*[@id='section-1']/descendant::li[contains(concat(' ', @class, ' '), ' glossary ')]/descendant::a[@title='Move left']" "xpath_element" should exists
And "//li[@id='section-1']/descendant::li[contains(concat(' ', @class, ' '), ' glossary ')]/descendant::a[@title='Move left']" "xpath_element" should exists
And "#section-1 li.glossary div.mod-indent-2" "css_element" should exists
And I reload the page
And "#section-1 li.glossary div.mod-indent-2" "css_element" should exists
@ -34,4 +34,4 @@ Feature: Indent items on the course page
And I indent left "Test glossary name" activity
And "#section-1 li.glossary div.mod-indent-2" "css_element" should not exists
And "#section-1 li.glossary div.mod-indent-1" "css_element" should not exists
And "//*[@id='section-1']/descendant::li[contains(concat(' ', @class, ' '), ' glossary ')]/descendant::a[@title='Move left']" "xpath_element" should not exists
And "//li[@id='section-1']/descendant::li[contains(concat(' ', @class, ' '), ' glossary ')]/descendant::a[@title='Move left']" "xpath_element" should not exists

View File

@ -100,7 +100,7 @@ class behat_course extends behat_base {
public function i_add_to_section_and_i_fill_the_form_with($activity, $section, TableNode $data) {
return array(
new Given('I add a "'.$activity.'" to section "'.$section.'"'),
new Given('I add a "' . $this->escape($activity) . '" to section "' . $this->escape($section) . '"'),
new Given('I fill the moodle form with:', $data),
new Given('I press "' . get_string('savechangesandreturntocourse') . '"')
);
@ -116,7 +116,9 @@ class behat_course extends behat_base {
*/
public function i_add_to_section($activity, $section) {
$sectionxpath = "//*[@id='section-" . $section . "']";
$sectionxpath = "//li[@id='section-" . $section . "']";
$activityliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral(ucfirst($activity));
if ($this->running_javascript()) {
@ -126,9 +128,9 @@ class behat_course extends behat_base {
$sectionnode->click();
// Clicks the selected activity if it exists.
$activity = ucfirst($activity);
$activityxpath = "//div[@id='chooseform']/descendant::label" .
"/descendant::span[contains(concat(' ', @class, ' '), ' typename ')][contains(.,'" . $activity . "')]" .
"/descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' typename ')]" .
"[contains(., $activityliteral)]" .
"/parent::label/child::input";
$activitynode = $this->find('xpath', $activityxpath);
$activitynode->doubleClick();
@ -137,8 +139,8 @@ class behat_course extends behat_base {
// Without Javascript.
// Selecting the option from the select box which contains the option.
$selectxpath = $sectionxpath . "/descendant::div[contains(concat(' ', @class, ' '), ' section_add_menus ')]" .
"/descendant::select[contains(., '" . $activity . "')]";
$selectxpath = $sectionxpath . "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' section_add_menus ')]" .
"/descendant::select[contains(., $activityliteral)]";
$selectnode = $this->find('xpath', $selectxpath);
$selectnode->selectOption($activity);
@ -162,7 +164,7 @@ class behat_course extends behat_base {
$xpath = $this->section_exists($sectionnumber);
return array(
new Given('I click on "' . get_string('markthistopic') . '" "link" in the "' . $xpath . '" "xpath_element"'),
new Given('I click on "' . get_string('markthistopic') . '" "link" in the "' . $this->escape($xpath) . '" "xpath_element"'),
new Given('I wait "2" seconds')
);
}
@ -179,7 +181,7 @@ class behat_course extends behat_base {
$xpath = $this->section_exists($sectionnumber);
return array(
new Given('I click on "' . get_string('markedthistopic') . '" "link" in the "' . $xpath . '" "xpath_element"'),
new Given('I click on "' . get_string('markedthistopic') . '" "link" in the "' . $this->escape($xpath) . '" "xpath_element"'),
new Given('I wait "2" seconds')
);
}
@ -268,7 +270,7 @@ class behat_course extends behat_base {
// Section should be hidden.
$exception = new ExpectationException('The section is not hidden', $this->getSession());
$this->find('xpath', $sectionxpath . "[contains(concat(' ', @class, ' '), ' hidden ')]", $exception);
$this->find('xpath', $sectionxpath . "[contains(concat(' ', normalize-space(@class), ' '), ' hidden ')]", $exception);
// The checking are different depending on user permissions.
if ($this->is_course_editor()) {
@ -284,8 +286,8 @@ class behat_course extends behat_base {
foreach ($activities as $activity) {
// Dimmed.
$this->find('xpath', "//div[contains(concat(' ', @class, ' '), ' activityinstance ')]" .
"/a[contains(concat(' ', @class, ' '), ' dimmed ')]", $dimmedexception, $activity);
$this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' activityinstance ')]" .
"/a[contains(concat(' ', normalize-space(@class), ' '), ' dimmed ')]", $dimmedexception, $activity);
// Non-JS browsers can not click on img elements.
if ($this->running_javascript()) {
@ -319,7 +321,8 @@ class behat_course extends behat_base {
$sectionxpath = $this->section_exists($sectionnumber);
// Section should not be hidden.
if (!$this->getSession()->getPage()->find('xpath', $sectionxpath . "[not(contains(concat(' ', @class, ' '), ' hidden '))]")) {
$xpath = $sectionxpath . "[not(contains(concat(' ', normalize-space(@class), ' '), ' hidden '))]";
if (!$this->getSession()->getPage()->find('xpath', $xpath)) {
throw new ExpectationException('The section is hidden', $this->getSession());
}
@ -449,10 +452,11 @@ class behat_course extends behat_base {
// JS enabled.
if ($this->running_javascript()) {
$destinationxpath = $sectionxpath . "/descendant::ul[contains(@class, 'yui3-dd-drop')]";
$destinationxpath = $sectionxpath . "/descendant::ul[contains(concat(' ', normalize-space(@class), ' '), ' yui3-dd-drop ')]";
return array(
new Given('I drag "' . $activitynode->getXpath() . '" "xpath_element" and I drop it in "' . $destinationxpath . '" "xpath_element"'),
new Given('I drag "' . $this->escape($activitynode->getXpath()) . '" "xpath_element" ' .
'and I drop it in "' . $this->escape($destinationxpath) . '" "xpath_element"'),
);
} else {
@ -460,8 +464,8 @@ class behat_course extends behat_base {
// Moving to the fist spot of the section (before all other section's activities).
return array(
new Given('I click on "a.editing_move" "css_element" in the "' . $activityname . '" activity'),
new Given('I click on "li.movehere a" "css_element" in the "' . $sectionxpath . '" "xpath_element"'),
new Given('I click on "a.editing_move" "css_element" in the "' . $this->escape($activityname) . '" activity'),
new Given('I click on "li.movehere a" "css_element" in the "' . $this->escape($sectionxpath) . '" "xpath_element"'),
);
}
}
@ -482,8 +486,8 @@ class behat_course extends behat_base {
// Adding chr(10) to save changes.
return array(
new Given('I click on "' . get_string('edittitle') . '" "link" in the "' . $activityname .'" activity'),
new Given('I fill in "title" with "' . $newactivityname . chr(10) . '"'),
new Given('I click on "' . get_string('edittitle') . '" "link" in the "' . $this->escape($activityname) .'" activity'),
new Given('I fill in "title" with "' . $this->escape($newactivityname) . chr(10) . '"'),
new Given('I wait "2" seconds')
);
}
@ -497,7 +501,7 @@ class behat_course extends behat_base {
public function i_indent_right_activity($activityname) {
$steps = array(
new Given('I click on "' . get_string('moveright') . '" "link" in the "' . $activityname . '" activity')
new Given('I click on "' . get_string('moveright') . '" "link" in the "' . $this->escape($activityname) . '" activity')
);
if ($this->running_javascript()) {
@ -516,7 +520,7 @@ class behat_course extends behat_base {
public function i_indent_left_activity($activityname) {
$steps = array(
new Given('I click on "' . get_string('moveleft') . '" "link" in the "' . $activityname . '" activity')
new Given('I click on "' . get_string('moveleft') . '" "link" in the "' . $this->escape($activityname) . '" activity')
);
if ($this->running_javascript()) {
@ -553,7 +557,7 @@ class behat_course extends behat_base {
// With JS disabled.
$steps = array(
new Given('I click on "' . $deletestring . '" "link" in the "' . $activityname . '" activity'),
new Given('I click on "' . $this->escape($deletestring) . '" "link" in the "' . $this->escape($activityname) . '" activity'),
new Given('I press "' . get_string('yes') . '"')
);
@ -569,7 +573,7 @@ class behat_course extends behat_base {
*/
public function i_duplicate_activity($activityname) {
return array(
new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $activityname . '" activity'),
new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $this->escape($activityname) . '" activity'),
new Given('I press "' . get_string('continue') .'"'),
new Given('I press "' . get_string('duplicatecontcourse') .'"')
);
@ -584,7 +588,7 @@ class behat_course extends behat_base {
*/
public function i_duplicate_activity_editing_the_new_copy_with($activityname, TableNode $data) {
return array(
new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $activityname . '" activity'),
new Given('I click on "' . get_string('duplicate') . '" "link" in the "' . $this->escape($activityname) . '" activity'),
new Given('I press "' . get_string('continue') .'"'),
new Given('I press "' . get_string('duplicatecontedit') . '"'),
new Given('I fill the moodle form with:', $data),
@ -657,9 +661,9 @@ class behat_course extends behat_base {
$courseformat = $this->get_course_format();
// Checking the show button alt text and show icon.
$showtext = get_string('showfromothers', $courseformat);
$linkxpath = $xpath . "/descendant::a[@title='". $showtext ."']";
$imgxpath = $linkxpath . "/descendant::img[@alt='". $showtext ."'][contains(@src, 'show')]";
$showtext = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('showfromothers', $courseformat));
$linkxpath = $xpath . "/descendant::a[@title=$showtext]";
$imgxpath = $linkxpath . "/descendant::img[@alt=$showtext][contains(@src, 'show')]";
$exception = new ElementNotFoundException($this->getSession(), 'Show section icon ');
$this->find('xpath', $imgxpath, $exception);
@ -684,9 +688,9 @@ class behat_course extends behat_base {
$courseformat = $this->get_course_format();
// Checking the hide button alt text and hide icon.
$hidetext = get_string('hidefromothers', $courseformat);
$linkxpath = $xpath . "/descendant::a[@title='" . $hidetext . "']";
$imgxpath = $linkxpath . "/descendant::img[@alt='" . $hidetext ."'][contains(@src, 'hide')]";
$hidetext = $this->getSession()->getSelectorsHandler()->xpathLiteral(get_string('hidefromothers', $courseformat));
$linkxpath = $xpath . "/descendant::a[@title=$hidetext]";
$imgxpath = $linkxpath . "/descendant::img[@alt=$hidetext][contains(@src, 'hide')]";
$exception = new ElementNotFoundException($this->getSession(), 'Hide section icon ');
$this->find('xpath', $imgxpath, $exception);
@ -730,7 +734,7 @@ class behat_course extends behat_base {
*/
protected function get_section_activities($sectionxpath) {
$xpath = $sectionxpath . "/descendant::li[contains(concat(' ', @class, ' '), ' activity ')]";
$xpath = $sectionxpath . "/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]";
// We spin here, as activities usually require a lot of time to load.
try {
@ -751,8 +755,8 @@ class behat_course extends behat_base {
*/
protected function get_activity_node($activityname) {
$activityname = str_replace("'", "\'", $activityname);
$xpath = "//li[contains(concat(' ', @class, ' '), ' activity ')][contains(., '" .$activityname. "')]";
$activityname = $this->getSession()->getSelectorsHandler()->xpathLiteral($activityname);
$xpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')][contains(., $activityname)]";
return $this->find('xpath', $xpath);
}

View File

@ -28,11 +28,11 @@ Feature: Force group mode in a course
| Group mode | Separate groups |
| Force group mode | Yes |
When I press "Save changes"
Then ".//a//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And ".//a//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'Separate groups (forced mode)')]" "xpath_element" should exists
Then "//a/child::img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And "//a/child::img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'Separate groups (forced mode)')]" "xpath_element" should exists
@javascript
Scenario: Forced group mode using visible groups
@ -40,11 +40,11 @@ Feature: Force group mode in a course
| Group mode | Visible groups |
| Force group mode | Yes |
And I press "Save changes"
Then ".//a//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And ".//a//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'Visible groups (forced mode)')]" "xpath_element" should exists
Then "//a/child::img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And "//a/child::img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'Visible groups (forced mode)')]" "xpath_element" should exists
@javascript
Scenario: Forced group mode without groups
@ -52,9 +52,9 @@ Feature: Force group mode in a course
| Group mode | No groups |
| Force group mode | Yes |
And I press "Save changes"
Then ".//a//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And ".//a//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" should not exists
And ".//img[contains(./@alt, 'No groups (forced mode)')]" "xpath_element" should exists
Then "//a/child::img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" should exists
And I click on "//img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" in the "li.activity.chat" "css_element"
And "//a/child::img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" should not exists
And "//img[contains(@alt, 'No groups (forced mode)')]" "xpath_element" should exists

View File

@ -52,7 +52,7 @@ class behat_enrol extends behat_base {
return array(
new Given('I expand "' . get_string('users', 'admin') . '" node'),
new Given('I follow "' . get_string('type_enrol_plural', 'plugin') . '"'),
new Given('I select "' . $enrolmethod . '" from "' . get_string('addinstance', 'enrol') . '"'),
new Given('I select "' . $this->escape($enrolmethod) . '" from "' . get_string('addinstance', 'enrol') . '"'),
new Given('I fill the moodle form with:', $table),
new Given('I press "' . get_string('addinstance', 'enrol') . '"')
);

View File

@ -51,30 +51,33 @@ class behat_groups extends behat_base {
global $DB;
$user = $DB->get_record('user', array('username' => $username));
$userfullname = fullname($user);
$userfullname = $this->getSession()->getSelectorsHandler()->xpathLiteral(fullname($user));
// Using a xpath liternal to avoid problems with quotes and double quotes.
$groupname = $this->getSession()->getSelectorsHandler()->xpathLiteral($groupname);
// We don't know the option text as it contains the number of users in the group.
$select = $this->find_field('groups');
$xpath = "//select[@id='groups']/descendant::option[contains(., '" . $groupname . "')]";
$xpath = "//select[@id='groups']/descendant::option[contains(., $groupname)]";
$groupoption = $this->find('xpath', $xpath);
$fulloption = $groupoption->getText();
$select->selectOption($fulloption);
// Here we don't need to wait for the AJAX response.
$this->find_button('Add/remove users')->click();
$this->find_button(get_string('adduserstogroup', 'group'))->click();
// Wait for add/remove members page to be loaded.
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');
// Getting the option and selecting it.
$select = $this->find_field('addselect');
$xpath = "//select[@id='addselect']/descendant::option[contains(., '" . $userfullname . "')]";
$xpath = "//select[@id='addselect']/descendant::option[contains(., $userfullname)]";
$memberoption = $this->find('xpath', $xpath);
$fulloption = $memberoption->getText();
$select->selectOption($fulloption);
// Click add button.
$this->find_button('Add')->click();
$this->find_button(get_string('add'))->click();
// Wait for the page to load.
$this->getSession()->wait(self::TIMEOUT, '(document.readyState === "complete")');

View File

@ -64,9 +64,10 @@ class behat_files extends behat_base {
$exception = new ExpectationException('"' . $filepickerelement . '" filepicker can not be found', $this->getSession());
// Gets the ffilemanager node specified by the locator which contains the filepicker container.
$filepickerelement = $this->getSession()->getSelectorsHandler()->xpathLiteral($filepickerelement);
$filepickercontainer = $this->find(
'xpath',
"//input[./@id = //label[contains(normalize-space(string(.)), '" . $filepickerelement . "')]/@for]" .
"//input[./@id = //label[normalize-space(.)=$filepickerelement]/@for]" .
"//ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' ffilemanager ') or " .
"contains(concat(' ', normalize-space(@class), ' '), ' ffilepicker ')]",
$exception
@ -118,6 +119,9 @@ class behat_files extends behat_base {
$exception = new ExpectationException($exceptionmsg, $this->getSession());
// Avoid quote-related problems.
$name = $this->getSession()->getSelectorsHandler()->xpathLiteral($name);
// Get a filepicker element (folder or file).
try {
@ -126,7 +130,8 @@ class behat_files extends behat_base {
'xpath',
"//div[@class='fp-content']" .
"//descendant::*[self::div | self::a][contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]" .
"[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')][contains(normalize-space(string(.)), '" . $name . "')]" .
"[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')]" .
"[normalize-space(.)=$name]" .
"//descendant::a[contains(concat(' ', normalize-space(@class), ' '), ' fp-contextmenu ')]",
$exception,
$containernode
@ -139,7 +144,7 @@ class behat_files extends behat_base {
'xpath',
"//div[@class='fp-content']" .
"//descendant::*[self::div | self::a][contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')]" .
"[contains(normalize-space(string(.)), '" . $name . "')]" .
"[normalize-space(.)=$name]" .
"//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-thumbnail ')]",
false,
$containernode
@ -176,12 +181,15 @@ class behat_files extends behat_base {
// Getting the repository link and opening it.
$repoexception = new ExpectationException('The "' . $repositoryname . '" repository has not been found', $this->getSession());
// Avoid problems with both double and single quotes in the same string.
$repositoryname = $this->getSession()->getSelectorsHandler()->xpathLiteral($repositoryname);
// Here we don't need to look inside the selected filepicker because there can only be one modal window.
$repositorylink = $this->find(
'xpath',
"//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-area ')]" .
"//descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-name ')]" .
"[contains(normalize-space(string(.)), '" . $repositoryname . "')]",
"[normalize-space(.)=$repositoryname]",
$repoexception
);
@ -226,10 +234,10 @@ class behat_files extends behat_base {
// only used when accessing the filepicker, there is no filemanager-loading after selecting the file.
$this->find(
'xpath',
"//div[contains(concat(' ', @class, ' '), ' filemanager ')]" .
"[not(contains(concat(' ', @class, ' '), ' fm-updating '))]" .
"//div[contains(concat(' ', normalize-space(@class), ' '), ' filemanager ')]" .
"[not(contains(concat(' ', normalize-space(@class), ' '), ' fm-updating '))]" .
"|" .
"//div[contains(concat(' ', @class, ' '), ' filemanager-loading ')]" .
"//div[contains(concat(' ', normalize-space(@class), ' '), ' filemanager-loading ')]" .
"[contains(@style, 'display: none;')]",
$exception,
$filepickernode

View File

@ -58,10 +58,13 @@ class behat_form_select extends behat_form_field {
// Single select needs an extra click in the option.
if (!$this->field->hasAttribute('multiple')) {
$value = $this->session->getSelectorsHandler()->xpathLiteral($value);
// Using the driver direcly because Element methods are messy when dealing
// with elements inside containers.
$optionxpath = $this->field->getXpath() .
"/descendant::option[(./@value = '" . $value . "' or contains(normalize-space(string(.)), '" . $value . "'))]";
"/descendant::option[(./@value=$value or normalize-space(.)=$value)]";
$optionnodes = $this->session->getDriver()->find($optionxpath);
if ($optionnodes) {
current($optionnodes)->click();

View File

@ -132,8 +132,8 @@ class behat_forms extends behat_base {
// Show all fields.
$showmorestr = get_string('showmore', 'form');
$showmores = $this->find_all('xpath', "//a[contains(concat(' ', normalize-space(.), ' '), '" . $showmorestr . "')]" .
"[contains(concat(' ', normalize-space(@class), ' '), ' moreless-toggler')]");
$showmores = $this->find_all('xpath', "//a[normalize-space(.)='" . $showmorestr . "']" .
"[contains(concat(' ', normalize-space(@class), ' '), ' moreless-toggler ')]");
// We are supposed to have 'show more's here, otherwise exception.
@ -186,9 +186,12 @@ class behat_forms extends behat_base {
return;
}
// Single select needs an extra click in the option.
if (!$selectnode->hasAttribute('multiple')) {
// Single select needs an extra click in the option.
$xpath = ".//option[(./@value = '" . $option . "' or contains(normalize-space(string(.)), '" . $option . "'))]";
// 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 {

View File

@ -242,8 +242,8 @@ class behat_general extends behat_base {
// The table row container.
$nocontainerexception = new ElementNotFoundException($this->getSession(), '"' . $tablerowtext . '" row text ');
$tablerowtext = str_replace("'", "\'", $tablerowtext);
$rownode = $this->find('xpath', "//tr[contains(., '" . $tablerowtext . "')]", $nocontainerexception);
$tablerowtext = $this->getSession()->getSelectorsHandler()->xpathLiteral($tablerowtext);
$rownode = $this->find('xpath', "//tr[contains(., $tablerowtext)]", $nocontainerexception);
// Looking for the element DOM node inside the specified row.
list($selector, $locator) = $this->transform_selector($selectortype, $element);
@ -285,7 +285,7 @@ class behat_general extends behat_base {
public function assert_page_contains_text($text) {
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
$xpath = "/descendant::*[contains(., " . $xpathliteral. ")]";
$xpath = "/descendant::*[contains(., $xpathliteral)]";
// Wait until it finds the text, otherwise custom exception.
try {
@ -305,7 +305,7 @@ class behat_general extends behat_base {
public function assert_page_not_contains_text($text) {
$xpathliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($text);
$xpath = "/descendant::*[not(contains(., " . $xpathliteral. "))]";
$xpath = "/descendant::*[not(contains(., $xpathliteral))]";
// Wait until it finds the text, otherwise custom exception.
try {

View File

@ -56,16 +56,19 @@ class behat_navigation extends behat_base {
return false;
}
// Avoid problems with quotes.
$nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($nodetext);
$xpath = "//ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
"/child::li[contains(concat(' ', normalize-space(@class), ' '), ' collapsed ')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]" .
"/child::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
"/child::span[normalize-space(.)=$nodetextliteral]" .
"|" .
"//ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed'))]" .
"/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' collapsed')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]" .
"/child::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]";
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]" .
"/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' collapsed ')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
"/child::span[normalize-space(.)=$nodetextliteral]";
$exception = new ExpectationException('The "' . $nodetext . '" node can not be expanded', $this->getSession());
$node = $this->find('xpath', $xpath, $exception);
@ -86,15 +89,18 @@ class behat_navigation extends behat_base {
return false;
}
// Avoid problems with quotes.
$nodetextliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($nodetext);
$xpath = "//ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
"/child::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]" .
"/child::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
"/child::span[normalize-space(.)=$nodetextliteral]" .
"|" .
"//ul[contains(concat(' ', normalize-space(@class), ' '), ' block_tree ')]" .
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed'))]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch')]" .
"/child::span[contains(concat(' ', normalize-space(.), ' '), '" . $nodetext . "')]";
"/descendant::li[not(contains(concat(' ', normalize-space(@class), ' '), ' collapsed '))]" .
"/child::p[contains(concat(' ', normalize-space(@class), ' '), ' branch ')]" .
"/child::span[normalize-space(.)=$nodetextliteral]";
$exception = new ExpectationException('The "' . $nodetext . '" node can not be collapsed', $this->getSession());
$node = $this->find('xpath', $xpath, $exception);

View File

@ -73,7 +73,7 @@ class behat_permissions extends behat_base {
public function i_override_the_system_permissions_of_role_with($rolename, $table) {
// We don't know the number of overrides so we have to get it to match the option contents.
$roleoption = $this->find('xpath', '//select[@name="roleid"]/option[contains(text(),"' . $this->escape($rolename) . '")]');
$roleoption = $this->find('xpath', '//select[@name="roleid"]/option[contains(.,"' . $this->escape($rolename) . '")]');
return array(
new Given('I select "' . $this->escape($roleoption->getText()) . '" from "' . get_string('advancedoverride', 'role') . '"'),

View File

@ -67,10 +67,10 @@ class behat_message extends behat_base {
}
$steps[] = new Given('I follow "' . get_string('messages', 'message') . '"');
$steps[] = new Given('I fill in "' . get_string('searchcombined', 'message') . '" with "' . $tofullname . '"');
$steps[] = new Given('I fill in "' . get_string('searchcombined', 'message') . '" with "' . $this->escape($tofullname) . '"');
$steps[] = new Given('I press "' . get_string('searchcombined', 'message') . '"');
$steps[] = new Given('I follow "' . get_string('sendmessageto', 'message', $tofullname) . '"');
$steps[] = new Given('I fill in "id_message" with "' . $messagecontent . '"');
$steps[] = new Given('I follow "' . $this->escape(get_string('sendmessageto', 'message', $tofullname)) . '"');
$steps[] = new Given('I fill in "id_message" with "' . $this->escape($messagecontent) . '"');
$steps[] = new Given('I press "' . get_string('sendmessage', 'message') . '"');
return $steps;

View File

@ -38,7 +38,7 @@ Feature: Set a certain number of discussions as a completion condition for a for
And I log out
And I log in as "student1"
And I follow "Course 1"
Then I hover "//li[contains(concat(' ', @class, ' '), ' modtype_forum ')]/descendant::img[@alt='Not completed: Test forum name']" "xpath_element"
Then I hover "//li[contains(concat(' ', normalize-space(@class), ' '), ' modtype_forum ')]/descendant::img[@alt='Not completed: Test forum name']" "xpath_element"
And I add a new discussion to "Test forum name" forum with:
| Subject | Post 1 subject |
| Message | Body 1 content |
@ -46,7 +46,7 @@ Feature: Set a certain number of discussions as a completion condition for a for
| Subject | Post 2 subject |
| Message | Body 2 content |
And I follow "Course 1"
And I hover "//li[contains(concat(' ', @class, ' '), ' modtype_forum ')]/descendant::img[contains(@alt, 'Completed: Test forum name')]" "xpath_element"
And I hover "//li[contains(concat(' ', normalize-space(@class), ' '), ' modtype_forum ')]/descendant::img[contains(@alt, 'Completed: Test forum name')]" "xpath_element"
And I log out
And I log in as "teacher1"
And I follow "Course 1"

View File

@ -66,7 +66,7 @@ class behat_mod_glossary extends behat_base {
new Given('I follow "' . get_string('categoryview', 'mod_glossary') . '"'),
new Given('I press "' . get_string('editcategories', 'mod_glossary') . '"'),
new Given('I press "' . get_string('add').' '.get_string('category', 'glossary') . '"'),
new Given('I fill in "name" with "' . $categoryname . '"'),
new Given('I fill in "name" with "' . $this->escape($categoryname) . '"'),
new Given('I press "' . get_string('savechanges') . '"'),
new Given('I press "' . get_string('back', 'mod_glossary') . '"')
);

View File

@ -34,7 +34,7 @@ Feature: A teacher can choose whether to provide a printer-friendly glossary ent
| Concept | Just a test concept |
| Definition | Concept definition |
Then "Printer-friendly version" "link" should exists
And "//*[contains(concat(' ', @class, ' '), ' printicon ')][contains(@href, 'print.php')]" "xpath_element" should exists
And "//*[contains(concat(' ', normalize-space(@class), ' '), ' printicon ')][contains(@href, 'print.php')]" "xpath_element" should exists
And I follow "Printer-friendly version"
And I should see "Just a test concept"
@ -51,4 +51,4 @@ Feature: A teacher can choose whether to provide a printer-friendly glossary ent
| Concept | Just a test concept |
| Definition | Concept definition |
Then "Printer-friendly version" "link" should not exists
And "//*[contains(concat(' ', @class, ' '), ' printicon ')][contains(@href, 'print.php')]" "xpath_element" should not exists
And "//*[contains(concat(' ', normalize-space(@class), ' '), ' printicon ')][contains(@href, 'print.php')]" "xpath_element" should not exists

View File

@ -40,7 +40,7 @@ Feature: In a lesson activity, students can navigate through a series of pages i
| id_jumpto_1 | Next page |
And I press "Save page"
And I follow "Expanded"
And I click on "Add a question page here" "link" in the "//div[contains(concat(' ', @class, ' '), ' addlinks ')][3]" "xpath_element"
And I click on "Add a question page here" "link" in the "//div[contains(concat(' ', normalize-space(@class), ' '), ' addlinks ')][3]" "xpath_element"
And I select "Numerical" from "Select a question type"
And I press "Add a question page"
And I fill the moodle form with:

View File

@ -51,13 +51,15 @@ class behat_question extends behat_base {
*/
public function i_add_a_question_filling_the_form_with($questiontypename, TableNode $questiondata) {
$questiontypexpath = "//span[@class='qtypename'][.='" . $questiontypename . "']" .
// Using xpath literal to avoid quotes problems.
$questiontypename = $this->getSession()->getSelectorsHandler()->xpathLiteral($questiontypename);
$questiontypexpath = "//span[@class='qtypename'][normalize-space(.)=$questiontypename]" .
"/ancestor::div[@class='qtypeoption']/descendant::input";
return array(
new Given('I follow "' . get_string('questionbank', 'question') . '"'),
new Given('I press "' . get_string('createnewquestion', 'question') . '"'),
new Given('I click on "' . $questiontypexpath . '" "xpath_element"'),
new Given('I click on "' . $this->escape($questiontypexpath) . '" "xpath_element"'),
new Given('I click on "Next" "button" in the "#qtypechoicecontainer" "css_element"'),
new Given('I fill the moodle form with:', $questiondata),
new Given('I press "' . get_string('savechanges') . '"')
@ -75,14 +77,18 @@ class behat_question extends behat_base {
*/
public function the_state_of_question_is_shown_as($questiondescription, $state) {
// Using xpath literal to avoid quotes problems.
$questiondescriptionliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($questiondescription);
$stateliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($state);
// Split in two checkings to give more feedback in case of exception.
$exception = new ElementNotFoundException($this->getSession(), 'Question "' . $questiondescription . '" ');
$questionxpath = "//div[contains(concat(' ', @class, ' '), ' qtext ')][contains(., '" . $questiondescription . "')]";
$questionxpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' qtext ')][contains(., $questiondescriptionliteral)]";
$this->find('xpath', $questionxpath, $exception);
$exception = new ExpectationException('Question "' . $questiondescription . '" state is not "' . $state . '"', $this->getSession());
$xpath = $questionxpath . "/ancestor::div[contains(concat(' ', @class, ' '), ' que ')]" .
"/descendant::div[@class='state'][contains(., '" . $state . "')]";
$xpath = $questionxpath . "/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' que ')]" .
"/descendant::div[@class='state'][contains(., $stateliteral)]";
$this->find('xpath', $xpath, $exception);
}

View File

@ -92,6 +92,8 @@ class behat_filepicker extends behat_files {
// Just in case there is any contents refresh in progress.
$this->wait_until_contents_are_updated($fieldnode);
$folderliteral = $this->getSession()->getSelectorsHandler()->xpathLiteral($foldername);
// We look both in the pathbar and in the contents.
try {
@ -99,8 +101,8 @@ class behat_filepicker extends behat_files {
$folder = $this->find(
'xpath',
"//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')]" .
"/descendant::div[contains(concat(' ', @class, ' '), ' fp-filename ')]" .
"[normalize-space(.)='" . $foldername . "']",
"/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-filename ')]" .
"[normalize-space(.)=$folderliteral]",
$exception,
$fieldnode
);
@ -110,7 +112,7 @@ class behat_filepicker extends behat_files {
$folder = $this->find(
'xpath',
"//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-path-folder-name ')]" .
"[normalize-space(.)='" . $foldername . "']",
"[normalize-space(.)=$folderliteral]",
$exception,
$fieldnode
);

View File

@ -24,7 +24,7 @@ Feature: A selected file can be cancelled
And I upload "lib/tests/fixtures/upload_users.csv" file to "Files" filepicker
And I click on "#fitem_id_files .fp-btn-add a" "css_element"
And I click on "Recent files" "link" in the ".fp-repo-area" "css_element"
And I click on "//a[contains(concat(' ', @class, ' '), ' fp-file ')][contains(., 'empty.txt')]" "xpath_element"
And I click on "//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')][normalize-space(.)='empty.txt']" "xpath_element"
And I click on ".yui3-panel-focused .fp-select .fp-select-cancel" "css_element"
And I click on ".yui3-panel-focused .file-picker button.yui3-button-close" "css_element"
And I press "Save and display"

View File

@ -65,10 +65,10 @@ class behat_repository_upload extends behat_files {
$noformexception = new ExpectationException('The upload file form is not ready', $this->getSession());
$this->find(
'xpath',
"//div[contains(concat(' ', @class, ' '), ' file-picker ')]" .
"[contains(concat(' ', @class, ' '), ' repository_upload ')]" .
"//div[contains(concat(' ', normalize-space(@class), ' '), ' file-picker ')]" .
"[contains(concat(' ', normalize-space(@class), ' '), ' repository_upload ')]" .
"/descendant::div[@class='fp-content']" .
"/descendant::div[contains(concat(' ', @class, ' '), ' fp-upload-form ')]" .
"/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-upload-form ')]" .
"/descendant::form",
$noformexception
);