2 // This file is part of Moodle - http://moodle.org/
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.
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);
21 use core_h5p\local\library\autoloader
;
24 * Test class covering the H5P helper.
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
{
35 * Register the H5P autoloader
37 protected function setUp(): void
{
38 autoloader
::register();
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();
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
);
76 * Data provider for test_get_display_options().
80 public function display_options_provider(): array {
82 'All display options disabled' => [
89 'All display options enabled' => [
96 'Frame disabled and the rest enabled' => [
103 'Only export enabled' => [
110 'Only embed enabled' => [
117 'Only copyright enabled' => [
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
();
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);
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
{
168 $this->resetAfterTest();
169 $factory = new \core_h5p\factory
();
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);
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
209 public function test_save_h5p_metadata(): void
{
212 $this->resetAfterTest();
213 $factory = new \core_h5p\factory
();
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);
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
();
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);
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
();
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
();
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);
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,
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(
410 file_storage
::COMPONENT
,
414 $deployedfile['filename'],
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().
451 public function parse_js_array_provider(): array {
454 " missingTranslation: '[Missing translation :key]',",
455 " loading: 'Loading, please wait...',",
456 " selectLibrary: 'Select the library you wish to use for your content.',",
460 'missingTranslation' => '[Missing translation :key]',
461 'loading' => 'Loading, please wait...',
462 'selectLibrary' => 'Select the library you wish to use for your content.',
465 'Strings with \n' => [
466 implode("\n", $lines),
469 'Strings with \r\n' => [
470 implode("\r\n", $lines),
473 'Strings with \r' => [
474 implode("\r", $lines),