See Release Notes
Long Term Support Release
Differences Between: [Versions 310 and 401] [Versions 311 and 401] [Versions 39 and 401]
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 namespace tool_usertours; 18 19 defined('MOODLE_INTERNAL') || die(); 20 21 global $CFG; 22 require_once($CFG->libdir . '/formslib.php'); 23 24 /** 25 * Tests for step. 26 * 27 * @package tool_usertours 28 * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> 29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 30 */ 31 class step_test extends \advanced_testcase { 32 33 /** 34 * @var moodle_database 35 */ 36 protected $db; 37 38 /** 39 * Setup to store the DB reference. 40 */ 41 public function setUp(): void { 42 global $DB; 43 44 $this->db = $DB; 45 } 46 47 /** 48 * Tear down to restore the original DB reference. 49 */ 50 public function tearDown(): void { 51 global $DB; 52 53 $DB = $this->db; 54 } 55 56 /** 57 * Helper to mock the database. 58 * 59 * @return moodle_database 60 */ 61 public function mock_database() { 62 global $DB; 63 64 $DB = $this->getMockBuilder('moodle_database') 65 ->getMock() 66 ; 67 68 return $DB; 69 } 70 71 /** 72 * Data provider for the dirty value tester. 73 * 74 * @return array 75 */ 76 public function dirty_value_provider() { 77 return [ 78 'tourid' => [ 79 'tourid', 80 [1], 81 ], 82 'title' => [ 83 'title', 84 ['Lorem'], 85 ], 86 'content' => [ 87 'content', 88 ['Lorem'], 89 ], 90 'targettype' => [ 91 'targettype', 92 ['Lorem'], 93 ], 94 'targetvalue' => [ 95 'targetvalue', 96 ['Lorem'], 97 ], 98 'sortorder' => [ 99 'sortorder', 100 [1], 101 ], 102 'config' => [ 103 'config', 104 ['key', 'value'], 105 ], 106 ]; 107 } 108 109 /** 110 * Test the fetch function. 111 */ 112 public function test_fetch() { 113 $step = $this->getMockBuilder(\tool_usertours\step::class) 114 ->onlyMethods(['reload_from_record']) 115 ->getMock() 116 ; 117 118 $idretval = rand(1, 100); 119 $DB = $this->mock_database(); 120 $DB->method('get_record') 121 ->willReturn($idretval) 122 ; 123 124 $retval = rand(1, 100); 125 $step->expects($this->once()) 126 ->method('reload_from_record') 127 ->with($this->equalTo($idretval)) 128 ->wilLReturn($retval) 129 ; 130 131 $rc = new \ReflectionClass(\tool_usertours\step::class); 132 $rcm = $rc->getMethod('fetch'); 133 $rcm->setAccessible(true); 134 135 $id = rand(1, 100); 136 $this->assertEquals($retval, $rcm->invoke($step, 'fetch', $id)); 137 } 138 139 /** 140 * Test that setters mark things as dirty. 141 * 142 * @dataProvider dirty_value_provider 143 * @param string $name The key to update 144 * @param string $value The value to set 145 */ 146 public function test_dirty_values($name, $value) { 147 $step = new \tool_usertours\step(); 148 $method = 'set_' . $name; 149 call_user_func_array([$step, $method], $value); 150 151 $rc = new \ReflectionClass(\tool_usertours\step::class); 152 $rcp = $rc->getProperty('dirty'); 153 $rcp->setAccessible(true); 154 155 $this->assertTrue($rcp->getValue($step)); 156 } 157 158 /** 159 * Provider for is_first_step. 160 * 161 * @return array 162 */ 163 public function step_sortorder_provider() { 164 return [ 165 [0, 5, true, false], 166 [1, 5, false, false], 167 [4, 5, false, true], 168 ]; 169 } 170 171 /** 172 * Test is_first_step. 173 * 174 * @dataProvider step_sortorder_provider 175 * @param int $sortorder The sortorder to check 176 * @param int $count Unused in this function 177 * @param bool $isfirst Whether this is the first step 178 * @param bool $islast Whether this is the last step 179 */ 180 public function test_is_first_step($sortorder, $count, $isfirst, $islast) { 181 $step = $this->getMockBuilder(\tool_usertours\step::class) 182 ->onlyMethods(['get_sortorder']) 183 ->getMock(); 184 185 $step->expects($this->once()) 186 ->method('get_sortorder') 187 ->willReturn($sortorder) 188 ; 189 190 $this->assertEquals($isfirst, $step->is_first_step()); 191 } 192 193 /** 194 * Test is_last_step. 195 * 196 * @dataProvider step_sortorder_provider 197 * @param int $sortorder The sortorder to check 198 * @param int $count Total number of steps for this test 199 * @param bool $isfirst Whether this is the first step 200 * @param bool $islast Whether this is the last step 201 */ 202 public function test_is_last_step($sortorder, $count, $isfirst, $islast) { 203 $step = $this->getMockBuilder(\tool_usertours\step::class) 204 ->onlyMethods(['get_sortorder', 'get_tour']) 205 ->getMock(); 206 207 $tour = $this->getMockBuilder(\tool_usertours\tour::class) 208 ->onlyMethods(['count_steps']) 209 ->getMock(); 210 211 $step->expects($this->once()) 212 ->method('get_tour') 213 ->willReturn($tour) 214 ; 215 216 $tour->expects($this->once()) 217 ->method('count_steps') 218 ->willReturn($count) 219 ; 220 221 $step->expects($this->once()) 222 ->method('get_sortorder') 223 ->willReturn($sortorder) 224 ; 225 226 $this->assertEquals($islast, $step->is_last_step()); 227 } 228 229 /** 230 * Test get_config with no keys provided. 231 */ 232 public function test_get_config_no_keys() { 233 $step = new \tool_usertours\step(); 234 235 $rc = new \ReflectionClass(\tool_usertours\step::class); 236 $rcp = $rc->getProperty('config'); 237 $rcp->setAccessible(true); 238 239 $allvalues = (object) [ 240 'some' => 'value', 241 'another' => 42, 242 'key' => [ 243 'somethingelse', 244 ], 245 ]; 246 247 $rcp->setValue($step, $allvalues); 248 249 $this->assertEquals($allvalues, $step->get_config()); 250 } 251 252 /** 253 * Data provider for get_config. 254 * 255 * @return array 256 */ 257 public function get_config_provider() { 258 $allvalues = (object) [ 259 'some' => 'value', 260 'another' => 42, 261 'key' => [ 262 'somethingelse', 263 ], 264 ]; 265 266 $tourconfig = rand(1, 100); 267 $forcedconfig = rand(1, 100); 268 269 return [ 270 'No initial config' => [ 271 null, 272 null, 273 null, 274 $tourconfig, 275 false, 276 $forcedconfig, 277 (object) [], 278 ], 279 'All values' => [ 280 $allvalues, 281 null, 282 null, 283 $tourconfig, 284 false, 285 $forcedconfig, 286 $allvalues, 287 ], 288 'Valid string value' => [ 289 $allvalues, 290 'some', 291 null, 292 $tourconfig, 293 false, 294 $forcedconfig, 295 'value', 296 ], 297 'Valid array value' => [ 298 $allvalues, 299 'key', 300 null, 301 $tourconfig, 302 false, 303 $forcedconfig, 304 ['somethingelse'], 305 ], 306 'Invalid value' => [ 307 $allvalues, 308 'notavalue', 309 null, 310 $tourconfig, 311 false, 312 $forcedconfig, 313 $tourconfig, 314 ], 315 'Configuration value' => [ 316 $allvalues, 317 'placement', 318 null, 319 $tourconfig, 320 false, 321 $forcedconfig, 322 $tourconfig, 323 ], 324 'Invalid value with default' => [ 325 $allvalues, 326 'notavalue', 327 'somedefault', 328 $tourconfig, 329 false, 330 $forcedconfig, 331 'somedefault', 332 ], 333 'Value forced at target' => [ 334 $allvalues, 335 'somevalue', 336 'somedefault', 337 $tourconfig, 338 true, 339 $forcedconfig, 340 $forcedconfig, 341 ], 342 ]; 343 } 344 345 /** 346 * Test get_config with valid keys provided. 347 * 348 * @dataProvider get_config_provider 349 * @param object $values The config values 350 * @param string $key The key 351 * @param mixed $default The default value 352 * @param mixed $tourconfig The tour config 353 * @param bool $isforced Whether the setting is forced 354 * @param mixed $forcedvalue The example value 355 * @param mixed $expected The expected value 356 */ 357 public function test_get_config_valid_keys($values, $key, $default, $tourconfig, $isforced, $forcedvalue, $expected) { 358 $step = $this->getMockBuilder(\tool_usertours\step::class) 359 ->onlyMethods(['get_target', 'get_targettype', 'get_tour']) 360 ->getMock(); 361 362 $rc = new \ReflectionClass(\tool_usertours\step::class); 363 $rcp = $rc->getProperty('config'); 364 $rcp->setAccessible(true); 365 $rcp->setValue($step, $values); 366 367 $target = $this->getMockBuilder(\tool_usertours\local\target\base::class) 368 ->disableOriginalConstructor() 369 ->getMock() 370 ; 371 372 $target->expects($this->any()) 373 ->method('is_setting_forced') 374 ->willReturn($isforced) 375 ; 376 377 $target->expects($this->any()) 378 ->method('get_forced_setting_value') 379 ->with($this->equalTo($key)) 380 ->willReturn($forcedvalue) 381 ; 382 383 $step->expects($this->any()) 384 ->method('get_targettype') 385 ->willReturn('type') 386 ; 387 388 $step->expects($this->any()) 389 ->method('get_target') 390 ->willReturn($target) 391 ; 392 393 $tour = $this->getMockBuilder(\tool_usertours\tour::class) 394 ->getMock() 395 ; 396 397 $tour->expects($this->any()) 398 ->method('get_config') 399 ->willReturn($tourconfig) 400 ; 401 402 $step->expects($this->any()) 403 ->method('get_tour') 404 ->willReturn($tour) 405 ; 406 407 $this->assertEquals($expected, $step->get_config($key, $default)); 408 } 409 410 /** 411 * Data provider for set_config. 412 */ 413 public function set_config_provider() { 414 $allvalues = (object) [ 415 'some' => 'value', 416 'another' => 42, 417 'key' => [ 418 'somethingelse', 419 ], 420 ]; 421 422 $randvalue = rand(1, 100); 423 424 $provider = []; 425 426 $newvalues = $allvalues; 427 $newvalues->some = 'unset'; 428 $provider['Unset an existing value'] = [ 429 $allvalues, 430 'some', 431 null, 432 $newvalues, 433 ]; 434 435 $newvalues = $allvalues; 436 $newvalues->some = $randvalue; 437 $provider['Set an existing value'] = [ 438 $allvalues, 439 'some', 440 $randvalue, 441 $newvalues, 442 ]; 443 444 $provider['Set a new value'] = [ 445 $allvalues, 446 'newkey', 447 $randvalue, 448 (object) array_merge((array) $allvalues, ['newkey' => $randvalue]), 449 ]; 450 451 return $provider; 452 } 453 454 /** 455 * Test that set_config works in the anticipated fashion. 456 * 457 * @dataProvider set_config_provider 458 * @param mixed $initialvalues The inital value to set 459 * @param string $key The key to test 460 * @param mixed $newvalue The new value to set 461 * @param mixed $expected The expected value 462 */ 463 public function test_set_config($initialvalues, $key, $newvalue, $expected) { 464 $step = new \tool_usertours\step(); 465 466 $rc = new \ReflectionClass(\tool_usertours\step::class); 467 $rcp = $rc->getProperty('config'); 468 $rcp->setAccessible(true); 469 $rcp->setValue($step, $initialvalues); 470 471 $target = $this->getMockBuilder(\tool_usertours\local\target\base::class) 472 ->disableOriginalConstructor() 473 ->getMock() 474 ; 475 476 $target->expects($this->any()) 477 ->method('is_setting_forced') 478 ->willReturn(false) 479 ; 480 481 $step->set_config($key, $newvalue); 482 483 $this->assertEquals($expected, $rcp->getValue($step)); 484 } 485 486 /** 487 * Ensure that non-dirty tours are not persisted. 488 */ 489 public function test_persist_non_dirty() { 490 $step = $this->getMockBuilder(\tool_usertours\step::class) 491 ->onlyMethods([ 492 'to_record', 493 'reload', 494 ]) 495 ->getMock() 496 ; 497 498 $step->expects($this->never()) 499 ->method('to_record') 500 ; 501 502 $step->expects($this->never()) 503 ->method('reload') 504 ; 505 506 $this->assertSame($step, $step->persist()); 507 } 508 509 /** 510 * Ensure that new dirty steps are persisted. 511 */ 512 public function test_persist_dirty_new() { 513 // Mock the database. 514 $DB = $this->mock_database(); 515 $DB->expects($this->once()) 516 ->method('insert_record') 517 ->willReturn(42) 518 ; 519 520 // Mock the tour. 521 $step = $this->getMockBuilder(\tool_usertours\step::class) 522 ->onlyMethods([ 523 'to_record', 524 'calculate_sortorder', 525 'reload', 526 ]) 527 ->getMock() 528 ; 529 530 $step->expects($this->once()) 531 ->method('to_record') 532 ->willReturn((object)['id' => 42]); 533 ; 534 535 $step->expects($this->once()) 536 ->method('calculate_sortorder') 537 ; 538 539 $step->expects($this->once()) 540 ->method('reload') 541 ; 542 543 $rc = new \ReflectionClass(\tool_usertours\step::class); 544 $rcp = $rc->getProperty('dirty'); 545 $rcp->setAccessible(true); 546 $rcp->setValue($step, true); 547 548 $tour = $this->createMock(\tool_usertours\tour::class); 549 $rcp = $rc->getProperty('tour'); 550 $rcp->setAccessible(true); 551 $rcp->setValue($step, $tour); 552 553 $this->assertSame($step, $step->persist()); 554 } 555 556 /** 557 * Ensure that new non-dirty, forced steps are persisted. 558 */ 559 public function test_persist_force_new() { 560 global $DB; 561 562 // Mock the database. 563 $DB = $this->mock_database(); 564 $DB->expects($this->once()) 565 ->method('insert_record') 566 ->willReturn(42) 567 ; 568 569 // Mock the tour. 570 $step = $this->getMockBuilder(\tool_usertours\step::class) 571 ->onlyMethods([ 572 'to_record', 573 'calculate_sortorder', 574 'reload', 575 ]) 576 ->getMock() 577 ; 578 579 $step->expects($this->once()) 580 ->method('to_record') 581 ->willReturn((object)['id' => 42]); 582 ; 583 584 $step->expects($this->once()) 585 ->method('calculate_sortorder') 586 ; 587 588 $step->expects($this->once()) 589 ->method('reload') 590 ; 591 592 $tour = $this->createMock(\tool_usertours\tour::class); 593 $rc = new \ReflectionClass(\tool_usertours\step::class); 594 $rcp = $rc->getProperty('tour'); 595 $rcp->setAccessible(true); 596 $rcp->setValue($step, $tour); 597 598 $this->assertSame($step, $step->persist(true)); 599 } 600 601 /** 602 * Ensure that existing dirty steps are persisted. 603 */ 604 public function test_persist_dirty_existing() { 605 // Mock the database. 606 $DB = $this->mock_database(); 607 $DB->expects($this->once()) 608 ->method('update_record') 609 ; 610 611 // Mock the tour. 612 $step = $this->getMockBuilder(\tool_usertours\step::class) 613 ->onlyMethods([ 614 'to_record', 615 'calculate_sortorder', 616 'reload', 617 ]) 618 ->getMock() 619 ; 620 621 $step->expects($this->once()) 622 ->method('to_record') 623 ->willReturn((object)['id' => 42]); 624 ; 625 626 $step->expects($this->never()) 627 ->method('calculate_sortorder') 628 ; 629 630 $step->expects($this->once()) 631 ->method('reload') 632 ; 633 634 $rc = new \ReflectionClass(\tool_usertours\step::class); 635 $rcp = $rc->getProperty('id'); 636 $rcp->setAccessible(true); 637 $rcp->setValue($step, 42); 638 639 $rcp = $rc->getProperty('dirty'); 640 $rcp->setAccessible(true); 641 $rcp->setValue($step, true); 642 643 $tour = $this->createMock(\tool_usertours\tour::class); 644 $rcp = $rc->getProperty('tour'); 645 $rcp->setAccessible(true); 646 $rcp->setValue($step, $tour); 647 648 $this->assertSame($step, $step->persist()); 649 } 650 651 /** 652 * Ensure that existing non-dirty, forced steps are persisted. 653 */ 654 public function test_persist_force_existing() { 655 global $DB; 656 657 // Mock the database. 658 $DB = $this->mock_database(); 659 $DB->expects($this->once()) 660 ->method('update_record') 661 ; 662 663 // Mock the tour. 664 $step = $this->getMockBuilder(\tool_usertours\step::class) 665 ->onlyMethods([ 666 'to_record', 667 'calculate_sortorder', 668 'reload', 669 ]) 670 ->getMock() 671 ; 672 673 $step->expects($this->once()) 674 ->method('to_record') 675 ->willReturn((object)['id' => 42]); 676 ; 677 678 $step->expects($this->never()) 679 ->method('calculate_sortorder') 680 ; 681 682 $step->expects($this->once()) 683 ->method('reload') 684 ; 685 686 $rc = new \ReflectionClass(\tool_usertours\step::class); 687 $rcp = $rc->getProperty('id'); 688 $rcp->setAccessible(true); 689 $rcp->setValue($step, 42); 690 691 $tour = $this->createMock(\tool_usertours\tour::class); 692 $rcp = $rc->getProperty('tour'); 693 $rcp->setAccessible(true); 694 $rcp->setValue($step, $tour); 695 696 $this->assertSame($step, $step->persist(true)); 697 } 698 699 /** 700 * Check that a tour which has never been persisted is removed correctly. 701 */ 702 public function test_remove_non_persisted() { 703 $step = $this->getMockBuilder(\tool_usertours\step::class) 704 ->onlyMethods([]) 705 ->getMock() 706 ; 707 708 // Mock the database. 709 $DB = $this->mock_database(); 710 $DB->expects($this->never()) 711 ->method('delete_records') 712 ; 713 714 $this->assertNull($step->remove()); 715 } 716 717 /** 718 * Check that a tour which has been persisted is removed correctly. 719 */ 720 public function test_remove_persisted() { 721 $id = rand(1, 100); 722 723 $tour = $this->getMockBuilder(\tool_usertours\tour::class) 724 ->onlyMethods([ 725 'reset_step_sortorder', 726 ]) 727 ->getMock() 728 ; 729 730 $tour->expects($this->once()) 731 ->method('reset_step_sortorder') 732 ; 733 734 $step = $this->getMockBuilder(\tool_usertours\step::class) 735 ->onlyMethods([ 736 'get_tour', 737 ]) 738 ->getMock() 739 ; 740 741 $step->expects($this->once()) 742 ->method('get_tour') 743 ->willReturn($tour) 744 ; 745 746 // Mock the database. 747 $DB = $this->mock_database(); 748 $DB->expects($this->once()) 749 ->method('delete_records') 750 ->with($this->equalTo('tool_usertours_steps'), $this->equalTo(['id' => $id])) 751 ; 752 753 $rc = new \ReflectionClass(\tool_usertours\step::class); 754 $rcp = $rc->getProperty('id'); 755 $rcp->setAccessible(true); 756 $rcp->setValue($step, $id); 757 758 $this->assertEquals($id, $step->get_id()); 759 $this->assertNull($step->remove()); 760 } 761 762 /** 763 * Data provider for the get_ tests. 764 * 765 * @return array 766 */ 767 public function getter_provider() { 768 return [ 769 'id' => [ 770 'id', 771 rand(1, 100), 772 ], 773 'tourid' => [ 774 'tourid', 775 rand(1, 100), 776 ], 777 'title' => [ 778 'title', 779 'Lorem', 780 ], 781 'content' => [ 782 'content', 783 'Lorem', 784 ], 785 'targettype' => [ 786 'targettype', 787 'Lorem', 788 ], 789 'targetvalue' => [ 790 'targetvalue', 791 'Lorem', 792 ], 793 'sortorder' => [ 794 'sortorder', 795 rand(1, 100), 796 ], 797 ]; 798 } 799 800 /** 801 * Test that getters return the configured value. 802 * 803 * @dataProvider getter_provider 804 * @param string $key The key to test 805 * @param mixed $value The expected value 806 */ 807 public function test_getters($key, $value) { 808 $step = new \tool_usertours\step(); 809 810 $rc = new \ReflectionClass(\tool_usertours\step::class); 811 812 $rcp = $rc->getProperty($key); 813 $rcp->setAccessible(true); 814 $rcp->setValue($step, $value); 815 816 $getter = 'get_' . $key; 817 818 $this->assertEquals($value, $step->$getter()); 819 } 820 821 /** 822 * Ensure that the get_step_image_from_input function replace PIXICON placeholder with the correct images correctly. 823 */ 824 public function test_get_step_image_from_input() { 825 // Test step content with single image. 826 $stepcontent = '@@PIXICON::tour/tour_mycourses::tool_usertours@@<br>Test'; 827 $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent); 828 829 // If the format is correct, PIXICON placeholder will be replaced with the img tag. 830 $this->assertStringStartsWith('<img', $stepcontent); 831 $this->assertStringEndsWith('Test', $stepcontent); 832 $this->assertStringNotContainsString('PIXICON', $stepcontent); 833 834 // Test step content with multiple images. 835 $stepcontent = '@@PIXICON::tour/tour_mycourses::tool_usertours@@<br>Test<br>@@PIXICON::tour/tour_myhomepage::tool_usertours@@'; 836 $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent); 837 // If the format is correct, PIXICON placeholder will be replaced with the img tag. 838 $this->assertStringStartsWith('<img', $stepcontent); 839 // We should have 2 img tags here. 840 $this->assertEquals(2, substr_count($stepcontent, '<img')); 841 $this->assertStringNotContainsString('PIXICON', $stepcontent); 842 843 // Test step content with incorrect format. 844 $stepcontent = '@@PIXICON::tour/tour_mycourses<br>Test'; 845 $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent); 846 847 // If the format is not correct, PIXICON placeholder will not be replaced with the img tag. 848 $this->assertStringStartsNotWith('<img', $stepcontent); 849 $this->assertStringStartsWith('@@PIXICON', $stepcontent); 850 $this->assertStringEndsWith('Test', $stepcontent); 851 $this->assertStringContainsString('PIXICON', $stepcontent); 852 } 853 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body