MDL-67518 phpunit: avoid DB sorting randomness in test
[moodle.git] / blocks / tests / externallib_test.php
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/>.
17 /**
18  * External block functions unit tests
19  *
20  * @package    core_block
21  * @category   external
22  * @copyright  2017 Juan Leyva <juan@moodle.com>
23  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24  * @since      Moodle 3.3
25  */
27 defined('MOODLE_INTERNAL') || die();
29 global $CFG;
31 require_once($CFG->dirroot . '/webservice/tests/helpers.php');
32 require_once($CFG->dirroot . '/my/lib.php');
34 /**
35  * External block functions unit tests
36  *
37  * @package    core_block
38  * @category   external
39  * @copyright  2015 Juan Leyva <juan@moodle.com>
40  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
41  * @since      Moodle 3.0
42  */
43 class core_block_externallib_testcase extends externallib_advanced_testcase {
45     /**
46      * Test get_course_blocks
47      */
48     public function test_get_course_blocks() {
49         global $DB, $FULLME;
51         $this->resetAfterTest(true);
53         $user = $this->getDataGenerator()->create_user();
54         $course = $this->getDataGenerator()->create_course();
55         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
56         $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
58         $page = new moodle_page();
59         $page->set_context(context_course::instance($course->id));
60         $page->set_pagelayout('course');
61         $course->format = course_get_format($course)->get_format();
62         $page->set_pagetype('course-view-' . $course->format);
63         $page->blocks->load_blocks();
64         $newblock = 'calendar_upcoming';
65         $page->blocks->add_block_at_end_of_default_region($newblock);
66         $this->setUser($user);
68         // Check for the new block.
69         $result = core_block_external::get_course_blocks($course->id);
70         // We need to execute the return values cleaning process to simulate the web service server.
71         $result = external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
73         // Expect the new block.
74         $this->assertCount(1, $result['blocks']);
75         $this->assertEquals($newblock, $result['blocks'][0]['name']);
76     }
78     /**
79      * Test get_course_blocks on site home
80      */
81     public function test_get_course_blocks_site_home() {
82         global $DB, $FULLME;
84         $this->resetAfterTest(true);
86         $user = $this->getDataGenerator()->create_user();
88         $page = new moodle_page();
89         $page->set_context(context_course::instance(SITEID));
90         $page->set_pagelayout('frontpage');
91         $page->set_pagetype('site-index');
92         $page->blocks->load_blocks();
93         $newblock = 'calendar_upcoming';
94         $page->blocks->add_block_at_end_of_default_region($newblock);
95         $this->setUser($user);
97         // Check for the new block.
98         $result = core_block_external::get_course_blocks(SITEID);
99         // We need to execute the return values cleaning process to simulate the web service server.
100         $result = external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
102         // Expect the new block.
103         $this->assertCount(1, $result['blocks']);
104         $this->assertEquals($newblock, $result['blocks'][0]['name']);
105     }
107     /**
108      * Test get_course_blocks
109      */
110     public function test_get_course_blocks_overrides() {
111         global $DB, $CFG, $FULLME;
113         $this->resetAfterTest(true);
115         $CFG->defaultblocks_override = 'search_forums,course_list:calendar_upcoming,recent_activity';
117         $user = $this->getDataGenerator()->create_user();
118         $course = $this->getDataGenerator()->create_course();
119         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
120         $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
122         $this->setUser($user);
124         // Try default blocks.
125         $result = core_block_external::get_course_blocks($course->id);
126         // We need to execute the return values cleaning process to simulate the web service server.
127         $result = external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
129         // Expect 4 default blocks.
130         $this->assertCount(4, $result['blocks']);
132         $expectedblocks = array('navigation', 'settings', 'search_forums', 'course_list',
133                                 'calendar_upcoming', 'recent_activity');
134         foreach ($result['blocks'] as $block) {
135             if (!in_array($block['name'], $expectedblocks)) {
136                 $this->fail("Unexpected block found: " . $block['name']);
137             }
138         }
140     }
142     /**
143      * Test get_course_blocks contents
144      */
145     public function test_get_course_blocks_contents() {
146         global $DB, $FULLME;
148         $this->resetAfterTest(true);
150         $user = $this->getDataGenerator()->create_user();
151         $course = $this->getDataGenerator()->create_course();
152         $studentrole = $DB->get_record('role', array('shortname' => 'student'));
153         $this->getDataGenerator()->enrol_user($user->id, $course->id, $studentrole->id);
154         $coursecontext = context_course::instance($course->id);
156         // Create a HTML block.
157         $title = 'Some course info';
158         $body = 'Some course info<br /><p>Some contents</p>';
159         $bodyformat = FORMAT_MOODLE;
160         $page = new moodle_page();
161         $page->set_context($coursecontext);
162         $page->set_pagelayout('course');
163         $course->format = course_get_format($course)->get_format();
164         $page->set_pagetype('course-view-' . $course->format);
165         $page->blocks->load_blocks();
166         $newblock = 'html';
167         $page->blocks->add_block_at_end_of_default_region($newblock);
169         $this->setUser($user);
170         // Re-create the page.
171         $page = new moodle_page();
172         $page->set_context($coursecontext);
173         $page->set_pagelayout('course');
174         $course->format = course_get_format($course)->get_format();
175         $page->set_pagetype('course-view-' . $course->format);
176         $page->blocks->load_blocks();
177         $blocks = $page->blocks->get_blocks_for_region($page->blocks->get_default_region());
178         $block = end($blocks);
179         $block = block_instance('html', $block->instance);
180         $nonscalar = [
181             'something' => true,
182         ];
183         $configdata = (object) [
184             'title' => $title,
185             'text' => [
186                 'itemid' => 0,
187                 'text' => $body,
188                 'format' => $bodyformat,
189             ],
190             'nonscalar' => $nonscalar
191         ];
192         $block->instance_config_save((object) $configdata);
193         $filename = 'img.png';
194         $filerecord = array(
195             'contextid' => context_block::instance($block->instance->id)->id,
196             'component' => 'block_html',
197             'filearea' => 'content',
198             'itemid' => 0,
199             'filepath' => '/',
200             'filename' => $filename,
201         );
202         // Create an area to upload the file.
203         $fs = get_file_storage();
204         // Create a file from the string that we made earlier.
205         $file = $fs->create_file_from_string($filerecord, 'some fake content (should be an image).');
207         // Check for the new block.
208         $result = core_block_external::get_course_blocks($course->id, true);
209         // We need to execute the return values cleaning process to simulate the web service server.
210         $result = external_api::clean_returnvalue(core_block_external::get_course_blocks_returns(), $result);
212         // Expect the new block.
213         $this->assertCount(1, $result['blocks']);
214         $this->assertEquals($title, $result['blocks'][0]['contents']['title']);
215         $this->assertEquals($body, $result['blocks'][0]['contents']['content']);
216         $this->assertEquals(FORMAT_HTML, $result['blocks'][0]['contents']['contentformat']);    // Format change for external.
217         $this->assertEquals('', $result['blocks'][0]['contents']['footer']);
218         $this->assertCount(1, $result['blocks'][0]['contents']['files']);
219         $this->assertEquals($newblock, $result['blocks'][0]['name']);
220         $configcounts = 0;
221         foreach ($result['blocks'][0]['configs'] as $config) {
222             if ($config['type'] = 'plugin' && $config['name'] == 'allowcssclasses' && $config['value'] == json_encode('0')) {
223                 $configcounts++;
224             } else if ($config['type'] = 'instance' && $config['name'] == 'text' && $config['value'] == json_encode($body)) {
225                 $configcounts++;
226             } else if ($config['type'] = 'instance' && $config['name'] == 'title' && $config['value'] == json_encode($title)) {
227                 $configcounts++;
228             } else if ($config['type'] = 'instance' && $config['name'] == 'format' && $config['value'] == json_encode('0')) {
229                 $configcounts++;
230             } else if ($config['type'] = 'instance' && $config['name'] == 'nonscalar' &&
231                     $config['value'] == json_encode($nonscalar)) {
232                 $configcounts++;
233             }
234         }
235         $this->assertEquals(5, $configcounts);
236     }
238     /**
239      * Test user get default dashboard blocks.
240      */
241     public function test_get_dashboard_blocks_default_dashboard() {
242         global $PAGE, $DB;
243         $this->resetAfterTest(true);
245         $user = $this->getDataGenerator()->create_user();
246         $PAGE->set_url('/my/index.php');    // Need this because some internal API calls require the $PAGE url to be set.
248         // Force a setting change to check the returned blocks settings.
249         set_config('displaycategories', 0, 'block_recentlyaccessedcourses');
251         // Get the expected default blocks.
252         $alldefaultblocksordered = $DB->get_records_menu('block_instances',
253             array('pagetypepattern' => 'my-index'), 'defaultregion, defaultweight ASC', 'id, blockname');
255         $this->setUser($user);
257         // Check for the default blocks.
258         $result = core_block_external::get_dashboard_blocks($user->id);
259         // We need to execute the return values cleaning process to simulate the web service server.
260         $result = external_api::clean_returnvalue(core_block_external::get_dashboard_blocks_returns(), $result);
261         // Expect all blogs except learning plans one (no learning plans to show).
262         $this->assertCount(count($alldefaultblocksordered) - 1, $result['blocks']);
263         $returnedblocks = array();
264         foreach ($result['blocks'] as $block) {
265             // Check all the returned blocks are in the expected blocks array.
266             $this->assertContains($block['name'], $alldefaultblocksordered);
267             $returnedblocks[] = $block['name'];
268             // Check the configuration returned for this default block.
269             if ($block['name'] == 'recentlyaccessedcourses') {
270                 // Convert config to associative array to avoid DB sorting randomness.
271                 $config = array_column($block['configs'], null, 'name');
272                 $this->assertArrayHasKey('displaycategories', $config);
273                 $this->assertEquals(json_encode('0'), $config['displaycategories']['value']);
274                 $this->assertEquals('plugin', $config['displaycategories']['type']);
275             }
276         }
277         // Remove lp block.
278         array_shift($alldefaultblocksordered);
279         // Check that we received the blocks in the expected order.
280         $this->assertEquals(array_values($alldefaultblocksordered), $returnedblocks);
281     }
283     /**
284      * Test user get default dashboard blocks including a sticky block.
285      */
286     public function test_get_dashboard_blocks_default_dashboard_including_sticky_block() {
287         global $PAGE, $DB;
288         $this->resetAfterTest(true);
290         $user = $this->getDataGenerator()->create_user();
291         $PAGE->set_url('/my/index.php');    // Need this because some internal API calls require the $PAGE url to be set.
293         // Get the expected default blocks.
294         $alldefaultblocks = $DB->get_records_menu('block_instances', array('pagetypepattern' => 'my-index'), '', 'id, blockname');
296         // Now, add a sticky block.
297         $page = new moodle_page();
298         $page->set_context(context_system::instance());
299         $page->set_pagetype('my-index');
300         $page->set_url(new moodle_url('/'));
301         $page->blocks->add_region('side-pre');
302         $page->blocks->load_blocks();
303         $page->blocks->add_block('myprofile', 'side-pre', 0, true, '*');
305         $this->setUser($user);
307         // Check for the default blocks plus the sticky.
308         $result = core_block_external::get_dashboard_blocks($user->id);
309         // We need to execute the return values cleaning process to simulate the web service server.
310         $result = external_api::clean_returnvalue(core_block_external::get_dashboard_blocks_returns(), $result);
311         // Expect all blogs plus sticky one except learning plans one (no learning plans to show).
312         $this->assertCount(count($alldefaultblocks), $result['blocks']);
313         $found = false;
314         foreach ($result['blocks'] as $block) {
315             if ($block['name'] == 'myprofile') {
316                 $this->assertEquals('side-pre', $block['region']);
317                 $found = true;
318                 continue;
319             }
320             // Check that the block is in the expected blocks array.
321             $this->assertContains($block['name'], $alldefaultblocks);
322         }
323         $this->assertTrue($found);
324     }
326     /**
327      * Test admin get user's custom dashboard blocks.
328      */
329     public function test_get_dashboard_blocks_custom_user_dashboard() {
330         global $PAGE, $DB;
331         $this->resetAfterTest(true);
333         $user = $this->getDataGenerator()->create_user();
334         $PAGE->set_url('/my/index.php');    // Need this because some internal API calls require the $PAGE url to be set.
336         // Get the expected default blocks.
337         $alldefaultblocks = $DB->get_records_menu('block_instances', array('pagetypepattern' => 'my-index'), '', 'id, blockname');
339         // Add a custom block.
340         $page = new moodle_page();
341         $page->set_context(context_user::instance($user->id));
342         $page->set_pagelayout('mydashboard');
343         $page->set_pagetype('my-index');
344         $page->blocks->add_region('content');
345         $currentpage = my_get_page($user->id, MY_PAGE_PRIVATE);
346         $page->set_subpage($currentpage->id);
347         $page->blocks->load_blocks();
348         $page->blocks->add_block('myprofile', 'content', 0, false);
350         $this->setAdminUser();
352         // Check for the new block as admin for a user.
353         $result = core_block_external::get_dashboard_blocks($user->id);
354         // We need to execute the return values cleaning process to simulate the web service server.
355         $result = external_api::clean_returnvalue(core_block_external::get_dashboard_blocks_returns(), $result);
356         // Expect all default blogs plys the one we added except learning plans one (no learning plans to show).
357         $this->assertCount(count($alldefaultblocks), $result['blocks']);
358         $found = false;
359         foreach ($result['blocks'] as $block) {
360             if ($block['name'] == 'myprofile') {
361                 $this->assertEquals('content', $block['region']);
362                 $found = true;
363                 continue;
364             }
365             // Check that the block is in the expected blocks array.
366             $this->assertContains($block['name'], $alldefaultblocks);
367         }
368         $this->assertTrue($found);
369     }
371     /**
372      * Test user tries to get other user blocks not having permission.
373      */
374     public function test_get_dashboard_blocks_other_user_missing_permissions() {
375         $this->resetAfterTest(true);
377         $user1 = $this->getDataGenerator()->create_user();
378         $user2 = $this->getDataGenerator()->create_user();
380         $this->setUser($user1);
382         $this->expectException('moodle_exception');
383         core_block_external::get_dashboard_blocks($user2->id);
384     }