[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 * Behat course-related steps definitions. 19 * 20 * @package core_course 21 * @category test 22 * @copyright 2012 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 30 use Behat\Gherkin\Node\TableNode as TableNode, 31 Behat\Mink\Exception\ExpectationException as ExpectationException, 32 Behat\Mink\Exception\DriverException as DriverException, 33 Behat\Mink\Exception\ElementNotFoundException as ElementNotFoundException; 34 35 /** 36 * Course-related steps definitions. 37 * 38 * @package core_course 39 * @category test 40 * @copyright 2012 David Monllaó 41 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 42 */ 43 class behat_course extends behat_base { 44 45 /** 46 * Turns editing mode on. 47 * @Given /^I turn editing mode on$/ 48 */ 49 public function i_turn_editing_mode_on() { 50 51 $this->execute("behat_forms::press_button", get_string('turneditingon')); 52 } 53 54 /** 55 * Turns editing mode off. 56 * @Given /^I turn editing mode off$/ 57 */ 58 public function i_turn_editing_mode_off() { 59 60 $this->execute("behat_forms::press_button", get_string('turneditingoff')); 61 } 62 63 /** 64 * Creates a new course with the provided table data matching course settings names with the desired values. 65 * 66 * @Given /^I create a course with:$/ 67 * @param TableNode $table The course data 68 */ 69 public function i_create_a_course_with(TableNode $table) { 70 71 // Go to course management page. 72 $this->i_go_to_the_courses_management_page(); 73 // Ensure you are on course management page. 74 $this->execute("behat_course::i_should_see_the_courses_management_page", get_string('categories')); 75 76 // Select Miscellaneous category. 77 $this->i_click_on_category_in_the_management_interface(get_string('miscellaneous')); 78 $this->execute("behat_course::i_should_see_the_courses_management_page", get_string('categoriesandcoures')); 79 80 // Click create new course. 81 $this->execute('behat_general::i_click_on_in_the', 82 array(get_string('createnewcourse'), "link", "#course-listing", "css_element") 83 ); 84 85 // If the course format is one of the fields we change how we 86 // fill the form as we need to wait for the form to be set. 87 $rowshash = $table->getRowsHash(); 88 $formatfieldrefs = array(get_string('format'), 'format', 'id_format'); 89 foreach ($formatfieldrefs as $fieldref) { 90 if (!empty($rowshash[$fieldref])) { 91 $formatfield = $fieldref; 92 } 93 } 94 95 // Setting the format separately. 96 if (!empty($formatfield)) { 97 98 // Removing the format field from the TableNode. 99 $rows = $table->getRows(); 100 $formatvalue = $rowshash[$formatfield]; 101 foreach ($rows as $key => $row) { 102 if ($row[0] == $formatfield) { 103 unset($rows[$key]); 104 } 105 } 106 $table = new TableNode($rows); 107 108 // Adding a forced wait until editors are loaded as otherwise selenium sometimes tries clicks on the 109 // format field when the editor is being rendered and the click misses the field coordinates. 110 $this->execute("behat_forms::i_expand_all_fieldsets"); 111 112 $this->execute("behat_forms::i_set_the_field_to", array($formatfield, $formatvalue)); 113 } 114 115 // Set form fields. 116 $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $table); 117 118 // Save course settings. 119 $this->execute("behat_forms::press_button", get_string('savechangesanddisplay')); 120 121 } 122 123 /** 124 * Goes to the system courses/categories management page. 125 * 126 * @Given /^I go to the courses management page$/ 127 */ 128 public function i_go_to_the_courses_management_page() { 129 130 $parentnodes = get_string('administrationsite') . ' > ' . get_string('courses', 'admin'); 131 132 // Go to home page. 133 $this->execute("behat_general::i_am_on_homepage"); 134 135 // Navigate to course management page via navigation block. 136 $this->execute("behat_navigation::i_navigate_to_node_in", 137 array(get_string('coursemgmt', 'admin'), $parentnodes) 138 ); 139 140 } 141 142 /** 143 * Adds the selected activity/resource filling the form data with the specified field/value pairs. Sections 0 and 1 are also allowed on frontpage. 144 * 145 * @When /^I add a "(?P<activity_or_resource_name_string>(?:[^"]|\\")*)" to section "(?P<section_number>\d+)" and I fill the form with:$/ 146 * @param string $activity The activity name 147 * @param int $section The section number 148 * @param TableNode $data The activity field/value data 149 */ 150 public function i_add_to_section_and_i_fill_the_form_with($activity, $section, TableNode $data) { 151 152 // Add activity to section. 153 $this->execute("behat_course::i_add_to_section", 154 array($this->escape($activity), $this->escape($section)) 155 ); 156 157 // Wait to be redirected. 158 $this->execute('behat_general::wait_until_the_page_is_ready'); 159 160 // Set form fields. 161 $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $data); 162 163 // Save course settings. 164 $this->execute("behat_forms::press_button", get_string('savechangesandreturntocourse')); 165 } 166 167 /** 168 * Opens the activity chooser and opens the activity/resource form page. Sections 0 and 1 are also allowed on frontpage. 169 * 170 * @Given /^I add a "(?P<activity_or_resource_name_string>(?:[^"]|\\")*)" to section "(?P<section_number>\d+)"$/ 171 * @throws ElementNotFoundException Thrown by behat_base::find 172 * @param string $activity 173 * @param int $section 174 */ 175 public function i_add_to_section($activity, $section) { 176 177 if ($this->getSession()->getPage()->find('css', 'body#page-site-index') && (int)$section <= 1) { 178 // We are on the frontpage. 179 if ($section) { 180 // Section 1 represents the contents on the frontpage. 181 $sectionxpath = "//body[@id='page-site-index']/descendant::div[contains(concat(' ',normalize-space(@class),' '),' sitetopic ')]"; 182 } else { 183 // Section 0 represents "Site main menu" block. 184 $sectionxpath = "//div[contains(concat(' ',normalize-space(@class),' '),' block_site_main_menu ')]"; 185 } 186 } else { 187 // We are inside the course. 188 $sectionxpath = "//li[@id='section-" . $section . "']"; 189 } 190 191 $activityliteral = behat_context_helper::escape(ucfirst($activity)); 192 193 if ($this->running_javascript()) { 194 195 // Clicks add activity or resource section link. 196 $sectionxpath = $sectionxpath . "/descendant::div[@class='section-modchooser']/span/a"; 197 $sectionnode = $this->find('xpath', $sectionxpath); 198 $sectionnode->click(); 199 200 // Clicks the selected activity if it exists. 201 $activityxpath = "//div[@id='chooseform']/descendant::label" . 202 "/descendant::span[contains(concat(' ', normalize-space(@class), ' '), ' typename ')]" . 203 "[normalize-space(.)=$activityliteral]" . 204 "/parent::label/child::input"; 205 $activitynode = $this->find('xpath', $activityxpath); 206 $activitynode->doubleClick(); 207 208 } else { 209 // Without Javascript. 210 211 // Selecting the option from the select box which contains the option. 212 $selectxpath = $sectionxpath . "/descendant::div[contains(concat(' ', normalize-space(@class), ' '), ' section_add_menus ')]" . 213 "/descendant::select[option[normalize-space(.)=$activityliteral]]"; 214 $selectnode = $this->find('xpath', $selectxpath); 215 $selectnode->selectOption($activity); 216 217 // Go button. 218 $gobuttonxpath = $selectxpath . "/ancestor::form/descendant::input[@type='submit']"; 219 $gobutton = $this->find('xpath', $gobuttonxpath); 220 $gobutton->click(); 221 } 222 223 } 224 225 226 /** 227 * Opens a section edit menu if it is not already opened. 228 * 229 * @Given /^I open section "(?P<section_number>\d+)" edit menu$/ 230 * @throws DriverException The step is not available when Javascript is disabled 231 * @param string $sectionnumber 232 */ 233 public function i_open_section_edit_menu($sectionnumber) { 234 if (!$this->running_javascript()) { 235 throw new DriverException('Section edit menu not available when Javascript is disabled'); 236 } 237 238 // Wait for section to be available, before clicking on the menu. 239 $this->i_wait_until_section_is_available($sectionnumber); 240 241 // If it is already opened we do nothing. 242 $xpath = $this->section_exists($sectionnumber); 243 $xpath .= "/descendant::div[contains(@class, 'section-actions')]/descendant::a[contains(@class, 'textmenu')]"; 244 245 $exception = new ExpectationException('Section "' . $sectionnumber . '" was not found', $this->getSession()); 246 $menu = $this->find('xpath', $xpath, $exception); 247 $menu->click(); 248 $this->i_wait_until_section_is_available($sectionnumber); 249 } 250 251 /** 252 * Deletes course section. 253 * 254 * @Given /^I delete section "(?P<section_number>\d+)"$/ 255 * @param int $sectionnumber The section number 256 */ 257 public function i_delete_section($sectionnumber) { 258 // Ensures the section exists. 259 $xpath = $this->section_exists($sectionnumber); 260 261 // We need to know the course format as the text strings depends on them. 262 $courseformat = $this->get_course_format(); 263 if (get_string_manager()->string_exists('deletesection', $courseformat)) { 264 $strdelete = get_string('deletesection', $courseformat); 265 } else { 266 $strdelete = get_string('deletesection'); 267 } 268 269 // If javascript is on, link is inside a menu. 270 if ($this->running_javascript()) { 271 $this->i_open_section_edit_menu($sectionnumber); 272 } 273 274 // Click on delete link. 275 $this->execute('behat_general::i_click_on_in_the', 276 array($strdelete, "link", $this->escape($xpath), "xpath_element") 277 ); 278 279 } 280 281 /** 282 * Turns course section highlighting on. 283 * 284 * @Given /^I turn section "(?P<section_number>\d+)" highlighting on$/ 285 * @param int $sectionnumber The section number 286 */ 287 public function i_turn_section_highlighting_on($sectionnumber) { 288 289 // Ensures the section exists. 290 $xpath = $this->section_exists($sectionnumber); 291 292 // If javascript is on, link is inside a menu. 293 if ($this->running_javascript()) { 294 $this->i_open_section_edit_menu($sectionnumber); 295 } 296 297 // Click on highlight topic link. 298 $this->execute('behat_general::i_click_on_in_the', 299 array(get_string('markthistopic'), "link", $this->escape($xpath), "xpath_element") 300 ); 301 } 302 303 /** 304 * Turns course section highlighting off. 305 * 306 * @Given /^I turn section "(?P<section_number>\d+)" highlighting off$/ 307 * @param int $sectionnumber The section number 308 */ 309 public function i_turn_section_highlighting_off($sectionnumber) { 310 311 // Ensures the section exists. 312 $xpath = $this->section_exists($sectionnumber); 313 314 // If javascript is on, link is inside a menu. 315 if ($this->running_javascript()) { 316 $this->i_open_section_edit_menu($sectionnumber); 317 } 318 319 // Click on un-highlight topic link. 320 $this->execute('behat_general::i_click_on_in_the', 321 array(get_string('markedthistopic'), "link", $this->escape($xpath), "xpath_element") 322 ); 323 } 324 325 /** 326 * Shows the specified hidden section. You need to be in the course page and on editing mode. 327 * 328 * @Given /^I show section "(?P<section_number>\d+)"$/ 329 * @param int $sectionnumber 330 */ 331 public function i_show_section($sectionnumber) { 332 $showlink = $this->show_section_icon_exists($sectionnumber); 333 334 // Ensure section edit menu is open before interacting with it. 335 if ($this->running_javascript()) { 336 $this->i_open_section_edit_menu($sectionnumber); 337 } 338 $showlink->click(); 339 340 if ($this->running_javascript()) { 341 $this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS); 342 $this->i_wait_until_section_is_available($sectionnumber); 343 } 344 } 345 346 /** 347 * Hides the specified visible section. You need to be in the course page and on editing mode. 348 * 349 * @Given /^I hide section "(?P<section_number>\d+)"$/ 350 * @param int $sectionnumber 351 */ 352 public function i_hide_section($sectionnumber) { 353 $hidelink = $this->hide_section_icon_exists($sectionnumber); 354 355 // Ensure section edit menu is open before interacting with it. 356 if ($this->running_javascript()) { 357 $this->i_open_section_edit_menu($sectionnumber); 358 } 359 $hidelink->click(); 360 361 if ($this->running_javascript()) { 362 $this->getSession()->wait(self::TIMEOUT * 1000, self::PAGE_READY_JS); 363 $this->i_wait_until_section_is_available($sectionnumber); 364 } 365 } 366 367 /** 368 * Go to editing section page for specified section number. You need to be in the course page and on editing mode. 369 * 370 * @Given /^I edit the section "(?P<section_number>\d+)"$/ 371 * @param int $sectionnumber 372 */ 373 public function i_edit_the_section($sectionnumber) { 374 // If javascript is on, link is inside a menu. 375 if ($this->running_javascript()) { 376 $this->i_open_section_edit_menu($sectionnumber); 377 } 378 379 // We need to know the course format as the text strings depends on them. 380 $courseformat = $this->get_course_format(); 381 if (get_string_manager()->string_exists('editsection', $courseformat)) { 382 $stredit = get_string('editsection', $courseformat); 383 } else { 384 $stredit = get_string('editsection'); 385 } 386 387 // Click on un-highlight topic link. 388 $this->execute('behat_general::i_click_on_in_the', 389 array($stredit, "link", "#section-" . $sectionnumber, "css_element") 390 ); 391 392 } 393 394 /** 395 * Edit specified section and fill the form data with the specified field/value pairs. 396 * 397 * @When /^I edit the section "(?P<section_number>\d+)" and I fill the form with:$/ 398 * @param int $sectionnumber The section number 399 * @param TableNode $data The activity field/value data 400 */ 401 public function i_edit_the_section_and_i_fill_the_form_with($sectionnumber, TableNode $data) { 402 403 // Edit given section. 404 $this->execute("behat_course::i_edit_the_section", $sectionnumber); 405 406 // Set form fields. 407 $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $data); 408 409 // Save section settings. 410 $this->execute("behat_forms::press_button", get_string('savechanges')); 411 } 412 413 /** 414 * Checks if the specified course section hightlighting is turned on. You need to be in the course page on editing mode. 415 * 416 * @Then /^section "(?P<section_number>\d+)" should be highlighted$/ 417 * @throws ExpectationException 418 * @param int $sectionnumber The section number 419 */ 420 public function section_should_be_highlighted($sectionnumber) { 421 422 // Ensures the section exists. 423 $xpath = $this->section_exists($sectionnumber); 424 425 // The important checking, we can not check the img. 426 $xpath = $xpath . "/descendant::img[contains(@src, 'marked')]"; 427 $exception = new ExpectationException('The "' . $sectionnumber . '" section is not highlighted', $this->getSession()); 428 $this->find('xpath', $xpath, $exception); 429 } 430 431 /** 432 * Checks if the specified course section highlighting is turned off. You need to be in the course page on editing mode. 433 * 434 * @Then /^section "(?P<section_number>\d+)" should not be highlighted$/ 435 * @throws ExpectationException 436 * @param int $sectionnumber The section number 437 */ 438 public function section_should_not_be_highlighted($sectionnumber) { 439 440 // We only catch ExpectationException, ElementNotFoundException should be thrown if the specified section does not exist. 441 try { 442 $this->section_should_be_highlighted($sectionnumber); 443 } catch (ExpectationException $e) { 444 // ExpectedException means that it is not highlighted. 445 return; 446 } 447 448 throw new ExpectationException('The "' . $sectionnumber . '" section is highlighted', $this->getSession()); 449 } 450 451 /** 452 * Checks that the specified section is visible. You need to be in the course page. It can be used being logged as a student and as a teacher on editing mode. 453 * 454 * @Then /^section "(?P<section_number>\d+)" should be hidden$/ 455 * @throws ExpectationException 456 * @throws ElementNotFoundException Thrown by behat_base::find 457 * @param int $sectionnumber 458 */ 459 public function section_should_be_hidden($sectionnumber) { 460 461 $sectionxpath = $this->section_exists($sectionnumber); 462 463 // Preventive in case there is any action in progress. 464 // Adding it here because we are interacting (click) with 465 // the elements, not necessary when we just find(). 466 $this->i_wait_until_section_is_available($sectionnumber); 467 468 // Section should be hidden. 469 $exception = new ExpectationException('The section is not hidden', $this->getSession()); 470 $this->find('xpath', $sectionxpath . "[contains(concat(' ', normalize-space(@class), ' '), ' hidden ')]", $exception); 471 } 472 473 /** 474 * Checks that all actiities in the specified section are hidden. You need to be in the course page. It can be used being logged as a student and as a teacher on editing mode. 475 * 476 * @Then /^all activities in section "(?P<section_number>\d+)" should be hidden$/ 477 * @throws ExpectationException 478 * @throws ElementNotFoundException Thrown by behat_base::find 479 * @param int $sectionnumber 480 */ 481 public function section_activities_should_be_hidden($sectionnumber) { 482 $sectionxpath = $this->section_exists($sectionnumber); 483 484 // Preventive in case there is any action in progress. 485 // Adding it here because we are interacting (click) with 486 // the elements, not necessary when we just find(). 487 $this->i_wait_until_section_is_available($sectionnumber); 488 489 // The checking are different depending on user permissions. 490 if ($this->is_course_editor()) { 491 492 // The section must be hidden. 493 $this->show_section_icon_exists($sectionnumber); 494 495 // If there are activities they should be hidden and the visibility icon should not be available. 496 if ($activities = $this->get_section_activities($sectionxpath)) { 497 498 $dimmedexception = new ExpectationException('There are activities that are not dimmed', $this->getSession()); 499 foreach ($activities as $activity) { 500 // Dimmed. 501 $this->find('xpath', "//div[contains(concat(' ', normalize-space(@class), ' '), ' activityinstance ')]" . 502 "//a[contains(concat(' ', normalize-space(@class), ' '), ' dimmed ')]", $dimmedexception, $activity); 503 } 504 } 505 } else { 506 // There shouldn't be activities. 507 if ($this->get_section_activities($sectionxpath)) { 508 throw new ExpectationException('There are activities in the section and they should be hidden', $this->getSession()); 509 } 510 } 511 512 } 513 514 /** 515 * Checks that the specified section is visible. You need to be in the course page. It can be used being logged as a student and as a teacher on editing mode. 516 * 517 * @Then /^section "(?P<section_number>\d+)" should be visible$/ 518 * @throws ExpectationException 519 * @param int $sectionnumber 520 */ 521 public function section_should_be_visible($sectionnumber) { 522 523 $sectionxpath = $this->section_exists($sectionnumber); 524 525 // Section should not be hidden. 526 $xpath = $sectionxpath . "[not(contains(concat(' ', normalize-space(@class), ' '), ' hidden '))]"; 527 if (!$this->getSession()->getPage()->find('xpath', $xpath)) { 528 throw new ExpectationException('The section is hidden', $this->getSession()); 529 } 530 531 // Edit menu should be visible. 532 if ($this->is_course_editor()) { 533 $xpath = $sectionxpath . 534 "/descendant::div[contains(@class, 'section-actions')]" . 535 "/descendant::a[contains(@class, 'textmenu')]"; 536 if (!$this->getSession()->getPage()->find('xpath', $xpath)) { 537 throw new ExpectationException('The section edit menu is not available', $this->getSession()); 538 } 539 } 540 } 541 542 /** 543 * Moves up the specified section, this step only works with Javascript disabled. Editing mode should be on. 544 * 545 * @Given /^I move up section "(?P<section_number>\d+)"$/ 546 * @throws DriverException Step not available when Javascript is enabled 547 * @param int $sectionnumber 548 */ 549 public function i_move_up_section($sectionnumber) { 550 551 if ($this->running_javascript()) { 552 throw new DriverException('Move a section up step is not available with Javascript enabled'); 553 } 554 555 // Ensures the section exists. 556 $sectionxpath = $this->section_exists($sectionnumber); 557 558 // If javascript is on, link is inside a menu. 559 if ($this->running_javascript()) { 560 $this->i_open_section_edit_menu($sectionnumber); 561 } 562 563 // Follows the link 564 $moveuplink = $this->get_node_in_container('link', get_string('moveup'), 'xpath_element', $sectionxpath); 565 $moveuplink->click(); 566 } 567 568 /** 569 * Moves down the specified section, this step only works with Javascript disabled. Editing mode should be on. 570 * 571 * @Given /^I move down section "(?P<section_number>\d+)"$/ 572 * @throws DriverException Step not available when Javascript is enabled 573 * @param int $sectionnumber 574 */ 575 public function i_move_down_section($sectionnumber) { 576 577 if ($this->running_javascript()) { 578 throw new DriverException('Move a section down step is not available with Javascript enabled'); 579 } 580 581 // Ensures the section exists. 582 $sectionxpath = $this->section_exists($sectionnumber); 583 584 // If javascript is on, link is inside a menu. 585 if ($this->running_javascript()) { 586 $this->i_open_section_edit_menu($sectionnumber); 587 } 588 589 // Follows the link 590 $movedownlink = $this->get_node_in_container('link', get_string('movedown'), 'xpath_element', $sectionxpath); 591 $movedownlink->click(); 592 } 593 594 /** 595 * Checks that the specified activity is visible. You need to be in the course page. It can be used being logged as a student and as a teacher on editing mode. 596 * 597 * @Then /^"(?P<activity_or_resource_string>(?:[^"]|\\")*)" activity should be visible$/ 598 * @param string $activityname 599 * @throws ExpectationException 600 */ 601 public function activity_should_be_visible($activityname) { 602 603 // The activity must exists and be visible. 604 $activitynode = $this->get_activity_node($activityname); 605 606 if ($this->is_course_editor()) { 607 608 // The activity should not be dimmed. 609 try { 610 $xpath = "/descendant-or-self::a[contains(concat(' ', normalize-space(@class), ' '), ' dimmed ')] | ". 611 "/descendant-or-self::div[contains(concat(' ', normalize-space(@class), ' '), ' dimmed_text ')]"; 612 $this->find('xpath', $xpath, false, $activitynode); 613 throw new ExpectationException('"' . $activityname . '" is hidden', $this->getSession()); 614 } catch (ElementNotFoundException $e) { 615 // All ok. 616 } 617 618 // The 'Hide' button should be available. 619 $nohideexception = new ExpectationException('"' . $activityname . '" don\'t have a "' . get_string('hide') . '" icon', $this->getSession()); 620 $this->find('named_partial', array('link', get_string('hide')), $nohideexception, $activitynode); 621 } 622 } 623 624 /** 625 * Checks that the specified activity is hidden. You need to be in the course page. It can be used being logged as a student and as a teacher on editing mode. 626 * 627 * @Then /^"(?P<activity_or_resource_string>(?:[^"]|\\")*)" activity should be hidden$/ 628 * @param string $activityname 629 * @throws ExpectationException 630 */ 631 public function activity_should_be_hidden($activityname) { 632 633 if ($this->is_course_editor()) { 634 635 // The activity should exist. 636 $activitynode = $this->get_activity_node($activityname); 637 638 // Should be hidden. 639 $exception = new ExpectationException('"' . $activityname . '" is not dimmed', $this->getSession()); 640 $xpath = "/descendant-or-self::a[contains(concat(' ', normalize-space(@class), ' '), ' dimmed ')] | ". 641 "/descendant-or-self::div[contains(concat(' ', normalize-space(@class), ' '), ' dimmed_text ')]"; 642 $this->find('xpath', $xpath, $exception, $activitynode); 643 644 // Also 'Show' icon. 645 $noshowexception = new ExpectationException('"' . $activityname . '" don\'t have a "' . get_string('show') . '" icon', $this->getSession()); 646 $this->find('named_partial', array('link', get_string('show')), $noshowexception, $activitynode); 647 648 } else { 649 650 // It should not exist at all. 651 try { 652 $this->find_link($activityname); 653 throw new ExpectationException('The "' . $activityname . '" should not appear'); 654 } catch (ElementNotFoundException $e) { 655 // This is good, the activity should not be there. 656 } 657 } 658 659 } 660 661 /** 662 * Moves the specified activity to the first slot of a section. This step is experimental when using it in Javascript tests. Editing mode should be on. 663 * 664 * @Given /^I move "(?P<activity_name_string>(?:[^"]|\\")*)" activity to section "(?P<section_number>\d+)"$/ 665 * @param string $activityname The activity name 666 * @param int $sectionnumber The number of section 667 */ 668 public function i_move_activity_to_section($activityname, $sectionnumber) { 669 670 // Ensure the destination is valid. 671 $sectionxpath = $this->section_exists($sectionnumber); 672 673 $activitynode = $this->get_activity_element('.editing_move img', 'css_element', $activityname); 674 675 // JS enabled. 676 if ($this->running_javascript()) { 677 678 $destinationxpath = $sectionxpath . "/descendant::ul[contains(concat(' ', normalize-space(@class), ' '), ' yui3-dd-drop ')]"; 679 680 $this->execute("behat_general::i_drag_and_i_drop_it_in", 681 array($this->escape($activitynode->getXpath()), "xpath_element", 682 $this->escape($destinationxpath), "xpath_element") 683 ); 684 685 } else { 686 // Following links with no-JS. 687 688 // Moving to the fist spot of the section (before all other section's activities). 689 $this->execute('behat_course::i_click_on_in_the_activity', 690 array("a.editing_move", "css_element", $this->escape($activityname)) 691 ); 692 693 $this->execute('behat_general::i_click_on_in_the', 694 array("li.movehere a", "css_element", $this->escape($sectionxpath), "xpath_element") 695 ); 696 } 697 } 698 699 /** 700 * Edits the activity name through the edit activity; this step only works with Javascript enabled. Editing mode should be on. 701 * 702 * @Given /^I change "(?P<activity_name_string>(?:[^"]|\\")*)" activity name to "(?P<new_name_string>(?:[^"]|\\")*)"$/ 703 * @throws DriverException Step not available when Javascript is disabled 704 * @param string $activityname 705 * @param string $newactivityname 706 */ 707 public function i_change_activity_name_to($activityname, $newactivityname) { 708 709 if (!$this->running_javascript()) { 710 throw new DriverException('Change activity name step is not available with Javascript disabled'); 711 } 712 713 $activity = $this->escape($activityname); 714 715 $this->execute('behat_course::i_click_on_in_the_activity', 716 array(get_string('edittitle'), "link", $activity) 717 ); 718 719 // Adding chr(10) to save changes. 720 $this->execute('behat_forms::i_set_the_field_to', 721 array('title', $this->escape($newactivityname) . chr(10)) 722 ); 723 724 } 725 726 /** 727 * Opens an activity actions menu if it is not already opened. 728 * 729 * @Given /^I open "(?P<activity_name_string>(?:[^"]|\\")*)" actions menu$/ 730 * @throws DriverException The step is not available when Javascript is disabled 731 * @param string $activityname 732 */ 733 public function i_open_actions_menu($activityname) { 734 735 if (!$this->running_javascript()) { 736 throw new DriverException('Activities actions menu not available when Javascript is disabled'); 737 } 738 739 // If it is already opened we do nothing. 740 $activitynode = $this->get_activity_node($activityname); 741 $classes = array_flip(explode(' ', $activitynode->getAttribute('class'))); 742 if (!empty($classes['action-menu-shown'])) { 743 return; 744 } 745 746 $this->execute('behat_course::i_click_on_in_the_activity', 747 array("a[role='menuitem']", "css_element", $this->escape($activityname)) 748 ); 749 750 } 751 752 /** 753 * Closes an activity actions menu if it is not already closed. 754 * 755 * @Given /^I close "(?P<activity_name_string>(?:[^"]|\\")*)" actions menu$/ 756 * @throws DriverException The step is not available when Javascript is disabled 757 * @param string $activityname 758 */ 759 public function i_close_actions_menu($activityname) { 760 761 if (!$this->running_javascript()) { 762 throw new DriverException('Activities actions menu not available when Javascript is disabled'); 763 } 764 765 // If it is already closed we do nothing. 766 $activitynode = $this->get_activity_node($activityname); 767 $classes = array_flip(explode(' ', $activitynode->getAttribute('class'))); 768 if (empty($classes['action-menu-shown'])) { 769 return; 770 } 771 772 $this->execute('behat_course::i_click_on_in_the_activity', 773 array("a[role='menuitem']", "css_element", $this->escape($activityname)) 774 ); 775 } 776 777 /** 778 * Checks that the specified activity's action menu is open. 779 * 780 * @Then /^"(?P<activity_name_string>(?:[^"]|\\")*)" actions menu should be open$/ 781 * @throws DriverException The step is not available when Javascript is disabled 782 * @param string $activityname 783 */ 784 public function actions_menu_should_be_open($activityname) { 785 786 if (!$this->running_javascript()) { 787 throw new DriverException('Activities actions menu not available when Javascript is disabled'); 788 } 789 790 // If it is already closed we do nothing. 791 $activitynode = $this->get_activity_node($activityname); 792 $classes = array_flip(explode(' ', $activitynode->getAttribute('class'))); 793 if (empty($classes['action-menu-shown'])) { 794 throw new ExpectationException(sprintf("The action menu for '%s' is not open", $activityname), $this->getSession()); 795 } 796 } 797 798 /** 799 * Indents to the right the activity or resource specified by it's name. Editing mode should be on. 800 * 801 * @Given /^I indent right "(?P<activity_name_string>(?:[^"]|\\")*)" activity$/ 802 * @param string $activityname 803 */ 804 public function i_indent_right_activity($activityname) { 805 806 $activity = $this->escape($activityname); 807 if ($this->running_javascript()) { 808 $this->i_open_actions_menu($activity); 809 } 810 811 $this->execute('behat_course::i_click_on_in_the_activity', 812 array(get_string('moveright'), "link", $this->escape($activity)) 813 ); 814 815 } 816 817 /** 818 * Indents to the left the activity or resource specified by it's name. Editing mode should be on. 819 * 820 * @Given /^I indent left "(?P<activity_name_string>(?:[^"]|\\")*)" activity$/ 821 * @param string $activityname 822 */ 823 public function i_indent_left_activity($activityname) { 824 825 $activity = $this->escape($activityname); 826 if ($this->running_javascript()) { 827 $this->i_open_actions_menu($activity); 828 } 829 830 $this->execute('behat_course::i_click_on_in_the_activity', 831 array(get_string('moveleft'), "link", $this->escape($activity)) 832 ); 833 834 } 835 836 /** 837 * Deletes the activity or resource specified by it's name. This step is experimental when using it in Javascript tests. You should be in the course page with editing mode on. 838 * 839 * @Given /^I delete "(?P<activity_name_string>(?:[^"]|\\")*)" activity$/ 840 * @param string $activityname 841 */ 842 public function i_delete_activity($activityname) { 843 $steps = array(); 844 $activity = $this->escape($activityname); 845 if ($this->running_javascript()) { 846 $this->i_open_actions_menu($activity); 847 } 848 849 $this->execute('behat_course::i_click_on_in_the_activity', 850 array(get_string('delete'), "link", $this->escape($activity)) 851 ); 852 853 // JS enabled. 854 // Not using chain steps here because the exceptions catcher have problems detecting 855 // JS modal windows and avoiding interacting them at the same time. 856 if ($this->running_javascript()) { 857 $this->execute('behat_general::i_click_on_in_the', 858 array(get_string('yes'), "button", "Confirm", "dialogue") 859 ); 860 } else { 861 $this->execute("behat_forms::press_button", get_string('yes')); 862 } 863 864 return $steps; 865 } 866 867 /** 868 * Duplicates the activity or resource specified by it's name. You should be in the course page with editing mode on. 869 * 870 * @Given /^I duplicate "(?P<activity_name_string>(?:[^"]|\\")*)" activity$/ 871 * @param string $activityname 872 */ 873 public function i_duplicate_activity($activityname) { 874 $steps = array(); 875 $activity = $this->escape($activityname); 876 if ($this->running_javascript()) { 877 $this->i_open_actions_menu($activity); 878 } 879 $this->execute('behat_course::i_click_on_in_the_activity', 880 array(get_string('duplicate'), "link", $activity) 881 ); 882 883 } 884 885 /** 886 * Duplicates the activity or resource and modifies the new activity with the provided data. You should be in the course page with editing mode on. 887 * 888 * @Given /^I duplicate "(?P<activity_name_string>(?:[^"]|\\")*)" activity editing the new copy with:$/ 889 * @param string $activityname 890 * @param TableNode $data 891 */ 892 public function i_duplicate_activity_editing_the_new_copy_with($activityname, TableNode $data) { 893 894 $activity = $this->escape($activityname); 895 $activityliteral = behat_context_helper::escape($activityname); 896 897 $this->execute("behat_course::i_duplicate_activity", $activity); 898 899 // Determine the future new activity xpath from the former one. 900 $duplicatedxpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]" . 901 "[contains(., $activityliteral)]/following-sibling::li"; 902 $duplicatedactionsmenuxpath = $duplicatedxpath . "/descendant::a[@role='menuitem']"; 903 904 if ($this->running_javascript()) { 905 // We wait until the AJAX request finishes and the section is visible again. 906 $hiddenlightboxxpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]" . 907 "[contains(., $activityliteral)]" . 908 "/ancestor::li[contains(concat(' ', normalize-space(@class), ' '), ' section ')]" . 909 "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]"; 910 911 $this->execute("behat_general::wait_until_exists", 912 array($this->escape($hiddenlightboxxpath), "xpath_element") 913 ); 914 915 // Close the original activity actions menu. 916 $this->i_close_actions_menu($activity); 917 918 // The next sibling of the former activity will be the duplicated one, so we click on it from it's xpath as, at 919 // this point, it don't even exists in the DOM (the steps are executed when we return them). 920 $this->execute('behat_general::i_click_on', 921 array($this->escape($duplicatedactionsmenuxpath), "xpath_element") 922 ); 923 } 924 925 // We force the xpath as otherwise mink tries to interact with the former one. 926 $this->execute('behat_general::i_click_on_in_the', 927 array(get_string('editsettings'), "link", $this->escape($duplicatedxpath), "xpath_element") 928 ); 929 930 $this->execute("behat_forms::i_set_the_following_fields_to_these_values", $data); 931 $this->execute("behat_forms::press_button", get_string('savechangesandreturntocourse')); 932 933 } 934 935 /** 936 * Waits until the section is available to interact with it. Useful when the section is performing an action and the section is overlayed with a loading layout. 937 * 938 * Using the protected method as this method will be usually 939 * called by other methods which are not returning a set of 940 * steps and performs the actions directly, so it would not 941 * be executed if it returns another step. 942 * 943 * Hopefully we would not require test writers to use this step 944 * and we will manage it from other step definitions. 945 * 946 * @Given /^I wait until section "(?P<section_number>\d+)" is available$/ 947 * @param int $sectionnumber 948 * @return void 949 */ 950 public function i_wait_until_section_is_available($sectionnumber) { 951 952 // Looks for a hidden lightbox or a non-existent lightbox in that section. 953 $sectionxpath = $this->section_exists($sectionnumber); 954 $hiddenlightboxxpath = $sectionxpath . "/descendant::div[contains(concat(' ', @class, ' '), ' lightbox ')][contains(@style, 'display: none')]" . 955 " | " . 956 $sectionxpath . "[count(child::div[contains(@class, 'lightbox')]) = 0]"; 957 958 $this->ensure_element_exists($hiddenlightboxxpath, 'xpath_element'); 959 } 960 961 /** 962 * Clicks on the specified element of the activity. You should be in the course page with editing mode turned on. 963 * 964 * @Given /^I click on "(?P<element_string>(?:[^"]|\\")*)" "(?P<selector_string>(?:[^"]|\\")*)" in the "(?P<activity_name_string>(?:[^"]|\\")*)" activity$/ 965 * @param string $element 966 * @param string $selectortype 967 * @param string $activityname 968 */ 969 public function i_click_on_in_the_activity($element, $selectortype, $activityname) { 970 $element = $this->get_activity_element($element, $selectortype, $activityname); 971 $element->click(); 972 } 973 974 /** 975 * Clicks on the specified element inside the activity container. 976 * 977 * @throws ElementNotFoundException 978 * @param string $element 979 * @param string $selectortype 980 * @param string $activityname 981 * @return NodeElement 982 */ 983 protected function get_activity_element($element, $selectortype, $activityname) { 984 $activitynode = $this->get_activity_node($activityname); 985 986 // Transforming to Behat selector/locator. 987 list($selector, $locator) = $this->transform_selector($selectortype, $element); 988 $exception = new ElementNotFoundException($this->getSession(), '"' . $element . '" "' . $selectortype . '" in "' . $activityname . '" '); 989 990 return $this->find($selector, $locator, $exception, $activitynode); 991 } 992 993 /** 994 * Checks if the course section exists. 995 * 996 * @throws ElementNotFoundException Thrown by behat_base::find 997 * @param int $sectionnumber 998 * @return string The xpath of the section. 999 */ 1000 protected function section_exists($sectionnumber) { 1001 1002 // Just to give more info in case it does not exist. 1003 $xpath = "//li[@id='section-" . $sectionnumber . "']"; 1004 $exception = new ElementNotFoundException($this->getSession(), "Section $sectionnumber "); 1005 $this->find('xpath', $xpath, $exception); 1006 1007 return $xpath; 1008 } 1009 1010 /** 1011 * Returns the show section icon or throws an exception. 1012 * 1013 * @throws ElementNotFoundException Thrown by behat_base::find 1014 * @param int $sectionnumber 1015 * @return NodeElement 1016 */ 1017 protected function show_section_icon_exists($sectionnumber) { 1018 1019 // Gets the section xpath and ensure it exists. 1020 $xpath = $this->section_exists($sectionnumber); 1021 1022 // We need to know the course format as the text strings depends on them. 1023 $courseformat = $this->get_course_format(); 1024 1025 // Checking the show button alt text and show icon. 1026 $showtext = behat_context_helper::escape(get_string('showfromothers', $courseformat)); 1027 $linkxpath = $xpath . "/descendant::a[@title=$showtext]"; 1028 $imgxpath = $linkxpath . "/descendant::img[contains(@src, 'show')]"; 1029 1030 $exception = new ElementNotFoundException($this->getSession(), 'Show section icon '); 1031 $this->find('xpath', $imgxpath, $exception); 1032 1033 // Returing the link so both Non-JS and JS browsers can interact with it. 1034 return $this->find('xpath', $linkxpath, $exception); 1035 } 1036 1037 /** 1038 * Returns the hide section icon link if it exists or throws exception. 1039 * 1040 * @throws ElementNotFoundException Thrown by behat_base::find 1041 * @param int $sectionnumber 1042 * @return NodeElement 1043 */ 1044 protected function hide_section_icon_exists($sectionnumber) { 1045 1046 // Gets the section xpath and ensure it exists. 1047 $xpath = $this->section_exists($sectionnumber); 1048 1049 // We need to know the course format as the text strings depends on them. 1050 $courseformat = $this->get_course_format(); 1051 1052 // Checking the hide button alt text and hide icon. 1053 $hidetext = behat_context_helper::escape(get_string('hidefromothers', $courseformat)); 1054 $linkxpath = $xpath . "/descendant::a[@title=$hidetext]"; 1055 $imgxpath = $linkxpath . "/descendant::img[contains(@src, 'hide')]"; 1056 1057 $exception = new ElementNotFoundException($this->getSession(), 'Hide section icon '); 1058 $this->find('xpath', $imgxpath, $exception); 1059 1060 // Returing the link so both Non-JS and JS browsers can interact with it. 1061 return $this->find('xpath', $linkxpath, $exception); 1062 } 1063 1064 /** 1065 * Gets the current course format. 1066 * 1067 * @throws ExpectationException If we are not in the course view page. 1068 * @return string The course format in a frankenstyled name. 1069 */ 1070 protected function get_course_format() { 1071 1072 $exception = new ExpectationException('You are not in a course page', $this->getSession()); 1073 1074 // The moodle body's id attribute contains the course format. 1075 $node = $this->getSession()->getPage()->find('css', 'body'); 1076 if (!$node) { 1077 throw $exception; 1078 } 1079 1080 if (!$bodyid = $node->getAttribute('id')) { 1081 throw $exception; 1082 } 1083 1084 if (strstr($bodyid, 'page-course-view-') === false) { 1085 throw $exception; 1086 } 1087 1088 return 'format_' . str_replace('page-course-view-', '', $bodyid); 1089 } 1090 1091 /** 1092 * Gets the section's activites DOM nodes. 1093 * 1094 * @param string $sectionxpath 1095 * @return array NodeElement instances 1096 */ 1097 protected function get_section_activities($sectionxpath) { 1098 1099 $xpath = $sectionxpath . "/descendant::li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')]"; 1100 1101 // We spin here, as activities usually require a lot of time to load. 1102 try { 1103 $activities = $this->find_all('xpath', $xpath); 1104 } catch (ElementNotFoundException $e) { 1105 return false; 1106 } 1107 1108 return $activities; 1109 } 1110 1111 /** 1112 * Returns the DOM node of the activity from <li>. 1113 * 1114 * @throws ElementNotFoundException Thrown by behat_base::find 1115 * @param string $activityname The activity name 1116 * @return NodeElement 1117 */ 1118 protected function get_activity_node($activityname) { 1119 1120 $activityname = behat_context_helper::escape($activityname); 1121 $xpath = "//li[contains(concat(' ', normalize-space(@class), ' '), ' activity ')][contains(., $activityname)]"; 1122 1123 return $this->find('xpath', $xpath); 1124 } 1125 1126 /** 1127 * Gets the activity instance name from the activity node. 1128 * 1129 * @throws ElementNotFoundException 1130 * @param NodeElement $activitynode 1131 * @return string 1132 */ 1133 protected function get_activity_name($activitynode) { 1134 $instancenamenode = $this->find('xpath', "//span[contains(concat(' ', normalize-space(@class), ' '), ' instancename ')]", false, $activitynode); 1135 return $instancenamenode->getText(); 1136 } 1137 1138 /** 1139 * Returns whether the user can edit the course contents or not. 1140 * 1141 * @return bool 1142 */ 1143 protected function is_course_editor() { 1144 1145 // We don't need to behat_base::spin() here as all is already loaded. 1146 if (!$this->getSession()->getPage()->findButton(get_string('turneditingoff')) && 1147 !$this->getSession()->getPage()->findButton(get_string('turneditingon'))) { 1148 return false; 1149 } 1150 1151 return true; 1152 } 1153 1154 /** 1155 * Returns the id of the category with the given idnumber. 1156 * 1157 * Please note that this function requires the category to exist. If it does not exist an ExpectationException is thrown. 1158 * 1159 * @param string $idnumber 1160 * @return string 1161 * @throws ExpectationException 1162 */ 1163 protected function get_category_id($idnumber) { 1164 global $DB; 1165 try { 1166 return $DB->get_field('course_categories', 'id', array('idnumber' => $idnumber), MUST_EXIST); 1167 } catch (dml_missing_record_exception $ex) { 1168 throw new ExpectationException(sprintf("There is no category in the database with the idnumber '%s'", $idnumber)); 1169 } 1170 } 1171 1172 /** 1173 * Returns the id of the course with the given idnumber. 1174 * 1175 * Please note that this function requires the category to exist. If it does not exist an ExpectationException is thrown. 1176 * 1177 * @param string $idnumber 1178 * @return string 1179 * @throws ExpectationException 1180 */ 1181 protected function get_course_id($idnumber) { 1182 global $DB; 1183 try { 1184 return $DB->get_field('course', 'id', array('idnumber' => $idnumber), MUST_EXIST); 1185 } catch (dml_missing_record_exception $ex) { 1186 throw new ExpectationException(sprintf("There is no course in the database with the idnumber '%s'", $idnumber)); 1187 } 1188 } 1189 1190 /** 1191 * Returns the category node from within the listing on the management page. 1192 * 1193 * @param string $idnumber 1194 * @return \Behat\Mink\Element\NodeElement 1195 */ 1196 protected function get_management_category_listing_node_by_idnumber($idnumber) { 1197 $id = $this->get_category_id($idnumber); 1198 $selector = sprintf('#category-listing .listitem-category[data-id="%d"] > div', $id); 1199 return $this->find('css', $selector); 1200 } 1201 1202 /** 1203 * Returns a category node from within the management interface. 1204 * 1205 * @param string $name The name of the category. 1206 * @param bool $link If set to true we'll resolve to the link rather than just the node. 1207 * @return \Behat\Mink\Element\NodeElement 1208 */ 1209 protected function get_management_category_listing_node_by_name($name, $link = false) { 1210 $selector = "//div[@id='category-listing']//li[contains(concat(' ', normalize-space(@class), ' '), ' listitem-category ')]//a[text()='{$name}']"; 1211 if ($link === false) { 1212 $selector .= "/ancestor::li[@data-id][1]"; 1213 } 1214 return $this->find('xpath', $selector); 1215 } 1216 1217 /** 1218 * Returns a course node from within the management interface. 1219 * 1220 * @param string $name The name of the course. 1221 * @param bool $link If set to true we'll resolve to the link rather than just the node. 1222 * @return \Behat\Mink\Element\NodeElement 1223 */ 1224 protected function get_management_course_listing_node_by_name($name, $link = false) { 1225 $selector = "//div[@id='course-listing']//li[contains(concat(' ', @class, ' '), ' listitem-course ')]//a[text()='{$name}']"; 1226 if ($link === false) { 1227 $selector .= "/ancestor::li[@data-id]"; 1228 } 1229 return $this->find('xpath', $selector); 1230 } 1231 1232 /** 1233 * Returns the course node from within the listing on the management page. 1234 * 1235 * @param string $idnumber 1236 * @return \Behat\Mink\Element\NodeElement 1237 */ 1238 protected function get_management_course_listing_node_by_idnumber($idnumber) { 1239 $id = $this->get_course_id($idnumber); 1240 $selector = sprintf('#course-listing .listitem-course[data-id="%d"] > div', $id); 1241 return $this->find('css', $selector); 1242 } 1243 1244 /** 1245 * Clicks on a category in the management interface. 1246 * 1247 * @Given /^I click on category "(?P<name_string>(?:[^"]|\\")*)" in the management interface$/ 1248 * @param string $name 1249 */ 1250 public function i_click_on_category_in_the_management_interface($name) { 1251 $node = $this->get_management_category_listing_node_by_name($name, true); 1252 $node->click(); 1253 } 1254 1255 /** 1256 * Clicks on a course in the management interface. 1257 * 1258 * @Given /^I click on course "(?P<name_string>(?:[^"]|\\")*)" in the management interface$/ 1259 * @param string $name 1260 */ 1261 public function i_click_on_course_in_the_management_interface($name) { 1262 $node = $this->get_management_course_listing_node_by_name($name, true); 1263 $node->click(); 1264 } 1265 1266 /** 1267 * Clicks on a category checkbox in the management interface, if not checked. 1268 * 1269 * @Given /^I select category "(?P<name_string>(?:[^"]|\\")*)" in the management interface$/ 1270 * @param string $name 1271 */ 1272 public function i_select_category_in_the_management_interface($name) { 1273 $node = $this->get_management_category_listing_node_by_name($name); 1274 $node = $node->findField('bcat[]'); 1275 if (!$node->isChecked()) { 1276 $node->click(); 1277 } 1278 } 1279 1280 /** 1281 * Clicks on a category checkbox in the management interface, if checked. 1282 * 1283 * @Given /^I unselect category "(?P<name_string>(?:[^"]|\\")*)" in the management interface$/ 1284 * @param string $name 1285 */ 1286 public function i_unselect_category_in_the_management_interface($name) { 1287 $node = $this->get_management_category_listing_node_by_name($name); 1288 $node = $node->findField('bcat[]'); 1289 if ($node->isChecked()) { 1290 $node->click(); 1291 } 1292 } 1293 1294 /** 1295 * Clicks course checkbox in the management interface, if not checked. 1296 * 1297 * @Given /^I select course "(?P<name_string>(?:[^"]|\\")*)" in the management interface$/ 1298 * @param string $name 1299 */ 1300 public function i_select_course_in_the_management_interface($name) { 1301 $node = $this->get_management_course_listing_node_by_name($name); 1302 $node = $node->findField('bc[]'); 1303 if (!$node->isChecked()) { 1304 $node->click(); 1305 } 1306 } 1307 1308 /** 1309 * Clicks course checkbox in the management interface, if checked. 1310 * 1311 * @Given /^I unselect course "(?P<name_string>(?:[^"]|\\")*)" in the management interface$/ 1312 * @param string $name 1313 */ 1314 public function i_unselect_course_in_the_management_interface($name) { 1315 $node = $this->get_management_course_listing_node_by_name($name); 1316 $node = $node->findField('bc[]'); 1317 if ($node->isChecked()) { 1318 $node->click(); 1319 } 1320 } 1321 1322 /** 1323 * Move selected categories to top level in the management interface. 1324 * 1325 * @Given /^I move category "(?P<name_string>(?:[^"]|\\")*)" to top level in the management interface$/ 1326 * @param string $name 1327 */ 1328 public function i_move_category_to_top_level_in_the_management_interface($name) { 1329 $this->i_select_category_in_the_management_interface($name); 1330 1331 $this->execute('behat_forms::i_set_the_field_to', 1332 array('menumovecategoriesto', coursecat::get(0)->get_formatted_name()) 1333 ); 1334 1335 // Save event. 1336 $this->execute("behat_forms::press_button", "bulkmovecategories"); 1337 } 1338 1339 /** 1340 * Checks that a category is a subcategory of specific category. 1341 * 1342 * @Given /^I should see category "(?P<subcatidnumber_string>(?:[^"]|\\")*)" as subcategory of "(?P<catidnumber_string>(?:[^"]|\\")*)" in the management interface$/ 1343 * @throws ExpectationException 1344 * @param string $subcatidnumber 1345 * @param string $catidnumber 1346 */ 1347 public function i_should_see_category_as_subcategory_of_in_the_management_interface($subcatidnumber, $catidnumber) { 1348 $categorynodeid = $this->get_category_id($catidnumber); 1349 $subcategoryid = $this->get_category_id($subcatidnumber); 1350 $exception = new ExpectationException('The category '.$subcatidnumber.' is not a subcategory of '.$catidnumber, $this->getSession()); 1351 $selector = sprintf('#category-listing .listitem-category[data-id="%d"] .listitem-category[data-id="%d"]', $categorynodeid, $subcategoryid); 1352 $this->find('css', $selector, $exception); 1353 } 1354 1355 /** 1356 * Checks that a category is not a subcategory of specific category. 1357 * 1358 * @Given /^I should not see category "(?P<subcatidnumber_string>(?:[^"]|\\")*)" as subcategory of "(?P<catidnumber_string>(?:[^"]|\\")*)" in the management interface$/ 1359 * @throws ExpectationException 1360 * @param string $subcatidnumber 1361 * @param string $catidnumber 1362 */ 1363 public function i_should_not_see_category_as_subcategory_of_in_the_management_interface($subcatidnumber, $catidnumber) { 1364 try { 1365 $this->i_should_see_category_as_subcategory_of_in_the_management_interface($subcatidnumber, $catidnumber); 1366 } catch (ExpectationException $e) { 1367 // ExpectedException means that it is not highlighted. 1368 return; 1369 } 1370 throw new ExpectationException('The category '.$subcatidnumber.' is a subcategory of '.$catidnumber, $this->getSession()); 1371 } 1372 1373 /** 1374 * Click to expand a category revealing its sub categories within the management UI. 1375 * 1376 * @Given /^I click to expand category "(?P<idnumber_string>(?:[^"]|\\")*)" in the management interface$/ 1377 * @param string $idnumber 1378 */ 1379 public function i_click_to_expand_category_in_the_management_interface($idnumber) { 1380 $categorynode = $this->get_management_category_listing_node_by_idnumber($idnumber); 1381 $exception = new ExpectationException('Category "' . $idnumber . '" does not contain an expand or collapse toggle.', $this->getSession()); 1382 $togglenode = $this->find('css', 'a[data-action=collapse],a[data-action=expand]', $exception, $categorynode); 1383 $togglenode->click(); 1384 } 1385 1386 /** 1387 * Checks that a category within the management interface is visible. 1388 * 1389 * @Given /^category in management listing should be visible "(?P<idnumber_string>(?:[^"]|\\")*)"$/ 1390 * @param string $idnumber 1391 */ 1392 public function category_in_management_listing_should_be_visible($idnumber) { 1393 $id = $this->get_category_id($idnumber); 1394 $exception = new ExpectationException('The category '.$idnumber.' is not visible.', $this->getSession()); 1395 $selector = sprintf('#category-listing .listitem-category[data-id="%d"][data-visible="1"]', $id); 1396 $this->find('css', $selector, $exception); 1397 } 1398 1399 /** 1400 * Checks that a category within the management interface is dimmed. 1401 * 1402 * @Given /^category in management listing should be dimmed "(?P<idnumber_string>(?:[^"]|\\")*)"$/ 1403 * @param string $idnumber 1404 */ 1405 public function category_in_management_listing_should_be_dimmed($idnumber) { 1406 $id = $this->get_category_id($idnumber); 1407 $selector = sprintf('#category-listing .listitem-category[data-id="%d"][data-visible="0"]', $id); 1408 $exception = new ExpectationException('The category '.$idnumber.' is visible.', $this->getSession()); 1409 $this->find('css', $selector, $exception); 1410 } 1411 1412 /** 1413 * Checks that a course within the management interface is visible. 1414 * 1415 * @Given /^course in management listing should be visible "(?P<idnumber_string>(?:[^"]|\\")*)"$/ 1416 * @param string $idnumber 1417 */ 1418 public function course_in_management_listing_should_be_visible($idnumber) { 1419 $id = $this->get_course_id($idnumber); 1420 $exception = new ExpectationException('The course '.$idnumber.' is not visible.', $this->getSession()); 1421 $selector = sprintf('#course-listing .listitem-course[data-id="%d"][data-visible="1"]', $id); 1422 $this->find('css', $selector, $exception); 1423 } 1424 1425 /** 1426 * Checks that a course within the management interface is dimmed. 1427 * 1428 * @Given /^course in management listing should be dimmed "(?P<idnumber_string>(?:[^"]|\\")*)"$/ 1429 * @param string $idnumber 1430 */ 1431 public function course_in_management_listing_should_be_dimmed($idnumber) { 1432 $id = $this->get_course_id($idnumber); 1433 $exception = new ExpectationException('The course '.$idnumber.' is visible.', $this->getSession()); 1434 $selector = sprintf('#course-listing .listitem-course[data-id="%d"][data-visible="0"]', $id); 1435 $this->find('css', $selector, $exception); 1436 } 1437 1438 /** 1439 * Toggles the visibility of a course in the management UI. 1440 * 1441 * If it was visible it will be hidden. If it is hidden it will be made visible. 1442 * 1443 * @Given /^I toggle visibility of course "(?P<idnumber_string>(?:[^"]|\\")*)" in management listing$/ 1444 * @param string $idnumber 1445 */ 1446 public function i_toggle_visibility_of_course_in_management_listing($idnumber) { 1447 $id = $this->get_course_id($idnumber); 1448 $selector = sprintf('#course-listing .listitem-course[data-id="%d"][data-visible]', $id); 1449 $node = $this->find('css', $selector); 1450 $exception = new ExpectationException('Course listing "' . $idnumber . '" does not contain a show or hide toggle.', $this->getSession()); 1451 if ($node->getAttribute('data-visible') === '1') { 1452 $toggle = $this->find('css', '.action-hide', $exception, $node); 1453 } else { 1454 $toggle = $this->find('css', '.action-show', $exception, $node); 1455 } 1456 $toggle->click(); 1457 } 1458 1459 /** 1460 * Toggles the visibility of a category in the management UI. 1461 * 1462 * If it was visible it will be hidden. If it is hidden it will be made visible. 1463 * 1464 * @Given /^I toggle visibility of category "(?P<idnumber_string>(?:[^"]|\\")*)" in management listing$/ 1465 */ 1466 public function i_toggle_visibility_of_category_in_management_listing($idnumber) { 1467 $id = $this->get_category_id($idnumber); 1468 $selector = sprintf('#category-listing .listitem-category[data-id="%d"][data-visible]', $id); 1469 $node = $this->find('css', $selector); 1470 $exception = new ExpectationException('Category listing "' . $idnumber . '" does not contain a show or hide toggle.', $this->getSession()); 1471 if ($node->getAttribute('data-visible') === '1') { 1472 $toggle = $this->find('css', '.action-hide', $exception, $node); 1473 } else { 1474 $toggle = $this->find('css', '.action-show', $exception, $node); 1475 } 1476 $toggle->click(); 1477 } 1478 1479 /** 1480 * Moves a category displayed in the management interface up or down one place. 1481 * 1482 * @Given /^I click to move category "(?P<idnumber_string>(?:[^"]|\\")*)" (?P<direction>up|down) one$/ 1483 * 1484 * @param string $idnumber The category idnumber 1485 * @param string $direction The direction to move in, either up or down 1486 */ 1487 public function i_click_to_move_category_by_one($idnumber, $direction) { 1488 $node = $this->get_management_category_listing_node_by_idnumber($idnumber); 1489 $this->user_moves_listing_by_one('category', $node, $direction); 1490 } 1491 1492 /** 1493 * Moves a course displayed in the management interface up or down one place. 1494 * 1495 * @Given /^I click to move course "(?P<idnumber_string>(?:[^"]|\\")*)" (?P<direction>up|down) one$/ 1496 * 1497 * @param string $idnumber The course idnumber 1498 * @param string $direction The direction to move in, either up or down 1499 */ 1500 public function i_click_to_move_course_by_one($idnumber, $direction) { 1501 $node = $this->get_management_course_listing_node_by_idnumber($idnumber); 1502 $this->user_moves_listing_by_one('course', $node, $direction); 1503 } 1504 1505 /** 1506 * Moves a course or category listing within the management interface up or down by one. 1507 * 1508 * @param string $listingtype One of course or category 1509 * @param \Behat\Mink\Element\NodeElement $listingnode 1510 * @param string $direction One of up or down. 1511 * @param bool $highlight If set to false we don't check the node has been highlighted. 1512 */ 1513 protected function user_moves_listing_by_one($listingtype, $listingnode, $direction, $highlight = true) { 1514 $up = (strtolower($direction) === 'up'); 1515 if ($up) { 1516 $exception = new ExpectationException($listingtype.' listing does not contain a moveup button.', $this->getSession()); 1517 $button = $this->find('css', 'a.action-moveup', $exception, $listingnode); 1518 } else { 1519 $exception = new ExpectationException($listingtype.' listing does not contain a movedown button.', $this->getSession()); 1520 $button = $this->find('css', 'a.action-movedown', $exception, $listingnode); 1521 } 1522 $button->click(); 1523 if ($this->running_javascript() && $highlight) { 1524 $listitem = $listingnode->getParent(); 1525 $exception = new ExpectationException('Nothing was highlighted, ajax didn\'t occur or didn\'t succeed.', $this->getSession()); 1526 $this->spin(array($this, 'listing_is_highlighted'), $listitem->getTagName().'#'.$listitem->getAttribute('id'), 2, $exception, true); 1527 } 1528 } 1529 1530 /** 1531 * Used by spin to determine the callback has been highlighted. 1532 * 1533 * @param behat_course $self A self reference (default first arg from a spin callback) 1534 * @param \Behat\Mink\Element\NodeElement $selector 1535 * @return bool 1536 */ 1537 protected function listing_is_highlighted($self, $selector) { 1538 $listitem = $this->find('css', $selector); 1539 return $listitem->hasClass('highlight'); 1540 } 1541 1542 /** 1543 * Check that one course appears before another in the course category management listings. 1544 * 1545 * @Given /^I should see course listing "(?P<preceedingcourse_string>(?:[^"]|\\")*)" before "(?P<followingcourse_string>(?:[^"]|\\")*)"$/ 1546 * 1547 * @param string $preceedingcourse The first course to find 1548 * @param string $followingcourse The second course to find (should be AFTER the first course) 1549 * @throws ExpectationException 1550 */ 1551 public function i_should_see_course_listing_before($preceedingcourse, $followingcourse) { 1552 $xpath = "//div[@id='course-listing']//li[contains(concat(' ', @class, ' '), ' listitem-course ')]//a[text()='{$preceedingcourse}']/ancestor::li[@data-id]//following::a[text()='{$followingcourse}']"; 1553 $msg = "{$preceedingcourse} course does not appear before {$followingcourse} course"; 1554 if (!$this->getSession()->getDriver()->find($xpath)) { 1555 throw new ExpectationException($msg, $this->getSession()); 1556 } 1557 } 1558 1559 /** 1560 * Check that one category appears before another in the course category management listings. 1561 * 1562 * @Given /^I should see category listing "(?P<preceedingcategory_string>(?:[^"]|\\")*)" before "(?P<followingcategory_string>(?:[^"]|\\")*)"$/ 1563 * 1564 * @param string $preceedingcategory The first category to find 1565 * @param string $followingcategory The second category to find (should be after the first category) 1566 * @throws ExpectationException 1567 */ 1568 public function i_should_see_category_listing_before($preceedingcategory, $followingcategory) { 1569 $xpath = "//div[@id='category-listing']//li[contains(concat(' ', @class, ' '), ' listitem-category ')]//a[text()='{$preceedingcategory}']/ancestor::li[@data-id]//following::a[text()='{$followingcategory}']"; 1570 $msg = "{$preceedingcategory} category does not appear before {$followingcategory} category"; 1571 if (!$this->getSession()->getDriver()->find($xpath)) { 1572 throw new ExpectationException($msg, $this->getSession()); 1573 } 1574 } 1575 1576 /** 1577 * Checks that we are on the course management page that we expect to be on and that no course has been selected. 1578 * 1579 * @Given /^I should see the "(?P<mode_string>(?:[^"]|\\")*)" management page$/ 1580 * @param string $mode The mode to expected. One of 'Courses', 'Course categories' or 'Course categories and courses' 1581 */ 1582 public function i_should_see_the_courses_management_page($mode) { 1583 $this->execute("behat_general::assert_element_contains_text", 1584 array("Course and category management", "h2", "css_element") 1585 ); 1586 1587 switch ($mode) { 1588 case "Courses": 1589 $this->execute("behat_general::should_not_exist", array("#category-listing", "css_element")); 1590 $this->execute("behat_general::should_exist", array("#course-listing", "css_element")); 1591 break; 1592 1593 case "Course categories": 1594 $this->execute("behat_general::should_exist", array("#category-listing", "css_element")); 1595 $this->execute("behat_general::should_exist", array("#course-listing", "css_element")); 1596 break; 1597 1598 case "Courses categories and courses": 1599 default: 1600 $this->execute("behat_general::should_exist", array("#category-listing", "css_element")); 1601 $this->execute("behat_general::should_exist", array("#course-listing", "css_element")); 1602 break; 1603 } 1604 1605 $this->execute("behat_general::should_not_exist", array("#course-detail", "css_element")); 1606 } 1607 1608 /** 1609 * Checks that we are on the course management page that we expect to be on and that a course has been selected. 1610 * 1611 * @Given /^I should see the "(?P<mode_string>(?:[^"]|\\")*)" management page with a course selected$/ 1612 * @param string $mode The mode to expected. One of 'Courses', 'Course categories' or 'Course categories and courses' 1613 */ 1614 public function i_should_see_the_courses_management_page_with_a_course_selected($mode) { 1615 $this->execute("behat_general::assert_element_contains_text", 1616 array("Course and category management", "h2", "css_element")); 1617 1618 switch ($mode) { 1619 case "Courses": 1620 $this->execute("behat_general::should_not_exist", array("#category-listing", "css_element")); 1621 $this->execute("behat_general::should_exist", array("#course-listing", "css_element")); 1622 break; 1623 1624 case "Course categories": 1625 $this->execute("behat_general::should_exist", array("#category-listing", "css_element")); 1626 $this->execute("behat_general::should_exist", array("#course-listing", "css_element")); 1627 break; 1628 1629 case "Courses categories and courses": 1630 default: 1631 $this->execute("behat_general::should_exist", array("#category-listing", "css_element")); 1632 $this->execute("behat_general::should_exist", array("#course-listing", "css_element")); 1633 break; 1634 } 1635 1636 $this->execute("behat_general::should_exist", array("#course-detail", "css_element")); 1637 } 1638 1639 /** 1640 * Locates a course in the course category management interface and then triggers an action for it. 1641 * 1642 * @Given /^I click on "(?P<action_string>(?:[^"]|\\")*)" action for "(?P<name_string>(?:[^"]|\\")*)" in management course listing$/ 1643 * 1644 * @param string $action The action to take. One of 1645 * @param string $name The name of the course as it is displayed in the management interface. 1646 */ 1647 public function i_click_on_action_for_item_in_management_course_listing($action, $name) { 1648 $node = $this->get_management_course_listing_node_by_name($name); 1649 $this->user_clicks_on_management_listing_action('course', $node, $action); 1650 } 1651 1652 /** 1653 * Locates a category in the course category management interface and then triggers an action for it. 1654 * 1655 * @Given /^I click on "(?P<action_string>(?:[^"]|\\")*)" action for "(?P<name_string>(?:[^"]|\\")*)" in management category listing$/ 1656 * 1657 * @param string $action The action to take. One of 1658 * @param string $name The name of the category as it is displayed in the management interface. 1659 */ 1660 public function i_click_on_action_for_item_in_management_category_listing($action, $name) { 1661 $node = $this->get_management_category_listing_node_by_name($name); 1662 $this->user_clicks_on_management_listing_action('category', $node, $action); 1663 } 1664 1665 /** 1666 * Clicks to expand or collapse a category displayed on the frontpage 1667 * 1668 * @Given /^I toggle "(?P<categoryname_string>(?:[^"]|\\")*)" category children visibility in frontpage$/ 1669 * @throws ExpectationException 1670 * @param string $categoryname 1671 */ 1672 public function i_toggle_category_children_visibility_in_frontpage($categoryname) { 1673 1674 $headingtags = array(); 1675 for ($i = 1; $i <= 6; $i++) { 1676 $headingtags[] = 'self::h' . $i; 1677 } 1678 1679 $exception = new ExpectationException('"' . $categoryname . '" category can not be found', $this->getSession()); 1680 $categoryliteral = behat_context_helper::escape($categoryname); 1681 $xpath = "//div[@class='info']/descendant::*[" . implode(' or ', $headingtags) . "][@class='categoryname'][./descendant::a[.=$categoryliteral]]"; 1682 $node = $this->find('xpath', $xpath, $exception); 1683 $node->click(); 1684 1685 // Smooth expansion. 1686 $this->getSession()->wait(1000, false); 1687 } 1688 1689 /** 1690 * Finds the node to use for a management listitem action and clicks it. 1691 * 1692 * @param string $listingtype Either course or category. 1693 * @param \Behat\Mink\Element\NodeElement $listingnode 1694 * @param string $action The action being taken 1695 * @throws Behat\Mink\Exception\ExpectationException 1696 */ 1697 protected function user_clicks_on_management_listing_action($listingtype, $listingnode, $action) { 1698 $actionsnode = $listingnode->find('xpath', "//*[contains(concat(' ', normalize-space(@class), ' '), '{$listingtype}-item-actions')]"); 1699 if (!$actionsnode) { 1700 throw new ExpectationException("Could not find the actions for $listingtype", $this->getSession()); 1701 } 1702 $actionnode = $actionsnode->find('css', '.action-'.$action); 1703 if (!$actionnode) { 1704 throw new ExpectationException("Expected action was not available or not found ($action)", $this->getSession()); 1705 } 1706 if ($this->running_javascript() && !$actionnode->isVisible()) { 1707 $actionsnode->find('css', 'a.toggle-display')->click(); 1708 $actionnode = $actionsnode->find('css', '.action-'.$action); 1709 } 1710 $actionnode->click(); 1711 } 1712 1713 /** 1714 * Clicks on a category in the management interface. 1715 * 1716 * @Given /^I click on "(?P<categoryname_string>(?:[^"]|\\")*)" category in the management category listing$/ 1717 * @param string $name The name of the category to click. 1718 */ 1719 public function i_click_on_category_in_the_management_category_listing($name) { 1720 $node = $this->get_management_category_listing_node_by_name($name); 1721 $node->find('css', 'a.categoryname')->click(); 1722 } 1723 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |