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