See Release Notes
Long Term Support Release
1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 namespace core\output; 18 19 /** 20 * Unit tests for the Mustache source loader class. 21 * 22 * Unit tests for lib/classes/output/mustache_template_source_loader.php 23 * 24 * @package core 25 * @copyright 2018 Ryan Wyllie <ryan@moodle.com> 26 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 27 */ 28 class mustache_template_source_loader_test extends \advanced_testcase { 29 /** 30 * Ensure that stripping comments from templates does not mutilate the template body. 31 */ 32 public function test_strip_template_comments() { 33 34 $templatebody = <<<'TBD' 35 <h1>{{# str }} pluginname, mod_lemmings {{/ str }}</h1> 36 <div>{{test}}</div> 37 <div>{{{unescapedtest}}}</div> 38 {{#lemmings}} 39 <div> 40 <h2>{{name}}</h2> 41 {{> mod_lemmings/lemmingprofile }} 42 {{# pix }} t/edit, core, Edit Lemming {{/ pix }} 43 </div> 44 {{/lemmings}} 45 {{^lemmings}}Sorry, no lemmings today{{/lemmings}} 46 <div id="{{ uniqid }}-tab-container"> 47 {{# tabheader }} 48 <ul role="tablist" class="nav nav-tabs"> 49 {{# iconlist }} 50 {{# icons }} 51 {{> core/pix_icon }} 52 {{/ icons }} 53 {{/ iconlist }} 54 </ul> 55 {{/ tabheader }} 56 {{# tabbody }} 57 <div class="tab-content"> 58 {{# tabcontent }} 59 {{# tabs }} 60 {{> core/notification_info}} 61 {{/ tabs }} 62 {{/ tabcontent }} 63 </div> 64 {{/ tabbody }} 65 </div> 66 {{#js}} 67 require(['jquery','core/tabs'], function($, tabs) { 68 69 var container = $("#{{ uniqid }}-tab-container"); 70 tabs.create(container); 71 }); 72 {{/js}} 73 TBD; 74 $templatewithcomment = <<<TBC 75 {{! 76 This file is part of Moodle - http://moodle.org/ 77 78 Moodle is free software: you can redistribute it and/or modify 79 it under the terms of the GNU General Public License as published by 80 the Free Software Foundation, either version 3 of the License, or 81 (at your option) any later version. 82 83 Moodle is distributed in the hope that it will be useful, 84 but WITHOUT ANY WARRANTY; without even the implied warranty of 85 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 86 GNU General Public License for more details. 87 88 You should have received a copy of the GNU General Public License 89 along with Moodle. If not, see <http://www.gnu.org/licenses/>. 90 }} 91 {{! 92 @template mod_lemmings/lemmings 93 94 Lemmings template. 95 96 The purpose of this template is to render a lot of lemmings. 97 98 Classes required for JS: 99 * none 100 101 Data attributes required for JS: 102 * none 103 104 Context variables required for this template: 105 * attributes Array of name / value pairs. 106 107 Example context (json): 108 { 109 "lemmings": [ 110 { "name": "Lemmy Winks", "age" : 1, "size" : "big" }, 111 { "name": "Rocky", "age" : 2, "size" : "small" } 112 ] 113 } 114 115 }} 116 $templatebody 117 {{! 118 Here's some more comment text 119 Note, there is no need to test bracketed variables inside comments as gherkin does not support that! 120 See this issue: https://github.com/mustache/spec/issues/8 121 }} 122 TBC; 123 124 $loader = new mustache_template_source_loader(); 125 $actual = \phpunit_util::call_internal_method( 126 $loader, 127 'strip_template_comments', 128 [$templatewithcomment], 129 \core\output\mustache_template_source_loader::class 130 ); 131 $this->assertEquals(trim($templatebody), trim($actual)); 132 } 133 134 /** 135 * Data provider for the test_load function. 136 */ 137 public function load_test_cases() { 138 $cache = [ 139 'core' => [ 140 'test' => '{{! a comment }}The rest of the template' 141 ] 142 ]; 143 $loader = $this->build_loader_from_static_cache($cache); 144 145 return [ 146 'with comments' => [ 147 'loader' => $loader, 148 'component' => 'core', 149 'name' => 'test', 150 'includecomments' => true, 151 'expected' => '{{! a comment }}The rest of the template' 152 ], 153 'without comments' => [ 154 'loader' => $loader, 155 'component' => 'core', 156 'name' => 'test', 157 'includecomments' => false, 158 'expected' => 'The rest of the template' 159 ], 160 ]; 161 } 162 163 /** 164 * Test the load function. 165 * 166 * @dataProvider load_test_cases 167 * @param mustache_template_source_loader $loader The loader 168 * @param string $component The moodle component 169 * @param string $name The template name 170 * @param bool $includecomments Whether to strip comments 171 * @param string $expected The expected output 172 */ 173 public function test_load($loader, $component, $name, $includecomments, $expected) { 174 $this->assertEquals($expected, $loader->load($component, $name, 'boost', $includecomments)); 175 } 176 177 /** 178 * Data provider for the load_with_dependencies function. 179 */ 180 public function load_with_dependencies_test_cases() { 181 // Create a bunch of templates that include one another in various ways. There is 182 // multiple instances of recursive inclusions to test that the code doensn't get 183 // stuck in an infinite loop. 184 $foo = '{{! a comment }}{{> core/bar }}{{< test/bop }}{{/ test/bop}}{{#str}} help, core {{/str}}'; 185 $foo2 = '{{! a comment }}hello'; 186 $bar = '{{! a comment }}{{> core/baz }}'; 187 $baz = '{{! a comment }}{{#str}} hide, core {{/str}}'; 188 $bop = '{{! a comment }}{{< test/bim }}{{/ test/bim }}{{> core/foo }}'; 189 $bim = '{{! a comment }}{{< core/foo }}{{/ core/foo}}{{> test/foo }}'; 190 $foonocomment = '{{> core/bar }}{{< test/bop }}{{/ test/bop}}{{#str}} help, core {{/str}}'; 191 $foo2nocomment = 'hello'; 192 $barnocomment = '{{> core/baz }}'; 193 $baznocomment = '{{#str}} hide, core {{/str}}'; 194 $bopnocomment = '{{< test/bim }}{{/ test/bim }}{{> core/foo }}'; 195 $bimnocomment = '{{< core/foo }}{{/ core/foo}}{{> test/foo }}'; 196 $cache = [ 197 'core' => [ 198 'foo' => $foo, 199 'bar' => $bar, 200 'baz' => $baz, 201 ], 202 'test' => [ 203 'foo' => $foo2, 204 'bop' => $bop, 205 'bim' => $bim 206 ] 207 ]; 208 $loader = $this->build_loader_from_static_cache($cache); 209 210 return [ 211 'no template includes w comments' => [ 212 'loader' => $loader, 213 'component' => 'test', 214 'name' => 'foo', 215 'includecomments' => true, 216 'expected' => [ 217 'templates' => [ 218 'test' => [ 219 'foo' => $foo2 220 ] 221 ], 222 'strings' => [] 223 ] 224 ], 225 'no template includes w/o comments' => [ 226 'loader' => $loader, 227 'component' => 'test', 228 'name' => 'foo', 229 'includecomments' => false, 230 'expected' => [ 231 'templates' => [ 232 'test' => [ 233 'foo' => $foo2nocomment 234 ] 235 ], 236 'strings' => [] 237 ] 238 ], 239 'no template includes with string w comments' => [ 240 'loader' => $loader, 241 'component' => 'core', 242 'name' => 'baz', 243 'includecomments' => true, 244 'expected' => [ 245 'templates' => [ 246 'core' => [ 247 'baz' => $baz 248 ] 249 ], 250 'strings' => [ 251 'core' => [ 252 'hide' => 'Hide' 253 ] 254 ] 255 ] 256 ], 257 'no template includes with string w/o comments' => [ 258 'loader' => $loader, 259 'component' => 'core', 260 'name' => 'baz', 261 'includecomments' => false, 262 'expected' => [ 263 'templates' => [ 264 'core' => [ 265 'baz' => $baznocomment 266 ] 267 ], 268 'strings' => [ 269 'core' => [ 270 'hide' => 'Hide' 271 ] 272 ] 273 ] 274 ], 275 'full with comments' => [ 276 'loader' => $loader, 277 'component' => 'core', 278 'name' => 'foo', 279 'includecomments' => true, 280 'expected' => [ 281 'templates' => [ 282 'core' => [ 283 'foo' => $foo, 284 'bar' => $bar, 285 'baz' => $baz 286 ], 287 'test' => [ 288 'foo' => $foo2, 289 'bop' => $bop, 290 'bim' => $bim 291 ] 292 ], 293 'strings' => [ 294 'core' => [ 295 'help' => 'Help', 296 'hide' => 'Hide' 297 ] 298 ] 299 ] 300 ], 301 'full without comments' => [ 302 'loader' => $loader, 303 'component' => 'core', 304 'name' => 'foo', 305 'includecomments' => false, 306 'expected' => [ 307 'templates' => [ 308 'core' => [ 309 'foo' => $foonocomment, 310 'bar' => $barnocomment, 311 'baz' => $baznocomment 312 ], 313 'test' => [ 314 'foo' => $foo2nocomment, 315 'bop' => $bopnocomment, 316 'bim' => $bimnocomment 317 ] 318 ], 319 'strings' => [ 320 'core' => [ 321 'help' => 'Help', 322 'hide' => 'Hide' 323 ] 324 ] 325 ] 326 ] 327 ]; 328 } 329 330 /** 331 * Test the load_with_dependencies function. 332 * 333 * @dataProvider load_with_dependencies_test_cases 334 * @param mustache_template_source_loader $loader The loader 335 * @param string $component The moodle component 336 * @param string $name The template name 337 * @param bool $includecomments Whether to strip comments 338 * @param string $expected The expected output 339 */ 340 public function test_load_with_dependencies($loader, $component, $name, $includecomments, $expected) { 341 $actual = $loader->load_with_dependencies($component, $name, 'boost', $includecomments); 342 $this->assertEquals($expected, $actual); 343 } 344 /** 345 * Data provider for the test_load function. 346 */ 347 public function scan_template_source_for_dependencies_test_cases() { 348 $foo = '{{! a comment }}{{> core/bar }}{{< test/bop }}{{/ test/bop}}{{#str}} help, core {{/str}}'; 349 $bar = '{{! a comment }}{{> core/baz }}'; 350 $baz = '{{! a comment }}{{#str}} hide, core {{/str}}'; 351 $bop = '{{! a comment }}hello'; 352 $multiline1 = <<<TEMPLATE 353 {{! a comment }}{{#str}} authorreplyingprivatelytoauthor, 354 mod_forum {{/str}} 355 TEMPLATE; 356 $multiline2 = <<<TEMPLATE 357 {{! a comment }}{{#str}} 358 authorreplyingprivatelytoauthor, 359 mod_forum {{/str}} 360 TEMPLATE; 361 $multiline3 = <<<TEMPLATE 362 {{! a comment }}{{#str}} 363 authorreplyingprivatelytoauthor, 364 mod_forum 365 {{/str}} 366 TEMPLATE; 367 $multiline4 = <<<TEMPLATE 368 {{! a comment }}{{#str}} 369 authorreplyingprivatelytoauthor, mod_forum 370 {{/str}} 371 TEMPLATE; 372 $multiline5 = <<<TEMPLATE 373 {{! a comment }}{{#str}} 374 hide 375 {{/str}} 376 TEMPLATE; 377 378 $cache = [ 379 'core' => [ 380 'foo' => $foo, 381 'bar' => $bar, 382 'baz' => $baz, 383 'bop' => $bop, 384 'multiline1' => $multiline1, 385 'multiline2' => $multiline2, 386 'multiline3' => $multiline3, 387 'multiline4' => $multiline4, 388 'multiline5' => $multiline5, 389 ] 390 ]; 391 $loader = $this->build_loader_from_static_cache($cache); 392 393 return [ 394 'single template include' => [ 395 'loader' => $loader, 396 'source' => $bar, 397 'expected' => [ 398 'templates' => [ 399 'core' => ['baz'] 400 ], 401 'strings' => [] 402 ] 403 ], 404 'single string include' => [ 405 'loader' => $loader, 406 'source' => $baz, 407 'expected' => [ 408 'templates' => [], 409 'strings' => [ 410 'core' => ['hide'] 411 ] 412 ] 413 ], 414 'no include' => [ 415 'loader' => $loader, 416 'source' => $bop, 417 'expected' => [ 418 'templates' => [], 419 'strings' => [] 420 ] 421 ], 422 'all include' => [ 423 'loader' => $loader, 424 'source' => $foo, 425 'expected' => [ 426 'templates' => [ 427 'core' => ['bar'], 428 'test' => ['bop'] 429 ], 430 'strings' => [ 431 'core' => ['help'] 432 ] 433 ] 434 ], 435 'string: component on new line' => [ 436 'loader' => $loader, 437 'source' => $multiline1, 438 'expected' => [ 439 'templates' => [], 440 'strings' => [ 441 'mod_forum' => ['authorreplyingprivatelytoauthor'] 442 ] 443 ] 444 ], 445 'string: identifier on own line' => [ 446 'loader' => $loader, 447 'source' => $multiline2, 448 'expected' => [ 449 'templates' => [], 450 'strings' => [ 451 'mod_forum' => ['authorreplyingprivatelytoauthor'] 452 ] 453 ] 454 ], 455 'string: all parts on new lines' => [ 456 'loader' => $loader, 457 'source' => $multiline3, 458 'expected' => [ 459 'templates' => [], 460 'strings' => [ 461 'mod_forum' => ['authorreplyingprivatelytoauthor'] 462 ] 463 ] 464 ], 465 'string: id and component on own line' => [ 466 'loader' => $loader, 467 'source' => $multiline4, 468 'expected' => [ 469 'templates' => [], 470 'strings' => [ 471 'mod_forum' => ['authorreplyingprivatelytoauthor'] 472 ] 473 ] 474 ], 475 'string: no component' => [ 476 'loader' => $loader, 477 'source' => $multiline5, 478 'expected' => [ 479 'templates' => [], 480 'strings' => [ 481 'core' => ['hide'] 482 ] 483 ] 484 ], 485 ]; 486 } 487 488 /** 489 * Test the scan_template_source_for_dependencies function. 490 * 491 * @dataProvider scan_template_source_for_dependencies_test_cases() 492 * @param mustache_template_source_loader $loader The loader 493 * @param string $source The template to test 494 * @param string $expected The expected output 495 */ 496 public function test_scan_template_source_for_dependencies($loader, $source, $expected) { 497 $actual = \phpunit_util::call_internal_method( 498 $loader, 499 'scan_template_source_for_dependencies', 500 [$source], 501 \core\output\mustache_template_source_loader::class 502 ); 503 $this->assertEquals($expected, $actual); 504 } 505 506 /** 507 * Create an instance of mustache_template_source_loader which loads its templates 508 * from the given cache rather than disk. 509 * 510 * @param array $cache A cache of templates 511 * @return mustache_template_source_loader 512 */ 513 private function build_loader_from_static_cache(array $cache) : mustache_template_source_loader { 514 return new mustache_template_source_loader(function($component, $name, $themename) use ($cache) { 515 return $cache[$component][$name]; 516 }); 517 } 518 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body