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 core_user; 18 19 /** 20 * Unit tests for user/profile/lib.php. 21 * 22 * @package core_user 23 * @copyright 2014 The Open University 24 * @licensehttp://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 25 */ 26 class profilelib_test extends \advanced_testcase { 27 28 /** 29 * Load required test libraries 30 */ 31 public static function setUpBeforeClass(): void { 32 global $CFG; 33 require_once("{$CFG->dirroot}/user/profile/lib.php"); 34 } 35 36 /** 37 * Tests profile_get_custom_fields function and checks it is consistent 38 * with profile_user_record. 39 */ 40 public function test_get_custom_fields() { 41 $this->resetAfterTest(); 42 $user = $this->getDataGenerator()->create_user(); 43 44 // Add a custom field of textarea type. 45 $id1 = $this->getDataGenerator()->create_custom_profile_field([ 46 'shortname' => 'frogdesc', 'name' => 'Description of frog', 47 'datatype' => 'textarea'])->id; 48 49 // Check the field is returned. 50 $result = profile_get_custom_fields(); 51 $this->assertArrayHasKey($id1, $result); 52 $this->assertEquals('frogdesc', $result[$id1]->shortname); 53 54 // Textarea types are not included in user data though, so if we 55 // use the 'only in user data' parameter, there is still nothing. 56 $this->assertArrayNotHasKey($id1, profile_get_custom_fields(true)); 57 58 // Check that profile_user_record returns same (no) fields. 59 $this->assertObjectNotHasAttribute('frogdesc', profile_user_record($user->id)); 60 61 // Check that profile_user_record returns all the fields when requested. 62 $this->assertObjectHasAttribute('frogdesc', profile_user_record($user->id, false)); 63 64 // Add another custom field, this time of normal text type. 65 $id2 = $this->getDataGenerator()->create_custom_profile_field(array( 66 'shortname' => 'frogname', 'name' => 'Name of frog', 67 'datatype' => 'text'))->id; 68 69 // Check both are returned using normal option. 70 $result = profile_get_custom_fields(); 71 $this->assertArrayHasKey($id2, $result); 72 $this->assertEquals('frogname', $result[$id2]->shortname); 73 74 // And check that only the one is returned the other way. 75 $this->assertArrayHasKey($id2, profile_get_custom_fields(true)); 76 77 // Check profile_user_record returns same field. 78 $this->assertObjectHasAttribute('frogname', profile_user_record($user->id)); 79 80 // Check that profile_user_record returns all the fields when requested. 81 $this->assertObjectHasAttribute('frogname', profile_user_record($user->id, false)); 82 } 83 84 /** 85 * Make sure that all profile fields can be initialised without arguments. 86 */ 87 public function test_default_constructor() { 88 global $DB, $CFG; 89 require_once($CFG->dirroot . '/user/profile/definelib.php'); 90 $datatypes = profile_list_datatypes(); 91 foreach ($datatypes as $datatype => $datatypename) { 92 require_once($CFG->dirroot . '/user/profile/field/' . 93 $datatype . '/field.class.php'); 94 $newfield = 'profile_field_' . $datatype; 95 $formfield = new $newfield(); 96 $this->assertNotNull($formfield); 97 } 98 } 99 100 /** 101 * Test profile_view function 102 */ 103 public function test_profile_view() { 104 global $USER; 105 106 $this->resetAfterTest(); 107 108 // Course without sections. 109 $course = $this->getDataGenerator()->create_course(); 110 $context = \context_course::instance($course->id); 111 $user = $this->getDataGenerator()->create_user(); 112 $usercontext = \context_user::instance($user->id); 113 114 $this->setUser($user); 115 116 // Redirect events to the sink, so we can recover them later. 117 $sink = $this->redirectEvents(); 118 119 profile_view($user, $context, $course); 120 $events = $sink->get_events(); 121 $event = reset($events); 122 123 // Check the event details are correct. 124 $this->assertInstanceOf('\core\event\user_profile_viewed', $event); 125 $this->assertEquals($context, $event->get_context()); 126 $this->assertEquals($user->id, $event->relateduserid); 127 $this->assertEquals($course->id, $event->other['courseid']); 128 $this->assertEquals($course->shortname, $event->other['courseshortname']); 129 $this->assertEquals($course->fullname, $event->other['coursefullname']); 130 131 profile_view($user, $usercontext); 132 $events = $sink->get_events(); 133 $event = array_pop($events); 134 $sink->close(); 135 136 $this->assertInstanceOf('\core\event\user_profile_viewed', $event); 137 $this->assertEquals($usercontext, $event->get_context()); 138 $this->assertEquals($user->id, $event->relateduserid); 139 140 } 141 142 /** 143 * Test that {@link user_not_fully_set_up()} takes required custom fields into account. 144 */ 145 public function test_profile_has_required_custom_fields_set() { 146 global $CFG; 147 require_once($CFG->dirroot.'/mnet/lib.php'); 148 149 $this->resetAfterTest(); 150 151 // Add a required, visible, unlocked custom field. 152 $this->getDataGenerator()->create_custom_profile_field(['shortname' => 'house', 'name' => 'House', 'required' => 1, 153 'visible' => 1, 'locked' => 0, 'datatype' => 'text']); 154 155 // Add an optional, visible, unlocked custom field. 156 $this->getDataGenerator()->create_custom_profile_field(['shortname' => 'pet', 'name' => 'Pet', 'required' => 0, 157 'visible' => 1, 'locked' => 0, 'datatype' => 'text']); 158 159 // Add required but invisible custom field. 160 $this->getDataGenerator()->create_custom_profile_field(['shortname' => 'secretid', 'name' => 'Secret ID', 161 'required' => 1, 'visible' => 0, 'locked' => 0, 'datatype' => 'text']); 162 163 // Add required but locked custom field. 164 $this->getDataGenerator()->create_custom_profile_field(['shortname' => 'muggleborn', 'name' => 'Muggle-born', 165 'required' => 1, 'visible' => 1, 'locked' => 1, 'datatype' => 'checkbox']); 166 167 // Create some student accounts. 168 $hermione = $this->getDataGenerator()->create_user(); 169 $harry = $this->getDataGenerator()->create_user(); 170 $ron = $this->getDataGenerator()->create_user(); 171 $draco = $this->getDataGenerator()->create_user(); 172 173 // Hermione has all available custom fields filled (of course she has). 174 profile_save_data((object)['id' => $hermione->id, 'profile_field_house' => 'Gryffindor']); 175 profile_save_data((object)['id' => $hermione->id, 'profile_field_pet' => 'Crookshanks']); 176 177 // Harry has only the optional field filled. 178 profile_save_data((object)['id' => $harry->id, 'profile_field_pet' => 'Hedwig']); 179 180 // Draco has only the required field filled. 181 profile_save_data((object)['id' => $draco->id, 'profile_field_house' => 'Slytherin']); 182 183 // Only students with required fields filled should be considered as fully set up in the default (strict) mode. 184 $this->assertFalse(user_not_fully_set_up($hermione)); 185 $this->assertFalse(user_not_fully_set_up($draco)); 186 $this->assertTrue(user_not_fully_set_up($harry)); 187 $this->assertTrue(user_not_fully_set_up($ron)); 188 189 // In the lax mode, students do not need to have required fields filled. 190 $this->assertFalse(user_not_fully_set_up($hermione, false)); 191 $this->assertFalse(user_not_fully_set_up($draco, false)); 192 $this->assertFalse(user_not_fully_set_up($harry, false)); 193 $this->assertFalse(user_not_fully_set_up($ron, false)); 194 195 // Lack of required core field is seen as a problem in either mode. 196 unset($hermione->email); 197 $this->assertTrue(user_not_fully_set_up($hermione, true)); 198 $this->assertTrue(user_not_fully_set_up($hermione, false)); 199 200 // When confirming remote MNet users, we do not have custom fields available. 201 $roamingharry = mnet_strip_user($harry, ['firstname', 'lastname', 'email']); 202 $roaminghermione = mnet_strip_user($hermione, ['firstname', 'lastname', 'email']); 203 204 $this->assertTrue(user_not_fully_set_up($roamingharry, true)); 205 $this->assertFalse(user_not_fully_set_up($roamingharry, false)); 206 $this->assertTrue(user_not_fully_set_up($roaminghermione, true)); 207 $this->assertTrue(user_not_fully_set_up($roaminghermione, false)); 208 } 209 210 /** 211 * Test that user generator sets the custom profile fields 212 */ 213 public function test_profile_fields_in_generator() { 214 global $CFG; 215 require_once($CFG->dirroot.'/mnet/lib.php'); 216 217 $this->resetAfterTest(); 218 219 // Add a required, visible, unlocked custom field. 220 $this->getDataGenerator()->create_custom_profile_field(['shortname' => 'house', 'name' => 'House', 'required' => 1, 221 'visible' => 1, 'locked' => 0, 'datatype' => 'text', 'defaultdata' => null]); 222 223 // Create some student accounts. 224 $hermione = $this->getDataGenerator()->create_user(['profile_field_house' => 'Gryffindor']); 225 $harry = $this->getDataGenerator()->create_user(); 226 227 // Only students with required fields filled should be considered as fully set up. 228 $this->assertFalse(user_not_fully_set_up($hermione)); 229 $this->assertTrue(user_not_fully_set_up($harry)); 230 231 // Test that the profile fields were actually set. 232 $profilefields1 = profile_user_record($hermione->id); 233 $this->assertEquals('Gryffindor', $profilefields1->house); 234 235 $profilefields2 = profile_user_record($harry->id); 236 $this->assertObjectHasAttribute('house', $profilefields2); 237 $this->assertNull($profilefields2->house); 238 } 239 240 /** 241 * Tests the profile_get_custom_field_data_by_shortname function when working normally. 242 */ 243 public function test_profile_get_custom_field_data_by_shortname_normal() { 244 global $DB; 245 246 $this->resetAfterTest(); 247 248 // Create 3 profile fields. 249 $generator = $this->getDataGenerator(); 250 $field1 = $generator->create_custom_profile_field(['datatype' => 'text', 251 'shortname' => 'speciality', 'name' => 'Speciality', 252 'visible' => PROFILE_VISIBLE_ALL]); 253 $field2 = $generator->create_custom_profile_field(['datatype' => 'menu', 254 'shortname' => 'veggie', 'name' => 'Vegetarian', 255 'visible' => PROFILE_VISIBLE_PRIVATE]); 256 257 // Get the first field data and check it is correct. 258 $data = profile_get_custom_field_data_by_shortname('speciality'); 259 $this->assertEquals('Speciality', $data->name); 260 $this->assertEquals(PROFILE_VISIBLE_ALL, $data->visible); 261 $this->assertEquals($field1->id, $data->id); 262 263 // Get the second field data, checking there is no database query this time. 264 $before = $DB->perf_get_queries(); 265 $data = profile_get_custom_field_data_by_shortname('veggie'); 266 $this->assertEquals($before, $DB->perf_get_queries()); 267 $this->assertEquals('Vegetarian', $data->name); 268 $this->assertEquals(PROFILE_VISIBLE_PRIVATE, $data->visible); 269 $this->assertEquals($field2->id, $data->id); 270 } 271 272 /** 273 * Tests the profile_get_custom_field_data_by_shortname function with a field that doesn't exist. 274 */ 275 public function test_profile_get_custom_field_data_by_shortname_missing() { 276 $this->assertNull(profile_get_custom_field_data_by_shortname('speciality')); 277 } 278 279 /** 280 * Data provider for {@see test_profile_get_custom_field_data_by_shortname_case_sensitivity} 281 * 282 * @return array[] 283 */ 284 public function profile_get_custom_field_data_by_shortname_case_sensitivity_provider(): array { 285 return [ 286 'Matching case, case-sensitive search' => ['hello', 'hello', true, true], 287 'Matching case, case-insensitive search' => ['hello', 'hello', false, true], 288 'Non-matching case, case-sensitive search' => ['hello', 'Hello', true, false], 289 'Non-matching case, case-insensitive search' => ['hello', 'Hello', false, true], 290 'Non-matching, case-sensitive search' => ['hello', 'hola', true, false], 291 'Non-matching, case-insensitive search' => ['hello', 'hola', false, false], 292 ]; 293 } 294 295 /** 296 * Test retrieving custom field by shortname, specifying case-sensitivity when matching 297 * 298 * @param string $shortname 299 * @param string $shortnamesearch 300 * @param bool $casesensitive 301 * @param bool $expectmatch 302 * 303 * @dataProvider profile_get_custom_field_data_by_shortname_case_sensitivity_provider 304 */ 305 public function test_profile_get_custom_field_data_by_shortname_case_sensitivity( 306 string $shortname, 307 string $shortnamesearch, 308 bool $casesensitive, 309 bool $expectmatch 310 ): void { 311 $this->resetAfterTest(); 312 313 $this->getDataGenerator()->create_custom_profile_field([ 314 'datatype' => 'text', 315 'shortname' => $shortname, 316 'name' => 'My field', 317 ]); 318 319 $customfield = profile_get_custom_field_data_by_shortname($shortnamesearch, $casesensitive); 320 if ($expectmatch) { 321 $this->assertInstanceOf(\stdClass::class, $customfield); 322 $this->assertEquals('text', $customfield->datatype); 323 $this->assertEquals($shortname, $customfield->shortname); 324 $this->assertEquals('My field', $customfield->name); 325 } else { 326 $this->assertNull($customfield); 327 } 328 } 329 330 /** 331 * Test profile field loading via profile_get_user_field helper 332 * 333 * @covers ::profile_get_user_field 334 */ 335 public function test_profile_get_user_field(): void { 336 $this->resetAfterTest(); 337 338 $profilefield = $this->getDataGenerator()->create_custom_profile_field([ 339 'shortname' => 'fruit', 340 'name' => 'Fruit', 341 'datatype' => 'text', 342 ]); 343 $user = $this->getDataGenerator()->create_user(['profile_field_fruit' => 'Apple']); 344 345 $fieldinstance = profile_get_user_field('text', $profilefield->id, $user->id); 346 $this->assertInstanceOf(\profile_field_text::class, $fieldinstance); 347 $this->assertEquals($profilefield->id, $fieldinstance->fieldid); 348 $this->assertEquals($user->id, $fieldinstance->userid); 349 $this->assertEquals('Apple', $fieldinstance->data); 350 } 351 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body