From a4534dceefca1be00c47ab6a9f36d19219bb8572 Mon Sep 17 00:00:00 2001 From: David Monllao Date: Thu, 28 Feb 2013 10:53:26 +0800 Subject: [PATCH 1/7] MDL-38184 behat: Fields management externalized Not only behat_forms needs to deal with form fields --- lib/behat/behat_field_manager.php | 131 ++++++++++++++++++++++ lib/behat/form_field/behat_form_field.php | 2 +- lib/tests/behat/behat_forms.php | 97 +--------------- 3 files changed, 136 insertions(+), 94 deletions(-) create mode 100644 lib/behat/behat_field_manager.php diff --git a/lib/behat/behat_field_manager.php b/lib/behat/behat_field_manager.php new file mode 100644 index 00000000000..01cc96560f0 --- /dev/null +++ b/lib/behat/behat_field_manager.php @@ -0,0 +1,131 @@ +. + +/** + * Form fields helper. + * + * @package core + * @category test + * @copyright 2013 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. + +use Behat\Mink\Session as Session, + Behat\Mink\Element\NodeElement as NodeElement; + +/** + * Helper to interact with form fields. + * + * @package core + * @category test + * @copyright 2013 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_field_manager { + + /** + * Gets an instance of the form field. + * + * Not all the fields are part of a moodle form, in this + * cases it fallsback to the generic form field. Also note + * that this generic field type is using a generic setValue() + * method from the Behat API, which is not always good to set + * the value of form elements. + * + * @param NodeElement $fieldnode + * @param string $locator The locator to help give more info about the possible problem. + * @param Session $session The behat browser session + * @return behat_form_field + */ + public static function get_field(NodeElement $fieldnode, $locator, Session $session) { + global $CFG; + + // Get the field type if is part of a moodleform. + if (self::is_moodleform_field($fieldnode)) { + $type = self::get_node_type($fieldnode, $locator, $session); + } + + // If is not a moodleforms field use the base field type. + if (empty($type)) { + $type = 'field'; + } + + $classname = 'behat_form_' . $type; + + // Fallsback on the default form field if nothing specific exists. + $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php'; + if (!file_exists($classpath)) { + $classname = 'behat_form_field'; + $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php'; + } + + // Returns the instance. + require_once($classpath); + return new $classname($session, $fieldnode); + } + + /** + * Detects when the field is a moodleform field type. + * + * Note that there are fields inside moodleforms that are not + * moodleform element; this method can not detect this, this will + * be managed by get_node_type, after failing to find the form + * element element type. + * + * @param NodeElement $fieldnode + * @return bool + */ + 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"); + + return ($parentformfound != false); + } + + /** + * Recursive method to find the field type. + * + * Depending on the field the felement class node is a level or in another. We + * look recursively for a parent node with a 'felement' class to find the field type. + * + * @param NodeElement $fieldnode The current node. + * @param string $locator Just to send an exception that makes sense for the user. + * @param Session $session The behat browser session + * @return mixed A NodeElement if we continue looking for the element type and String or false when we are done. + */ + protected static function get_node_type(NodeElement $fieldnode, $locator, Session $session) { + + // We look for a parent node with 'felement' class. + if ($class = $fieldnode->getParent()->getAttribute('class')) { + + if (strstr($class, 'felement') != false) { + // Remove 'felement f' from class value. + return substr($class, 10); + } + + // Stop propagation through the DOM, if it does not have a felement is not part of a moodle form. + if (strstr($class, 'fcontainer') != false) { + return false; + } + } + + return self::get_node_type($fieldnode->getParent(), $locator, $session); + } + +} diff --git a/lib/behat/form_field/behat_form_field.php b/lib/behat/form_field/behat_form_field.php index 5f19d59a53d..938bde631e8 100644 --- a/lib/behat/form_field/behat_form_field.php +++ b/lib/behat/form_field/behat_form_field.php @@ -48,7 +48,7 @@ class behat_form_field { /** * @var NodeElement The field DOM node to interact with. */ - protected $fieldnode; + protected $field; /** * General constructor with the node and the session to interact with. diff --git a/lib/tests/behat/behat_forms.php b/lib/tests/behat/behat_forms.php index 78cda2b7d0e..4ce80619172 100644 --- a/lib/tests/behat/behat_forms.php +++ b/lib/tests/behat/behat_forms.php @@ -26,6 +26,7 @@ // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php. require_once(__DIR__ . '/../../../lib/behat/behat_base.php'); +require_once(__DIR__ . '/../../../lib/behat/behat_field_manager.php'); use Behat\Behat\Context\Step\Given as Given, Behat\Behat\Context\Step\When as When, @@ -76,13 +77,11 @@ class behat_forms extends behat_base { // The action depends on the field type. foreach ($datahash as $locator => $value) { - unset($fieldnode); - - // Getting the NodeElement. + // Getting the node element pointed by the label. $fieldnode = $this->find_field($locator); // Gets the field type from a parent node. - $field = $this->get_field($fieldnode, $locator); + $field = behat_field_manager::get_field($fieldnode, $locator, $this->getSession()); // Delegates to the field class. $field->set_value($value); @@ -220,7 +219,7 @@ class behat_forms extends behat_base { $fieldnode = $this->find_field($locator); // Get the field. - $field = $this->get_field($fieldnode, $locator); + $field = behat_field_manager::get_field($fieldnode, $locator, $this->getSession()); $fieldvalue = $field->get_value(); // Checks if the provided value matches the current field value. @@ -299,92 +298,4 @@ class behat_forms extends behat_base { } } - /** - * Gets an instance of the form field. - * - * Not all the fields are part of a moodle form, in this - * cases it fallsback to the generic form field. Also note - * that this generic field type is using a generic setValue() - * method from the Behat API, which is not always good to set - * the value of form elements. - * - * @param NodeElement $fieldnode The current node - * @param string $locator Just to send an exception that makes sense for the user - * @return behat_form_field - */ - protected function get_field(NodeElement $fieldnode, $locator) { - global $CFG; - - // Get the field type if is part of a moodleform. - if ($this->is_moodleform_field($fieldnode)) { - $type = $this->get_node_type($fieldnode, $locator); - } - - // If is not a moodleforms field use the base field type. - if (empty($type)) { - $type = 'field'; - } - - $classname = 'behat_form_' . $type; - - // Fallsback on the default form field if nothing specific exists. - $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php'; - if (!file_exists($classpath)) { - $classname = 'behat_form_field'; - $classpath = $CFG->libdir . '/behat/form_field/' . $classname . '.php'; - } - - // Returns the instance. - require_once($classpath); - return new $classname($this->getSession(), $fieldnode); - } - - /** - * Detects when the field is a moodleform field type. - * - * Note that there are fields inside moodleforms that are not - * moodleform element; this method can not detect this, this will - * be managed by get_node_type, after failing to find the form - * element element type. - * - * @param NodeElement $fieldnode - * @return bool - */ - protected 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"); - - return ($parentformfound != false); - } - - /** - * Recursive method to find the field type. - * - * Depending on the field the felement class node is a level or in another. We - * look recursively for a parent node with a 'felement' class to find the field type. - * - * @param NodeElement $fieldnode The current node. - * @param string $locator Just to send an exception that makes sense for the user. - * @return mixed A NodeElement if we continue looking for the element type and String or false when we are done. - */ - protected function get_node_type(NodeElement $fieldnode, $locator) { - - // We look for a parent node with 'felement' class. - if ($class = $fieldnode->getParent()->getAttribute('class')) { - - if (strstr($class, 'felement') != false) { - // Remove 'felement f' from class value. - return substr($class, 10); - } - - // Stop propagation through the DOM, if it does not have a felement is not part of a moodle form. - if (strstr($class, 'fcontainer') != false) { - return false; - } - } - - return $this->get_node_type($fieldnode->getParent(), $locator); - } - } From 1d823d93224f919cf97249c0f2ba57594f126c17 Mon Sep 17 00:00:00 2001 From: David Monllao Date: Thu, 28 Feb 2013 15:57:25 +0800 Subject: [PATCH 2/7] MDL-38184 behat: File-related actions --- lib/behat/behat_files.php | 178 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 178 insertions(+) create mode 100644 lib/behat/behat_files.php diff --git a/lib/behat/behat_files.php b/lib/behat/behat_files.php new file mode 100644 index 00000000000..7ab6a5b498b --- /dev/null +++ b/lib/behat/behat_files.php @@ -0,0 +1,178 @@ +. + +/** + * Files interactions with behat. + * + * Note that steps definitions files can not extend other steps definitions files, so + * steps definitions which makes use of file attachments or filepicker should + * extend behat_files instead of behat_base. + * + * @package core + * @category test + * @copyright 2013 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_base.php'); + +use Behat\Mink\Exception\ExpectationException as ExpectationException; + +/** + * Files-related actions. + * + * Steps definitions related with filepicker or repositories should extend + * this class instead of behat_base as it provides useful methods to deal + * with the common filepicker issues. + * + * @package core + * @category test + * @copyright 2013 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_files extends behat_base { + + /** + * Gets the filepicker NodeElement. + * + * The filepicker field label is pointing to a hidden input which is + * not recognized as a named selector, as it is hidden... + * + * @throws ExpectationException Thrown by behat_base::find + * @param string $filepickerelement + * @return NodeElement The hidden element node. + */ + protected function get_filepicker_node($filepickerelement) { + + // More info about the problem (in case there is a problem). + $exception = new ExpectationException('"' . $filepickerelement . '" filepicker can not be found', $this->getSession()); + + // Gets the ffilemanager node specified by the locator which contains the filepicker container. + $filepickercontainer = $this->find( + 'xpath', + "//input[./@id = //label[contains(normalize-space(string(.)), '" . $filepickerelement . "')]/@for]/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' ffilemanager ') or contains(concat(' ', normalize-space(@class), ' '), ' ffilepicker ')]", + $exception + ); + + return $filepickercontainer; + } + + /** + * Performs $action on a filepicker container element (file or folder). + * + * It works together with open_element_contextual_menu + * as this method needs the contextual menu to be opened. + * + * @throws ExpectationException Thrown by behat_base::find + * @param string $action + * @param ExpectationException $exception + * @return void + */ + protected function perform_on_element($action, ExpectationException $exception) { + + // Finds the button inside the DOM, is a modal window, so should be unique. + $classname = 'fp-file-' . $action; + $button = $this->find('css', 'button.' . $classname, $exception); + + $button->click(); + } + + /** + * Opens the contextual menu of a folder or a file. + * + * @throws ExpectationException Thrown by behat_base::find + * @param string $name The name of the folder/file + * @param string $filepickerelement The filepicker locator, usually the form element label + * @return void + */ + protected function open_element_contextual_menu($name, $filepickerelement) { + + $filepickernode = $this->get_filepicker_node($filepickerelement); + + $exception = new ExpectationException('The "'.$filepickerelement.'" filepicker "'.$name.'" element can not be found', $this->getSession()); + + // Get a filepicker element (folder or file). + try { + + // First we look at the folder as we need the contextual menu otherwise it would be opened. + $node = $this->find( + 'xpath', + "//div[@class='fp-content'] +//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')] +//descendant::a[contains(concat(' ', normalize-space(@class), ' '), ' fp-contextmenu ')] +[contains(concat(' ', normalize-space(.), ' '), '" . $name . "')]", + $exception, + $filepickernode + ); + + } catch (Exception $e) { + + $node = $this->find( + 'xpath', + "//div[@class='fp-content'] +//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')] +//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-filename ')] +[contains(concat(' ', normalize-space(.), ' '), '" . $name . "')]", + $exception, + $filepickernode + ); + } + + // Right click opens the contextual menu in both folder and file. + $node->rightClick(); + } + + /** + * Opens the 'add file' modal window and selects the repository. + * + * @throws ExpectationException Thrown by behat_base::find + * @param NodeElement $filepickernode The filepicker DOM node. + * @param mixed $repositoryname The repo name. + * @return void + */ + protected function open_add_file_window($filepickernode, $repositoryname) { + + $exception = new ExpectationException('No files can be added to the specified filepicker', $this->getSession()); + + // We should deal with single-file and multiple-file filepickers, + // catching the exception thrown by behat_base::find() in case is not multiple + try { + // Looking for the add button inside the specified filepicker. + $add = $this->find('css', 'div.fp-btn-add a', $exception, $filepickernode); + } catch (Exception $e) { + // Otherwise should be a single-file filepicker. + $add = $this->find('css', 'input.fp-btn-choose', $exception, $filepickernode); + } + $add->click(); + + // Getting the repository link and opening it. + $repoexception = new ExpectationException('The "' . $repositoryname . '" repository has not been found', $this->getSession()); + + // 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(concat(' ', normalize-space(.), ' '), ' " . $repositoryname . "')]", + $repoexception + ); + + // Selecting the repo. + $repositorylink->click(); + } +} From a8d910a82a45d43d6e1bc6df604bcf996d6d0adc Mon Sep 17 00:00:00 2001 From: David Monllao Date: Thu, 28 Feb 2013 15:57:49 +0800 Subject: [PATCH 3/7] MDL-38184 behat: Solving problem with the tests loader --- lib/behat/classes/behat_config_manager.php | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/lib/behat/classes/behat_config_manager.php b/lib/behat/classes/behat_config_manager.php index e0ec4d46d25..c277e13f0eb 100644 --- a/lib/behat/classes/behat_config_manager.php +++ b/lib/behat/classes/behat_config_manager.php @@ -124,6 +124,10 @@ class behat_config_manager { $stepsdefinitions = array(); foreach ($components as $componentname => $componentpath) { $componentpath = self::clean_path($componentpath); + + if (!file_exists($componentpath . self::get_behat_tests_path())) { + continue; + } $diriterator = new DirectoryIterator($componentpath . self::get_behat_tests_path()); $regite = new RegexIterator($diriterator, '|behat_.*\.php$|'); From 3db1aec5281a3c6691c9f06f91439c43205d8839 Mon Sep 17 00:00:00 2001 From: David Monllao Date: Thu, 28 Feb 2013 15:59:03 +0800 Subject: [PATCH 4/7] MDL-38184 behat: Filepicker management steps Includes create/open/delete/zip/unzip of files and folders --- lib/behat/behat_base.php | 3 + lib/behat/behat_files.php | 25 ++-- lib/tests/behat/behat_filepicker.php | 189 +++++++++++++++++++++++++++ 3 files changed, 206 insertions(+), 11 deletions(-) create mode 100644 lib/tests/behat/behat_filepicker.php diff --git a/lib/behat/behat_base.php b/lib/behat/behat_base.php index 532086b084b..5917d8804ed 100644 --- a/lib/behat/behat_base.php +++ b/lib/behat/behat_base.php @@ -143,6 +143,9 @@ class behat_base extends Behat\MinkExtension\Context\RawMinkContext { // We are in the container node. if (strpos($union, '.') === 0) { $union = substr($union, 1); + } else if (strpos($union, '/') !== 0) { + // Adding the path separator in case it is not there. + $union = '/' . $union; } $unions[$key] = $args['node']->getXpath() . $union; } diff --git a/lib/behat/behat_files.php b/lib/behat/behat_files.php index 7ab6a5b498b..a17c1f09bcd 100644 --- a/lib/behat/behat_files.php +++ b/lib/behat/behat_files.php @@ -65,7 +65,8 @@ class behat_files extends behat_base { // Gets the ffilemanager node specified by the locator which contains the filepicker container. $filepickercontainer = $this->find( 'xpath', - "//input[./@id = //label[contains(normalize-space(string(.)), '" . $filepickerelement . "')]/@for]/ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' ffilemanager ') or contains(concat(' ', normalize-space(@class), ' '), ' ffilepicker ')]", + "//input[./@id = //label[contains(normalize-space(string(.)), '" . $filepickerelement . "')]/@for] +//ancestor::div[contains(concat(' ', normalize-space(@class), ' '), ' ffilemanager ') or contains(concat(' ', normalize-space(@class), ' '), ' ffilepicker ')]", $exception ); @@ -109,32 +110,34 @@ class behat_files extends behat_base { // Get a filepicker element (folder or file). try { - // First we look at the folder as we need the contextual menu otherwise it would be opened. + // First we look at the folder as we need to click on the contextual menu otherwise it would be opened. $node = $this->find( 'xpath', "//div[@class='fp-content'] //descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')] +[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')][contains(normalize-space(string(.)), '" . $name . "')] //descendant::a[contains(concat(' ', normalize-space(@class), ' '), ' fp-contextmenu ')] -[contains(concat(' ', normalize-space(.), ' '), '" . $name . "')]", +", $exception, $filepickernode ); - } catch (Exception $e) { + } catch (ExpectationException $e) { + // Here the contextual menu is hidden, we click on the thumbnail. $node = $this->find( 'xpath', "//div[@class='fp-content'] -//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')] -//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-filename ')] -[contains(concat(' ', normalize-space(.), ' '), '" . $name . "')]", +//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-file ')][contains(normalize-space(string(.)), '" . $name . "')] +//descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' fp-thumbnail ')] +", $exception, $filepickernode ); } - // Right click opens the contextual menu in both folder and file. - $node->rightClick(); + // Click opens the contextual menu when clicking on files. + $node->click(); } /** @@ -167,8 +170,8 @@ class behat_files extends behat_base { $repositorylink = $this->find( 'xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-area ')] -/descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-name ')] -[contains(concat(' ', normalize-space(.), ' '), ' " . $repositoryname . "')]", +//descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' fp-repo-name ')] +[contains(normalize-space(string(.)), '" . $repositoryname . "')]", $repoexception ); diff --git a/lib/tests/behat/behat_filepicker.php b/lib/tests/behat/behat_filepicker.php new file mode 100644 index 00000000000..68a79b11d94 --- /dev/null +++ b/lib/tests/behat/behat_filepicker.php @@ -0,0 +1,189 @@ +. + +/** + * Files and filepicker manipulation steps definitions. + * + * @package core + * @category test + * @copyright 2013 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/behat_files.php'); + +use Behat\Mink\Exception\ExpectationException as ExpectationException; + +/** + * Steps definitions to deal with the filepicker. + * + * Extends behat_files rather than behat_base as is file-related. + * + * @package core + * @category test + * @copyright 2013 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_filepicker extends behat_files { + + /** + * Creates a folder with specified name in the current folder and in the specified filepicker field. + * + * @Given /^I create "(?P(?:[^"]|\\")*)" folder in "(?P(?:[^"]|\\")*)" filepicker$/ + * @throws ExpectationException Thrown by behat_base::find + * @param string $foldername + * @param string $filepickerelement + */ + public function i_create_folder_in_filepicker($foldername, $filepickerelement) { + + $fieldnode = $this->get_filepicker_node($filepickerelement); + + // Looking for the create folder button inside the specified filepicker. + $exception = new ExpectationException('No folders can be created in "'.$filepickerelement.'" filepicker', $this->getSession()); + $newfolder = $this->find('css', 'div.fp-btn-mkdir a', $exception, $fieldnode); + $newfolder->click(); + + // Setting the folder name in the modal window. + $exception = new ExpectationException('The dialog to enter the folder name does not appear', $this->getSession()); + $dialoginput = $this->find('css', '.fp-mkdir-dlg-text input'); + $dialoginput->setValue($foldername); + + $this->getSession()->getPage()->pressButton('Create folder'); + + // Wait few seconds for the folder to be created and filepicker contents reloaded. + $this->getSession()->wait(4 * 1000, false); + } + + /** + * Opens the contents of a filepicker folder. It looks for the folder in the current folder and in the path bar. + * + * @Given /^I open "(?P(?:[^"]|\\")*)" folder from "(?P(?:[^"]|\\")*)" filepicker$/ + * @throws ExpectationException Thrown by behat_base::find + * @param string $foldername + * @param string $filepickerelement + */ + public function i_open_folder_from_filepicker($foldername, $filepickerelement) { + + $fieldnode = $this->get_filepicker_node($filepickerelement); + + $exception = new ExpectationException( + 'The "'.$foldername.'" folder can not be found in the "'.$filepickerelement.'" filepicker', + $this->getSession() + ); + + // We look both in the pathbar and in the contents. + try { + + // In the current folder workspace. + $folder = $this->find( + 'xpath', + "//div[contains(concat(' ', normalize-space(@class), ' '), ' fp-folder ')] +//descendant::div[contains(concat(' ', normalize-space(.), ' '), '" . $foldername . "')]", + $exception, + $fieldnode + ); + } catch (ExpectationException $e) { + + // And in the pathbar. + $folder = $this->find( + 'xpath', + "//a[contains(concat(' ', normalize-space(@class), ' '), ' fp-path-folder-name ')] +[contains(concat(' ', normalize-space(.), ' '), '" . $foldername . "')]", + $exception, + $fieldnode + ); + } + + // It should be a NodeElement, otherwise an exception would have been thrown. + $folder->click(); + + // Wait few seconds for the filepicker contents to be updated. + $this->getSession()->wait(4 * 1000, false); + } + + /** + * Unzips the specified file from the specified filepicker field. The zip file has to be visible in the current folder. + * + * @Given /^I unzip "(?P(?:[^"]|\\")*)" file from "(?P(?:[^"]|\\")*)" filepicker$/ + * @throws ExpectationException Thrown by behat_base::find + * @param string $filename + * @param string $filepickerelement + */ + public function i_unzip_file_from_filepicker($filename, $filepickerelement) { + + // Open the contextual menu of the filepicker element. + $this->open_element_contextual_menu($filename, $filepickerelement); + + // Execute the action. + $exception = new ExpectationException($filename.' element can not be unzipped', $this->getSession()); + $this->perform_on_element('unzip', $exception); + + // Wait few seconds. + // Here the time will depend on the zip contents and the server load, so it better to be conservative. + $this->getSession()->wait(8 * 1000, false); + } + + /** + * Zips the specified folder from the specified filepicker field. The folder has to be in the current folder. + * + * @Given /^I zip "(?P(?:[^"]|\\")*)" folder from "(?P(?:[^"]|\\")*)" filepicker$/ + * @throws ExpectationException Thrown by behat_base::find + * @param string $foldername + * @param string $filepickerelement + */ + public function i_zip_folder_from_filepicker($foldername, $filepickerelement) { + + // Open the contextual menu of the filepicker element. + $this->open_element_contextual_menu($foldername, $filepickerelement); + + // Execute the action. + $exception = new ExpectationException($foldername.' element can not be zipped', $this->getSession()); + $this->perform_on_element('zip', $exception); + + // Wait few seconds. + // Here the time will depend on the folder contents and the server load, so it better to be conservative. + $this->getSession()->wait(8 * 1000, false); + } + + /** + * Deletes the specified file or folder from the specified filepicker field. + * + * @Given /^I delete "(?P(?:[^"]|\\")*)" from "(?P(?:[^"]|\\")*)" filepicker$/ + * @throws ExpectationException Thrown by behat_base::find + * @param string $foldername + * @param string $filepickerelement + */ + public function i_delete_file_from_filepicker($name, $filepickerelement) { + + // Open the contextual menu of the filepicker element. + $this->open_element_contextual_menu($name, $filepickerelement); + + // Execute the action. + $exception = new ExpectationException($name.' element can not be deleted', $this->getSession()); + $this->perform_on_element('delete', $exception); + + // Yes, we are sure. + // Using xpath + click instead of pressButton as 'Ok' it is a common string. + $okbutton = $this->find('css', 'div.fp-dlg button.fp-dlg-butconfirm'); + $okbutton->click(); + + // Wait few seconds until filepicker contents are reloaded. + $this->getSession()->wait(4 * 1000, false); + } + +} From bd21f796cb785c7d40da52a13606ce62b79e07c3 Mon Sep 17 00:00:00 2001 From: David Monllao Date: Thu, 28 Feb 2013 16:01:08 +0800 Subject: [PATCH 5/7] MDL-38184 behat: Tests for filepicker steps --- .../tests/behat/manipulate_filepicker.feature | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 admin/tool/behat/tests/behat/manipulate_filepicker.feature diff --git a/admin/tool/behat/tests/behat/manipulate_filepicker.feature b/admin/tool/behat/tests/behat/manipulate_filepicker.feature new file mode 100644 index 00000000000..b5b3d94cb9f --- /dev/null +++ b/admin/tool/behat/tests/behat/manipulate_filepicker.feature @@ -0,0 +1,40 @@ +@tool_behat @core_form @filepicker +Feature: Manipulate filepicker + In order to provide external resources + As a moodle user + I need to upload files to moodle + + Background: + Given the following "courses" exists: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And I log in as "admin" + And I follow "Course 1" + And I turn editing mode on + And I add a "Folder" to section "0" + And I fill the moodle form with: + | Name | Folder resource | + | Description | The description | + And I create "Folder 1" folder in "Files" filepicker + And I open "Folder 1" folder from "Files" filepicker + And I create "SubFolder 1" folder in "Files" filepicker + + @javascript + Scenario: Create folders and subfolders + When I open "Files" folder from "Files" filepicker + Then I should see "Folder 1" + And I open "Folder 1" folder from "Files" filepicker + And I should see "SubFolder 1" + And I press "Save and return to course" + + @javascript + Scenario: Zip and unzip folders and files + Given I open "Files" folder from "Files" filepicker + And I zip "Folder 1" folder from "Files" filepicker + And I delete "Folder 1" from "Files" filepicker + When I unzip "Folder 1.zip" file from "Files" filepicker + And I delete "Folder 1.zip" from "Files" filepicker + Then I should see "Folder 1" + And I open "Folder 1" folder from "Files" filepicker + And I should see "SubFolder 1" + And I press "Save and return to course" From aa988a9a6c58db44f981e7a10ab15ef1711f7aa5 Mon Sep 17 00:00:00 2001 From: David Monllao Date: Thu, 28 Feb 2013 16:28:19 +0800 Subject: [PATCH 6/7] MDL-38184 repository_upload: New step definition --- .../tests/behat/behat_repository_upload.php | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 repository/upload/tests/behat/behat_repository_upload.php diff --git a/repository/upload/tests/behat/behat_repository_upload.php b/repository/upload/tests/behat/behat_repository_upload.php new file mode 100644 index 00000000000..51c3abf1ddb --- /dev/null +++ b/repository/upload/tests/behat/behat_repository_upload.php @@ -0,0 +1,72 @@ +. + +/** + * Steps definitions for the upload repository type. + * + * @package repository_upload + * @category test + * @copyright 2013 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__ . '/../../../../lib/behat/behat_files.php'); + +use Behat\Mink\Exception\ExpectationException as ExpectationException; + +/** + * Steps definitions to deal with the filepicker. + * + * Extends behat_files rather than behat_base as is file-related. + * + * @package repository_upload + * @category test + * @copyright 2013 David Monllaó + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class behat_repository_upload extends behat_files { + + /** + * Uploads a file to the specified file picker. It deals both with single-file and multiple-file filepickers. The paths should be relative to moodle codebase. + * + * @When /^I upload "(?P(?:[^"]|\\")*)" file to "(?P(?:[^"]|\\")*)" filepicker$/ + * @throws ExpectationException Thrown by behat_base::find + * @param string $filepath + * @param string $filepickerelement + */ + public function i_upload_file_to_filepicker($filepath, $filepickerelement) { + global $CFG; + + $filepickernode = $this->get_filepicker_node($filepickerelement); + + // Opening the select repository window and selecting the upload repository. + $this->open_add_file_window($filepickernode, get_string('pluginname', 'repository_upload')); + + // Attaching specified file to the node. + $fileabsolutepath = $CFG->dirroot . '/' . $filepath; + $inputfilenode = $this->find_file('repo_upload_file'); + $inputfilenode->attachFile($fileabsolutepath); + + // Submit the file. + $this->getSession()->getPage()->pressButton('Upload this file'); + + // Wait a while for the file to be uploaded. + $this->getSession()->wait(6 * 1000, false); + } + +} From 4f07b654e15e281d7d3f8fefbcff54a69e4b9c01 Mon Sep 17 00:00:00 2001 From: David Monllao Date: Thu, 28 Feb 2013 16:34:21 +0800 Subject: [PATCH 7/7] MDL-38184 repository_upload: Adding tests --- lib/tests/fixtures/empty.txt | 1 + lib/tests/fixtures/upload_users.csv | 2 ++ .../upload/tests/behat/upload_file.feature | 32 +++++++++++++++++++ 3 files changed, 35 insertions(+) create mode 100644 lib/tests/fixtures/empty.txt create mode 100644 lib/tests/fixtures/upload_users.csv create mode 100644 repository/upload/tests/behat/upload_file.feature diff --git a/lib/tests/fixtures/empty.txt b/lib/tests/fixtures/empty.txt new file mode 100644 index 00000000000..aa73032ca8f --- /dev/null +++ b/lib/tests/fixtures/empty.txt @@ -0,0 +1 @@ +empty file for testing purposes diff --git a/lib/tests/fixtures/upload_users.csv b/lib/tests/fixtures/upload_users.csv new file mode 100644 index 00000000000..62571311020 --- /dev/null +++ b/lib/tests/fixtures/upload_users.csv @@ -0,0 +1,2 @@ +username,password,email,firstname,lastname +teacher1,moodle,teacher1@teacher1.com,Teacher,1 diff --git a/repository/upload/tests/behat/upload_file.feature b/repository/upload/tests/behat/upload_file.feature new file mode 100644 index 00000000000..63baab8d67f --- /dev/null +++ b/repository/upload/tests/behat/upload_file.feature @@ -0,0 +1,32 @@ +@repository_upload @core_form @repository @_only_local +Feature: Upload files + In order to add contents + As a moodle user + I need to upload files + + @javascript + Scenario: Upload a file in a single file filepicker + Given I log in as "admin" + And I expand "Front page settings" node + And I expand "Site administration" node + And I expand "Users" node + And I expand "Accounts" node + And I follow "Upload users" + When I upload "lib/tests/fixtures/upload_users.csv" file to "File" filepicker + And I press "Upload users" + Then I should see "Upload users preview" + And I should see "Teacher" + And I should see "teacher1@teacher1.com" + And I press "Cancel" + + @javascript + Scenario: Upload a file in a multiple file filepicker + Given the following "courses" exists: + | fullname | shortname | category | + | Course 1 | C1 | 0 | + And I log in as "admin" + And I follow "Admin User" + And I follow "My private files" + And I upload "lib/tests/fixtures/empty.txt" file to "Files" filepicker + Then I should see "empty.txt" in the "div.fp-content" "css_element" + And I press "Cancel"