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