Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle.  If not, see <http://www.gnu.org/licenses/>.

< /** < * Tests for step. < * < * @package tool_usertours < * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> < * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later < */
> namespace tool_usertours;
defined('MOODLE_INTERNAL') || die(); global $CFG; require_once($CFG->libdir . '/formslib.php'); /** * Tests for step. * * @package tool_usertours * @copyright 2016 Andrew Nicols <andrew@nicols.co.uk> * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */
< class step_testcase extends advanced_testcase {
> class step_test extends \advanced_testcase {
/** * @var moodle_database */ protected $db; /** * Setup to store the DB reference. */
< public function setUp() {
> public function setUp(): void {
global $DB; $this->db = $DB; } /** * Tear down to restore the original DB reference. */
< public function tearDown() {
> public function tearDown(): void {
global $DB; $DB = $this->db; } /** * Helper to mock the database. * * @return moodle_database */ public function mock_database() { global $DB; $DB = $this->getMockBuilder('moodle_database') ->getMock() ; return $DB; } /** * Data provider for the dirty value tester. * * @return array */ public function dirty_value_provider() { return [ 'tourid' => [ 'tourid', [1], ], 'title' => [ 'title', ['Lorem'], ], 'content' => [ 'content', ['Lorem'], ], 'targettype' => [ 'targettype', ['Lorem'], ], 'targetvalue' => [ 'targetvalue', ['Lorem'], ], 'sortorder' => [ 'sortorder', [1], ], 'config' => [ 'config', ['key', 'value'], ], ]; } /** * Test the fetch function. */ public function test_fetch() { $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods(['reload_from_record'])
> ->onlyMethods(['reload_from_record'])
->getMock() ; $idretval = rand(1, 100); $DB = $this->mock_database(); $DB->method('get_record') ->willReturn($idretval) ; $retval = rand(1, 100); $step->expects($this->once()) ->method('reload_from_record') ->with($this->equalTo($idretval)) ->wilLReturn($retval) ; $rc = new \ReflectionClass(\tool_usertours\step::class); $rcm = $rc->getMethod('fetch'); $rcm->setAccessible(true); $id = rand(1, 100); $this->assertEquals($retval, $rcm->invoke($step, 'fetch', $id)); } /** * Test that setters mark things as dirty. * * @dataProvider dirty_value_provider * @param string $name The key to update * @param string $value The value to set */ public function test_dirty_values($name, $value) { $step = new \tool_usertours\step(); $method = 'set_' . $name; call_user_func_array([$step, $method], $value); $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('dirty'); $rcp->setAccessible(true); $this->assertTrue($rcp->getValue($step)); } /** * Provider for is_first_step. * * @return array */ public function step_sortorder_provider() { return [ [0, 5, true, false], [1, 5, false, false], [4, 5, false, true], ]; } /** * Test is_first_step. * * @dataProvider step_sortorder_provider * @param int $sortorder The sortorder to check * @param int $count Unused in this function * @param bool $isfirst Whether this is the first step * @param bool $islast Whether this is the last step */ public function test_is_first_step($sortorder, $count, $isfirst, $islast) { $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods(['get_sortorder'])
> ->onlyMethods(['get_sortorder'])
->getMock(); $step->expects($this->once()) ->method('get_sortorder') ->willReturn($sortorder) ; $this->assertEquals($isfirst, $step->is_first_step()); } /** * Test is_last_step. * * @dataProvider step_sortorder_provider * @param int $sortorder The sortorder to check * @param int $count Total number of steps for this test * @param bool $isfirst Whether this is the first step * @param bool $islast Whether this is the last step */ public function test_is_last_step($sortorder, $count, $isfirst, $islast) { $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods(['get_sortorder', 'get_tour'])
> ->onlyMethods(['get_sortorder', 'get_tour'])
->getMock(); $tour = $this->getMockBuilder(\tool_usertours\tour::class)
< ->setMethods(['count_steps'])
> ->onlyMethods(['count_steps'])
->getMock(); $step->expects($this->once()) ->method('get_tour') ->willReturn($tour) ; $tour->expects($this->once()) ->method('count_steps') ->willReturn($count) ; $step->expects($this->once()) ->method('get_sortorder') ->willReturn($sortorder) ; $this->assertEquals($islast, $step->is_last_step()); } /** * Test get_config with no keys provided. */ public function test_get_config_no_keys() { $step = new \tool_usertours\step(); $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('config'); $rcp->setAccessible(true); $allvalues = (object) [ 'some' => 'value', 'another' => 42, 'key' => [ 'somethingelse', ], ]; $rcp->setValue($step, $allvalues); $this->assertEquals($allvalues, $step->get_config()); } /** * Data provider for get_config. * * @return array */ public function get_config_provider() { $allvalues = (object) [ 'some' => 'value', 'another' => 42, 'key' => [ 'somethingelse', ], ]; $tourconfig = rand(1, 100); $forcedconfig = rand(1, 100); return [ 'No initial config' => [ null, null, null, $tourconfig, false, $forcedconfig, (object) [], ], 'All values' => [ $allvalues, null, null, $tourconfig, false, $forcedconfig, $allvalues, ], 'Valid string value' => [ $allvalues, 'some', null, $tourconfig, false, $forcedconfig, 'value', ], 'Valid array value' => [ $allvalues, 'key', null, $tourconfig, false, $forcedconfig, ['somethingelse'], ], 'Invalid value' => [ $allvalues, 'notavalue', null, $tourconfig, false, $forcedconfig, $tourconfig, ], 'Configuration value' => [ $allvalues, 'placement', null, $tourconfig, false, $forcedconfig, $tourconfig, ], 'Invalid value with default' => [ $allvalues, 'notavalue', 'somedefault', $tourconfig, false, $forcedconfig, 'somedefault', ], 'Value forced at target' => [ $allvalues, 'somevalue', 'somedefault', $tourconfig, true, $forcedconfig, $forcedconfig, ], ]; } /** * Test get_config with valid keys provided. * * @dataProvider get_config_provider * @param object $values The config values * @param string $key The key * @param mixed $default The default value * @param mixed $tourconfig The tour config * @param bool $isforced Whether the setting is forced * @param mixed $forcedvalue The example value * @param mixed $expected The expected value */ public function test_get_config_valid_keys($values, $key, $default, $tourconfig, $isforced, $forcedvalue, $expected) { $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods(['get_target', 'get_targettype', 'get_tour'])
> ->onlyMethods(['get_target', 'get_targettype', 'get_tour'])
->getMock(); $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('config'); $rcp->setAccessible(true); $rcp->setValue($step, $values); $target = $this->getMockBuilder(\tool_usertours\local\target\base::class) ->disableOriginalConstructor() ->getMock() ; $target->expects($this->any()) ->method('is_setting_forced') ->willReturn($isforced) ; $target->expects($this->any()) ->method('get_forced_setting_value') ->with($this->equalTo($key)) ->willReturn($forcedvalue) ; $step->expects($this->any()) ->method('get_targettype') ->willReturn('type') ; $step->expects($this->any()) ->method('get_target') ->willReturn($target) ; $tour = $this->getMockBuilder(\tool_usertours\tour::class) ->getMock() ; $tour->expects($this->any()) ->method('get_config') ->willReturn($tourconfig) ; $step->expects($this->any()) ->method('get_tour') ->willReturn($tour) ; $this->assertEquals($expected, $step->get_config($key, $default)); } /** * Data provider for set_config. */ public function set_config_provider() { $allvalues = (object) [ 'some' => 'value', 'another' => 42, 'key' => [ 'somethingelse', ], ]; $randvalue = rand(1, 100); $provider = []; $newvalues = $allvalues; $newvalues->some = 'unset'; $provider['Unset an existing value'] = [ $allvalues, 'some', null, $newvalues, ]; $newvalues = $allvalues; $newvalues->some = $randvalue; $provider['Set an existing value'] = [ $allvalues, 'some', $randvalue, $newvalues, ]; $provider['Set a new value'] = [ $allvalues, 'newkey', $randvalue, (object) array_merge((array) $allvalues, ['newkey' => $randvalue]), ]; return $provider; } /** * Test that set_config works in the anticipated fashion. * * @dataProvider set_config_provider * @param mixed $initialvalues The inital value to set * @param string $key The key to test * @param mixed $newvalue The new value to set * @param mixed $expected The expected value */ public function test_set_config($initialvalues, $key, $newvalue, $expected) { $step = new \tool_usertours\step(); $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('config'); $rcp->setAccessible(true); $rcp->setValue($step, $initialvalues); $target = $this->getMockBuilder(\tool_usertours\local\target\base::class) ->disableOriginalConstructor() ->getMock() ; $target->expects($this->any()) ->method('is_setting_forced') ->willReturn(false) ; $step->set_config($key, $newvalue); $this->assertEquals($expected, $rcp->getValue($step)); } /** * Ensure that non-dirty tours are not persisted. */ public function test_persist_non_dirty() { $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods([
> ->onlyMethods([
'to_record', 'reload', ]) ->getMock() ; $step->expects($this->never()) ->method('to_record') ; $step->expects($this->never()) ->method('reload') ; $this->assertSame($step, $step->persist()); } /** * Ensure that new dirty steps are persisted. */ public function test_persist_dirty_new() { // Mock the database. $DB = $this->mock_database(); $DB->expects($this->once()) ->method('insert_record') ->willReturn(42) ; // Mock the tour. $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods([
> ->onlyMethods([
'to_record', 'calculate_sortorder', 'reload', ]) ->getMock() ; $step->expects($this->once()) ->method('to_record') ->willReturn((object)['id' => 42]); ; $step->expects($this->once()) ->method('calculate_sortorder') ; $step->expects($this->once()) ->method('reload') ; $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('dirty'); $rcp->setAccessible(true); $rcp->setValue($step, true); $tour = $this->createMock(\tool_usertours\tour::class); $rcp = $rc->getProperty('tour'); $rcp->setAccessible(true); $rcp->setValue($step, $tour); $this->assertSame($step, $step->persist()); } /** * Ensure that new non-dirty, forced steps are persisted. */ public function test_persist_force_new() { global $DB; // Mock the database. $DB = $this->mock_database(); $DB->expects($this->once()) ->method('insert_record') ->willReturn(42) ; // Mock the tour. $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods([
> ->onlyMethods([
'to_record', 'calculate_sortorder', 'reload', ]) ->getMock() ; $step->expects($this->once()) ->method('to_record') ->willReturn((object)['id' => 42]); ; $step->expects($this->once()) ->method('calculate_sortorder') ; $step->expects($this->once()) ->method('reload') ; $tour = $this->createMock(\tool_usertours\tour::class); $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('tour'); $rcp->setAccessible(true); $rcp->setValue($step, $tour); $this->assertSame($step, $step->persist(true)); } /** * Ensure that existing dirty steps are persisted. */ public function test_persist_dirty_existing() { // Mock the database. $DB = $this->mock_database(); $DB->expects($this->once()) ->method('update_record') ; // Mock the tour. $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods([
> ->onlyMethods([
'to_record', 'calculate_sortorder', 'reload', ]) ->getMock() ; $step->expects($this->once()) ->method('to_record') ->willReturn((object)['id' => 42]); ; $step->expects($this->never()) ->method('calculate_sortorder') ; $step->expects($this->once()) ->method('reload') ; $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('id'); $rcp->setAccessible(true); $rcp->setValue($step, 42); $rcp = $rc->getProperty('dirty'); $rcp->setAccessible(true); $rcp->setValue($step, true); $tour = $this->createMock(\tool_usertours\tour::class); $rcp = $rc->getProperty('tour'); $rcp->setAccessible(true); $rcp->setValue($step, $tour); $this->assertSame($step, $step->persist()); } /** * Ensure that existing non-dirty, forced steps are persisted. */ public function test_persist_force_existing() { global $DB; // Mock the database. $DB = $this->mock_database(); $DB->expects($this->once()) ->method('update_record') ; // Mock the tour. $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods([
> ->onlyMethods([
'to_record', 'calculate_sortorder', 'reload', ]) ->getMock() ; $step->expects($this->once()) ->method('to_record') ->willReturn((object)['id' => 42]); ; $step->expects($this->never()) ->method('calculate_sortorder') ; $step->expects($this->once()) ->method('reload') ; $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('id'); $rcp->setAccessible(true); $rcp->setValue($step, 42); $tour = $this->createMock(\tool_usertours\tour::class); $rcp = $rc->getProperty('tour'); $rcp->setAccessible(true); $rcp->setValue($step, $tour); $this->assertSame($step, $step->persist(true)); } /** * Check that a tour which has never been persisted is removed correctly. */ public function test_remove_non_persisted() { $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods(null)
> ->onlyMethods([])
->getMock() ; // Mock the database. $DB = $this->mock_database(); $DB->expects($this->never()) ->method('delete_records') ; $this->assertNull($step->remove()); } /** * Check that a tour which has been persisted is removed correctly. */ public function test_remove_persisted() { $id = rand(1, 100); $tour = $this->getMockBuilder(\tool_usertours\tour::class)
< ->setMethods([
> ->onlyMethods([
'reset_step_sortorder', ]) ->getMock() ; $tour->expects($this->once()) ->method('reset_step_sortorder') ; $step = $this->getMockBuilder(\tool_usertours\step::class)
< ->setMethods([
> ->onlyMethods([
'get_tour', ]) ->getMock() ; $step->expects($this->once()) ->method('get_tour') ->willReturn($tour) ; // Mock the database. $DB = $this->mock_database(); $DB->expects($this->once()) ->method('delete_records') ->with($this->equalTo('tool_usertours_steps'), $this->equalTo(['id' => $id])) ; $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty('id'); $rcp->setAccessible(true); $rcp->setValue($step, $id); $this->assertEquals($id, $step->get_id()); $this->assertNull($step->remove()); } /** * Data provider for the get_ tests. * * @return array */ public function getter_provider() { return [ 'id' => [ 'id', rand(1, 100), ], 'tourid' => [ 'tourid', rand(1, 100), ], 'title' => [ 'title', 'Lorem', ], 'content' => [ 'content', 'Lorem', ], 'targettype' => [ 'targettype', 'Lorem', ], 'targetvalue' => [ 'targetvalue', 'Lorem', ], 'sortorder' => [ 'sortorder', rand(1, 100), ], ]; } /** * Test that getters return the configured value. * * @dataProvider getter_provider * @param string $key The key to test * @param mixed $value The expected value */ public function test_getters($key, $value) { $step = new \tool_usertours\step(); $rc = new \ReflectionClass(\tool_usertours\step::class); $rcp = $rc->getProperty($key); $rcp->setAccessible(true); $rcp->setValue($step, $value); $getter = 'get_' . $key; $this->assertEquals($value, $step->$getter()); } /**
< * Data Provider for get_string_from_input. < * < * @return array < */ < public function get_string_from_input_provider() { < return [ < 'Text' => [ < 'example', < 'example', < ], < 'Text which looks like a langstring' => [ < 'example,fakecomponent', < 'example,fakecomponent', < ], < 'Text which is a langstring' => [ < 'administration,core', < 'Administration', < ], < 'Text which is a langstring but uses "moodle" instead of "core"' => [ < 'administration,moodle', < 'Administration', < ], < 'Text which is a langstring, but with extra whitespace' => [ < ' administration,moodle ', < 'Administration', < ], < 'Looks like a langstring, but has incorrect space around comma' => [ < 'administration , moodle', < 'administration , moodle', < ], < ]; < } < < /** < * Ensure that the get_string_from_input function returns langstring strings correctly. < * < * @dataProvider get_string_from_input_provider < * @param string $string The string to test < * @param string $expected The expected result
> * Ensure that the get_step_image_from_input function replace PIXICON placeholder with the correct images correctly.
*/
< public function test_get_string_from_input($string, $expected) { < $this->assertEquals($expected, \tool_usertours\step::get_string_from_input($string));
> public function test_get_step_image_from_input() { > // Test step content with single image. > $stepcontent = '@@PIXICON::tour/tour_mycourses::tool_usertours@@<br>Test'; > $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent); > > // If the format is correct, PIXICON placeholder will be replaced with the img tag. > $this->assertStringStartsWith('<img', $stepcontent); > $this->assertStringEndsWith('Test', $stepcontent); > $this->assertStringNotContainsString('PIXICON', $stepcontent); > > // Test step content with multiple images. > $stepcontent = '@@PIXICON::tour/tour_mycourses::tool_usertours@@<br>Test<br>@@PIXICON::tour/tour_myhomepage::tool_usertours@@'; > $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent); > // If the format is correct, PIXICON placeholder will be replaced with the img tag. > $this->assertStringStartsWith('<img', $stepcontent); > // We should have 2 img tags here. > $this->assertEquals(2, substr_count($stepcontent, '<img')); > $this->assertStringNotContainsString('PIXICON', $stepcontent); > > // Test step content with incorrect format. > $stepcontent = '@@PIXICON::tour/tour_mycourses<br>Test'; > $stepcontent = \tool_usertours\step::get_step_image_from_input($stepcontent); > > // If the format is not correct, PIXICON placeholder will not be replaced with the img tag. > $this->assertStringStartsNotWith('<img', $stepcontent); > $this->assertStringStartsWith('@@PIXICON', $stepcontent); > $this->assertStringEndsWith('Test', $stepcontent); > $this->assertStringContainsString('PIXICON', $stepcontent);
} }