MDL-56981 behat: Fixed xpath of radio button to contain class
[moodle.git] / backup / util / ui / tests / behat / behat_backup.php
CommitLineData
030a606e
DM
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/>.
16
17/**
18 * Backup and restore actions to help behat feature files writting.
19 *
abe572e3 20 * @package core_backup
030a606e
DM
21 * @category test
22 * @copyright 2013 David MonllaĆ³
23 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26// NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
27
28require_once(__DIR__ . '/../../../../../lib/behat/behat_base.php');
29require_once(__DIR__ . '/../../../../../lib/behat/behat_field_manager.php');
ef03842a 30require_once(__DIR__ . '/../../../../../lib/tests/behat/behat_navigation.php');
030a606e
DM
31
32use Behat\Gherkin\Node\TableNode as TableNode,
33 Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException,
34 Behat\Mink\Exception\ExpectationException as ExpectationException;
35
36/**
37 * Backup-related steps definitions.
38 *
abe572e3 39 * @package core_backup
030a606e
DM
40 * @category test
41 * @copyright 2013 David MonllaĆ³
42 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
43 */
44class behat_backup extends behat_base {
45
ef03842a
TH
46 /**
47 * Follow a link like 'Backup' or 'Import', where the link name comes from
48 * a language string, in the settings nav block of a course.
49 * @param string $langstring the lang string to look for. E.g. 'backup' or 'import'.
50 * @param string $component (optional) second argument to {@link get_string}.
51 */
52 protected function navigate_to_course_settings_link($langstring, $component = '') {
53 $behatnavigation = new behat_navigation();
54 $behatnavigation->setMink($this->getMink());
55 $behatnavigation->i_navigate_to_node_in(get_string($langstring, $component),
56 get_string('courseadministration'));
57 }
58
030a606e 59 /**
c866fe44
SH
60 * Backups the specified course using the provided options. If you are interested in restoring this backup would be
61 * useful to provide a 'Filename' option.
030a606e
DM
62 *
63 * @Given /^I backup "(?P<course_fullname_string>(?:[^"]|\\")*)" course using this options:$/
64 * @param string $backupcourse
65 * @param TableNode $options Backup options or false if no options provided
66 */
67 public function i_backup_course_using_this_options($backupcourse, $options = false) {
030a606e
DM
68 // We can not use other steps here as we don't know where the provided data
69 // table elements are used, and we need to catch exceptions contantly.
70
71 // Go to homepage.
46a2fbc4 72 $this->getSession()->visit($this->locate_path('/?redirect=0'));
030a606e
DM
73
74 // Click the course link.
75 $this->find_link($backupcourse)->click();
76
77 // Click the backup link.
ef03842a 78 $this->navigate_to_course_settings_link('backup');
c1faf86b 79 $this->wait();
030a606e
DM
80
81 // Initial settings.
87a37ebe 82 $this->fill_backup_restore_form($this->get_step_options($options, "Initial"));
dedb9738 83 $this->find_button(get_string('backupstage1action', 'backup'))->press();
c1faf86b 84 $this->wait();
030a606e
DM
85
86 // Schema settings.
87a37ebe 87 $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
dedb9738 88 $this->find_button(get_string('backupstage2action', 'backup'))->press();
c1faf86b 89 $this->wait();
030a606e
DM
90
91 // Confirmation and review, backup filename can also be specified.
87a37ebe 92 $this->fill_backup_restore_form($this->get_step_options($options, "Confirmation"));
dedb9738 93 $this->find_button(get_string('backupstage4action', 'backup'))->press();
030a606e
DM
94
95 // Waiting for it to finish.
c1faf86b 96 $this->wait(self::EXTENDED_TIMEOUT);
030a606e 97
bae30ef3
SH
98 // Last backup continue button.
99 $this->find_button(get_string('backupstage16action', 'backup'))->press();
100 }
101
102 /**
103 * Performs a quick (one click) backup of a course.
104 *
105 * Please note that because you can't set settings with this there is no way to know what the filename
106 * that was produced was. It contains a timestamp making it hard to find.
107 *
108 * @Given /^I perform a quick backup of course "(?P<course_fullname_string>(?:[^"]|\\")*)"$/
109 * @param string $backupcourse
110 */
111 public function i_perform_a_quick_backup_of_course($backupcourse) {
112 // We can not use other steps here as we don't know where the provided data
113 // table elements are used, and we need to catch exceptions contantly.
114
115 // Go to homepage.
46a2fbc4 116 $this->getSession()->visit($this->locate_path('/?redirect=0'));
bae30ef3
SH
117
118 // Click the course link.
119 $this->find_link($backupcourse)->click();
120
121 // Click the backup link.
122 $this->find_link(get_string('backup'))->click();
123 $this->wait();
124
125 // Initial settings.
70e7b634 126 $this->find_button(get_string('jumptofinalstep', 'backup'))->press();
bae30ef3
SH
127 $this->wait();
128
129 // Waiting for it to finish.
130 $this->wait(self::EXTENDED_TIMEOUT);
131
030a606e 132 // Last backup continue button.
dedb9738 133 $this->find_button(get_string('backupstage16action', 'backup'))->press();
030a606e
DM
134 }
135
4bd605da
DM
136 /**
137 * Imports the specified origin course into the other course using the provided options.
138 *
139 * Keeping it separatelly from backup & restore, it the number of
140 * steps and duplicate code becomes bigger a common method should
141 * be generalized.
142 *
143 * @Given /^I import "(?P<from_course_fullname_string>(?:[^"]|\\")*)" course into "(?P<to_course_fullname_string>(?:[^"]|\\")*)" course using this options:$/
144 * @param string $fromcourse
145 * @param string $tocourse
146 * @param TableNode $options
147 */
148 public function i_import_course_into_course($fromcourse, $tocourse, $options = false) {
149
150 // We can not use other steps here as we don't know where the provided data
151 // table elements are used, and we need to catch exceptions contantly.
152
153 // Go to homepage.
46a2fbc4 154 $this->getSession()->visit($this->locate_path('/?redirect=0'));
c1faf86b 155 $this->wait();
4bd605da
DM
156
157 // Click the course link.
158 $this->find_link($tocourse)->click();
c1faf86b 159 $this->wait();
4bd605da 160
38976081 161 // Click the import link.
ef03842a 162 $this->navigate_to_course_settings_link('import');
c1faf86b 163 $this->wait();
4bd605da
DM
164
165 // Select the course.
7519d366
RL
166 $exception = new ExpectationException('"' . $fromcourse . '" course not found in the list of courses to import from',
167 $this->getSession());
4bd605da 168
38976081 169 // The argument should be converted to an xpath literal.
921faad9 170 $fromcourse = behat_context_helper::escape($fromcourse);
38976081
DM
171 $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' ics-results ')]" .
172 "/descendant::tr[contains(., $fromcourse)]" .
00ea74cb 173 "/descendant::input[@type='radio']";
4bd605da 174 $radionode = $this->find('xpath', $xpath, $exception);
788beb67
RT
175 $radiofield = new behat_form_field($this->getSession(), $radionode);
176 $radiofield->set_value(1);
4bd605da 177
dedb9738 178 $this->find_button(get_string('continue'))->press();
c1faf86b 179 $this->wait();
4bd605da
DM
180
181 // Initial settings.
87a37ebe 182 $this->fill_backup_restore_form($this->get_step_options($options, "Initial"));
dedb9738 183 $this->find_button(get_string('importbackupstage1action', 'backup'))->press();
c1faf86b 184 $this->wait();
4bd605da
DM
185
186 // Schema settings.
87a37ebe 187 $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
dedb9738 188 $this->find_button(get_string('importbackupstage2action', 'backup'))->press();
c1faf86b 189 $this->wait();
4bd605da
DM
190
191 // Run it.
dedb9738 192 $this->find_button(get_string('importbackupstage4action', 'backup'))->press();
c1faf86b 193 $this->wait(self::EXTENDED_TIMEOUT);
4bd605da
DM
194
195 // Continue and redirect to 'to' course.
dedb9738 196 $this->find_button(get_string('continue'))->press();
4bd605da
DM
197 }
198
030a606e 199 /**
c866fe44
SH
200 * Restores the backup into the specified course and the provided options.
201 *
202 * You should be in the 'Restore' page where the backup is.
030a606e
DM
203 *
204 * @Given /^I restore "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into "(?P<existing_course_fullname_string>(?:[^"]|\\")*)" course using this options:$/
205 * @param string $backupfilename
206 * @param string $existingcourse
207 * @param TableNode $options Restore forms options or false if no options provided
208 */
209 public function i_restore_backup_into_course_using_this_options($backupfilename, $existingcourse, $options = false) {
210
211 // Confirm restore.
212 $this->select_backup($backupfilename);
213
38976081 214 // The argument should be converted to an xpath literal.
921faad9 215 $existingcourse = behat_context_helper::escape($existingcourse);
38976081 216
030a606e 217 // Selecting the specified course (we can not call behat_forms::select_radio here as is in another behat subcontext).
38976081 218 $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
813afc67 219 "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' restore-course-search ')]" .
38976081 220 "/descendant::tr[contains(., $existingcourse)]" .
00ea74cb 221 "/descendant::input[@type='radio']");
030a606e
DM
222 $radionode->click();
223
224 // Pressing the continue button of the restore into an existing course section.
38976081 225 $continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
dedb9738 226 "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
030a606e
DM
227 $continuenode->click();
228 $this->wait();
229
230 // Common restore process using provided key/value options.
231 $this->process_restore($options);
232 }
233
234 /**
c866fe44
SH
235 * Restores the specified backup into a new course using the provided options.
236 *
237 * You should be in the 'Restore' page where the backup is.
030a606e
DM
238 *
239 * @Given /^I restore "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into a new course using this options:$/
240 * @param string $backupfilename
241 * @param TableNode $options Restore forms options or false if no options provided
242 */
243 public function i_restore_backup_into_a_new_course_using_this_options($backupfilename, $options = false) {
244
245 // Confirm restore.
246 $this->select_backup($backupfilename);
247
248 // The first category in the list.
38976081 249 $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
4ea8f814 250 "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' restore-course-search ')]" .
00ea74cb 251 "/descendant::input[@type='radio']");
030a606e
DM
252 $radionode->click();
253
254 // Pressing the continue button of the restore into an existing course section.
38976081 255 $continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
dedb9738 256 "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
030a606e
DM
257 $continuenode->click();
258 $this->wait();
259
260 // Common restore process using provided key/value options.
261 $this->process_restore($options);
262 }
263
264 /**
c866fe44
SH
265 * Merges the backup into the current course using the provided restore options.
266 *
267 * You should be in the 'Restore' page where the backup is.
030a606e
DM
268 *
269 * @Given /^I merge "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into the current course using this options:$/
270 * @param string $backupfilename
271 * @param TableNode $options Restore forms options or false if no options provided
272 */
273 public function i_merge_backup_into_the_current_course($backupfilename, $options = false) {
274
275 // Confirm restore.
276 $this->select_backup($backupfilename);
277
278 // Merge without deleting radio option.
38976081 279 $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
00ea74cb 280 "/descendant::input[@type='radio'][@name='target'][@value='1']");
030a606e
DM
281 $radionode->click();
282
283 // Pressing the continue button of the restore merging section.
38976081 284 $continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
dedb9738 285 "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
030a606e
DM
286 $continuenode->click();
287 $this->wait();
288
289 // Common restore process using provided key/value options.
290 $this->process_restore($options);
291 }
292
293 /**
c866fe44
SH
294 * Merges the backup into the current course after deleting this contents, using the provided restore options.
295 *
296 * You should be in the 'Restore' page where the backup is.
030a606e
DM
297 *
298 * @Given /^I merge "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into the current course after deleting it's contents using this options:$/
299 * @param string $backupfilename
300 * @param TableNode $options Restore forms options or false if no options provided
301 */
302 public function i_merge_backup_into_current_course_deleting_its_contents($backupfilename, $options = false) {
303
304 // Confirm restore.
305 $this->select_backup($backupfilename);
306
307 // Delete contents radio option.
38976081 308 $radionode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
00ea74cb 309 "/descendant::input[@type='radio'][@name='target'][@value='0']");
030a606e
DM
310 $radionode->click();
311
312 // Pressing the continue button of the restore merging section.
38976081 313 $continuenode = $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
dedb9738 314 "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']");
030a606e
DM
315 $continuenode->click();
316 $this->wait();
317
318 // Common restore process using provided key/value options.
319 $this->process_restore($options);
320 }
321
322 /**
323 * Selects the backup to restore.
324 *
325 * @throws ExpectationException
326 * @param string $backupfilename
327 * @return void
328 */
329 protected function select_backup($backupfilename) {
330
331 // Using xpath as there are other restore links before this one.
7519d366
RL
332 $exception = new ExpectationException('The "' . $backupfilename . '" backup file can not be found in this page',
333 $this->getSession());
38976081
DM
334
335 // The argument should be converted to an xpath literal.
921faad9 336 $backupfilename = behat_context_helper::escape($backupfilename);
38976081
DM
337
338 $xpath = "//tr[contains(., $backupfilename)]/descendant::a[contains(., '" . get_string('restore') . "')]";
030a606e
DM
339 $restorelink = $this->find('xpath', $xpath, $exception);
340 $restorelink->click();
341
342 // Confirm the backup contents.
dedb9738 343 $restore = $this->find_button(get_string('continue'))->press();
030a606e
DM
344 }
345
346 /**
347 * Executes the common steps of all restore processes.
348 *
349 * @param TableNode $options The backup and restore options or false if no options provided
350 * @return void
351 */
352 protected function process_restore($options) {
353
4bd605da
DM
354 // We can not use other steps here as we don't know where the provided data
355 // table elements are used, and we need to catch exceptions contantly.
356
030a606e 357 // Settings.
87a37ebe 358 $this->fill_backup_restore_form($this->get_step_options($options, "Settings"));
dedb9738 359 $this->find_button(get_string('restorestage4action', 'backup'))->press();
c1faf86b 360 $this->wait();
030a606e
DM
361
362 // Schema.
87a37ebe 363 $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
dedb9738 364 $this->find_button(get_string('restorestage8action', 'backup'))->press();
c1faf86b 365 $this->wait();
030a606e
DM
366
367 // Review, no options here.
dedb9738 368 $this->find_button(get_string('restorestage16action', 'backup'))->press();
d1e55a47 369 $this->wait();
030a606e
DM
370
371 // Last restore continue button, redirected to restore course after this.
dedb9738 372 $this->find_button(get_string('restorestage32action', 'backup'))->press();
c1faf86b
DM
373
374 // Long wait when waiting for the restore to finish.
375 $this->wait(self::EXTENDED_TIMEOUT);
030a606e
DM
376 }
377
378 /**
379 * Tries to fill the current page form elements with the provided options.
380 *
381 * This step is slow as it spins over each provided option, we are
382 * not expected to have lots of provided options, anyways, is better
383 * to be conservative and wait for the elements to appear rather than
384 * to have false failures.
385 *
386 * @param TableNode $options The backup and restore options or false if no options provided
387 * @return void
388 */
389 protected function fill_backup_restore_form($options) {
390
391 // Nothing to fill if no options are provided.
392 if (!$options) {
393 return;
394 }
395
396 // If we find any of the provided options in the current form we should set the value.
397 $datahash = $options->getRowsHash();
398 foreach ($datahash as $locator => $value) {
87a37ebe
AN
399 $field = behat_field_manager::get_form_field_from_label($locator, $this);
400 $field->set_value($value);
401 }
402 }
030a606e 403
87a37ebe
AN
404 /**
405 * Get the options specific to this step of the backup/restore process.
406 *
407 * @param TableNode $options The options table to filter
408 * @param string $step The name of the step
409 * @return TableNode The filtered options table
410 * @throws ExpectationException
411 */
412 protected function get_step_options($options, $step) {
413 // Nothing to fill if no options are provided.
414 if (!$options) {
415 return;
416 }
030a606e 417
87a37ebe
AN
418 $rows = $options->getRows();
419 $newrows = array();
420 foreach ($rows as $k => $data) {
421 if (count($data) !== 3) {
422 // Not enough information to guess the page.
7519d366
RL
423 throw new ExpectationException("The backup/restore step must be specified for all backup options",
424 $this->getSession());
87a37ebe
AN
425 } else if ($data[0] == $step) {
426 unset($data[0]);
427 $newrows[] = $data;
030a606e
DM
428 }
429 }
42ad096f
RT
430 $pageoptions = new TableNode($newrows);
431
87a37ebe 432 return $pageoptions;
030a606e
DM
433 }
434
87a37ebe 435
030a606e 436 /**
c1faf86b 437 * Waits until the DOM and the page Javascript code is ready.
030a606e 438 *
c1faf86b 439 * @param int $timeout The number of seconds that we wait.
030a606e
DM
440 * @return void
441 */
c1faf86b 442 protected function wait($timeout = false) {
030a606e 443
38976081
DM
444 if (!$this->running_javascript()) {
445 return;
446 }
447
c1faf86b
DM
448 if (!$timeout) {
449 $timeout = self::TIMEOUT;
450 }
451
452 $this->getSession()->wait($timeout * 1000, self::PAGE_READY_JS);
030a606e
DM
453 }
454
455}