diff --git a/admin/tool/behat/tests/behat/data_generators.feature b/admin/tool/behat/tests/behat/data_generators.feature index 558c848b264..1480ff990f9 100644 --- a/admin/tool/behat/tests/behat/data_generators.feature +++ b/admin/tool/behat/tests/behat/data_generators.feature @@ -90,10 +90,10 @@ Feature: Set up contextual data for tests And I expand "Users" node And I follow "Permissions" And I select "Student (1)" from "Advanced role override" - Then the "mod/forum:editanypost" field should match "1" value + Then "mod/forum:editanypost" capability has "Allow" permission And I press "Cancel" And I select "Teacher (1)" from "Advanced role override" - And the "mod/forum:replynews" field should match "-1" value + And "mod/forum:replynews" capability has "Prevent" permission And I press "Cancel" Scenario: Add course enrolments diff --git a/admin/tool/behat/tests/behat/edit_permissions.feature b/admin/tool/behat/tests/behat/edit_permissions.feature index b16b0e0c8a0..a7935b9cbae 100644 --- a/admin/tool/behat/tests/behat/edit_permissions.feature +++ b/admin/tool/behat/tests/behat/edit_permissions.feature @@ -25,10 +25,10 @@ Feature: Edit capabilities | moodle/grade:managesharedforms | Prevent | | moodle/course:request | Prohibit | When I follow "Edit Teacher role" - Then the "block/mnet_hosts:myaddinstance" field should match "1" value - And the "moodle/community:add" field should match "0" value - And the "moodle/grade:managesharedforms" field should match "-1" value - And the "moodle/course:request" field should match "-1000" value + Then "block/mnet_hosts:myaddinstance" capability has "Allow" permission + And "moodle/community:add" capability has "Not set" permission + And "moodle/grade:managesharedforms" capability has "Prevent" permission + And "moodle/course:request" capability has "Prohibit" permission @javascript Scenario: Course capabilities overrides @@ -41,9 +41,9 @@ Feature: Edit capabilities | mod/forum:editanypost | Prevent | | mod/forum:addquestion | Allow | When I select "Student (3)" from "Advanced role override" - Then the "mod/forum:deleteanypost" field should match "-1000" value - And the "mod/forum:editanypost" field should match "-1" value - And the "mod/forum:addquestion" field should match "1" value + Then "mod/forum:deleteanypost" capability has "Prohibit" permission + And "mod/forum:editanypost" capability has "Prevent" permission + And "mod/forum:addquestion" capability has "Allow" permission @javascript Scenario: Module capabilities overrides @@ -60,6 +60,6 @@ Feature: Edit capabilities | mod/forum:editanypost | Prevent | | mod/forum:addquestion | Allow | When I select "Student (3)" from "Advanced role override" - Then the "mod/forum:deleteanypost" field should match "-1000" value - And the "mod/forum:editanypost" field should match "-1" value - And the "mod/forum:addquestion" field should match "1" value + Then "mod/forum:deleteanypost" capability has "Prohibit" permission + And "mod/forum:editanypost" capability has "Prevent" permission + And "mod/forum:addquestion" capability has "Allow" permission diff --git a/admin/tool/behat/tests/behat/get_and_set_fields.feature b/admin/tool/behat/tests/behat/get_and_set_fields.feature new file mode 100644 index 00000000000..45eebe845b0 --- /dev/null +++ b/admin/tool/behat/tests/behat/get_and_set_fields.feature @@ -0,0 +1,173 @@ +@tool_behat +Feature: Verify that all form fields values can be get and set + In order to use behat steps definitions + As a test writer + I need to verify it all works in real moodle forms + + Background: + Given the following "courses" exists: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And the following "users" exists: + | username | email | firstname | lastname | + | student1 | s1@asd.com | Student | 1 | + | student2 | s2@asd.com | Student | 2 | + | student3 | s3@asd.com | Student | 3 | + And the following "course enrolments" exists: + | user | course | role | + | student1 | C1 | student | + | student2 | C1 | student | + | student3 | C1 | student | + | admin | C1 | editingteacher | + And the following "groups" exists: + | name | description | course | idnumber | + | Group 1 | G1 description | C1 | G1 | + | Group 2 | G1 description | C1 | G2 | + And the following "group members" exists: + | user | group | + | student1 | G1 | + | student2 | G1 | + | student2 | G2 | + | student3 | G2 | + And the following "activities" exists: + | activity | course | idnumber | name | intro | firstpagetitle | wikimode | visible | + | wiki | C1 | wiki1 | Test this one | Test this one | Test this one | collaborative | 0 | + And I log in as "admin" + And I expand "Site administration" node + And I expand "Appearance" node + And I follow "Manage tags" + # Select (multi-select) - We will check "I fill in...". + And I fill in "otagsadd" with "OT1, OT2, OT3, OT4, OT5" + And I press "Add official tags" + And I am on homepage + And I follow "Course 1" + And I turn editing mode on + And I follow "Test this one" + And I press "Create page" + # Select (multi-select) - Checking "the select box should contain". + And the "tags[officialtags][]" select box should contain "OT1" + And the "tags[officialtags][]" select box should contain "OT2" + And the "tags[officialtags][]" select box should contain "OT3" + And the "tags[officialtags][]" select box should contain "OT4" + And the "tags[officialtags][]" select box should contain "OT5" + And the "tags[officialtags][]" select box should contain "OT1, OT2, OT3, OT4, OT5" + And the "tags[officialtags][]" select box should contain "OT5, OT4, OT3, OT2, OT1" + And the "tags[officialtags][]" select box should not contain "OT6" + And the "tags[officialtags][]" select box should not contain "OT7" + And the "tags[officialtags][]" select box should not contain "OT6, OT7" + # Text (textarea & editor) & Select (multi-select) - Checking "I fill the moodle form with". + When I fill the moodle form with: + | HTML format | Student page contents to be tagged | + | tags[officialtags][] | OT1, OT3, OT5 | + And I press "Save" + Then I should see "Student page contents to be tagged" in the "region-main" "region" + And I should see "OT1" in the ".wiki-tags" "css_element" + And I should see "OT3" in the ".wiki-tags" "css_element" + And I should see "OT5" in the ".wiki-tags" "css_element" + And I should not see "OT2" in the ".wiki-tags" "css_element" + And I should not see "OT4" in the ".wiki-tags" "css_element" + And I follow "Edit" + # Select (multi-select) - Checking "I select from". + And I select "OT2, OT4" from "tags[officialtags][]" + And I press "Save" + And I should see "OT2" in the ".wiki-tags" "css_element" + And I should see "OT4" in the ".wiki-tags" "css_element" + And I should not see "OT1" in the ".wiki-tags" "css_element" + And I should not see "OT3" in the ".wiki-tags" "css_element" + And I should not see "OT5" in the ".wiki-tags" "css_element" + And I follow "Edit" + # Select (multi-select) - Checking "the field should match" and "the field does not match value". + And the "tags[officialtags][]" field should match "OT2, OT4" value + And the field "tags[officialtags][]" does not match value "OT4" + And the field "tags[officialtags][]" does not match value "OT2" + And the field "tags[officialtags][]" does not match value "OT1, OT3, OT5" + And I press "Cancel" + And I follow "Edit settings" + And I expand all fieldsets + # Checkbox - Checking "I check". + And I check "Display description on course page" + # Checkbox - Checking "I fill the moodle form with:". + And I fill the moodle form with: + | Force format | 1 | + # Checkbox - Checking "the field should match" and "the field does not match value". + And the "Display description on course page" field should match "1" value + And the field "Display description on course page" does not match value "" + And I press "Save and return to course" + And I should see "Test this one" + And I follow "Test this one" + And I follow "Edit settings" + # Checkbox - Checking "the field should match" and "the following fields match these values". + And the following fields match these values: + | Display description on course page | 1 | + | Default format | HTML | + | Wiki name | Test this one | + And the "Force format" field should match "1" value + # Select (simple) - Checking "I fill the moodle form with:". + And I fill the moodle form with: + | Default format | NWiki | + | Display description on course page | | + # Checkbox - Checking "I uncheck". + And I uncheck "Force format" + # Select (simple) - Checking "I select from". + And I select "Separate groups" from "Group mode" + And I press "Save and display" + And I follow "Edit settings" + And the following fields match these values: + | Default format | NWiki | + | Group mode | Separate groups | + | Display description on course page | | + | Force format | | + # All fields - Checking "the following fields do not match these values". + And the following fields do not match these values: + | Wiki name | Test this one baby | + | Default format | HTML | + | Force format | 1 | + And I press "Cancel" + And I follow "Course 1" + # Radio - Checking "I check" and "the field should match". + And I add a "Choice" to section "1" and I fill the form with: + | Choice name | Test choice name | + | Description | Test choice description | + | Allow choice to be updated | Yes | + | Option 1 | one | + | Option 2 | two | + | Option 3 | three | + And I follow "Test choice name" + And I check "one" + And I press "Save my choice" + And the "one" field should match "1" value + And the "two" field should match "" value + + Scenario: with JS disabled all form fields getters and setters works as expected + + @javascript + Scenario: with JS enabled all form fields getters and setters works as expected + Then I follow "Course 1" + And I expand "Users" node + And I follow "Groups" + # Select (multi-select & AJAX) - Checking "I select from" and "select box should contain". + And I select "Group 2" from "groups" + And the "members" select box should contain "Student 2" + And the "members" select box should contain "Student 3" + And the "members" select box should not contain "Student 1" + And I select "Group 1" from "groups" + And the "members" select box should contain "Student 1" + And the "members" select box should contain "Student 2" + And the "members" select box should not contain "Student 3" + # Checkbox (AJAX) - Checking "I check" and "I fill the moodle form with". + And I follow "Course 1" + And I add a "Lesson" to section "1" + And I fill the moodle form with: + | Name | Test lesson | + | available[enabled] | 1 | + And I check "deadline[enabled]" + # Checkbox (AJAX) - Checking "field should match" before saving. + And the "available[enabled]" field should match "1" value + And the "available[day]" "field" should be enabled + And the "deadline[enabled]" field should match "1" value + And I press "Save and display" + And I follow "Edit settings" + And the "available[enabled]" field should match "1" value + And the "available[day]" "field" should be enabled + And the "deadline[enabled]" field should match "1" value + And I press "Cancel" diff --git a/lib/behat/behat_field_manager.php b/lib/behat/behat_field_manager.php index b83b18e2b90..7454da8890c 100644 --- a/lib/behat/behat_field_manager.php +++ b/lib/behat/behat_field_manager.php @@ -80,9 +80,16 @@ class behat_field_manager { global $CFG; + // If the field is not part of a moodleform, we should still try to find out + // which field type are we dealing with. + if ($type == 'field' && + $guessedtype = self::guess_field_type($fieldnode, $session)) { + $type = $guessedtype; + } + $classname = 'behat_form_' . $type; - // Fallsback on the default form field if nothing specific exists. + // Fallsback on the type guesser if nothing specific exists. $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php'; if (!file_exists($classpath)) { $classname = 'behat_form_field'; @@ -94,6 +101,58 @@ class behat_field_manager { return new $classname($session, $fieldnode); } + /** + * Guesses a basic field type and returns it. + * + * This method is intended to detect HTML form fields when no + * moodleform-specific elements have been detected. + * + * @param NodeElement $fieldnode + * @param Session $session + * @return string|bool The field type or false. + */ + public static function guess_field_type(NodeElement $fieldnode, Session $session) { + + // Textareas are considered text based elements. + $tagname = strtolower($fieldnode->getTagName()); + if ($tagname == 'textarea') { + + // If there is an iframe with $id + _ifr there a TinyMCE editor loaded. + $xpath = '//iframe[@id="' . $fieldnode->getAttribute('id') . '_ifr"]'; + if ($session->getPage()->find('xpath', $xpath)) { + return 'editor'; + } + return 'textarea'; + + } else if ($tagname == 'input') { + $type = $fieldnode->getAttribute('type'); + switch ($type) { + case 'text': + case 'password': + case 'email': + case 'file': + return 'text'; + case 'checkbox': + return 'checkbox'; + break; + case 'radio': + return 'radio'; + break; + default: + // Here we return false because all text-based + // fields should be included in the first switch case. + return false; + } + + } else if ($tagname == 'select') { + // Select tag. + return 'select'; + } + + // We can not provide a closer field type. + return false; + } + /** * Detects when the field is a moodleform field type. * @@ -108,7 +167,10 @@ class behat_field_manager { protected static function is_moodleform_field(NodeElement $fieldnode) { // We already waited when getting the NodeElement and we don't want an exception if it's not part of a moodleform. - $parentformfound = $fieldnode->find('xpath', "/ancestor::form[contains(concat(' ', normalize-space(@class), ' '), ' mform ')]/fieldset"); + $parentformfound = $fieldnode->find('xpath', + "/ancestor::fieldset" . + "/ancestor::form[contains(concat(' ', normalize-space(@class), ' '), ' mform ')]" + ); return ($parentformfound != false); } diff --git a/lib/behat/form_field/behat_form_checkbox.php b/lib/behat/form_field/behat_form_checkbox.php index fb220d1cf35..69a9cbc8b34 100644 --- a/lib/behat/form_field/behat_form_checkbox.php +++ b/lib/behat/form_field/behat_form_checkbox.php @@ -47,18 +47,37 @@ class behat_form_checkbox extends behat_form_field { */ public function set_value($value) { - if (!$this->running_javascript()) { - $this->field->check(); - return; - } - if (!empty($value) && !$this->field->isChecked()) { + + if (!$this->running_javascript()) { + $this->field->check(); + return; + } + // Check it if it should be checked and it is not. $this->field->click(); + // Trigger the onchange event as triggered when 'checking' the checkbox. + $this->session->getDriver()->triggerSynScript( + $this->field->getXPath(), + "Syn.trigger('change', {}, {{ELEMENT}})" + ); + } else if (empty($value) && $this->field->isChecked()) { + + if (!$this->running_javascript()) { + $this->field->uncheck(); + return; + } + // Uncheck if it is checked and shouldn't. $this->field->click(); + + // Trigger the onchange event as triggered when 'checking' the checkbox. + $this->session->getDriver()->triggerSynScript( + $this->field->getXPath(), + "Syn.trigger('change', {}, {{ELEMENT}})" + ); } } @@ -70,4 +89,25 @@ class behat_form_checkbox extends behat_form_field { public function get_value() { return $this->field->isChecked(); } + + /** + * Is it enabled? + * + * @param string $expectedvalue Anything !empty() is considered checked. + * @return bool + */ + public function matches($expectedvalue = false) { + + $ischecked = $this->field->isChecked(); + + // Any non-empty value provided means that it should be checked. + if (!empty($expectedvalue) && $ischecked) { + return true; + } else if (empty($expectedvalue) && !$ischecked) { + return true; + } + + return false; + } + } diff --git a/lib/behat/form_field/behat_form_date_selector.php b/lib/behat/form_field/behat_form_date_selector.php index 2d86a768cf6..71c477c9596 100644 --- a/lib/behat/form_field/behat_form_date_selector.php +++ b/lib/behat/form_field/behat_form_date_selector.php @@ -25,15 +25,11 @@ // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. -require_once(__DIR__ . '/behat_form_select.php'); +require_once(__DIR__ . '/behat_form_group.php'); /** * Date form field. * - * Simple extension of behat_form_select to allow date-type - * select fields to be filled like select elements instead of - * text elements. - * * This class will be refactored in case we are interested in * creating more complex formats to fill date and date-time fields. * @@ -42,4 +38,5 @@ require_once(__DIR__ . '/behat_form_select.php'); * @copyright 2013 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class behat_form_date_selector extends behat_form_select {} +class behat_form_date_selector extends behat_form_group { +} diff --git a/lib/behat/form_field/behat_form_date_time_selector.php b/lib/behat/form_field/behat_form_date_time_selector.php index 22c4befa610..19181cf6b40 100644 --- a/lib/behat/form_field/behat_form_date_time_selector.php +++ b/lib/behat/form_field/behat_form_date_time_selector.php @@ -25,21 +25,18 @@ // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. -require_once(__DIR__ . '/behat_form_select.php'); +require_once(__DIR__ . '/behat_form_date_selector.php'); /** * Date time form field. * - * Simple extension of behat_form_select to allow datetime-type - * select fields to be filled like select elements instead of - * text elements. - * * This class will be refactored in case we are interested in - * creating more complex formats to fill date and date-time fields. + * creating more complex formats to fill date-time fields. * * @package core_form * @category test * @copyright 2013 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class behat_form_date_time_selector extends behat_form_select {} +class behat_form_date_time_selector extends behat_form_date_selector { +} diff --git a/lib/behat/form_field/behat_form_editor.php b/lib/behat/form_field/behat_form_editor.php index 4f100ac1f10..b97976abf2e 100644 --- a/lib/behat/form_field/behat_form_editor.php +++ b/lib/behat/form_field/behat_form_editor.php @@ -27,7 +27,7 @@ use Behat\Mink\Element\NodeElement as NodeElement; -require_once(__DIR__ . '/behat_form_field.php'); +require_once(__DIR__ . '/behat_form_textarea.php'); /** * Moodle editor field. @@ -38,7 +38,7 @@ require_once(__DIR__ . '/behat_form_field.php'); * @copyright 2012 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class behat_form_editor extends behat_form_field { +class behat_form_editor extends behat_form_textarea { /** * Sets the value to a field. diff --git a/lib/behat/form_field/behat_form_field.php b/lib/behat/form_field/behat_form_field.php index 284befd3be5..4d3f1b497fa 100644 --- a/lib/behat/form_field/behat_form_field.php +++ b/lib/behat/form_field/behat_form_field.php @@ -69,14 +69,11 @@ class behat_form_field { * @return void */ public function set_value($value) { - - // If we are not dealing with a text-based tag try to find the most appropiate - // behat_form_* class to deal with it. - if ($instance = $this->guess_type()) { - $instance->set_value($value); - } else { - $this->field->setValue($value); - } + // We delegate to the best guess, if we arrived here + // using the generic behat_form_field is because we are + // dealing with a fgroup element. + $instance = $this->guess_type(); + return $instance->set_value($value); } /** @@ -85,14 +82,28 @@ class behat_form_field { * @return string */ public function get_value() { + // We delegate to the best guess, if we arrived here + // using the generic behat_form_field is because we are + // dealing with a fgroup element. + $instance = $this->guess_type(); + return $instance->get_value(); + } - // If we are not dealing with a text-based tag try to find the most appropiate - // behat_form_* class to deal with it. - if ($instance = $this->guess_type()) { - return $instance->get_value(); - } else { - return $this->field->getValue(); - } + /** + * Generic match implementation + * + * Will work well with text-based fields, extension required + * for most of the other cases. + * + * @param string $expectedvalue + * @return bool The provided value matches the field value? + */ + public function matches($expectedvalue) { + // We delegate to the best guess, if we arrived here + // using the generic behat_form_field is because we are + // dealing with a fgroup element. + $instance = $this->guess_type(); + return $instance->matches($expectedvalue); } /** @@ -106,53 +117,17 @@ class behat_form_field { * moodle form elements we will need to refactor this simple HTML elements * guess method. * - * @return mixed False if no need for an special behat_form_*, otherwise the behat_form_* + * @return behat_form_field */ private function guess_type() { global $CFG; - // Textareas are considered text based elements. - $tagname = strtolower($this->field->getTagName()); - if ($tagname == 'textarea') { - - if (!$this->running_javascript()) { - return false; - } - - // If there is an iframe with $id + _ifr there a TinyMCE editor loaded. - $xpath = '//iframe[@id="' . $this->field->getAttribute('id') . '_ifr"]'; - if (!$this->session->getPage()->find('xpath', $xpath)) { - - // Generic one if it is a normal textarea. - return false; - } - - $classname = 'behat_form_editor'; - - } else if ($tagname == 'input') { - $type = $this->field->getAttribute('type'); - switch ($type) { - case 'text': - return false; - case 'checkbox': - $classname = 'behat_form_checkbox'; - break; - case 'radio': - $classname = 'behat_form_radio'; - break; - default: - return false; - } - - } else if ($tagname == 'select') { - // Select tag. - $classname = 'behat_form_select'; - - } else { - // We can not provide a closer field type. - return false; + // We default to the text-based field if nothing was detected. + if (!$type = behat_field_manager::guess_field_type($this->field, $this->session)) { + $type = 'text'; } + $classname = 'behat_form_' . $type; $classpath = $CFG->dirroot . '/lib/behat/form_field/' . $classname . '.php'; require_once($classpath); return new $classname($this->session, $this->field); @@ -181,10 +156,19 @@ class behat_form_field { throw new coding_exception('You can only get an internal ID using the selenium driver.'); } - return $this->session-> - getDriver()-> - getWebDriverSession()-> - element('xpath', $this->field->getXPath())-> - getID(); + return $this->session->getDriver()->getWebDriverSession()->element('xpath', $this->field->getXPath())->getID(); + } + + /** + * Checks if the provided text matches the field value. + * + * @param string $expectedvalue + * @return bool + */ + protected function text_matches($expectedvalue) { + if (trim($expectedvalue) != trim($this->get_value())) { + return false; + } + return true; } } diff --git a/lib/behat/form_field/behat_form_group.php b/lib/behat/form_field/behat_form_group.php new file mode 100644 index 00000000000..3e8b2e58fd8 --- /dev/null +++ b/lib/behat/form_field/behat_form_group.php @@ -0,0 +1,43 @@ +. + +/** + * Generic group field class. + * + * @package core_form + * @category test + * @copyright 2014 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. + +require_once(__DIR__ . '/behat_form_field.php'); + +/** + * Class to re-guess the field type as grouped fields can have different field types. + * + * When filling fields inside a fgroup field element we don't know what kind + * of field are we dealing with, so we should re-guess it as behat_form_field + * does. + * + * @package core_form + * @category test + * @copyright 2014 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_form_group extends behat_form_field { +} diff --git a/lib/behat/form_field/behat_form_modvisible.php b/lib/behat/form_field/behat_form_modvisible.php index 04e65ffa634..74752b91d17 100644 --- a/lib/behat/form_field/behat_form_modvisible.php +++ b/lib/behat/form_field/behat_form_modvisible.php @@ -38,4 +38,5 @@ require_once(__DIR__ . '/behat_form_select.php'); * @copyright 2013 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class behat_form_modvisible extends behat_form_select {} +class behat_form_modvisible extends behat_form_select { +} diff --git a/lib/behat/form_field/behat_form_radio.php b/lib/behat/form_field/behat_form_radio.php index 1ca020ebec3..8304133dae6 100644 --- a/lib/behat/form_field/behat_form_radio.php +++ b/lib/behat/form_field/behat_form_radio.php @@ -31,7 +31,8 @@ require_once(__DIR__ . '/behat_form_checkbox.php'); * Radio input form field. * * Extends behat_form_checkbox as the set_value() behaviour - * is the same. + * is the same and it behaves closer to a checkbox than to + * a text field. * * This form field type can be added to forms as any other * moodle form element, but it does not make sense without @@ -51,9 +52,43 @@ class behat_form_radio extends behat_form_checkbox { /** * Returns the radio input value attribute. * + * Here we can not extend behat_form_checkbox because + * isChecked() does internally a (bool)getValue() and + * it is not good for radio buttons. + * * @return string The value attribute */ public function get_value() { - return $this->field->getValue(); + return (bool)$this->field->getAttribute('checked'); + } + + /** + * Sets the value of a radio + * + * Partially overwriting behat_form_checkbox + * implementation as when JS is disabled we + * can not check() and we should use setValue() + * + * @param string $value + * @return void + */ + public function set_value($value) { + + if ($this->running_javascript()) { + parent::set_value($value); + } 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); } } diff --git a/lib/behat/form_field/behat_form_select.php b/lib/behat/form_field/behat_form_select.php index 208b95a6ac7..ac63c01b337 100644 --- a/lib/behat/form_field/behat_form_select.php +++ b/lib/behat/form_field/behat_form_select.php @@ -38,13 +38,13 @@ require_once(__DIR__ . '/behat_form_field.php'); class behat_form_select extends behat_form_field { /** - * Sets the value of a single select. + * Sets the value(s) of a select element. * * Seems an easy select, but there are lots of combinations * of browsers and operative systems and each one manages the - * autosubmits and the multiple option selects in a diferent way. + * autosubmits and the multiple option selects in a different way. * - * @param string $value + * @param string $value plain value or comma separated values if multiple. Commas in values escaped with backslash. * @return void */ public function set_value($value) { @@ -61,8 +61,26 @@ class behat_form_select extends behat_form_field { $currentelementid = $this->get_internal_field_id(); } - // Here we select an option. - $this->field->selectOption($value); + // Is the select multiple? + $multiple = $this->field->hasAttribute('multiple'); + + // By default, assume the passed value is a non-multiple option. + $options = array(trim($value)); + + // 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('/(?field->selectOption(trim($option), $afterfirstoption); + $afterfirstoption = true; + } + } else { + // 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()) { @@ -75,6 +93,7 @@ class behat_form_select extends behat_form_field { // 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; @@ -87,12 +106,9 @@ class behat_form_select extends behat_form_field { return; } - // We also check that the option is still there. We neither wait. - $valueliteral = $this->session->getSelectorsHandler()->xpathLiteral($value); - $optionxpath = $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]"; - if (!$this->session->getDriver()->find($optionxpath)) { - return; - } + // 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 { @@ -105,17 +121,16 @@ class behat_form_select extends behat_form_field { return; } - // 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); - // 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. - $optionnodes = $this->session->getDriver()->find($optionxpath); - if ($optionnodes) { + 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 { @@ -138,21 +153,29 @@ class behat_form_select extends behat_form_field { return; } - // We ensure that the option is still there. - if (!$this->session->getDriver()->find($optionxpath)) { - 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; + } } // Wait for all the possible AJAX requests that have been - // already triggered by selectOption() to be finished. + // 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 as some drivers (chrome that I know) are moving + + // Repeating the select(s) as some drivers (chrome that I know) are moving // to another option after the general select field click above. - $this->field->selectOption($value); + $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; @@ -161,12 +184,165 @@ class behat_form_select extends behat_form_field { } /** - * Returns the text of the current value. + * Returns the text of the currently selected options. * - * @return string + * @return string Comma separated if multiple options are selected. Commas in option texts escaped with backslash. */ public function get_value() { - $selectedoption = $this->field->find('xpath', '//option[@selected="selected"]'); - return $selectedoption->getText(); + return $this->get_selected_options(); + } + + /** + * Returns whether the provided argument matches the current value. + * + * @param mixed $expectedvalue + * @return bool + */ + public function matches($expectedvalue) { + + $multiple = $this->field->hasAttribute('multiple'); + + // Same implementation as the parent if it is a single select. + if (!$multiple) { + $cleanexpectedvalue = trim($expectedvalue); + $selectedtext = trim($this->get_selected_options()); + $selectedvalue = trim($this->get_selected_options(false)); + if ($cleanexpectedvalue != $selectedvalue && $cleanexpectedvalue != $selectedtext) { + return false; + } + return true; + } + + // 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); + + // Get currently selected option's texts. + $texts = $this->get_selected_options(true); + $selectedoptiontexts = $this->get_unescaped_options($texts); + + // Get currently selected option's values. + $values = $this->get_selected_options(false); + $selectedoptionvalues = $this->get_unescaped_options($values); + + // Precheck to speed things up. + if (count($expectedoptions) !== count($selectedoptiontexts) || + count($expectedoptions) !== count($selectedoptionvalues)) { + return false; + } + + // We check against string-ordered lists of options. + if ($expectedoptions != $selectedoptiontexts && + $expectedoptions != $selectedoptionvalues) { + return false; + } + + return true; + } + + /** + * Cleans the list of options and returns it as a string separating options with |||. + * + * @param string $value The string containing the escaped options. + * @return string The options + */ + protected function get_unescaped_options($value) { + + // Can be multiple comma separated, with valuable commas escaped with backslash. + $optionsarray = array_map( + 'trim', + preg_replace('/\\\,/', ',', + preg_split('/(?field->hasAttribute('multiple'); + + $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); + } + + // 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}()); + } + } + } + } + + return implode(', ', $selectedoptions); + } + + /** + * Returns the opton XPath based on it's select xpath. + * + * @param string $option + * @param string $selectxpath + * @return string xpath + */ + protected function get_option_xpath($option, $selectxpath) { + $valueliteral = $this->session->getSelectorsHandler()->xpathLiteral(trim($option)); + return $selectxpath . "/descendant::option[(./@value=$valueliteral or normalize-space(.)=$valueliteral)]"; } } diff --git a/lib/behat/form_field/behat_form_selectyesno.php b/lib/behat/form_field/behat_form_selectyesno.php index 3759a17a21e..73f8bef779e 100644 --- a/lib/behat/form_field/behat_form_selectyesno.php +++ b/lib/behat/form_field/behat_form_selectyesno.php @@ -38,4 +38,5 @@ require_once(__DIR__ . '/behat_form_select.php'); * @copyright 2013 David Monllaó * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class behat_form_selectyesno extends behat_form_select {} +class behat_form_selectyesno extends behat_form_select { +} diff --git a/lib/behat/form_field/behat_form_text.php b/lib/behat/form_field/behat_form_text.php new file mode 100644 index 00000000000..dffdf86ffa2 --- /dev/null +++ b/lib/behat/form_field/behat_form_text.php @@ -0,0 +1,69 @@ +. + +/** + * Text field class. + * + * @package core_form + * @category test + * @copyright 2014 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. + +require_once(__DIR__ . '/behat_form_field.php'); + +/** + * Class for test-based fields. + * + * @package core_form + * @category test + * @copyright 2014 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_form_text extends behat_form_field { + + /** + * Sets the value to a field. + * + * @param string $value + * @return void + */ + public function set_value($value) { + $this->field->setValue($value); + } + + /** + * Returns the current value of the element. + * + * @return string + */ + public function get_value() { + return $this->field->getValue(); + } + + /** + * Matches the provided value against the current field value. + * + * @param string $expectedvalue + * @return bool The provided value matches the field value? + */ + public function matches($expectedvalue) { + return $this->text_matches($expectedvalue); + } + +} diff --git a/lib/behat/form_field/behat_form_textarea.php b/lib/behat/form_field/behat_form_textarea.php new file mode 100644 index 00000000000..c1730c287f9 --- /dev/null +++ b/lib/behat/form_field/behat_form_textarea.php @@ -0,0 +1,39 @@ +. + +/** + * Textarea field class. + * + * @package core_form + * @category test + * @copyright 2014 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. + +require_once(__DIR__ . '/behat_form_text.php'); + +/** + * Textarea field class. + * + * @package core_form + * @category test + * @copyright 2014 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_form_textarea extends behat_form_text { +} diff --git a/lib/tests/behat/behat_data_generators.php b/lib/tests/behat/behat_data_generators.php index 0a5b39007a7..18dfb22bf57 100644 --- a/lib/tests/behat/behat_data_generators.php +++ b/lib/tests/behat/behat_data_generators.php @@ -299,13 +299,13 @@ class behat_data_generators extends behat_base { $context = $this->get_context($data['contextlevel'], $data['reference']); switch ($data['permission']) { - case self::cap_allow: + case get_string('allow', 'role'): $permission = CAP_ALLOW; break; - case self::cap_prevent: + case get_string('prevent', 'role'): $permission = CAP_PREVENT; break; - case self::cap_prohibit: + case get_string('prohibit', 'role'): $permission = CAP_PROHIBIT; break; default: diff --git a/lib/tests/behat/behat_forms.php b/lib/tests/behat/behat_forms.php index f3fdf3757a5..fe7ac60dbe5 100644 --- a/lib/tests/behat/behat_forms.php +++ b/lib/tests/behat/behat_forms.php @@ -60,6 +60,29 @@ class behat_forms extends behat_base { $buttonnode->press(); } + /** + * Fills a form with field/value data. More info in http://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps. + * + * Backport of Moodle 2.7 method to help backporting + * features to 2.6. + * + * @Given /^I set the following fields to these values:$/ + * @throws ElementNotFoundException Thrown by behat_base::find + * @param TableNode $data + */ + public function i_set_the_following_fields_to_these_values(TableNode $data) { + + // Expand all fields in case we have. + $this->expand_all_fields(); + + $datahash = $data->getRowsHash(); + + // The action depends on the field type. + foreach ($datahash as $locator => $value) { + $this->set_field_value($locator, $value); + } + } + /** * Fills a moodle form with field/value data. * @@ -154,7 +177,23 @@ class behat_forms extends behat_base { } /** - * Fills in form field with specified id|name|label|value. + * Sets the specified value to the field. + * + * Backport of Moodle 2.7 method to help backporting + * features to 2.6. + * + * @Given /^I set the field "(?P(?:[^"]|\\")*)" to "(?P(?:[^"]|\\")*)"$/ + * @throws ElementNotFoundException Thrown by behat_base::find + * @param string $field + * @param string $value + * @return void + */ + public function i_set_the_field_to($field, $value) { + $this->set_field_value($field, $value); + } + + /** + * Fills in form text field with specified id|name|label|value. It works with text-based fields. * * @When /^I fill in "(?P(?:[^"]|\\")*)" with "(?P(?:[^"]|\\")*)"$/ * @throws ElementNotFoundException Thrown by behat_base::find @@ -162,9 +201,7 @@ class behat_forms extends behat_base { * @param string $value */ public function fill_field($field, $value) { - - $fieldnode = $this->find_field($field); - $fieldnode->setValue($value); + $this->set_field_value($field, $value); } /** @@ -176,13 +213,7 @@ class behat_forms extends behat_base { * @param string $select */ public function select_option($option, $select) { - - $selectnode = $this->find_field($select); - - // We delegate to behat_form_field class, it will - // guess the type properly as it is a select tag. - $selectformfield = behat_field_manager::get_form_field($selectnode, $this->getSession()); - $selectformfield->set_value($option); + $this->set_field_value($select, $option); } /** @@ -193,14 +224,7 @@ class behat_forms extends behat_base { * @param string $radio The radio button id, name or label value */ public function select_radio($radio) { - - $radionode = $this->find_radio($radio); - $radionode->check(); - - // Adding a click as Selenium requires it to fire some JS events. - if ($this->running_javascript()) { - $radionode->click(); - } + $this->set_field_value($radio, 1); } /** @@ -211,11 +235,7 @@ class behat_forms extends behat_base { * @param string $option */ public function check_option($option) { - - // We don't delegate to behat_form_checkbox as the - // step is explicitly saying I check. - $checkboxnode = $this->find_field($option); - $checkboxnode->check(); + $this->set_field_value($option, 1); } /** @@ -226,15 +246,11 @@ class behat_forms extends behat_base { * @param string $option */ public function uncheck_option($option) { - - // We don't delegate to behat_form_checkbox as the - // step is explicitly saying I uncheck. - $checkboxnode = $this->find_field($option); - $checkboxnode->uncheck(); + $this->set_field_value($option, ''); } /** - * Checks that the form element field have the specified value. + * Checks that the field matches the specified value. When using multi-select fields use commas to separate selected options. * * @Then /^the "(?P(?:[^"]|\\")*)" field should match "(?P(?:[^"]|\\")*)" value$/ * @throws ExpectationException @@ -248,10 +264,10 @@ class behat_forms extends behat_base { // Get the field. $field = behat_field_manager::get_form_field($fieldnode, $this->getSession()); - $fieldvalue = $field->get_value(); // Checks if the provided value matches the current field value. - if (trim($value) != trim($fieldvalue)) { + if (!$field->matches($value)) { + $fieldvalue = $field->get_value(); throw new ExpectationException( 'The \'' . $locator . '\' value is \'' . $fieldvalue . '\', \'' . $value . '\' expected' , $this->getSession() @@ -259,26 +275,120 @@ class behat_forms extends behat_base { } } + /** + * Checks, the field matches the value. More info in http://docs.moodle.org/dev/Acceptance_testing#Providing_values_to_steps. + * + * Backport of Moodle 2.7 method to help backporting + * features to 2.6. + * + * @Then /^the field "(?P(?:[^"]|\\")*)" matches value "(?P(?:[^"]|\\")*)"$/ + * @throws ElementNotFoundException Thrown by behat_base::find + * @param string $field + * @param string $value + * @return void + */ + public function the_field_matches_value($field, $value) { + + $fieldnode = $this->find_field($field); + + // Get the field. + $formfield = behat_field_manager::get_form_field($fieldnode, $this->getSession()); + + // Checks if the provided value matches the current field value. + if (!$formfield->matches($value)) { + $fieldvalue = $formfield->get_value(); + throw new ExpectationException( + 'The \'' . $field . '\' value is \'' . $fieldvalue . '\', \'' . $value . '\' expected' , + $this->getSession() + ); + } + } + + /** + * Checks that the form element field does not match the specified value. + * + * @Then /^the field "(?P(?:[^"]|\\")*)" does not match value "(?P(?:[^"]|\\")*)"$/ + * @throws ExpectationException + * @throws ElementNotFoundException Thrown by behat_base::find + * @param string $field + * @param string $value + * @return void + */ + public function the_field_does_not_match_value($field, $value) { + + $fieldnode = $this->find_field($field); + + // Get the field. + $formfield = behat_field_manager::get_form_field($fieldnode, $this->getSession()); + + // Checks if the provided value matches the current field value. + if ($formfield->matches($value)) { + $fieldvalue = $formfield->get_value(); + throw new ExpectationException( + 'The \'' . $field . '\' value matches \'' . $value . '\' and it should not match it' , + $this->getSession() + ); + } + } + + /** + * Checks if fields values matches the provided values. Provide a table with field/value data. + * + * @Then /^the following fields match these values:$/ + * @throws ExpectationException + * @param TableNode $data Pairs of | field | value | + */ + public function the_following_fields_match_these_values(TableNode $data) { + + // Expand all fields in case we have. + $this->expand_all_fields(); + + $datahash = $data->getRowsHash(); + + // The action depends on the field type. + foreach ($datahash as $locator => $value) { + $this->the_field_should_match_value($locator, $value); + } + } + + /** + * Checks that fields values do not match the provided values. Provide a table with field/value data. + * + * @Then /^the following fields do not match these values:$/ + * @throws ExpectationException + * @param TableNode $data Pairs of | field | value | + */ + public function the_following_fields_do_not_match_these_values(TableNode $data) { + + // Expand all fields in case we have. + $this->expand_all_fields(); + + $datahash = $data->getRowsHash(); + + // The action depends on the field type. + foreach ($datahash as $locator => $value) { + $this->the_field_does_not_match_value($locator, $value); + } + } + /** * Checks, that checkbox with specified in|name|label|value is checked. * * @Then /^the "(?P(?:[^"]|\\")*)" checkbox should be checked$/ - * @see Behat\MinkExtension\Context\MinkContext * @param string $checkbox */ public function assert_checkbox_checked($checkbox) { - $this->assertSession()->checkboxChecked($checkbox); + $this->the_field_should_match_value($checkbox, 1); } /** * Checks, that checkbox with specified in|name|label|value is unchecked. * * @Then /^the "(?P(?:[^"]|\\")*)" checkbox should not be checked$/ - * @see Behat\MinkExtension\Context\MinkContext * @param string $checkbox */ public function assert_checkbox_not_checked($checkbox) { - $this->assertSession()->checkboxNotChecked($checkbox); + $this->the_field_should_match_value($checkbox, ''); } /** @@ -288,20 +398,41 @@ class behat_forms extends behat_base { * @throws ExpectationException * @throws ElementNotFoundException Thrown by behat_base::find * @param string $select The select element name - * @param string $option The option text/value + * @param string $option The option text/value. Plain value or comma separated + * values if multiple. Commas in multiple values escaped with backslash. */ public function the_select_box_should_contain($select, $option) { $selectnode = $this->find_field($select); + $multiple = $selectnode->hasAttribute('multiple'); + $optionsarr = array(); // Array of passed value/text options to test. - $regex = '/' . preg_quote($option, '/') . '/ui'; - if (!preg_match($regex, $selectnode->getText())) { - throw new ExpectationException( - 'The select box "' . $select . '" does not contains the option "' . $option . '"', - $this->getSession() - ); + if ($multiple) { + // Can pass multiple comma separated, with valuable commas escaped with backslash. + foreach (preg_replace('/\\\,/', ',', preg_split('/(?findAll('xpath', '//option'); + $values = array(); + foreach ($options as $opt) { + $values[trim($opt->getValue())] = trim($opt->getText()); + } + + foreach ($optionsarr as $opt) { + // Verify every option is a valid text or value. + if (!in_array($opt, $values) && !array_key_exists($opt, $values)) { + throw new ExpectationException( + 'The select box "' . $select . '" does not contain the option "' . $opt . '"', + $this->getSession() + ); + } + } } /** @@ -311,19 +442,61 @@ class behat_forms extends behat_base { * @throws ExpectationException * @throws ElementNotFoundException Thrown by behat_base::find * @param string $select The select element name - * @param string $option The option text/value + * @param string $option The option text/value. Plain value or comma separated + * values if multiple. Commas in multiple values escaped with backslash. */ public function the_select_box_should_not_contain($select, $option) { $selectnode = $this->find_field($select); + $multiple = $selectnode->hasAttribute('multiple'); + $optionsarr = array(); // Array of passed value/text options to test. - $regex = '/' . preg_quote($option, '/') . '/ui'; - if (preg_match($regex, $selectnode->getText())) { - throw new ExpectationException( - 'The select box "' . $select . '" contains the option "' . $option . '"', - $this->getSession() - ); + if ($multiple) { + // Can pass multiple comma separated, with valuable commas escaped with backslash. + foreach (preg_replace('/\\\,/', ',', preg_split('/(?findAll('xpath', '//option'); + $values = array(); + foreach ($options as $opt) { + $values[trim($opt->getValue())] = trim($opt->getText()); + } + + foreach ($optionsarr as $opt) { + // Verify every option is not a valid text or value. + if (in_array($opt, $values) || array_key_exists($opt, $values)) { + throw new ExpectationException( + 'The select box "' . $select . '" contains the option "' . $opt . '"', + $this->getSession() + ); + } } } + /** + * Generic field setter. + * + * Internal API method, a generic *I set "VALUE" to "FIELD" field* + * could be created based on it. + * + * @param string $fieldlocator The pointer to the field, it will depend on the field type. + * @param string $value + * @return void + */ + protected function set_field_value($fieldlocator, $value) { + + $node = $this->find_field($fieldlocator); + + // We delegate to behat_form_field class, it will + // guess the type properly as it is a select tag. + $field = behat_field_manager::get_form_field($node, $this->getSession()); + $field->set_value($value); + } + } diff --git a/lib/tests/behat/behat_permissions.php b/lib/tests/behat/behat_permissions.php index 562be1a709f..ec265f9ce5e 100644 --- a/lib/tests/behat/behat_permissions.php +++ b/lib/tests/behat/behat_permissions.php @@ -142,4 +142,44 @@ class behat_permissions extends behat_base { } } + /** + * Checks if the capability has the specified permission. Works in the role definition advanced page. + * + * @Then /^"(?P(?:[^"]|\\")*)" capability has "(?PNot set|Allow|Prevent|Prohibit)" permission$/ + * @throws ExpectationException + * @param string $capabilityname + * @param string $permission + * @return void + */ + public function capability_has_permission($capabilityname, $permission) { + + // We already know the name, so we just need the value. + $radioxpath = "//table[@class='rolecap']/descendant::input[@type='radio']" . + "[@name='" . $capabilityname . "'][@checked]"; + + $checkedradio = $this->find('xpath', $radioxpath); + + switch ($permission) { + case get_string('notset', 'role'): + $perm = CAP_INHERIT; + break; + case get_string('allow', 'role'): + $perm = CAP_ALLOW; + break; + case get_string('prevent', 'role'): + $perm = CAP_PREVENT; + break; + case get_string('prohibit', 'role'): + $perm = CAP_PROHIBIT; + break; + default: + throw new ExpectationException('"' . $permission . '" permission does not exist', $this->getSession()); + break; + } + + if ($checkedradio->getAttribute('value') != $perm) { + throw new ExpectationException('"' . $capabilityname . '" permission is not "' . $permission . '"', $this->getSession()); + } + } + }