Merge branch 'MDL-57319-master' of git://github.com/danpoltawski/moodle
[moodle.git] / backup / util / ui / tests / behat / behat_backup.php
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
17 /**
18  * Backup and restore actions to help behat feature files writting.
19  *
20  * @package    core_backup
21  * @category   test
22  * @copyright  2013 David MonllaĆ³
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  */
26 // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
28 require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php');
29 require_once(__DIR__ . '/../../../../../lib/behat/behat_field_manager.php');
30 require_once(__DIR__ . '/../../../../../lib/tests/behat/behat_navigation.php');
32 use Behat\Gherkin\Node\TableNode as TableNode,
33     Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
34     Behat\Mink\Exception\ExpectationException as ExpectationException;
36 /**
37  * Backup-related steps definitions.
38  *
39  * @package    core_backup
40  * @category   test
41  * @copyright  2013 David MonllaĆ³
42  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43  */
44 class behat_backup extends behat_base {
46     /**
47      * Backups the specified course using the provided options. If you are interested in restoring this backup would be
48      * useful to provide a 'Filename' option.
49      *
50      * @Given /^I backup "(?P<course_fullname_string>(?:[^"]|\\")*)" course using this options:$/
51      * @param string $backupcourse
52      * @param TableNode $options Backup options or false if no options provided
53      */
54     public function i_backup_course_using_this_options($backupcourse, $options = false) {
55         // We can not use other steps here as we don't know where the provided data
56         // table elements are used, and we need to catch exceptions contantly.
58         // Go to homepage.
59         $this->getSession()->visit($this->locate_path('/?redirect=0'));
60         $this->execute("behat_general::wait_until_the_page_is_ready");
62         // Click the course link.
63         $this->execute("behat_general::click_link", $backupcourse);
65         // Click the backup link.
66         $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", get_string('backup'));
68         // Initial settings.
69         $this->fill_backup_restore_form($this->get_step_options($options, "Initial"));
70         $this->execute("behat_forms::press_button", get_string('backupstage1action', 'backup'));
72         // Schema settings.
73         $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
74         $this->execute("behat_forms::press_button", get_string('backupstage2action', 'backup'));
76         // Confirmation and review, backup filename can also be specified.
77         $this->fill_backup_restore_form($this->get_step_options($options, "Confirmation"));
78         $this->execute("behat_forms::press_button", get_string('backupstage4action', 'backup'));
80         // Waiting for it to finish.
81         $this->execute("behat_general::wait_until_the_page_is_ready");
83         // Last backup continue button.
84         $this->execute("behat_general::i_click_on", array(get_string('backupstage16action', 'backup'), 'button'));
85     }
87     /**
88      * Performs a quick (one click) backup of a course.
89      *
90      * Please note that because you can't set settings with this there is no way to know what the filename
91      * that was produced was. It contains a timestamp making it hard to find.
92      *
93      * @Given /^I perform a quick backup of course "(?P<course_fullname_string>(?:[^"]|\\")*)"$/
94      * @param string $backupcourse
95      */
96     public function i_perform_a_quick_backup_of_course($backupcourse) {
97         // We can not use other steps here as we don't know where the provided data
98         // table elements are used, and we need to catch exceptions contantly.
100         // Go to homepage.
101         $this->getSession()->visit($this->locate_path('/?redirect=0'));
103         // Click the course link.
104         $this->execute("behat_general::click_link", $backupcourse);
106         // Click the backup link.
107         $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", get_string('backup'));
109         // Initial settings.
110         $this->execute("behat_forms::press_button", get_string('jumptofinalstep', 'backup'));
112         // Waiting for it to finish.
113         $this->execute("behat_general::wait_until_the_page_is_ready");
115         // Last backup continue button.
116         $this->execute("behat_general::i_click_on", array(get_string('backupstage16action', 'backup'), 'button'));
117     }
119     /**
120      * Imports the specified origin course into the other course using the provided options.
121      *
122      * Keeping it separatelly from backup & restore, it the number of
123      * steps and duplicate code becomes bigger a common method should
124      * be generalized.
125      *
126      * @Given /^I import "(?P<from_course_fullname_string>(?:[^"]|\\")*)" course into "(?P<to_course_fullname_string>(?:[^"]|\\")*)" course using this options:$/
127      * @param string $fromcourse
128      * @param string $tocourse
129      * @param TableNode $options
130      */
131     public function i_import_course_into_course($fromcourse, $tocourse, $options = false) {
133         // We can not use other steps here as we don't know where the provided data
134         // table elements are used, and we need to catch exceptions contantly.
136         // Go to homepage.
137         $this->getSession()->visit($this->locate_path('/?redirect=0'));
138         $this->execute("behat_general::wait_until_the_page_is_ready");
140         // Click the course link.
141         $this->execute("behat_general::click_link", $tocourse);
143         // Click the import link.
144         $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", get_string('import'));
146         // Select the course.
147         $exception = new ExpectationException('"' . $fromcourse . '" course not found in the list of courses to import from',
148             $this->getSession());
150         // The argument should be converted to an xpath literal.
151         $fromcourse = behat_context_helper::escape($fromcourse);
152         $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' ics-results ')]" .
153             "/descendant::tr[contains(., $fromcourse)]" .
154             "/descendant::input[@type='radio']";
155         $radionode = $this->find('xpath', $xpath, $exception);
156         $radiofield = new behat_form_field($this->getSession(), $radionode);
157         $radiofield->set_value(1);
159         $this->execute("behat_forms::press_button", get_string('continue'));
161         // Initial settings.
162         $this->fill_backup_restore_form($this->get_step_options($options, "Initial"));
163         $this->execute("behat_forms::press_button", get_string('importbackupstage1action', 'backup'));
165         // Schema settings.
166         $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
167         $this->execute("behat_forms::press_button", get_string('importbackupstage2action', 'backup'));
169         // Run it.
170         $this->execute("behat_forms::press_button", get_string('importbackupstage4action', 'backup'));
172         // Wait to ensure restore is complete.
173         $this->execute("behat_general::wait_until_the_page_is_ready");
175         // Continue and redirect to 'to' course.
176         $this->execute("behat_general::i_click_on", array(get_string('continue'), 'button'));
177     }
179     /**
180      * Restores the backup into the specified course and the provided options.
181      *
182      * You should be in the 'Restore' page where the backup is.
183      *
184      * @Given /^I restore "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into "(?P<existing_course_fullname_string>(?:[^"]|\\")*)" course using this options:$/
185      * @param string $backupfilename
186      * @param string $existingcourse
187      * @param TableNode $options Restore forms options or false if no options provided
188      */
189     public function i_restore_backup_into_course_using_this_options($backupfilename, $existingcourse, $options = false) {
191         // Confirm restore.
192         $this->select_backup($backupfilename);
194         // The argument should be converted to an xpath literal.
195         $existingcourse = behat_context_helper::escape($existingcourse);
197         // Selecting the specified course (we can not call behat_forms::select_radio here as is in another behat subcontext).
198         $radionodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
199             "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' restore-course-search ')]" .
200             "/descendant::tr[contains(., $existingcourse)]" .
201             "/descendant::input[@type='radio']";
202         $this->execute("behat_general::i_click_on", array($radionodexpath, 'xpath_element'));
204         // Pressing the continue button of the restore into an existing course section.
205         $continuenodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
206             "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']";
207         $this->execute("behat_general::i_click_on", array($continuenodexpath, 'xpath_element'));
209         // Common restore process using provided key/value options.
210         $this->process_restore($options);
211     }
213     /**
214      * Restores the specified backup into a new course using the provided options.
215      *
216      * You should be in the 'Restore' page where the backup is.
217      *
218      * @Given /^I restore "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into a new course using this options:$/
219      * @param string $backupfilename
220      * @param TableNode $options Restore forms options or false if no options provided
221      */
222     public function i_restore_backup_into_a_new_course_using_this_options($backupfilename, $options = false) {
224         // Confirm restore.
225         $this->select_backup($backupfilename);
227         // The first category in the list.
228         $radionodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
229             "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' restore-course-search ')]" .
230             "/descendant::input[@type='radio']";
231         $this->execute("behat_general::i_click_on", array($radionodexpath, 'xpath_element'));
233         // Pressing the continue button of the restore into an existing course section.
234         $continuenodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
235             "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']";
236         $this->execute("behat_general::i_click_on", array($continuenodexpath, 'xpath_element'));
238         // Common restore process using provided key/value options.
239         $this->process_restore($options);
240     }
242     /**
243      * Merges the backup into the current course using the provided restore options.
244      *
245      * You should be in the 'Restore' page where the backup is.
246      *
247      * @Given /^I merge "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into the current course using this options:$/
248      * @param string $backupfilename
249      * @param TableNode $options Restore forms options or false if no options provided
250      */
251     public function i_merge_backup_into_the_current_course($backupfilename, $options = false) {
253         // Confirm restore.
254         $this->select_backup($backupfilename);
256         // Merge without deleting radio option.
257         $radionodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
258             "/descendant::input[@type='radio'][@name='target'][@value='1']";
259         $this->execute("behat_general::i_click_on", array($radionodexpath, 'xpath_element'));
261         // Pressing the continue button of the restore merging section.
262         $continuenodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
263             "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']";
264         $this->execute("behat_general::i_click_on", array($continuenodexpath, 'xpath_element'));
266         // Common restore process using provided key/value options.
267         $this->process_restore($options);
268     }
270     /**
271      * Merges the backup into the current course after deleting this contents, using the provided restore options.
272      *
273      * You should be in the 'Restore' page where the backup is.
274      *
275      * @Given /^I merge "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into the current course after deleting it's contents using this options:$/
276      * @param string $backupfilename
277      * @param TableNode $options Restore forms options or false if no options provided
278      */
279     public function i_merge_backup_into_current_course_deleting_its_contents($backupfilename, $options = false) {
281         // Confirm restore.
282         $this->select_backup($backupfilename);
284         // Delete contents radio option.
285         $radionodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
286             "/descendant::input[@type='radio'][@name='target'][@value='0']";
287         $this->execute("behat_general::i_click_on", array($radionodexpath, 'xpath_element'));
289         // Pressing the continue button of the restore merging section.
290         $continuenodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
291             "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']";
292         $this->execute("behat_general::i_click_on", array($continuenodexpath, 'xpath_element'));
294         // Common restore process using provided key/value options.
295         $this->process_restore($options);
296     }
298     /**
299      * Selects the backup to restore.
300      *
301      * @throws ExpectationException
302      * @param string $backupfilename
303      * @return void
304      */
305     protected function select_backup($backupfilename) {
307         // Using xpath as there are other restore links before this one.
308         $exception = new ExpectationException('The "' . $backupfilename . '" backup file can not be found in this page',
309             $this->getSession());
311         // The argument should be converted to an xpath literal.
312         $backupfilename = behat_context_helper::escape($backupfilename);
314         $xpath = "//tr[contains(., $backupfilename)]/descendant::a[contains(., '" . get_string('restore') . "')]";
315         $restorelink = $this->find('xpath', $xpath, $exception);
316         $restorelink->click();
318         // Confirm the backup contents.
319         $this->find_button(get_string('continue'))->press();
320     }
322     /**
323      * Executes the common steps of all restore processes.
324      *
325      * @param TableNode $options The backup and restore options or false if no options provided
326      * @return void
327      */
328     protected function process_restore($options) {
330         // We can not use other steps here as we don't know where the provided data
331         // table elements are used, and we need to catch exceptions contantly.
333         // Settings.
334         $this->fill_backup_restore_form($this->get_step_options($options, "Settings"));
335         $this->execute("behat_forms::press_button", get_string('restorestage4action', 'backup'));
337         // Schema.
338         $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
339         $this->execute("behat_forms::press_button", get_string('restorestage8action', 'backup'));
341         // Review, no options here.
342         $this->execute("behat_forms::press_button", get_string('restorestage16action', 'backup'));
344         // Wait till the final button is visible.
345         $this->execute("behat_general::wait_until_the_page_is_ready");
347         // Last restore continue button, redirected to restore course after this.
348         $this->execute("behat_general::i_click_on", array(get_string('restorestage32action', 'backup'), 'button'));
349     }
351     /**
352      * Tries to fill the current page form elements with the provided options.
353      *
354      * This step is slow as it spins over each provided option, we are
355      * not expected to have lots of provided options, anyways, is better
356      * to be conservative and wait for the elements to appear rather than
357      * to have false failures.
358      *
359      * @param TableNode $options The backup and restore options or false if no options provided
360      * @return void
361      */
362     protected function fill_backup_restore_form($options) {
364         // Nothing to fill if no options are provided.
365         if (!$options) {
366             return;
367         }
369         // If we find any of the provided options in the current form we should set the value.
370         $datahash = $options->getRowsHash();
371         foreach ($datahash as $locator => $value) {
372             $field = behat_field_manager::get_form_field_from_label($locator, $this);
373             $field->set_value($value);
374         }
375     }
377     /**
378      * Get the options specific to this step of the backup/restore process.
379      *
380      * @param TableNode $options The options table to filter
381      * @param string $step The name of the step
382      * @return TableNode The filtered options table
383      * @throws ExpectationException
384      */
385     protected function get_step_options($options, $step) {
386         // Nothing to fill if no options are provided.
387         if (!$options) {
388             return;
389         }
391         $rows = $options->getRows();
392         $newrows = array();
393         foreach ($rows as $k => $data) {
394             if (count($data) !== 3) {
395                 // Not enough information to guess the page.
396                 throw new ExpectationException("The backup/restore step must be specified for all backup options",
397                     $this->getSession());
398             } else if ($data[0] == $step) {
399                 unset($data[0]);
400                 $newrows[] = $data;
401             }
402         }
403         $pageoptions = new TableNode($newrows);
405         return $pageoptions;
406     }