Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 3.11.x will end 14 Nov 2022 (12 months plus 6 months extension).
  • Bug fixes for security issues in 3.11.x will end 13 Nov 2023 (18 months plus 12 months extension).
  • PHP version: minimum PHP 7.3.0 Note: minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is supported too.

Differences Between: [Versions 311 and 400] [Versions 311 and 401] [Versions 311 and 402] [Versions 311 and 403]

   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   *
  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   */
  25  
  26  // NOTE: no MOODLE_INTERNAL test here, this file may be required by behat before including /config.php.
  27  
  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');
  31  
  32  use 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   *
  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 {
  45  
  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.
  57  
  58          // Go to homepage.
  59          $this->execute('behat_general::i_visit', ['/?redirect=0']);
  60          $this->execute("behat_general::wait_until_the_page_is_ready");
  61  
  62          // Click the course link.
  63          $this->execute("behat_general::click_link", $backupcourse);
  64  
  65          // Click the backup link.
  66          $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", get_string('backup'));
  67  
  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'));
  71  
  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'));
  75  
  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'));
  79  
  80          // Waiting for it to finish.
  81          $this->execute("behat_general::wait_until_the_page_is_ready");
  82  
  83          // Last backup continue button.
  84          $this->execute("behat_general::i_click_on", array(get_string('backupstage16action', 'backup'), 'button'));
  85      }
  86  
  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.
  99  
 100          // Go to homepage.
 101          $this->execute('behat_general::i_visit', ['/?redirect=0']);
 102  
 103          // Click the course link.
 104          $this->execute("behat_general::click_link", $backupcourse);
 105  
 106          // Click the backup link.
 107          $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", get_string('backup'));
 108  
 109          // Initial settings.
 110          $this->execute("behat_forms::press_button", get_string('jumptofinalstep', 'backup'));
 111  
 112          // Waiting for it to finish.
 113          $this->execute("behat_general::wait_until_the_page_is_ready");
 114  
 115          // Last backup continue button.
 116          $this->execute("behat_general::i_click_on", array(get_string('backupstage16action', 'backup'), 'button'));
 117      }
 118  
 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) {
 132  
 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.
 135  
 136          // Go to homepage.
 137          $this->execute('behat_general::i_visit', ['/?redirect=0']);
 138          $this->execute("behat_general::wait_until_the_page_is_ready");
 139  
 140          // Click the course link.
 141          $this->execute("behat_general::click_link", $tocourse);
 142  
 143          // Click the import link.
 144          $this->execute("behat_navigation::i_navigate_to_in_current_page_administration", get_string('import'));
 145  
 146          // Select the course.
 147          $fromcourse = behat_context_helper::escape($fromcourse);
 148          $xpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' ics-results ')]" .
 149              "/descendant::tr[contains(., $fromcourse)]" .
 150              "/descendant::input[@type='radio']";
 151          $this->execute('behat_forms::i_set_the_field_with_xpath_to', [$xpath, 1]);
 152  
 153          $this->execute("behat_forms::press_button", get_string('continue'));
 154  
 155          // Initial settings.
 156          $this->fill_backup_restore_form($this->get_step_options($options, "Initial"));
 157          $this->execute("behat_forms::press_button", get_string('importbackupstage1action', 'backup'));
 158  
 159          // Schema settings.
 160          $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
 161          $this->execute("behat_forms::press_button", get_string('importbackupstage2action', 'backup'));
 162  
 163          // Run it.
 164          $this->execute("behat_forms::press_button", get_string('importbackupstage4action', 'backup'));
 165  
 166          // Wait to ensure restore is complete.
 167          $this->execute("behat_general::wait_until_the_page_is_ready");
 168  
 169          // Continue and redirect to 'to' course.
 170          $this->execute("behat_general::i_click_on", array(get_string('continue'), 'button'));
 171      }
 172  
 173      /**
 174       * Restores the backup into the specified course and the provided options.
 175       *
 176       * You should be in the 'Restore' page where the backup is.
 177       *
 178       * @Given /^I restore "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into "(?P<existing_course_fullname_string>(?:[^"]|\\")*)" course using this options:$/
 179       * @param string $backupfilename
 180       * @param string $existingcourse
 181       * @param TableNode $options Restore forms options or false if no options provided
 182       */
 183      public function i_restore_backup_into_course_using_this_options($backupfilename, $existingcourse, $options = false) {
 184  
 185          // Confirm restore.
 186          $this->select_backup($backupfilename);
 187  
 188          // The argument should be converted to an xpath literal.
 189          $existingcourse = behat_context_helper::escape($existingcourse);
 190  
 191          // Selecting the specified course (we can not call behat_forms::select_radio here as is in another behat subcontext).
 192          $radionodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
 193              "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' restore-course-search ')]" .
 194              "/descendant::tr[contains(., $existingcourse)]" .
 195              "/descendant::input[@type='radio']";
 196          $this->execute("behat_general::i_click_on", array($radionodexpath, 'xpath_element'));
 197  
 198          // Pressing the continue button of the restore into an existing course section.
 199          $continuenodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-existing-course ')]" .
 200              "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']";
 201          $this->execute("behat_general::i_click_on", array($continuenodexpath, 'xpath_element'));
 202  
 203          // Common restore process using provided key/value options.
 204          $this->process_restore($options);
 205      }
 206  
 207      /**
 208       * Restores the specified backup into a new course using the provided options.
 209       *
 210       * You should be in the 'Restore' page where the backup is.
 211       *
 212       * @Given /^I restore "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into a new course using this options:$/
 213       * @param string $backupfilename
 214       * @param TableNode $options Restore forms options or false if no options provided
 215       */
 216      public function i_restore_backup_into_a_new_course_using_this_options($backupfilename, $options = false) {
 217  
 218          // Confirm restore.
 219          $this->select_backup($backupfilename);
 220  
 221          // The first category in the list.
 222          $radionodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
 223              "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' restore-course-search ')]" .
 224              "/descendant::input[@type='radio']";
 225          $this->execute("behat_general::i_click_on", array($radionodexpath, 'xpath_element'));
 226  
 227          // Pressing the continue button of the restore into an existing course section.
 228          $continuenodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), ' bcs-new-course ')]" .
 229              "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']";
 230          $this->execute("behat_general::i_click_on", array($continuenodexpath, 'xpath_element'));
 231  
 232          // Common restore process using provided key/value options.
 233          $this->process_restore($options);
 234      }
 235  
 236      /**
 237       * Merges the backup into the current course using the provided restore options.
 238       *
 239       * You should be in the 'Restore' page where the backup is.
 240       *
 241       * @Given /^I merge "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into the current course using this options:$/
 242       * @param string $backupfilename
 243       * @param TableNode $options Restore forms options or false if no options provided
 244       */
 245      public function i_merge_backup_into_the_current_course($backupfilename, $options = false) {
 246  
 247          // Confirm restore.
 248          $this->select_backup($backupfilename);
 249  
 250          // Merge without deleting radio option.
 251          $radionodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
 252              "/descendant::input[@type='radio'][@name='target'][@value='1']";
 253          $this->execute("behat_general::i_click_on", array($radionodexpath, 'xpath_element'));
 254  
 255          // Pressing the continue button of the restore merging section.
 256          $continuenodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
 257              "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']";
 258          $this->execute("behat_general::i_click_on", array($continuenodexpath, 'xpath_element'));
 259  
 260          // Common restore process using provided key/value options.
 261          $this->process_restore($options);
 262      }
 263  
 264      /**
 265       * Merges the backup into the current course after deleting this contents, using the provided restore options.
 266       *
 267       * You should be in the 'Restore' page where the backup is.
 268       *
 269       * @Given /^I merge "(?P<backup_filename_string>(?:[^"]|\\")*)" backup into the current course after deleting it's contents 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_current_course_deleting_its_contents($backupfilename, $options = false) {
 274  
 275          // Confirm restore.
 276          $this->select_backup($backupfilename);
 277  
 278          // Delete contents radio option.
 279          $radionodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
 280              "/descendant::input[@type='radio'][@name='target'][@value='0']";
 281          $this->execute("behat_general::i_click_on", array($radionodexpath, 'xpath_element'));
 282  
 283          // Pressing the continue button of the restore merging section.
 284          $continuenodexpath = "//div[contains(concat(' ', normalize-space(@class), ' '), 'bcs-current-course')]" .
 285              "/descendant::input[@type='submit'][@value='" . get_string('continue') . "']";
 286          $this->execute("behat_general::i_click_on", array($continuenodexpath, 'xpath_element'));
 287  
 288          // Common restore process using provided key/value options.
 289          $this->process_restore($options);
 290      }
 291  
 292      /**
 293       * Selects the backup to restore.
 294       *
 295       * @throws ExpectationException
 296       * @param string $backupfilename
 297       * @return void
 298       */
 299      protected function select_backup($backupfilename) {
 300  
 301          // Using xpath as there are other restore links before this one.
 302          $exception = new ExpectationException('The "' . $backupfilename . '" backup file can not be found in this page',
 303              $this->getSession());
 304  
 305          // The argument should be converted to an xpath literal.
 306          $backupfilename = behat_context_helper::escape($backupfilename);
 307  
 308          $xpath = "//tr[contains(., $backupfilename)]/descendant::a[contains(., '" . get_string('restore') . "')]";
 309          $restorelink = $this->find('xpath', $xpath, $exception);
 310          $restorelink->click();
 311  
 312          // Confirm the backup contents.
 313          $this->find_button(get_string('continue'))->press();
 314      }
 315  
 316      /**
 317       * Executes the common steps of all restore processes.
 318       *
 319       * @param TableNode $options The backup and restore options or false if no options provided
 320       * @return void
 321       */
 322      protected function process_restore($options) {
 323  
 324          // We can not use other steps here as we don't know where the provided data
 325          // table elements are used, and we need to catch exceptions contantly.
 326  
 327          // Settings.
 328          $this->fill_backup_restore_form($this->get_step_options($options, "Settings"));
 329          $this->execute("behat_forms::press_button", get_string('restorestage4action', 'backup'));
 330  
 331          // Schema.
 332          $this->fill_backup_restore_form($this->get_step_options($options, "Schema"));
 333          $this->execute("behat_forms::press_button", get_string('restorestage8action', 'backup'));
 334  
 335          // Review, no options here.
 336          $this->execute("behat_forms::press_button", get_string('restorestage16action', 'backup'));
 337  
 338          // Wait till the final button is visible.
 339          $this->execute("behat_general::wait_until_the_page_is_ready");
 340  
 341          // Last restore continue button, redirected to restore course after this.
 342          $this->execute("behat_general::i_click_on", array(get_string('restorestage32action', 'backup'), 'button'));
 343      }
 344  
 345      /**
 346       * Tries to fill the current page form elements with the provided options.
 347       *
 348       * This step is slow as it spins over each provided option, we are
 349       * not expected to have lots of provided options, anyways, is better
 350       * to be conservative and wait for the elements to appear rather than
 351       * to have false failures.
 352       *
 353       * @param TableNode $options The backup and restore options or false if no options provided
 354       * @return void
 355       */
 356      protected function fill_backup_restore_form($options) {
 357  
 358          // Nothing to fill if no options are provided.
 359          if (!$options) {
 360              return;
 361          }
 362  
 363          // If we find any of the provided options in the current form we should set the value.
 364          $datahash = $options->getRowsHash();
 365          foreach ($datahash as $locator => $value) {
 366              $field = behat_field_manager::get_form_field_from_label($locator, $this);
 367              $field->set_value($value);
 368          }
 369      }
 370  
 371      /**
 372       * Get the options specific to this step of the backup/restore process.
 373       *
 374       * @param TableNode $options The options table to filter
 375       * @param string $step The name of the step
 376       * @return TableNode The filtered options table
 377       * @throws ExpectationException
 378       */
 379      protected function get_step_options($options, $step) {
 380          // Nothing to fill if no options are provided.
 381          if (!$options) {
 382              return;
 383          }
 384  
 385          $rows = $options->getRows();
 386          $newrows = array();
 387          foreach ($rows as $k => $data) {
 388              if (count($data) !== 3) {
 389                  // Not enough information to guess the page.
 390                  throw new ExpectationException("The backup/restore step must be specified for all backup options",
 391                      $this->getSession());
 392              } else if ($data[0] == $step) {
 393                  unset($data[0]);
 394                  $newrows[] = $data;
 395              }
 396          }
 397          $pageoptions = new TableNode($newrows);
 398  
 399          return $pageoptions;
 400      }
 401  }