Merge branch 'MDL-81456-main' of https://github.com/andrewnicols/moodle
[moodle.git] / h5p / tests / helper_test.php
blob66be51cd030798de52a1284537aa0282e3bb5e68
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.
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 declare(strict_types = 1);
19 namespace core_h5p;
21 use core_h5p\local\library\autoloader;
23 /**
24 * Test class covering the H5P helper.
26 * @package core_h5p
27 * @category test
28 * @copyright 2019 Sara Arjona <sara@moodle.com>
29 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
30 * @covers \core_h5p\helper
32 class helper_test extends \advanced_testcase {
34 /**
35 * Register the H5P autoloader
37 protected function setUp(): void {
38 autoloader::register();
41 /**
42 * Test the behaviour of get_display_options().
44 * @dataProvider display_options_provider
45 * @param bool $frame Whether the frame should be displayed or not
46 * @param bool $export Whether the export action button should be displayed or not
47 * @param bool $embed Whether the embed action button should be displayed or not
48 * @param bool $copyright Whether the copyright action button should be displayed or not
49 * @param int $expected The expectation with the displayoptions value
51 public function test_display_options(bool $frame, bool $export, bool $embed, bool $copyright, int $expected): void {
52 $this->setRunTestInSeparateProcess(true);
53 $this->resetAfterTest();
55 $factory = new \core_h5p\factory();
56 $core = $factory->get_core();
57 $config = (object)[
58 'frame' => $frame,
59 'export' => $export,
60 'embed' => $embed,
61 'copyright' => $copyright,
64 // Test getting display options.
65 $displayoptions = helper::get_display_options($core, $config);
66 $this->assertEquals($expected, $displayoptions);
68 // Test decoding display options.
69 $decoded = helper::decode_display_options($core, $expected);
70 $this->assertEquals($decoded->export, $config->export);
71 $this->assertEquals($decoded->embed, $config->embed);
72 $this->assertEquals($decoded->copyright, $config->copyright);
75 /**
76 * Data provider for test_get_display_options().
78 * @return array
80 public function display_options_provider(): array {
81 return [
82 'All display options disabled' => [
83 false,
84 false,
85 false,
86 false,
87 15,
89 'All display options enabled' => [
90 true,
91 true,
92 true,
93 true,
96 'Frame disabled and the rest enabled' => [
97 false,
98 true,
99 true,
100 true,
103 'Only export enabled' => [
104 false,
105 true,
106 false,
107 false,
110 'Only embed enabled' => [
111 false,
112 false,
113 true,
114 false,
117 'Only copyright enabled' => [
118 false,
119 false,
120 false,
121 true,
128 * Test the behaviour of save_h5p() when there are some missing libraries in the system.
129 * @runInSeparateProcess
131 public function test_save_h5p_missing_libraries(): void {
132 $this->resetAfterTest();
133 $factory = new \core_h5p\factory();
135 // Create a user.
136 $user = $this->getDataGenerator()->create_user();
137 $this->setUser($user);
139 // This is a valid .H5P file.
140 $path = __DIR__ . '/fixtures/greeting-card.h5p';
141 $file = helper::create_fake_stored_file_from_path($path, (int)$user->id);
142 $factory->get_framework()->set_file($file);
144 $config = (object)[
145 'frame' => 1,
146 'export' => 1,
147 'embed' => 0,
148 'copyright' => 0,
151 // There are some missing libraries in the system, so an error should be returned.
152 $h5pid = helper::save_h5p($factory, $file, $config);
153 $this->assertFalse($h5pid);
154 $errors = $factory->get_framework()->getMessages('error');
155 $this->assertCount(1, $errors);
156 $error = reset($errors);
157 $this->assertEquals('missing-main-library', $error->code);
158 $this->assertEquals('Missing main library H5P.GreetingCard 1.0', $error->message);
162 * Test the behaviour of save_h5p() when the libraries exist in the system.
163 * @runInSeparateProcess
165 public function test_save_h5p_existing_libraries(): void {
166 global $DB;
168 $this->resetAfterTest();
169 $factory = new \core_h5p\factory();
171 // Create a user.
172 $user = $this->getDataGenerator()->create_user();
173 $this->setUser($user);
175 // This is a valid .H5P file.
176 $path = __DIR__ . '/fixtures/greeting-card.h5p';
177 $file = helper::create_fake_stored_file_from_path($path, (int)$user->id);
178 $factory->get_framework()->set_file($file);
180 $config = (object)[
181 'frame' => 1,
182 'export' => 1,
183 'embed' => 0,
184 'copyright' => 0,
186 // The required libraries exist in the system before saving the .h5p file.
187 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
188 $lib = $generator->create_library_record('H5P.GreetingCard', 'GreetingCard', 1, 0);
189 $h5pid = helper::save_h5p($factory, $file, $config);
190 $this->assertNotEmpty($h5pid);
192 // No errors are raised.
193 $errors = $factory->get_framework()->getMessages('error');
194 $this->assertCount(0, $errors);
196 // And the content in the .h5p file has been saved as expected.
197 $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
198 $this->assertEquals($lib->id, $h5p->mainlibraryid);
199 $this->assertEquals(helper::get_display_options($factory->get_core(), $config), $h5p->displayoptions);
200 $this->assertStringContainsString('Hello world!', $h5p->jsoncontent);
204 * Test the behaviour of save_h5p() when the H5P file contains metadata.
206 * @runInSeparateProcess
207 * @covers ::save_h5p
209 public function test_save_h5p_metadata(): void {
210 global $DB;
212 $this->resetAfterTest();
213 $factory = new \core_h5p\factory();
215 // Create a user.
216 $user = $this->getDataGenerator()->create_user();
217 $this->setUser($user);
219 // This is a valid .H5P file.
220 $path = __DIR__ . '/fixtures/guess-the-answer.h5p';
221 $file = helper::create_fake_stored_file_from_path($path, (int)$user->id);
222 $factory->get_framework()->set_file($file);
224 $config = (object)[
225 'frame' => 1,
226 'export' => 1,
227 'embed' => 0,
228 'copyright' => 1,
230 // The required libraries exist in the system before saving the .h5p file.
231 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
232 $lib = $generator->create_library_record('H5P.GuessTheAnswer', 'Guess the Answer', 1, 5);
233 $generator->create_library_record('H5P.Image', 'Image', 1, 1);
234 $generator->create_library_record('FontAwesome', 'Font Awesome', 4, 5);
235 $h5pid = helper::save_h5p($factory, $file, $config);
236 $this->assertNotEmpty($h5pid);
238 // No errors are raised.
239 $errors = $factory->get_framework()->getMessages('error');
240 $this->assertCount(0, $errors);
242 // And the content in the .h5p file has been saved as expected.
243 $h5p = $DB->get_record('h5p', ['id' => $h5pid]);
244 $this->assertEquals($lib->id, $h5p->mainlibraryid);
245 $this->assertEquals(helper::get_display_options($factory->get_core(), $config), $h5p->displayoptions);
246 $this->assertStringContainsString('Which fruit is this?', $h5p->jsoncontent);
247 // Metadata has been also saved.
248 $this->assertStringContainsString('This is licence extras information added for testing purposes.', $h5p->jsoncontent);
249 $this->assertStringContainsString('H5P Author', $h5p->jsoncontent);
250 $this->assertStringContainsString('Add metadata information', $h5p->jsoncontent);
254 * Test the behaviour of save_h5p() when the .h5p file is invalid.
255 * @runInSeparateProcess
257 public function test_save_h5p_invalid_file(): void {
258 $this->resetAfterTest();
259 $factory = new \core_h5p\factory();
261 // Create a user.
262 $user = $this->getDataGenerator()->create_user();
263 $this->setUser($user);
265 // Prepare an invalid .H5P file.
266 $path = __DIR__ . '/fixtures/h5ptest.zip';
267 $file = helper::create_fake_stored_file_from_path($path, (int)$user->id);
268 $factory->get_framework()->set_file($file);
269 $config = (object)[
270 'frame' => 1,
271 'export' => 1,
272 'embed' => 0,
273 'copyright' => 0,
276 // When saving an invalid .h5p file, an error should be raised.
277 $h5pid = helper::save_h5p($factory, $file, $config);
278 $this->assertFalse($h5pid);
279 $errors = $factory->get_framework()->getMessages('error');
280 $this->assertCount(2, $errors);
282 $expectederrorcodes = ['invalid-content-folder', 'invalid-h5p-json-file'];
283 foreach ($errors as $error) {
284 $this->assertContains($error->code, $expectederrorcodes);
289 * Test the behaviour of can_deploy_package().
291 public function test_can_deploy_package(): void {
292 $this->resetAfterTest();
293 $factory = new \core_h5p\factory();
295 // Create a user.
296 $user = $this->getDataGenerator()->create_user();
297 $admin = get_admin();
299 // Prepare a valid .H5P file.
300 $path = __DIR__ . '/fixtures/greeting-card.h5p';
302 // Files created by users can't be deployed.
303 $file = helper::create_fake_stored_file_from_path($path, (int)$user->id);
304 $factory->get_framework()->set_file($file);
305 $candeploy = helper::can_deploy_package($file);
306 $this->assertFalse($candeploy);
308 // Files created by admins can be deployed, even when the current user is not the admin.
309 $this->setUser($user);
310 $file = helper::create_fake_stored_file_from_path($path, (int)$admin->id);
311 $factory->get_framework()->set_file($file);
312 $candeploy = helper::can_deploy_package($file);
313 $this->assertTrue($candeploy);
317 * Test the behaviour of can_update_library().
319 public function test_can_update_library(): void {
320 $this->resetAfterTest();
321 $factory = new \core_h5p\factory();
323 // Create a user.
324 $user = $this->getDataGenerator()->create_user();
325 $admin = get_admin();
327 // Prepare a valid .H5P file.
328 $path = __DIR__ . '/fixtures/greeting-card.h5p';
330 // Libraries can't be updated when the file has been created by users.
331 $file = helper::create_fake_stored_file_from_path($path, (int)$user->id);
332 $factory->get_framework()->set_file($file);
333 $candeploy = helper::can_update_library($file);
334 $this->assertFalse($candeploy);
336 // Libraries can be updated when the file has been created by admin, even when the current user is not the admin.
337 $this->setUser($user);
338 $file = helper::create_fake_stored_file_from_path($path, (int)$admin->id);
339 $factory->get_framework()->set_file($file);
340 $candeploy = helper::can_update_library($file);
341 $this->assertTrue($candeploy);
345 * Test the behaviour of get_messages().
347 public function test_get_messages(): void {
348 $this->resetAfterTest();
350 $factory = new \core_h5p\factory();
351 $messages = new \stdClass();
353 helper::get_messages($messages, $factory);
354 $this->assertTrue(empty($messages->error));
355 $this->assertTrue(empty($messages->info));
357 // Add an some messages manually and check they are still there.
358 $messages->error = [];
359 $messages->error['error1'] = 'Testing ERROR message';
360 $messages->info = [];
361 $messages->info['info1'] = 'Testing INFO message';
362 $messages->info['info2'] = 'Testing INFO message';
363 helper::get_messages($messages, $factory);
364 $this->assertCount(1, $messages->error);
365 $this->assertCount(2, $messages->info);
367 // When saving an invalid .h5p file, 6 errors should be raised.
368 $path = __DIR__ . '/fixtures/h5ptest.zip';
369 $file = helper::create_fake_stored_file_from_path($path);
370 $factory->get_framework()->set_file($file);
371 $config = (object)[
372 'frame' => 1,
373 'export' => 1,
374 'embed' => 0,
375 'copyright' => 0,
377 $h5pid = helper::save_h5p($factory, $file, $config);
378 $this->assertFalse($h5pid);
379 helper::get_messages($messages, $factory);
380 $this->assertCount(7, $messages->error);
381 $this->assertCount(2, $messages->info);
385 * Test the behaviour of get_export_info().
387 public function test_get_export_info(): void {
388 $this->resetAfterTest();
390 $filename = 'guess-the-answer.h5p';
391 $syscontext = \context_system::instance();
393 $generator = $this->getDataGenerator()->get_plugin_generator('core_h5p');
394 $deployedfile = $generator->create_export_file($filename,
395 $syscontext->id,
396 file_storage::COMPONENT,
397 file_storage::EXPORT_FILEAREA);
399 // Test scenario 1: Get export information from correct filename.
400 $helperfile = helper::get_export_info($deployedfile['filename']);
401 $this->assertEquals($deployedfile['filename'], $helperfile['filename']);
402 $this->assertEquals($deployedfile['filepath'], $helperfile['filepath']);
403 $this->assertEquals($deployedfile['filesize'], $helperfile['filesize']);
404 $this->assertEquals($deployedfile['timemodified'], $helperfile['timemodified']);
405 $this->assertEquals($deployedfile['fileurl'], $helperfile['fileurl']);
407 // Test scenario 2: Get export information from correct filename and url.
408 $url = \moodle_url::make_pluginfile_url(
409 $syscontext->id,
410 file_storage::COMPONENT,
411 'unittest',
413 '/',
414 $deployedfile['filename'],
415 false,
416 true
418 $helperfile = helper::get_export_info($deployedfile['filename'], $url);
419 $this->assertEquals($url, $helperfile['fileurl']);
421 // Test scenario 3: Get export information from correct filename and factory.
422 $factory = new \core_h5p\factory();
423 $helperfile = helper::get_export_info($deployedfile['filename'], null, $factory);
424 $this->assertEquals($deployedfile['filename'], $helperfile['filename']);
425 $this->assertEquals($deployedfile['filepath'], $helperfile['filepath']);
426 $this->assertEquals($deployedfile['filesize'], $helperfile['filesize']);
427 $this->assertEquals($deployedfile['timemodified'], $helperfile['timemodified']);
428 $this->assertEquals($deployedfile['fileurl'], $helperfile['fileurl']);
430 // Test scenario 4: Get export information from wrong filename.
431 $helperfile = helper::get_export_info('nofileexist.h5p', $url);
432 $this->assertNull($helperfile);
436 * Test the parse_js_array function with a range of content.
438 * @dataProvider parse_js_array_provider
439 * @param string $content
440 * @param array $expected
442 public function test_parse_js_array(string $content, array $expected): void {
443 $this->assertEquals($expected, helper::parse_js_array($content));
447 * Data provider for test_parse_js_array().
449 * @return array
451 public function parse_js_array_provider(): array {
452 $lines = [
453 "{",
454 " missingTranslation: '[Missing translation :key]',",
455 " loading: 'Loading, please wait...',",
456 " selectLibrary: 'Select the library you wish to use for your content.',",
457 "}",
459 $expected = [
460 'missingTranslation' => '[Missing translation :key]',
461 'loading' => 'Loading, please wait...',
462 'selectLibrary' => 'Select the library you wish to use for your content.',
464 return [
465 'Strings with \n' => [
466 implode("\n", $lines),
467 $expected,
469 'Strings with \r\n' => [
470 implode("\r\n", $lines),
471 $expected,
473 'Strings with \r' => [
474 implode("\r", $lines),
475 $expected,