first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+159
View File
@@ -0,0 +1,159 @@
<?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/>.
namespace core\output;
/**
* Unit tests for activity header
*
* @package core
* @category test
* @coversDefaultClass \core\output\activity_header
* @copyright 2021 Peter
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class activity_header_test extends \advanced_testcase {
/**
* Test the title setter
*
* @dataProvider set_title_provider
* @param string $value
* @param string $expected
* @covers ::set_title
*/
public function test_set_title(string $value, string $expected): void {
global $PAGE, $DB;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course(['enablecompletion' => true]);
$assign = $this->getDataGenerator()->create_module('assign', [
'course' => $course->id,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionview' => 1
]);
$this->setAdminUser();
$cm = $DB->get_record('course_modules', ['id' => $assign->cmid]);
$PAGE->set_cm($cm);
$PAGE->set_activity_record($assign);
$header = $PAGE->activityheader;
$header->set_title($value);
$data = $header->export_for_template($PAGE->get_renderer('core'));
$this->assertEquals($expected, $data['title']);
}
/**
* Provider for the test_set_title unit test.
* @return array
*/
public function set_title_provider(): array {
return [
"Set the title with a plain text" => [
"Activity title", "Activity title"
],
"Set the title with a string with standard header tags" => [
"<h2>Activity title</h2>", "Activity title"
],
"Set the title with a string with multiple header content" => [
"<h2 id='heading'>Activity title</h2><h2>Header 2</h2>", "Activity title</h2><h2>Header 2"
],
];
}
/**
* Test setting multiple attributes
*
* @covers ::set_attrs
*/
public function test_set_attrs(): void {
global $DB, $PAGE;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course(['enablecompletion' => true]);
$assign = $this->getDataGenerator()->create_module('assign', [
'course' => $course->id,
'completion' => COMPLETION_TRACKING_AUTOMATIC,
'completionview' => 1
]);
$cm = $DB->get_record('course_modules', ['id' => $assign->cmid]);
$PAGE->set_cm($cm);
$PAGE->set_activity_record($assign);
$PAGE->activityheader->set_attrs([
'hidecompletion' => true,
'additionalnavitems' => new \url_select([]),
'hideoverflow' => true,
'title' => 'My title',
'description' => 'My description',
]);
$renderer = $PAGE->get_renderer('core');
$export = $PAGE->activityheader->export_for_template($renderer);
$this->assertEquals('My title', $export['title']);
$this->assertEquals('My description', $export['description']);
$this->assertEmpty($export['completion']); // Because hidecompletion = true.
$this->assertEmpty($export['additional_items']); // Because hideoverflow = true.
}
/**
* Test calling set_attrs with an invalid variable name
*
* @covers ::set_attrs
*/
public function test_set_attrs_invalid_variable(): void {
global $PAGE;
$PAGE->activityheader->set_attrs(['unknown' => true]);
$this->assertDebuggingCalledCount(1, ['Invalid class member variable: unknown']);
}
/**
* Data provider for {@see test_get_heading_level()}.
*
* @return array[]
*/
public function get_heading_level_provider(): array {
return [
'Title not allowed' => [false, '', 2],
'Title allowed, no title' => [true, '', 2],
'Title allowed, empty string title' => [true, ' ', 2],
'Title allowed, non-empty string title' => [true, 'Cool', 3],
];
}
/**
* Test the heading level getter
*
* @dataProvider get_heading_level_provider
* @covers ::get_heading_level
* @param bool $allowtitle Whether the title is allowed.
* @param string $title The activity heading.
* @param int $expectedheadinglevel The expected heading level.
*/
public function test_get_heading_level(bool $allowtitle, string $title, int $expectedheadinglevel): void {
$activityheaderstub = $this->getMockBuilder(activity_header::class)
->disableOriginalConstructor()
->onlyMethods(['is_title_allowed'])
->getMock();
$activityheaderstub->method('is_title_allowed')->willReturn($allowtitle);
$activityheaderstub->set_title($title);
$this->assertEquals($expectedheadinglevel, $activityheaderstub->get_heading_level());
}
}
+276
View File
@@ -0,0 +1,276 @@
<?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/>.
/**
* Unit tests for core\output\choice class.
*
* @package core
* @category test
* @copyright 2023 Ferran Recio <ferran@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use advanced_testcase;
/**
* Unit tests for the `icon_system` class.
*
* @coversDefaultClass \core\output\choicelist
*/
class choicelist_test extends advanced_testcase {
/**
* Test for a choice without options.
*
* @covers ::_construct
* @covers ::add_option
* @covers ::export_for_template
*/
public function test_empty_export(): void {
$page = new \moodle_page();
$page->set_url('/user/profile.php');
$page->set_context(\context_system::instance());
$renderer = $page->get_renderer('core');
$choice = new choicelist('Choose an option');
$export = $choice->export_for_template($renderer);
$this->assertEquals('Choose an option', $export['description']);
$this->assertEquals(false, $export['hasoptions']);
$this->assertEquals([], $export['options']);
}
/**
* Test for a choice with basic options.
*
* @covers ::_construct
* @covers ::add_option
* @covers ::export_for_template
*/
public function test_basic_export(): void {
$page = new \moodle_page();
$page->set_url('/user/profile.php');
$page->set_context(\context_system::instance());
$renderer = $page->get_renderer('core');
$choice = new choicelist('Choose an option');
$choice->add_option('option1', 'Option 1');
$choice->add_option('option2', 'Option 2');
$export = $choice->export_for_template($renderer);
$this->assertEquals('Choose an option', $export['description']);
$this->assertEquals(true, $export['hasoptions']);
$this->assertCount(2, $export['options']);
$this->validate_option($export['options'][0], 'option1', 'Option 1', []);
$this->validate_option($export['options'][1], 'option2', 'Option 2', []);
}
/**
* Test for a choice with extras options definition.
*
* @covers ::_construct
* @covers ::add_option
* @covers ::set_option_extras
* @covers ::export_for_template
*/
public function test_option_defintion_export(): void {
$page = new \moodle_page();
$page->set_url('/user/profile.php');
$page->set_context(\context_system::instance());
$renderer = $page->get_renderer('core');
$choice = new choicelist('Choose an option');
$definition1 = [
'disabled' => true,
'description' => 'Description',
'url' => new \moodle_url('/user/profile.php'),
'icon' => new \pix_icon('i/grade', 'Grade'),
'extras' => [
'data-attribute' => 'value',
],
];
$choice->add_option('option1', 'Option 1', $definition1);
$definition2 = [
'disabled' => false,
'description' => null,
'url' => null,
'icon' => null,
'extras' => null,
];
$choice->add_option('option2', 'Option 2', $definition2);
$export = $choice->export_for_template($renderer);
$this->assertEquals('Choose an option', $export['description']);
$this->assertEquals(true, $export['hasoptions']);
$this->assertCount(2, $export['options']);
$definition1['iconexport'] = $definition1['icon']->export_for_pix($renderer);
$this->validate_option($export['options'][0], 'option1', 'Option 1', $definition1);
$this->validate_option($export['options'][1], 'option2', 'Option 2', $definition2);
}
/**
* Test for a choice with option selected.
*
* @covers ::_construct
* @covers ::add_option
* @covers ::set_selected_value
* @covers ::get_selected_value
* @covers ::export_for_template
*/
public function test_option_selected_export(): void {
$page = new \moodle_page();
$page->set_url('/user/profile.php');
$page->set_context(\context_system::instance());
$renderer = $page->get_renderer('core');
$choice = new choicelist('Choose an option');
$choice->add_option('option1', 'Option 1');
$choice->add_option('option2', 'Option 2');
$choice->set_selected_value('option1');
$export = $choice->export_for_template($renderer);
$this->assertEquals('Choose an option', $export['description']);
$this->assertEquals(true, $export['hasoptions']);
$this->assertCount(2, $export['options']);
$this->assertEquals('option1', $choice->get_selected_value());
$this->validate_option($export['options'][0], 'option1', 'Option 1', [], true);
$this->validate_option($export['options'][1], 'option2', 'Option 2', []);
}
/**
* Validate a choice option export.
* @param array $option the option export
* @param string $value the option value
* @param string $name the option name
* @param array|null $definition the option definition
* @param bool $selected if the option is selected
*/
private function validate_option(
array $option,
string $value,
string $name,
?array $definition,
bool $selected = false
): void {
$this->assertEquals($value, $option['value']);
$this->assertEquals($name, $option['name']);
$this->assertEquals($definition['disabled'] ?? false, $option['disabled']);
$this->assertEquals($definition['description'] ?? null, $option['description']);
if (isset($definition['url'])) {
$this->assertEquals($definition['url']->out(true), $option['url']);
$this->assertTrue($option['hasurl']);
}
if (isset($definition['icon'])) {
$this->assertEquals($definition['iconexport'], $option['icon']);
$this->assertTrue($option['hasicon']);
}
if ($selected) {
$this->assertTrue($option['selected']);
}
if (isset($definition['extras'])) {
foreach ($option['extras'] as $extra) {
$attribute = $extra['attribute'];
$this->assertEquals($definition['extras'][$attribute], $extra['value']);
}
} else {
$this->assertFalse(isset($option['extras']));
}
}
/**
* Test for a choice with option selected.
*
* @covers ::_construct
* @covers ::add_option
* @covers ::set_selected_value
* @covers ::get_selected_value
* @covers ::set_allow_empty
* @covers ::get_allow_empty
* @covers ::export_for_template
*/
public function test_set_allow_empty(): void {
$choice = new choicelist('Choose an option');
$choice->add_option('option1', 'Option 1');
$choice->add_option('option2', 'Option 2');
$choice->set_allow_empty(true);
$this->assertTrue($choice->get_allow_empty());
$this->assertNull($choice->get_selected_value());
$choice->set_allow_empty(false);
$this->assertFalse($choice->get_allow_empty());
$this->assertEquals('option1', $choice->get_selected_value());
// Validate the null selected value is not changed when allow empty is set to true.
$choice->set_allow_empty(true);
$this->assertTrue($choice->get_allow_empty());
$this->assertNull($choice->get_selected_value());
$choice->set_selected_value('option2');
$choice->set_allow_empty(false);
$this->assertFalse($choice->get_allow_empty());
$this->assertEquals('option2', $choice->get_selected_value());
$choice->set_allow_empty(true);
$this->assertTrue($choice->get_allow_empty());
$this->assertEquals('option2', $choice->get_selected_value());
}
/**
* Test for a choice with option selected.
*
* @covers ::get_selectable_options
*/
public function test_get_selectable_options(): void {
$choice = new choicelist('Choose an option');
$choice->add_option('option1', 'Option 1');
$choice->add_option('option2', 'Option 2');
$choice->add_option('option3', 'Option 3', ['disabled' => true]);
$choice->add_option('option4', 'Option 4', ['disabled' => true]);
$choice->add_option('option5', 'Option 5');
$selectable = $choice->get_selectable_options();
$this->assertCount(3, $selectable);
$this->assertEquals('option1', $selectable[0]->value);
$this->assertEquals('option2', $selectable[1]->value);
$this->assertEquals('option5', $selectable[2]->value);
$choice->set_selected_value('option2');
$selectable = $choice->get_selectable_options();
$this->assertCount(2, $selectable);
$this->assertEquals('option1', $selectable[0]->value);
$this->assertEquals('option5', $selectable[1]->value);
}
/**
* Test for a choice with option selected.
*
* @covers ::get_option_extras
*/
public function test_get_option_extras(): void {
$choice = new choicelist('Choose an option');
$choice->add_option('option1', 'Option 1');
$choice->add_option('option2', 'Option 2', ['extras' => ['data-attribute' => 'value2']]);
$this->assertEquals([], $choice->get_option_extras('option1'));
$this->assertEquals(['data-attribute' => 'value2'], $choice->get_option_extras('option2'));
$this->assertEquals([], $choice->get_option_extras('inexistent'));
}
}
@@ -0,0 +1,60 @@
<?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/>.
namespace core\output;
/**
* Unit tests for the FontAwesome icon system.
*
* @package core
* @copyright 2023 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \core\output\icon_system_fontawesome
*/
class icon_system_fontawesome_test extends \advanced_testcase {
/**
* Test that the specified icon has an SVG fallback.
*/
public function test_svg_fallback(): void {
// This can't be tested using data provider because it initializes the theme system when running filtered tests.
$instance = icon_system::instance(icon_system::FONTAWESOME);
$icons = array_map(function($key) {
global $CFG;
[$component, $file] = explode(':', $key);
if ($component === 'core') {
$componentdir = $CFG->dirroot;
} else if ($component === 'theme') {
$componentdir = \core_component::get_component_directory('theme_' . \theme_config::DEFAULT_THEME);
} else {
$componentdir = \core_component::get_component_directory($component);
}
return [
'key' => $key,
'path' => $componentdir,
'filename' => $file,
];
}, array_keys($instance->get_icon_name_map()));
foreach ($icons as $icon) {
$this->assertTrue(
file_exists("{$icon['path']}/pix/{$icon['filename']}.svg"), "No SVG equivalent found for '{$icon['key']}'",
);
}
}
}
+248
View File
@@ -0,0 +1,248 @@
<?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/>.
/**
* Unit tests for lib/outputcomponents.php.
*
* @package core
* @category test
* @copyright 2011 David Mudrak <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core\output;
use advanced_testcase;
use coding_exception;
/**
* Unit tests for the `icon_system` class.
*
* @coversDefaultClass \core\output\icon_system
*/
class icon_system_test extends advanced_testcase {
/**
* Check whether the supplied classes are valid icon subsystems of the supplied one.
*
* @covers ::is_valid_system
* @dataProvider is_valid_subsystem_provider
* @param string $parent The class to call ::is_valid_system() on
* @param string $system The class to request
* @param bool $expected Whether the supplied relationship is valid
*/
public function test_is_valid_subsystem(string $parent, string $system, bool $expected): void {
$this->assertEquals($expected, $parent::is_valid_system($system));
}
/**
* Ensure that the ::instance() function throws an appropriate Exception when an inappropriate relationship is
* specified.
*
* @covers ::instance
* @dataProvider invalid_instance_provider
* @param string $parent The class to call ::instance() on
* @param string $system The class to request
*/
public function test_invalid_instance(string $parent, string $system): void {
$this->expectException(coding_exception::class);
$this->expectExceptionMessage("Invalid icon system requested '{$system}'");
$parent::instance($system);
}
/**
* Ensure that the ::instance() function returns an instance of the supplied system for a valid icon system
* relationship.
*
* @covers ::instance
* @dataProvider valid_instance_provider
* @param string $parent The class to call ::instance() on
* @param string $system The class to request
*/
public function test_valid_instance(string $parent, string $system): void {
$instance = $parent::instance($system);
$this->assertInstanceOf($parent, $instance);
$this->assertInstanceOf($system, $instance);
}
/**
* Ensure that subsequent calls without arguments to ::instance() return the exact same instance.
*
* @covers ::instance
*/
public function test_instance_singleton(): void {
$singleton = icon_system::instance();
// Calling instance() again returns the same singleton.
$this->assertSame($singleton, icon_system::instance());
}
/**
* Ensure thaat subsequent calls with an argument to ::instance() return the exact same instance.
*
* @covers ::instance
*/
public function test_instance_singleton_named_default(): void {
global $PAGE;
$singleton = icon_system::instance();
$defaultsystem = $PAGE->theme->get_icon_system();
$this->assertSame($singleton, icon_system::instance($defaultsystem));
}
/**
* Ensure that ::instance() returns an instance of the correct icon system when requested on the core icon_system
* class.
*
* @covers ::instance
* @dataProvider valid_instance_provider
* @param string $parent The class to call ::instance() on
* @param string $child The class to request
*/
public function test_instance_singleton_named(string $parent, string $child): void {
$iconsystem = icon_system::instance($child);
$this->assertInstanceOf($child, $iconsystem);
}
/**
* Ensure that ::instance() returns an instance of the correct icon system when called on a named parent class.
*
* @covers ::instance
* @dataProvider valid_instance_provider
* @param string $parent The class to call ::instance() on
* @param string $child The class to request
*/
public function test_instance_singleton_named_child(string $parent, string $child): void {
$iconsystem = $parent::instance($child);
$this->assertInstanceOf($parent, $iconsystem);
$this->assertInstanceOf($child, $iconsystem);
}
/**
* Ensure that the ::reset_caches() function resets the stored instance such that ::instance() returns a new
* instance in subsequent calls.
*
* @covers ::instance
* @covers ::reset_caches
*/
public function test_instance_singleton_reset(): void {
$singleton = icon_system::instance();
// Reset the cache.
icon_system::reset_caches();
// Calling instance() again returns a new singleton.
$newsingleton = icon_system::instance();
$this->assertNotSame($singleton, $newsingleton);
// Calling it again gets the new singleton.
$this->assertSame($newsingleton, icon_system::instance());
}
/**
* Returns data for data providers containing:
* - parent icon system
* - child icon system
* - whether it is a valid child
*
* @return array
*/
public function icon_system_provider(): array {
return [
'icon_system => icon_system_standard' => [
icon_system::class,
icon_system_standard::class,
true,
],
'icon_system => icon_system_fontawesome' => [
icon_system::class,
icon_system_fontawesome::class,
true,
],
'icon_system => \theme_classic\output\icon_system_fontawesome' => [
icon_system::class,
\theme_classic\output\icon_system_fontawesome::class,
true,
],
'icon_system => notification' => [
icon_system::class,
notification::class,
false,
],
'icon_system_standard => icon_system_standard' => [
icon_system_standard::class,
icon_system_standard::class,
true,
],
'icon_system_standard => icon_system_fontawesome' => [
icon_system_standard::class,
icon_system_fontawesome::class,
false,
],
'icon_system_standard => \theme_classic\output\icon_system_fontawesome' => [
icon_system_standard::class,
\theme_classic\output\icon_system_fontawesome::class,
false,
],
'icon_system_fontawesome => icon_system_standard' => [
icon_system_fontawesome::class,
icon_system_standard::class,
false,
],
];
}
/**
* Data provider for tests of `is_valid`.
*
* @return array
*/
public function is_valid_subsystem_provider(): array {
return $this->icon_system_provider();
}
/**
* Data provider for tests of `instance` containing only invalid tests.
*
* @return array
*/
public function invalid_instance_provider(): array {
return array_filter(
$this->icon_system_provider(),
function($data) {
return !$data[2];
},
ARRAY_FILTER_USE_BOTH
);
}
/**
* Data provider for tests of `instance` containing only valid tests.
*
* @return array
*/
public function valid_instance_provider(): array {
return array_filter(
$this->icon_system_provider(),
function($data) {
return $data[2];
},
ARRAY_FILTER_USE_BOTH
);
}
}
+167
View File
@@ -0,0 +1,167 @@
<?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/>.
namespace core\output;
use ReflectionMethod;
/**
* Primary navigation renderable test
*
* @package core
* @category output
* @copyright 2021 onwards Peter Dias
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class language_menu_test extends \advanced_testcase {
/**
* Basic setup to make sure the nav objects gets generated without any issues.
*/
public function setUp(): void {
global $PAGE;
$this->resetAfterTest();
$PAGE->set_url('/');
}
/**
* Test the get_lang_menu
*
* @dataProvider get_lang_menu_provider
* @param bool $withadditionallangs
* @param string $language
* @param array $expected
*/
public function test_get_lang_menu(bool $withadditionallangs, string $language, array $expected): void {
global $CFG, $PAGE;
// Mimic multiple langs installed. To trigger responses 'get_list_of_translations'.
// Note: The text/title of the nodes generated will be 'English(fr), English(de)' but we don't care about this.
// We are testing whether the nodes gets generated when the lang menu is available.
if ($withadditionallangs) {
mkdir("$CFG->dataroot/lang/de", 0777, true);
mkdir("$CFG->dataroot/lang/fr", 0777, true);
// Ensure the new langs are picked up and not taken from the cache.
$stringmanager = get_string_manager();
$stringmanager->reset_caches(true);
}
force_current_language($language);
$output = new language_menu($PAGE);
$method = new ReflectionMethod('\core\output\language_menu', 'export_for_template');
$renderer = $PAGE->get_renderer('core');
$response = $method->invoke($output, $renderer);
if ($withadditionallangs) { // If there are multiple languages installed.
// Assert that the title of the language menu matches the expected one.
$this->assertEquals($expected['title'], $response['title']);
// Assert that the number of language menu items matches the number of the expected items.
$this->assertEquals(count($expected['items']), count($response['items']));
foreach ($expected['items'] as $expecteditem) {
$lang = $expecteditem['lang'];
// We need to manually generate the url key and its value in the expected item array as this cannot
// be done in the data provider due to the change of the state of $PAGE.
if ($expecteditem['isactive']) {
$expecteditem['url'] = new \moodle_url('#');
} else {
$expecteditem['url'] = new \moodle_url($PAGE->url, ['lang' => $lang]);
// When the language menu item is not the current language, it will contain the lang attribute.
$expecteditem['attributes'][] = [
'key' => 'lang',
'value' => $lang
];
}
// The lang value is only used to generate the url, so this key can be removed.
unset($expecteditem['lang']);
// Assert that the given expected item exists in the returned items.
$this->assertTrue(in_array($expecteditem, $response['items']));
}
} else { // No multiple languages.
$this->assertEquals($expected, $response);
}
}
/**
* Provider for test_get_lang_menu
*
* @return array
*/
public function get_lang_menu_provider(): array {
return [
'Lang menu with only the current language' => [
false, 'en', []
],
'Lang menu with only multiple languages installed' => [
true, 'en', [
'title' => 'English (en)',
'items' => [
[
'title' => 'English (en)',
'text' => 'English (en)',
'link' => true,
'isactive' => true,
'lang' => 'en'
],
[
'title' => 'English (de)',
'text' => 'English (de)',
'link' => true,
'isactive' => false,
'lang' => 'de'
],
[
'title' => 'English (fr)',
'text' => 'English (fr)',
'link' => true,
'isactive' => false,
'lang' => 'fr'
],
],
],
],
'Lang menu with only multiple languages installed and other than EN set active.' => [
true, 'de', [
'title' => 'English (de)',
'items' => [
[
'title' => 'English (en)',
'text' => 'English (en)',
'link' => true,
'isactive' => false,
'lang' => 'en'
],
[
'title' => 'English (de)',
'text' => 'English (de)',
'link' => true,
'isactive' => true,
'lang' => 'de'
],
[
'title' => 'English (fr)',
'text' => 'English (fr)',
'link' => true,
'isactive' => false,
'lang' => 'fr'
],
],
],
],
];
}
}
@@ -0,0 +1,56 @@
<?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/>.
declare(strict_types=1);
namespace core\output;
/**
* Unit tests for the mustache_clean_string_helper class.
*
* @package core
* @category test
* @copyright 2021 Shamim Rezaie <shamim@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core\output\mustache_clean_string_helper
*/
class mustache_clean_string_helper_test extends \basic_testcase {
/**
* Test the get_lang_menu
*
* @covers ::cleanstr
*/
function test_cleanstr(): void {
$engine = new \Mustache_Engine();
$context = new \Mustache_Context();
$lambdahelper = new \Mustache_LambdaHelper($engine, $context);
$cleanstringhelper = new mustache_clean_string_helper();
// Simple string.
$this->assertEquals('Log in', $cleanstringhelper->cleanstr('login, core', $lambdahelper));
// Quotes in string.
$this->assertEquals('Today&#039;s logs', $cleanstringhelper->cleanstr('todaylogs, core', $lambdahelper));
// Quotes in string with parameter.
$this->assertEquals('After &quot;test&quot;', $cleanstringhelper->cleanstr('movecontentafter, core, test', $lambdahelper));
// Quotes in parameter.
$this->assertEquals('Add a new &quot;&#039;&amp;', $cleanstringhelper->cleanstr('addnew, core, "\'&', $lambdahelper));
}
}
@@ -0,0 +1,174 @@
<?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/>.
namespace core\output;
/**
* Unit tests for the mustache_helper_collection class.
*
* @package core
* @copyright 2019 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @covers \core\output\mustache_helper_collection
*/
class mustache_helper_collection_test extends \advanced_testcase {
/**
* Test cases to confirm that disallowed helpers are stripped from the source
* text by the helper before being passed to other another helper. This prevents
* nested calls to helpers.
*/
public function get_strip_disallowed_helpers_testcases() {
return [
'no disallowed' => [
'disallowed' => [],
'input' => 'core, move, {{#js}} some nasty JS {{/js}}',
'expected' => 'core, move, {{#js}} some nasty JS {{/js}}'
],
'disallowed no match' => [
'disallowed' => ['foo'],
'input' => 'core, move, {{#js}} some nasty JS {{/js}}',
'expected' => 'core, move, {{#js}} some nasty JS {{/js}}'
],
'disallowed partial match 1' => [
'disallowed' => ['js'],
'input' => 'core, move, {{#json}} some nasty JS {{/json}}',
'expected' => 'core, move, {{#json}} some nasty JS {{/json}}'
],
'disallowed partial match 2' => [
'disallowed' => ['js'],
'input' => 'core, move, {{#onjs}} some nasty JS {{/onjs}}',
'expected' => 'core, move, {{#onjs}} some nasty JS {{/onjs}}'
],
'single disallowed 1' => [
'disallowed' => ['js'],
'input' => 'core, move, {{#js}} some nasty JS {{/js}}',
'expected' => 'core, move, {{}}'
],
'single disallowed 2' => [
'disallowed' => ['js'],
'input' => 'core, move, {{ # js }} some nasty JS {{ / js }}',
'expected' => 'core, move, {{}}'
],
'single disallowed 3' => [
'disallowed' => ['js'],
'input' => 'core, {{#js}} some nasty JS {{/js}}, test',
'expected' => 'core, {{}}, test'
],
'single disallowed 4' => [
'disallowed' => ['js'],
'input' => 'core, {{#ok}} this is ok {{/ok}}, {{#js}} some nasty JS {{/js}}',
'expected' => 'core, {{#ok}} this is ok {{/ok}}, {{}}'
],
'single disallowed multiple matches 1' => [
'disallowed' => ['js'],
'input' => 'core, {{#js}} some nasty JS {{/js}}, {{#js}} some nasty JS {{/js}}',
'expected' => 'core, {{}}'
],
'single disallowed multiple matches 2' => [
'disallowed' => ['js'],
'input' => 'core, {{ # js }} some nasty JS {{ / js }}, {{ # js }} some nasty JS {{ / js }}',
'expected' => 'core, {{}}'
],
'single disallowed multiple matches nested 1' => [
'disallowed' => ['js'],
'input' => 'core, move, {{#js}} some nasty JS {{#js}} some nasty JS {{/js}} {{/js}}',
'expected' => 'core, move, {{}}'
],
'single disallowed multiple matches nested 2' => [
'disallowed' => ['js'],
'input' => 'core, move, {{ # js }} some nasty JS {{ # js }} some nasty JS {{ / js }}{{ / js }}',
'expected' => 'core, move, {{}}'
],
'multiple disallowed 1' => [
'disallowed' => ['js', 'foo'],
'input' => 'core, move, {{#js}} some nasty JS {{/js}}',
'expected' => 'core, move, {{}}'
],
'multiple disallowed 2' => [
'disallowed' => ['js', 'foo'],
'input' => 'core, {{#foo}} blah {{/foo}}, {{#js}} js {{/js}}',
'expected' => 'core, {{}}, {{}}'
],
'multiple disallowed 3' => [
'disallowed' => ['js', 'foo'],
'input' => '{{#foo}} blah {{/foo}}, {{#foo}} blah {{/foo}}, {{#js}} js {{/js}}',
'expected' => '{{}}, {{}}'
],
'multiple disallowed 4' => [
'disallowed' => ['js', 'foo'],
'input' => '{{#foo}} blah {{/foo}}, {{#js}} js {{/js}}, {{#foo}} blah {{/foo}}',
'expected' => '{{}}'
],
'multiple disallowed 5' => [
'disallowed' => ['js', 'foo'],
'input' => 'core, move, {{#js}} JS {{#foo}} blah {{/foo}} {{/js}}',
'expected' => 'core, move, {{}}'
],
];
}
/**
* Test that the mustache_helper_collection class correctly strips
* @dataProvider get_strip_disallowed_helpers_testcases()
* @param string[] $disallowed The list of helpers to strip
* @param string $input The input string for the helper
* @param string $expected The expected output of the string after disallowed strip
*/
public function test_strip_disallowed_helpers($disallowed, $input, $expected): void {
$collection = new mustache_helper_collection(null, $disallowed);
$this->assertEquals($expected, $collection->strip_disallowed_helpers($disallowed, $input));
}
/**
* Test that the disallowed helpers are disabled during the execution of other
* helpers.
*
* Any allowed helper should still be available to call during the
* execution of a helper.
*/
public function test_disallowed_helpers_disabled_during_execution(): void {
$engine = new \Mustache_Engine();
$context = new \Mustache_Context();
$lambdahelper = new \Mustache_LambdaHelper($engine, $context);
$disallowed = ['bad'];
$collection = new mustache_helper_collection(null, $disallowed);
$badcalled = false;
$goodcalled = false;
$badhelper = function() use (&$badcalled) {
$badcalled = true;
return '';
};
$goodhelper = function() use (&$goodcalled) {
$goodcalled = true;
return '';
};
// A test helper that just returns the text without modifying it.
$testhelper = function($text, $lambda) use ($collection) {
$collection->get('good')($text, $lambda);
$collection->get('bad')($text, $lambda);
return $text;
};
$collection->add('bad', $badhelper);
$collection->add('good', $goodhelper);
$collection->add('test', $testhelper);
$this->assertEquals('success output', $collection->get('test')('success output', $lambdahelper));
$this->assertTrue($goodcalled);
$this->assertFalse($badcalled);
}
}
@@ -0,0 +1,86 @@
<?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/>.
declare(strict_types=1);
namespace core\output;
/**
* Unit tests for the mustache_quote_helper class.
*
* @package core
* @category test
* @copyright 2022 TU Berlin
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @coversDefaultClass \core\output\mustache_quote_helper
*/
class mustache_quote_helper_test extends \basic_testcase {
/**
* Tests the quote helper
*
* @covers ::quote
*/
public function test_quote(): void {
$engine = new \Mustache_Engine();
$context = new \Mustache_Context([
'world' => '{{planet}}',
'planet' => '<earth>'
]);
$lambdahelper = new \Mustache_LambdaHelper($engine, $context);
$quotehelper = new mustache_quote_helper();
// Simple string.
$this->assertEquals('"Hello world!"', $quotehelper->quote('Hello world!', $lambdahelper));
// Special JSON characters in string.
$this->assertEquals(
'"Hello \\"world\\"! (\\b,\\f,\\n,\\r,\\t,\\\\)"',
$quotehelper->quote('Hello "world"! (' . chr(8) . ",\f,\n,\r,\t,\\)", $lambdahelper)
);
// Double curly braces in string.
$this->assertEquals(
'"Hello {{=<% %>=}}{{<%={{ }}=%>world{{=<% %>=}}}}<%={{ }}=%>!"',
$quotehelper->quote('{{=<% %>=}}Hello {{world}}!<%={{ }}=%>', $lambdahelper)
);
// Triple curly braces in string.
$this->assertEquals(
'"Hello {{=<% %>=}}{{{<%={{ }}=%>world{{=<% %>=}}}}}<%={{ }}=%>!"',
$quotehelper->quote('{{=<% %>=}}Hello {{{world}}}!<%={{ }}=%>', $lambdahelper)
);
// Variable interpolation with double braces.
$this->assertEquals(
'"Hello &lt;earth&gt;!"',
$quotehelper->quote('Hello {{planet}}!', $lambdahelper)
);
// Variable interpolation with triple braces.
$this->assertEquals(
'"Hello <earth>!"',
$quotehelper->quote('Hello {{{planet}}}!', $lambdahelper)
);
// Variables interpolated only once.
$this->assertEquals(
'"Hello {{=<% %>=}}{{<%={{ }}=%>planet{{=<% %>=}}}}<%={{ }}=%>!"',
$quotehelper->quote('Hello {{world}}!', $lambdahelper)
);
}
}
@@ -0,0 +1,194 @@
<?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/>.
namespace core\output;
/**
* Unit tests for lib/classes/output/mustache_template_finder.php
*
* Unit tests for the Mustache template finder class (contains logic about
* resolving mustache template locations.
*
* @package core
* @category test
* @copyright 2015 Damyon Wiese
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_template_finder_test extends \advanced_testcase {
/**
* Data provider which reutrns a set of valid template directories to be used when testing
* get_template_directories_for_component.
*
* @return array
*/
public function valid_template_directories_provider(): array {
return [
'plugin: mod_assign' => [
'component' => 'mod_assign',
'theme' => '',
'paths' => [
'theme/boost/templates/mod_assign/',
'mod/assign/templates/'
],
],
'plugin: mod_assign with classic' => [
'component' => 'mod_assign',
'theme' => 'classic',
'paths' => [
'theme/classic/templates/mod_assign/',
'theme/boost/templates/mod_assign/',
'mod/assign/templates/'
],
],
'subsystem: core_user' => [
'component' => 'core_user',
'theme' => 'classic',
'paths' => [
'theme/classic/templates/core_user/',
'theme/boost/templates/core_user/',
'user/templates/'
],
],
'core' => [
'component' => 'core',
'theme' => 'classic',
'paths' => [
'theme/classic/templates/core/',
'theme/boost/templates/core/',
'lib/templates/'
],
],
];
}
/**
* Tests for get_template_directories_for_component.
*
* @dataProvider valid_template_directories_provider
* @param string $component
* @param string $theme
* @param array $paths
*/
public function test_get_template_directories_for_component(string $component, string $theme, array $paths): void {
global $CFG;
// Test a plugin.
$dirs = mustache_template_finder::get_template_directories_for_component($component, $theme, $paths);
$correct = array_map(function($path) use ($CFG) {
return implode('/', [$CFG->dirroot, $path]);
}, $paths);
$this->assertEquals($correct, $dirs);
}
/**
* Tests for get_template_directories_for_component when dealing with an invalid component.
*/
public function test_invalid_component_get_template_directories_for_component(): void {
// Test something invalid.
$this->expectException(\coding_exception::class);
mustache_template_finder::get_template_directories_for_component('octopus', 'classic');
}
/**
* Data provider which reutrns a set of valid template directories to be used when testing
* get_template_directories_for_component.
*
* @return array
*/
public function valid_template_filepath_provider(): array {
return [
'Standard core template' => [
'template' => 'core/modal',
'theme' => '',
'location' => 'lib/templates/modal.mustache',
],
'Template overridden by theme' => [
'template' => 'core_form/element-float-inline',
'theme' => '',
'location' => 'theme/boost/templates/core_form/element-float-inline.mustache',
],
'Template overridden by theme but child theme selected' => [
'template' => 'core_form/element-float-inline',
'theme' => 'classic',
'location' => 'theme/boost/templates/core_form/element-float-inline.mustache',
],
'Template overridden by child theme' => [
'template' => 'core/full_header',
'theme' => 'classic',
'location' => 'theme/classic/templates/core/full_header.mustache',
],
'Template overridden by child theme but tested against defualt theme' => [
'template' => 'core/full_header',
'theme' => '',
'location' => 'lib/templates/full_header.mustache',
],
'Standard plugin template' => [
'template' => 'mod_assign/grading_panel',
'theme' => '',
'location' => 'mod/assign/templates/grading_panel.mustache',
],
'Subsystem template' => [
'template' => 'core_user/status_details',
'theme' => '',
'location' => 'user/templates/status_details.mustache',
],
'Theme own template' => [
'template' => 'theme_classic/columns',
'theme' => '',
'location' => 'theme/classic/templates/columns.mustache',
],
'Theme overridden template against that theme' => [
'template' => 'theme_classic/navbar',
'theme' => 'classic',
'location' => 'theme/classic/templates/navbar.mustache',
],
// Note: This one looks strange but is correct. It is legitimate to request theme's component template in
// the context of another theme. For example, this is used by child themes making use of parent theme
// templates.
'Theme overridden template against the default theme' => [
'template' => 'theme_classic/navbar',
'theme' => '',
'location' => 'theme/classic/templates/navbar.mustache',
],
];
}
/**
* Tests for get_template_filepath.
*
* @dataProvider valid_template_filepath_provider
* @param string $template
* @param string $theme
* @param string $location
*/
public function test_get_template_filepath(string $template, string $theme, string $location): void {
global $CFG;
$filename = mustache_template_finder::get_template_filepath($template, $theme);
$this->assertEquals("{$CFG->dirroot}/{$location}", $filename);
}
/**
* Tests for get_template_filepath when dealing with an invalid component.
*/
public function test_invalid_component_get_template_filepath(): void {
$this->expectException(\moodle_exception::class);
mustache_template_finder::get_template_filepath('core/octopus', 'classic');
}
}
@@ -0,0 +1,518 @@
<?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/>.
namespace core\output;
/**
* Unit tests for the Mustache source loader class.
*
* Unit tests for lib/classes/output/mustache_template_source_loader.php
*
* @package core
* @copyright 2018 Ryan Wyllie <ryan@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mustache_template_source_loader_test extends \advanced_testcase {
/**
* Ensure that stripping comments from templates does not mutilate the template body.
*/
public function test_strip_template_comments(): void {
$templatebody = <<<'TBD'
<h1>{{# str }} pluginname, mod_lemmings {{/ str }}</h1>
<div>{{test}}</div>
<div>{{{unescapedtest}}}</div>
{{#lemmings}}
<div>
<h2>{{name}}</h2>
{{> mod_lemmings/lemmingprofile }}
{{# pix }} t/edit, core, Edit Lemming {{/ pix }}
</div>
{{/lemmings}}
{{^lemmings}}Sorry, no lemmings today{{/lemmings}}
<div id="{{ uniqid }}-tab-container">
{{# tabheader }}
<ul role="tablist" class="nav nav-tabs">
{{# iconlist }}
{{# icons }}
{{> core/pix_icon }}
{{/ icons }}
{{/ iconlist }}
</ul>
{{/ tabheader }}
{{# tabbody }}
<div class="tab-content">
{{# tabcontent }}
{{# tabs }}
{{> core/notification_info}}
{{/ tabs }}
{{/ tabcontent }}
</div>
{{/ tabbody }}
</div>
{{#js}}
require(['jquery','core/tabs'], function($, tabs) {
var container = $("#{{ uniqid }}-tab-container");
tabs.create(container);
});
{{/js}}
TBD;
$templatewithcomment = <<<TBC
{{!
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/>.
}}
{{!
@template mod_lemmings/lemmings
Lemmings template.
The purpose of this template is to render a lot of lemmings.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* attributes Array of name / value pairs.
Example context (json):
{
"lemmings": [
{ "name": "Lemmy Winks", "age" : 1, "size" : "big" },
{ "name": "Rocky", "age" : 2, "size" : "small" }
]
}
}}
$templatebody
{{!
Here's some more comment text
Note, there is no need to test bracketed variables inside comments as gherkin does not support that!
See this issue: https://github.com/mustache/spec/issues/8
}}
TBC;
$loader = new mustache_template_source_loader();
$actual = \phpunit_util::call_internal_method(
$loader,
'strip_template_comments',
[$templatewithcomment],
\core\output\mustache_template_source_loader::class
);
$this->assertEquals(trim($templatebody), trim($actual));
}
/**
* Data provider for the test_load function.
*/
public function load_test_cases() {
$cache = [
'core' => [
'test' => '{{! a comment }}The rest of the template'
]
];
$loader = $this->build_loader_from_static_cache($cache);
return [
'with comments' => [
'loader' => $loader,
'component' => 'core',
'name' => 'test',
'includecomments' => true,
'expected' => '{{! a comment }}The rest of the template'
],
'without comments' => [
'loader' => $loader,
'component' => 'core',
'name' => 'test',
'includecomments' => false,
'expected' => 'The rest of the template'
],
];
}
/**
* Test the load function.
*
* @dataProvider load_test_cases
* @param mustache_template_source_loader $loader The loader
* @param string $component The moodle component
* @param string $name The template name
* @param bool $includecomments Whether to strip comments
* @param string $expected The expected output
*/
public function test_load($loader, $component, $name, $includecomments, $expected): void {
$this->assertEquals($expected, $loader->load($component, $name, 'boost', $includecomments));
}
/**
* Data provider for the load_with_dependencies function.
*/
public function load_with_dependencies_test_cases() {
// Create a bunch of templates that include one another in various ways. There is
// multiple instances of recursive inclusions to test that the code doensn't get
// stuck in an infinite loop.
$foo = '{{! a comment }}{{> core/bar }}{{< test/bop }}{{/ test/bop}}{{#str}} help, core {{/str}}';
$foo2 = '{{! a comment }}hello';
$bar = '{{! a comment }}{{> core/baz }}';
$baz = '{{! a comment }}{{#str}} hide, core {{/str}}';
$bop = '{{! a comment }}{{< test/bim }}{{/ test/bim }}{{> core/foo }}';
$bim = '{{! a comment }}{{< core/foo }}{{/ core/foo}}{{> test/foo }}';
$foonocomment = '{{> core/bar }}{{< test/bop }}{{/ test/bop}}{{#str}} help, core {{/str}}';
$foo2nocomment = 'hello';
$barnocomment = '{{> core/baz }}';
$baznocomment = '{{#str}} hide, core {{/str}}';
$bopnocomment = '{{< test/bim }}{{/ test/bim }}{{> core/foo }}';
$bimnocomment = '{{< core/foo }}{{/ core/foo}}{{> test/foo }}';
$cache = [
'core' => [
'foo' => $foo,
'bar' => $bar,
'baz' => $baz,
],
'test' => [
'foo' => $foo2,
'bop' => $bop,
'bim' => $bim
]
];
$loader = $this->build_loader_from_static_cache($cache);
return [
'no template includes w comments' => [
'loader' => $loader,
'component' => 'test',
'name' => 'foo',
'includecomments' => true,
'expected' => [
'templates' => [
'test' => [
'foo' => $foo2
]
],
'strings' => []
]
],
'no template includes w/o comments' => [
'loader' => $loader,
'component' => 'test',
'name' => 'foo',
'includecomments' => false,
'expected' => [
'templates' => [
'test' => [
'foo' => $foo2nocomment
]
],
'strings' => []
]
],
'no template includes with string w comments' => [
'loader' => $loader,
'component' => 'core',
'name' => 'baz',
'includecomments' => true,
'expected' => [
'templates' => [
'core' => [
'baz' => $baz
]
],
'strings' => [
'core' => [
'hide' => 'Hide'
]
]
]
],
'no template includes with string w/o comments' => [
'loader' => $loader,
'component' => 'core',
'name' => 'baz',
'includecomments' => false,
'expected' => [
'templates' => [
'core' => [
'baz' => $baznocomment
]
],
'strings' => [
'core' => [
'hide' => 'Hide'
]
]
]
],
'full with comments' => [
'loader' => $loader,
'component' => 'core',
'name' => 'foo',
'includecomments' => true,
'expected' => [
'templates' => [
'core' => [
'foo' => $foo,
'bar' => $bar,
'baz' => $baz
],
'test' => [
'foo' => $foo2,
'bop' => $bop,
'bim' => $bim
]
],
'strings' => [
'core' => [
'help' => 'Help',
'hide' => 'Hide'
]
]
]
],
'full without comments' => [
'loader' => $loader,
'component' => 'core',
'name' => 'foo',
'includecomments' => false,
'expected' => [
'templates' => [
'core' => [
'foo' => $foonocomment,
'bar' => $barnocomment,
'baz' => $baznocomment
],
'test' => [
'foo' => $foo2nocomment,
'bop' => $bopnocomment,
'bim' => $bimnocomment
]
],
'strings' => [
'core' => [
'help' => 'Help',
'hide' => 'Hide'
]
]
]
]
];
}
/**
* Test the load_with_dependencies function.
*
* @dataProvider load_with_dependencies_test_cases
* @param mustache_template_source_loader $loader The loader
* @param string $component The moodle component
* @param string $name The template name
* @param bool $includecomments Whether to strip comments
* @param string $expected The expected output
*/
public function test_load_with_dependencies($loader, $component, $name, $includecomments, $expected): void {
$actual = $loader->load_with_dependencies($component, $name, 'boost', $includecomments);
$this->assertEquals($expected, $actual);
}
/**
* Data provider for the test_load function.
*/
public function scan_template_source_for_dependencies_test_cases() {
$foo = '{{! a comment }}{{> core/bar }}{{< test/bop }}{{/ test/bop}}{{#str}} help, core {{/str}}';
$bar = '{{! a comment }}{{> core/baz }}';
$baz = '{{! a comment }}{{#str}} hide, core {{/str}}';
$bop = '{{! a comment }}hello';
$multiline1 = <<<TEMPLATE
{{! a comment }}{{#str}} authorreplyingprivatelytoauthor,
mod_forum {{/str}}
TEMPLATE;
$multiline2 = <<<TEMPLATE
{{! a comment }}{{#str}}
authorreplyingprivatelytoauthor,
mod_forum {{/str}}
TEMPLATE;
$multiline3 = <<<TEMPLATE
{{! a comment }}{{#str}}
authorreplyingprivatelytoauthor,
mod_forum
{{/str}}
TEMPLATE;
$multiline4 = <<<TEMPLATE
{{! a comment }}{{#str}}
authorreplyingprivatelytoauthor, mod_forum
{{/str}}
TEMPLATE;
$multiline5 = <<<TEMPLATE
{{! a comment }}{{#str}}
hide
{{/str}}
TEMPLATE;
$cache = [
'core' => [
'foo' => $foo,
'bar' => $bar,
'baz' => $baz,
'bop' => $bop,
'multiline1' => $multiline1,
'multiline2' => $multiline2,
'multiline3' => $multiline3,
'multiline4' => $multiline4,
'multiline5' => $multiline5,
]
];
$loader = $this->build_loader_from_static_cache($cache);
return [
'single template include' => [
'loader' => $loader,
'source' => $bar,
'expected' => [
'templates' => [
'core' => ['baz']
],
'strings' => []
]
],
'single string include' => [
'loader' => $loader,
'source' => $baz,
'expected' => [
'templates' => [],
'strings' => [
'core' => ['hide']
]
]
],
'no include' => [
'loader' => $loader,
'source' => $bop,
'expected' => [
'templates' => [],
'strings' => []
]
],
'all include' => [
'loader' => $loader,
'source' => $foo,
'expected' => [
'templates' => [
'core' => ['bar'],
'test' => ['bop']
],
'strings' => [
'core' => ['help']
]
]
],
'string: component on new line' => [
'loader' => $loader,
'source' => $multiline1,
'expected' => [
'templates' => [],
'strings' => [
'mod_forum' => ['authorreplyingprivatelytoauthor']
]
]
],
'string: identifier on own line' => [
'loader' => $loader,
'source' => $multiline2,
'expected' => [
'templates' => [],
'strings' => [
'mod_forum' => ['authorreplyingprivatelytoauthor']
]
]
],
'string: all parts on new lines' => [
'loader' => $loader,
'source' => $multiline3,
'expected' => [
'templates' => [],
'strings' => [
'mod_forum' => ['authorreplyingprivatelytoauthor']
]
]
],
'string: id and component on own line' => [
'loader' => $loader,
'source' => $multiline4,
'expected' => [
'templates' => [],
'strings' => [
'mod_forum' => ['authorreplyingprivatelytoauthor']
]
]
],
'string: no component' => [
'loader' => $loader,
'source' => $multiline5,
'expected' => [
'templates' => [],
'strings' => [
'core' => ['hide']
]
]
],
];
}
/**
* Test the scan_template_source_for_dependencies function.
*
* @dataProvider scan_template_source_for_dependencies_test_cases()
* @param mustache_template_source_loader $loader The loader
* @param string $source The template to test
* @param string $expected The expected output
*/
public function test_scan_template_source_for_dependencies($loader, $source, $expected): void {
$actual = \phpunit_util::call_internal_method(
$loader,
'scan_template_source_for_dependencies',
[$source],
\core\output\mustache_template_source_loader::class
);
$this->assertEquals($expected, $actual);
}
/**
* Create an instance of mustache_template_source_loader which loads its templates
* from the given cache rather than disk.
*
* @param array $cache A cache of templates
* @return mustache_template_source_loader
*/
private function build_loader_from_static_cache(array $cache): mustache_template_source_loader {
return new mustache_template_source_loader(function($component, $name, $themename) use ($cache) {
return $cache[$component][$name];
});
}
}
@@ -0,0 +1,81 @@
<?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/>.
namespace core\output;
use ReflectionMethod;
/**
* Participants tertiary navigation renderable test
*
* @package core
* @category output
* @copyright 2021 onwards Peter Dias
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class participants_action_bar_test extends \advanced_testcase {
/**
* Test the get_content_for_select function
*
* @dataProvider get_content_for_select_provider
* @param string $type Whether we are checking content in the course/module
* @param int $expectedcount Expected number of 1st level tertiary items
* @param array $expecteditems Expected keys of the 1st level tertiary items.
*/
public function test_get_content_for_select($type, $expectedcount, $expecteditems): void {
global $PAGE;
$this->resetAfterTest();
$course = $this->getDataGenerator()->create_course();
$module = $this->getDataGenerator()->create_module('assign', [
'course' => $course->id
]);
if ($type == 'course') {
$context = \context_course::instance($course->id);
$url = new \moodle_url('/course/view.php', ['id' => $course->id]);
} else {
$url = new \moodle_url('/mod/assign/view.php', ['id' => $module->id]);
$context = \context_module::instance($module->cmid);
$cm = get_coursemodule_from_instance('assign', $module->id, $course->id);
$PAGE->set_cm($cm);
}
$this->setAdminUser();
$PAGE->set_url($url);
$PAGE->set_context($context);
$output = new participants_action_bar($course, $PAGE, null);
$method = new ReflectionMethod('\core\output\participants_action_bar', 'get_content_for_select');
$renderer = $PAGE->get_renderer('core');
$response = $method->invoke($output, $renderer);
$this->assertCount($expectedcount, $response);
$this->assertSame($expecteditems, array_keys(array_merge(...$response)));
}
/**
* Provider for test_get_content_for_select
* @return array[]
*/
public function get_content_for_select_provider() {
return [
'Get dropdown content when in a course context' => [
'course', 3, ['Enrolments', 'Groups', 'Permissions']
],
'Get dropdown content when in a module context' => [
'module', 1, ['Permissions']
]
];
}
}