Merge pull request #15878 from civicrm/5.20
[civicrm-core.git] / tests / phpunit / CRM / Core / ResourcesTest.php
CommitLineData
6a488035
TO
1<?php
2/*
3 +--------------------------------------------------------------------+
7d61e75f 4 | Copyright CiviCRM LLC. All rights reserved. |
6a488035 5 | |
7d61e75f
TO
6 | This work is published under the GNU AGPLv3 license with some |
7 | permitted exceptions and without any warranty. For full license |
8 | and copyright information, see https://civicrm.org/licensing |
6a488035 9 +--------------------------------------------------------------------+
d25dd0ee 10 */
6a488035 11
6a488035
TO
12/**
13 * Tests for linking to resource files
acb109b7 14 * @group headless
6a488035
TO
15 */
16class CRM_Core_ResourcesTest extends CiviUnitTestCase {
6a488035
TO
17
18 /**
19 * @var CRM_Core_Resources
20 */
21 protected $res;
22
23 /**
24 * @var CRM_Extension_Mapper
25 */
26 protected $mapper;
27
6f12c6eb 28 /**
29 * @var string for testing cache buster generation
30 */
31 protected $cacheBusterString = 'xBkdk3';
32
42a40a1c 33 protected $originalRequest;
34 protected $originalGet;
35
00be9182 36 public function setUp() {
6a488035
TO
37 parent::setUp();
38
39 list ($this->basedir, $this->container, $this->mapper) = $this->_createMapper();
9099cab3 40 $cache = new CRM_Utils_Cache_Arraycache([]);
6a488035
TO
41 $this->res = new CRM_Core_Resources($this->mapper, $cache, NULL);
42 $this->res->setCacheCode('resTest');
43 CRM_Core_Resources::singleton($this->res);
44
45 // Templates injected into regions should normally be file names, but for unit-testing it's handy to use "string:" notation
46 require_once 'CRM/Core/Smarty/resources/String.php';
481a74f4 47 civicrm_smarty_register_string_resource();
42a40a1c 48
49 $this->originalRequest = $_REQUEST;
50 $this->originalGet = $_GET;
51 }
52
53 /**
54 * Restore globals so this test doesn't interfere with others.
55 */
56 public function tearDown() {
57 $_REQUEST = $this->originalRequest;
58 $_GET = $this->originalGet;
6a488035
TO
59 }
60
00be9182 61 public function testAddScriptFile() {
6a488035
TO
62 $this->res
63 ->addScriptFile('com.example.ext', 'foo%20bar.js', 0, 'testAddScriptFile')
39b959db
SL
64 // extra
65 ->addScriptFile('com.example.ext', 'foo%20bar.js', 0, 'testAddScriptFile')
6c6e6187 66 ->addScriptFile('civicrm', 'foo%20bar.js', 0, 'testAddScriptFile');
6a488035
TO
67
68 $smarty = CRM_Core_Smarty::singleton();
69 $actual = $smarty->fetch('string:{crmRegion name=testAddScriptFile}{/crmRegion}');
39b959db
SL
70 // stable ordering: alphabetical by (snippet.weight,snippet.name)
71 $expected = ""
6a488035 72 . "<script type=\"text/javascript\" src=\"http://core-app/foo%20bar.js?r=resTest\">\n</script>\n"
6c6e6187 73 . "<script type=\"text/javascript\" src=\"http://ext-dir/com.example.ext/foo%20bar.js?r=resTest\">\n</script>\n";
6a488035
TO
74 $this->assertEquals($expected, $actual);
75 }
76
77 /**
78 * When adding a script file, any ts() expressions should be translated and added to the 'strings'
79 *
80 * FIXME: This can't work because the tests run in English and CRM_Core_Resources optimizes
81 * away the English data from $settings['strings']
92915c55
TO
82 * public function testAddScriptFile_strings() {
83 * file_put_contents($this->mapper->keyToBasePath('com.example.ext') . '/hello.js', 'alert(ts("Hello world"));');
84 * $this->res->addScriptFile('com.example.ext', 'hello.js', 0, 'testAddScriptFile_strings');
85 * $settings = $this->res->getSettings();
86 * $expected = array('Hello world');
87 * $this->assertEquals($expected, $settings['strings']);
88 * }
6c6e6187 89 */
6a488035 90
a1a2a83d
TO
91 /**
92 * Ensure that adding a script URL creates expected markup.
93 */
00be9182 94 public function testAddScriptURL() {
6a488035
TO
95 $this->res
96 ->addScriptUrl('/whiz/foo%20bar.js', 0, 'testAddScriptURL')
39b959db
SL
97 // extra
98 ->addScriptUrl('/whiz/foo%20bar.js', 0, 'testAddScriptURL')
6c6e6187 99 ->addScriptUrl('/whizbang/foo%20bar.js', 0, 'testAddScriptURL');
6a488035
TO
100
101 $smarty = CRM_Core_Smarty::singleton();
102 $actual = $smarty->fetch('string:{crmRegion name=testAddScriptURL}{/crmRegion}');
39b959db
SL
103 // stable ordering: alphabetical by (snippet.weight,snippet.name)
104 $expected = ""
6a488035 105 . "<script type=\"text/javascript\" src=\"/whiz/foo%20bar.js\">\n</script>\n"
6c6e6187 106 . "<script type=\"text/javascript\" src=\"/whizbang/foo%20bar.js\">\n</script>\n";
6a488035
TO
107 $this->assertEquals($expected, $actual);
108 }
109
00be9182 110 public function testAddScript() {
6a488035
TO
111 $this->res
112 ->addScript('alert("hi");', 0, 'testAddScript')
6c6e6187 113 ->addScript('alert("there");', 0, 'testAddScript');
6a488035
TO
114
115 $smarty = CRM_Core_Smarty::singleton();
116 $actual = $smarty->fetch('string:{crmRegion name=testAddScript}{/crmRegion}');
117 $expected = ""
118 . "<script type=\"text/javascript\">\nalert(\"hi\");\n</script>\n"
6c6e6187 119 . "<script type=\"text/javascript\">\nalert(\"there\");\n</script>\n";
6a488035
TO
120 $this->assertEquals($expected, $actual);
121 }
122
00be9182 123 public function testAddVars() {
73bcd446 124 $this->res
9099cab3
CW
125 ->addVars('food', ['fruit' => ['mine' => 'apple', 'ours' => 'banana']])
126 ->addVars('food', ['fruit' => ['mine' => 'new apple', 'yours' => 'orange']]);
73bcd446 127 $this->assertTreeEquals(
9099cab3
CW
128 [
129 'vars' => [
130 'food' => [
131 'fruit' => [
92915c55
TO
132 'yours' => 'orange',
133 'mine' => 'new apple',
af9b09df 134 'ours' => 'banana',
9099cab3
CW
135 ],
136 ],
137 ],
138 ],
73bcd446
CW
139 $this->res->getSettings()
140 );
141 }
142
00be9182 143 public function testAddSetting() {
6a488035 144 $this->res
9099cab3
CW
145 ->addSetting(['fruit' => ['mine' => 'apple']])
146 ->addSetting(['fruit' => ['yours' => 'orange']]);
6a488035 147 $this->assertTreeEquals(
9099cab3 148 ['fruit' => ['yours' => 'orange', 'mine' => 'apple']],
6a488035
TO
149 $this->res->getSettings()
150 );
151 $actual = $this->res->renderSetting();
9099cab3 152 $expected = json_encode(['fruit' => ['yours' => 'orange', 'mine' => 'apple']]);
6521a306 153 $this->assertTrue(strpos($actual, $expected) !== FALSE);
6a488035
TO
154 }
155
898951c6
TO
156 public function testAddSettingHook() {
157 $test = $this;
158 Civi::dispatcher()->addListener('hook_civicrm_alterResourceSettings', function($event) use ($test) {
159 $test->assertEquals('apple', $event->data['fruit']['mine']);
160 $event->data['fruit']['mine'] = 'banana';
161 });
9099cab3 162 $this->res->addSetting(['fruit' => ['mine' => 'apple']]);
898951c6 163 $settings = $this->res->getSettings();
9099cab3 164 $this->assertTreeEquals(['fruit' => ['mine' => 'banana']], $settings);
898951c6
TO
165 }
166
00be9182 167 public function testAddSettingFactory() {
92915c55 168 $this->res->addSettingsFactory(function () {
9099cab3 169 return ['fruit' => ['yours' => 'orange']];
6a488035 170 });
92915c55 171 $this->res->addSettingsFactory(function () {
9099cab3 172 return ['fruit' => ['mine' => 'apple']];
6a488035
TO
173 });
174
175 $actual = $this->res->getSettings();
9099cab3 176 $expected = ['fruit' => ['yours' => 'orange', 'mine' => 'apple']];
6a488035
TO
177 $this->assertTreeEquals($expected, $actual);
178 }
179
00be9182 180 public function testAddSettingAndSettingFactory() {
9099cab3 181 $this->res->addSetting(['fruit' => ['mine' => 'apple']]);
6a488035 182
9099cab3 183 $muckableValue = ['fruit' => ['yours' => 'orange', 'theirs' => 'apricot']];
92915c55 184 $this->res->addSettingsFactory(function () use (&$muckableValue) {
6a488035
TO
185 return $muckableValue;
186 });
187 $actual = $this->res->getSettings();
9099cab3 188 $expected = ['fruit' => ['mine' => 'apple', 'yours' => 'orange', 'theirs' => 'apricot']];
6a488035
TO
189 $this->assertTreeEquals($expected, $actual);
190
191 // note: the setting is not fixed based on what the factory returns when registered; it's based
192 // on what the factory returns when getSettings is called
9099cab3 193 $muckableValue = ['fruit' => ['yours' => 'banana']];
6a488035 194 $actual = $this->res->getSettings();
9099cab3 195 $expected = ['fruit' => ['mine' => 'apple', 'yours' => 'banana']];
6a488035
TO
196 $this->assertTreeEquals($expected, $actual);
197 }
198
00be9182 199 public function testCrmJS() {
6a488035
TO
200 $smarty = CRM_Core_Smarty::singleton();
201
202 $actual = $smarty->fetch('string:{crmScript ext=com.example.ext file=foo%20bar.js region=testCrmJS}');
203 $this->assertEquals('', $actual);
204
205 $actual = $smarty->fetch('string:{crmScript url=/whiz/foo%20bar.js region=testCrmJS weight=1}');
206 $this->assertEquals('', $actual);
207
208 $actual = $smarty->fetch('string:{crmRegion name=testCrmJS}{/crmRegion}');
39b959db
SL
209 // stable ordering: alphabetical by (snippet.weight,snippet.name)
210 $expected = ""
6a488035 211 . "<script type=\"text/javascript\" src=\"http://ext-dir/com.example.ext/foo%20bar.js?r=resTest\">\n</script>\n"
6c6e6187 212 . "<script type=\"text/javascript\" src=\"/whiz/foo%20bar.js\">\n</script>\n";
6a488035
TO
213 $this->assertEquals($expected, $actual);
214 }
215
00be9182 216 public function testAddStyleFile() {
6a488035
TO
217 $this->res
218 ->addStyleFile('com.example.ext', 'foo%20bar.css', 0, 'testAddStyleFile')
39b959db
SL
219 // extra
220 ->addStyleFile('com.example.ext', 'foo%20bar.css', 0, 'testAddStyleFile')
6c6e6187 221 ->addStyleFile('civicrm', 'foo%20bar.css', 0, 'testAddStyleFile');
6a488035
TO
222
223 $smarty = CRM_Core_Smarty::singleton();
224 $actual = $smarty->fetch('string:{crmRegion name=testAddStyleFile}{/crmRegion}');
39b959db
SL
225 // stable ordering: alphabetical by (snippet.weight,snippet.name)
226 $expected = ""
6a488035 227 . "<link href=\"http://core-app/foo%20bar.css?r=resTest\" rel=\"stylesheet\" type=\"text/css\"/>\n"
6c6e6187 228 . "<link href=\"http://ext-dir/com.example.ext/foo%20bar.css?r=resTest\" rel=\"stylesheet\" type=\"text/css\"/>\n";
6a488035
TO
229 $this->assertEquals($expected, $actual);
230 }
231
00be9182 232 public function testAddStyleURL() {
6a488035
TO
233 $this->res
234 ->addStyleUrl('/whiz/foo%20bar.css', 0, 'testAddStyleURL')
39b959db
SL
235 // extra
236 ->addStyleUrl('/whiz/foo%20bar.css', 0, 'testAddStyleURL')
6c6e6187 237 ->addStyleUrl('/whizbang/foo%20bar.css', 0, 'testAddStyleURL');
6a488035
TO
238
239 $smarty = CRM_Core_Smarty::singleton();
240 $actual = $smarty->fetch('string:{crmRegion name=testAddStyleURL}{/crmRegion}');
39b959db
SL
241 // stable ordering: alphabetical by (snippet.weight,snippet.name)
242 $expected = ""
6a488035 243 . "<link href=\"/whiz/foo%20bar.css\" rel=\"stylesheet\" type=\"text/css\"/>\n"
6c6e6187 244 . "<link href=\"/whizbang/foo%20bar.css\" rel=\"stylesheet\" type=\"text/css\"/>\n";
6a488035
TO
245 $this->assertEquals($expected, $actual);
246 }
247
00be9182 248 public function testAddStyle() {
6a488035
TO
249 $this->res
250 ->addStyle('body { background: black; }', 0, 'testAddStyle')
6c6e6187 251 ->addStyle('body { text-color: black; }', 0, 'testAddStyle');
6a488035
TO
252
253 $smarty = CRM_Core_Smarty::singleton();
254 $actual = $smarty->fetch('string:{crmRegion name=testAddStyle}{/crmRegion}');
255 $expected = ""
256 . "<style type=\"text/css\">\nbody { background: black; }\n</style>\n"
6c6e6187 257 . "<style type=\"text/css\">\nbody { text-color: black; }\n</style>\n";
6a488035
TO
258 $this->assertEquals($expected, $actual);
259 }
260
00be9182 261 public function testCrmCSS() {
6a488035
TO
262 $smarty = CRM_Core_Smarty::singleton();
263
264 $actual = $smarty->fetch('string:{crmStyle ext=com.example.ext file=foo%20bar.css region=testCrmCSS}');
265 $this->assertEquals('', $actual);
266
267 $actual = $smarty->fetch('string:{crmStyle url=/whiz/foo%20bar.css region=testCrmCSS weight=1}');
268 $this->assertEquals('', $actual);
269
270 $actual = $smarty->fetch('string:{crmRegion name=testCrmCSS}{/crmRegion}');
39b959db
SL
271 // stable ordering: alphabetical by (snippet.weight,snippet.name)
272 $expected = ""
6a488035 273 . "<link href=\"http://ext-dir/com.example.ext/foo%20bar.css?r=resTest\" rel=\"stylesheet\" type=\"text/css\"/>\n"
6c6e6187 274 . "<link href=\"/whiz/foo%20bar.css\" rel=\"stylesheet\" type=\"text/css\"/>\n";
6a488035
TO
275 $this->assertEquals($expected, $actual);
276 }
277
00be9182 278 public function testGetURL() {
6a488035
TO
279 $this->assertEquals(
280 'http://core-app/dir/file%20name.txt',
281 $this->res->getURL('civicrm', 'dir/file%20name.txt')
282 );
283 $this->assertEquals(
284 'http://ext-dir/com.example.ext/dir/file%20name.txt',
285 $this->res->getURL('com.example.ext', 'dir/file%20name.txt')
286 );
287 $this->assertEquals(
288 'http://core-app/',
289 $this->res->getURL('civicrm')
290 );
291 $this->assertEquals(
292 'http://ext-dir/com.example.ext/',
293 $this->res->getURL('com.example.ext')
294 );
295 }
296
00be9182 297 public function testCrmResURL() {
6a488035
TO
298 $smarty = CRM_Core_Smarty::singleton();
299
300 $actual = $smarty->fetch('string:{crmResURL ext=com.example.ext file=foo%20bar.png}');
301 $this->assertEquals('http://ext-dir/com.example.ext/foo%20bar.png', $actual);
302
303 $actual = $smarty->fetch('string:{crmResURL ext=com.example.ext file=foo%20bar.png addCacheCode=1}');
304 $this->assertEquals('http://ext-dir/com.example.ext/foo%20bar.png?r=resTest', $actual);
305
306 $actual = $smarty->fetch('string:{crmResURL ext=com.example.ext}');
307 $this->assertEquals('http://ext-dir/com.example.ext/', $actual);
308 }
309
16cd1eca
TO
310 public function testGlob() {
311 $this->assertEquals(
9099cab3 312 ['info.xml'],
16cd1eca
TO
313 $this->res->glob('com.example.ext', 'info.xml')
314 );
315 $this->assertEquals(
9099cab3 316 ['js/example.js'],
16cd1eca
TO
317 $this->res->glob('com.example.ext', 'js/*.js')
318 );
319 $this->assertEquals(
9099cab3
CW
320 ['js/example.js'],
321 $this->res->glob('com.example.ext', ['js/*.js'])
16cd1eca
TO
322 );
323 }
324
42a40a1c 325 /**
326 * @dataProvider ajaxModeData
327 */
328 public function testIsAjaxMode($query, $result) {
329 $_REQUEST = $_GET = $query;
330 $this->assertEquals($result, CRM_Core_Resources::isAjaxMode());
331 }
332
333 public function ajaxModeData() {
9099cab3
CW
334 return [
335 [['q' => 'civicrm/ajax/foo'], TRUE],
336 [['q' => 'civicrm/angularprofiles/template'], TRUE],
337 [['q' => 'civicrm/asset/builder'], TRUE],
338 [['q' => 'civicrm/test/page'], FALSE],
339 [['q' => 'civicrm/test/page', 'snippet' => 'json'], TRUE],
340 [['q' => 'civicrm/test/page', 'snippet' => 'foo'], FALSE],
341 ];
42a40a1c 342 }
343
6a488035
TO
344 /**
345 * @param CRM_Utils_Cache_Interface $cache
72b3a70c 346 * @param string $cacheKey
2a6da8d7 347 *
72b3a70c
CW
348 * @return array
349 * [string $basedir, CRM_Extension_Container_Interface, CRM_Extension_Mapper]
6a488035 350 */
00be9182 351 public function _createMapper(CRM_Utils_Cache_Interface $cache = NULL, $cacheKey = NULL) {
6a488035
TO
352 $basedir = rtrim($this->createTempDir('ext-'), '/');
353 mkdir("$basedir/com.example.ext");
16cd1eca 354 mkdir("$basedir/com.example.ext/js");
6a488035 355 file_put_contents("$basedir/com.example.ext/info.xml", "<extension key='com.example.ext' type='report'><file>oddball</file></extension>");
16cd1eca 356 file_put_contents("$basedir/com.example.ext/js/example.js", "alert('Boo!');");
6a488035
TO
357 // not needed for now // file_put_contents("$basedir/weird/bar/oddball.php", "<?php\n");
358 $c = new CRM_Extension_Container_Basic($basedir, 'http://ext-dir', $cache, $cacheKey);
359 $mapper = new CRM_Extension_Mapper($c, NULL, NULL, '/pathto/civicrm', 'http://core-app');
9099cab3 360 return [$basedir, $c, $mapper];
6a488035
TO
361 }
362
6f12c6eb 363 /**
364 * @param string $url
365 * @param string $expected
366 *
367 * @dataProvider urlForCacheCodeProvider
368 */
369 public function testAddingCacheCode($url, $expected) {
370 $resources = CRM_Core_Resources::singleton();
595caa48 371 $resources->setCacheCode($this->cacheBusterString);
6f12c6eb 372 $this->assertEquals($expected, $resources->addCacheCode($url));
373 }
374
375 /**
376 * @return array
377 */
378 public function urlForCacheCodeProvider() {
9099cab3
CW
379 return [
380 [
6f12c6eb 381 'http://www.civicrm.org',
382 'http://www.civicrm.org?r=' . $this->cacheBusterString,
9099cab3
CW
383 ],
384 [
6f12c6eb 385 'www.civicrm.org/custom.css?foo=bar',
386 'www.civicrm.org/custom.css?foo=bar&r=' . $this->cacheBusterString,
9099cab3
CW
387 ],
388 [
6f12c6eb 389 'civicrm.org/custom.css?car=blue&foo=bar',
390 'civicrm.org/custom.css?car=blue&foo=bar&r=' . $this->cacheBusterString,
9099cab3
CW
391 ],
392 ];
6f12c6eb 393 }
394
adcd4bf7
CW
395 /**
396 * return array
397 */
398 public function urlsToCheckIfFullyFormed() {
399 return [
400 ['civicrm/test/page', FALSE],
401 ['#', FALSE],
402 ['', FALSE],
403 ['/civicrm/test/page', TRUE],
404 ['http://test.com/civicrm/test/page', TRUE],
405 ['https://test.com/civicrm/test/page', TRUE],
406 ];
407 }
408
409 /**
410 * @param string $url
411 * @param string $expected
412 *
413 * @dataProvider urlsToCheckIfFullyFormed
414 */
415 public function testIsFullyFormedUrl($url, $expected) {
416 $this->assertEquals($expected, CRM_Core_Resources::isFullyFormedUrl($url));
417 }
418
7022db93
PN
419 /**
420 * Test for hook_civicrm_entityRefFilters().
421 *
422 */
423 public function testEntityRefFiltersHook() {
424 CRM_Utils_Hook_UnitTests::singleton()->setHook('civicrm_entityRefFilters', [$this, 'entityRefFilters']);
425 $data = CRM_Core_Resources::getEntityRefMetadata();
426 $this->assertEquals(count($data['links']['Contact']), 4);
427 $this->assertEquals(!empty($data['links']['Contact']['new_staff']), TRUE);
428 }
429
430 /**
431 * @param array $filters
432 * @param array $links
433 */
434 public function entityRefFilters(&$filters, &$links) {
435 $links['Contact']['new_staff'] = [
436 'label' => ts('New Staff'),
437 'url' => '/civicrm/profile/create&reset=1&context=dialog&gid=5',
438 'type' => 'Individual',
439 'icon' => 'fa-user',
440 ];
441 }
442
6a488035 443}