Completed
Push — master ( c8fdc7...6f28ac )
by Daniel
30s
created

RequirementsTest::assertFileIncluded()   B

Complexity

Conditions 4
Paths 4

Size

Total Lines 25
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 18
nc 4
nop 3
dl 0
loc 25
rs 8.5806
c 0
b 0
f 0
1
<?php
2
3
use SilverStripe\Core\Injector\Injector;
4
use SilverStripe\Dev\SapphireTest;
5
use SilverStripe\Control\Director;
6
use SilverStripe\View\Requirements;
7
use SilverStripe\View\ArrayData;
8
9
10
11
12
/**
13
 * @package framework
14
 * @subpackage tests
15
 *
16
 * @todo Test that order of combine_files() is correct
17
 * @todo Figure out how to clear the modified state of Requirements class - might affect other tests.
18
 */
19
class RequirementsTest extends SapphireTest {
20
21
	static $html_template = '<html><head></head><body></body></html>';
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $html_template.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
22
23
	public function setUp() {
24
		parent::setUp();
25
		AssetStoreTest_SpyStore::activate('RequirementsTest'); // Set backend root to /RequirementsTest
26
	}
27
28
	public function tearDown() {
29
		AssetStoreTest_SpyStore::reset();
30
		parent::tearDown();
31
	}
32
33
	public function testExternalUrls() {
34
		/** @var Requirements_Backend $backend */
35
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
36
		$backend->setCombinedFilesEnabled(true);
37
38
		$backend->javascript('http://www.mydomain.com/test.js');
39
		$backend->javascript('https://www.mysecuredomain.com/test.js');
40
		$backend->javascript('//scheme-relative.example.com/test.js');
41
		$backend->css('http://www.mydomain.com/test.css');
42
		$backend->css('https://www.mysecuredomain.com/test.css');
43
		$backend->css('//scheme-relative.example.com/test.css');
44
45
		$html = $backend->includeInHTML(self::$html_template);
46
47
		$this->assertTrue(
48
			(strpos($html, 'http://www.mydomain.com/test.js') !== false),
49
			'Load external javascript URL'
50
		);
51
		$this->assertTrue(
52
			(strpos($html, 'https://www.mysecuredomain.com/test.js') !== false),
53
			'Load external secure javascript URL'
54
		);
55
		$this->assertTrue(
56
			(strpos($html, '//scheme-relative.example.com/test.js') !== false),
57
			'Load external scheme-relative javascript URL'
58
		);
59
		$this->assertTrue(
60
			(strpos($html, 'http://www.mydomain.com/test.css') !== false),
61
			'Load external CSS URL'
62
		);
63
		$this->assertTrue(
64
			(strpos($html, 'https://www.mysecuredomain.com/test.css') !== false),
65
			'Load external secure CSS URL'
66
		);
67
		$this->assertTrue(
68
			(strpos($html, '//scheme-relative.example.com/test.css') !== false),
69
			'Load scheme-relative CSS URL'
70
		);
71
	}
72
73
	/**
74
	 * Setup new backend
75
	 *
76
	 * @param Requirements_Backend $backend
77
	 */
78
	protected function setupRequirements($backend) {
79
		// Flush requirements
80
		$backend->clear();
81
		$backend->clearCombinedFiles();
82
		$backend->setCombinedFilesFolder('_combinedfiles');
83
		$backend->setMinifyCombinedJSFiles(false);
84
		Requirements::flush();
85
	}
86
87
	/**
88
	 * Setup combined and non-combined js with the backend
89
	 *
90
	 * @param Requirements_Backend $backend
91
	 */
92
	protected function setupCombinedRequirements($backend) {
93
		$basePath = $this->getCurrentRelativePath();
94
		$this->setupRequirements($backend);
95
96
		// require files normally (e.g. called from a FormField instance)
97
		$backend->javascript($basePath . '/RequirementsTest_a.js');
98
		$backend->javascript($basePath . '/RequirementsTest_b.js');
99
		$backend->javascript($basePath . '/RequirementsTest_c.js');
100
101
		// require two of those files as combined includes
102
		$backend->combineFiles(
103
			'RequirementsTest_bc.js',
104
			array(
105
				$basePath . '/RequirementsTest_b.js',
106
				$basePath . '/RequirementsTest_c.js'
107
			)
108
		);
109
	}
110
111
	/**
112
	 * Setup combined files with the backend
113
	 *
114
	 * @param Requirements_Backend $backend
115
	 */
116
	protected function setupCombinedNonrequiredRequirements($backend) {
117
		$basePath = $this->getCurrentRelativePath();
118
		$this->setupRequirements($backend);
119
120
		// require files as combined includes
121
		$backend->combineFiles(
122
			'RequirementsTest_bc.js',
123
			array(
124
				$basePath . '/RequirementsTest_b.js',
125
				$basePath . '/RequirementsTest_c.js'
126
			)
127
		);
128
	}
129
130
	protected function setupCombinedRequirementsJavascriptAsyncDefer($backend, $async, $defer) {
131
        $basePath = $this->getCurrentRelativePath();
132
        $this->setupRequirements($backend);
133
134
        // require files normally (e.g. called from a FormField instance)
135
        $backend->javascript($basePath . '/RequirementsTest_a.js');
136
        $backend->javascript($basePath . '/RequirementsTest_b.js');
137
        $backend->javascript($basePath . '/RequirementsTest_c.js');
138
139
        // require two of those files as combined includes
140
        $backend->combineFiles(
141
            'RequirementsTest_bc.js',
142
            array(
143
                $basePath . '/RequirementsTest_b.js',
144
                $basePath . '/RequirementsTest_c.js'
145
            ),
146
            array(
147
                'async' => $async,
148
                'defer' => $defer,
149
            )
150
        );
151
    }
152
153
    public function testCustomType() {
154
		/** @var Requirements_Backend $backend */
155
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
156
		$basePath = $this->getCurrentRelativePath();
157
		$this->setupRequirements($backend);
158
159
		// require files normally (e.g. called from a FormField instance)
160
		$backend->javascript($basePath . '/RequirementsTest_a.js', [
161
			'type' => 'application/json'
162
		]);
163
		$backend->javascript($basePath . '/RequirementsTest_b.js');
164
		$result = $backend->includeInHTML(self::$html_template);
165
		$this->assertRegexp(
166
			'#<script type="application/json" src=".*/tests/forms/RequirementsTest_a.js#',
167
			$result
168
		);
169
		$this->assertRegexp(
170
			'#<script type="application/javascript" src=".*/tests/forms/RequirementsTest_b.js#',
171
			$result
172
		);
173
	}
174
175
	public function testCombinedJavascript() {
176
		/** @var Requirements_Backend $backend */
177
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
178
		$this->setupCombinedRequirements($backend);
179
180
		$combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
181
		$combinedFilePath = AssetStoreTest_SpyStore::base_path() . $combinedFileName;
182
183
		$html = $backend->includeInHTML(self::$html_template);
184
185
		/* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
186
		$this->assertRegExp(
187
			'/src=".*' . preg_quote($combinedFileName, '/') . '/',
188
			$html,
189
			'combined javascript file is included in html header'
190
		);
191
192
		/* COMBINED JAVASCRIPT FILE EXISTS */
193
		$this->assertTrue(
194
			file_exists($combinedFilePath),
195
			'combined javascript file exists'
196
		);
197
198
		/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
199
		$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
200
			'combined javascript has correct content');
201
		$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
202
			'combined javascript has correct content');
203
204
		/* COMBINED FILES ARE NOT INCLUDED TWICE */
205
		$this->assertNotRegExp(
206
			'/src=".*\/RequirementsTest_b\.js/',
207
			$html,
208
			'combined files are not included twice'
209
		);
210
		$this->assertNotRegExp(
211
			'/src=".*\/RequirementsTest_c\.js/',
212
			$html,
213
			'combined files are not included twice'
214
		);
215
216
		/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
217
		$this->assertRegExp(
218
			'/src=".*\/RequirementsTest_a\.js/',
219
			$html,
220
			'normal requirements are still included'
221
		);
222
223
		// Then do it again, this time not requiring the files beforehand
224
		unlink($combinedFilePath);
225
		/** @var Requirements_Backend $backend */
226
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
227
		$this->setupCombinedNonrequiredRequirements($backend);
228
		$html = $backend->includeInHTML(self::$html_template);
229
230
		/* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
231
		$this->assertRegExp(
232
			'/src=".*' . preg_quote($combinedFileName, '/') . '/',
233
			$html,
234
			'combined javascript file is included in html header'
235
		);
236
237
		/* COMBINED JAVASCRIPT FILE EXISTS */
238
		$this->assertTrue(
239
			file_exists($combinedFilePath),
240
			'combined javascript file exists'
241
		);
242
243
		/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
244
		$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
245
			'combined javascript has correct content');
246
		$this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
247
			'combined javascript has correct content');
248
249
		/* COMBINED FILES ARE NOT INCLUDED TWICE */
250
		$this->assertNotRegExp(
251
			'/src=".*\/RequirementsTest_b\.js/',
252
			$html,
253
			'combined files are not included twice'
254
		);
255
		$this->assertNotRegExp(
256
			'/src=".*\/RequirementsTest_c\.js/',
257
			$html,
258
			'combined files are not included twice'
259
		);
260
	}
261
262
	public function testCombinedJavascriptAsyncDefer() {
263
	    /** @var Requirements_Backend $backend */
264
	    $backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
265
266
	    $this->setupCombinedRequirementsJavascriptAsyncDefer($backend, true, false);
267
268
	    $combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
269
	    $combinedFilePath = AssetStoreTest_SpyStore::base_path() . $combinedFileName;
270
271
	    $html = $backend->includeInHTML(false, self::$html_template);
272
273
	    /* ASYNC IS INCLUDED IN SCRIPT TAG */
274
	    $this->assertRegExp(
275
	        '/src=".*' . preg_quote($combinedFileName, '/') . '" async/',
276
	        $html,
277
	        'async is included in script tag'
278
	    );
279
280
	    /* DEFER IS NOT INCLUDED IN SCRIPT TAG */
281
	    $this->assertNotContains('defer', $html, 'defer is not included');
282
283
	    /* COMBINED JAVASCRIPT FILE EXISTS */
284
	    clearstatcache(); // needed to get accurate file_exists() results
285
	    $this->assertFileExists($combinedFilePath,
286
	        'combined javascript file exists');
287
288
	    /* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
289
	    $this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
290
	        'combined javascript has correct content');
291
	    $this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
292
	        'combined javascript has correct content');
293
294
	    /* COMBINED FILES ARE NOT INCLUDED TWICE */
295
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_b\.js/', $html,
296
	        'combined files are not included twice');
297
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html,
298
	        'combined files are not included twice');
299
300
	    /* NORMAL REQUIREMENTS ARE STILL INCLUDED */
301
	    $this->assertRegExp('/src=".*\/RequirementsTest_a\.js/', $html,
302
	        'normal requirements are still included');
303
304
	    /* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
305
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" async/', $html,
306
	        'normal requirements don\'t have async');
307
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/', $html,
308
	        'normal requirements don\'t have defer');
309
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/', $html,
310
	        'normal requirements don\'t have async/defer');
311
312
	    // setup again for testing defer
313
	    unlink($combinedFilePath);
314
	    /** @var Requirements_Backend $backend */
315
	    $backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
316
317
	    $this->setupCombinedRequirementsJavascriptAsyncDefer($backend, false, true);
318
319
	    $html = $backend->includeInHTML(self::$html_template);
320
321
	    /* DEFER IS INCLUDED IN SCRIPT TAG */
322
	    $this->assertRegExp(
323
	        '/src=".*' . preg_quote($combinedFileName, '/') . '" defer/',
324
	        $html,
325
	        'defer is included in script tag'
326
	    );
327
328
	    /* ASYNC IS NOT INCLUDED IN SCRIPT TAG */
329
	    $this->assertNotContains('async', $html, 'async is not included');
330
331
	    /* COMBINED JAVASCRIPT FILE EXISTS */
332
	    clearstatcache(); // needed to get accurate file_exists() results
333
	    $this->assertFileExists($combinedFilePath,
334
	        'combined javascript file exists');
335
336
	    /* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
337
	    $this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
338
	        'combined javascript has correct content');
339
	    $this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
340
	        'combined javascript has correct content');
341
342
	    /* COMBINED FILES ARE NOT INCLUDED TWICE */
343
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_b\.js/', $html,
344
	        'combined files are not included twice');
345
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html,
346
	        'combined files are not included twice');
347
348
	    /* NORMAL REQUIREMENTS ARE STILL INCLUDED */
349
	    $this->assertRegExp('/src=".*\/RequirementsTest_a\.js/', $html,
350
	        'normal requirements are still included');
351
352
	    /* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
353
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" async/', $html,
354
	        'normal requirements don\'t have async');
355
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/', $html,
356
	        'normal requirements don\'t have defer');
357
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/', $html,
358
	        'normal requirements don\'t have async/defer');
359
360
	    // setup again for testing async and defer
361
	    unlink($combinedFilePath);
362
	    /** @var Requirements_Backend $backend */
363
	    $backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
364
365
	    $this->setupCombinedRequirementsJavascriptAsyncDefer($backend, true, true);
366
367
	    $html = $backend->includeInHTML(self::$html_template);
368
369
	    /* ASYNC/DEFER IS INCLUDED IN SCRIPT TAG */
370
	    $this->assertRegExp(
371
	        '/src=".*' . preg_quote($combinedFileName, '/') . '" async defer/',
372
	        $html,
373
	        'async and defer are included in script tag'
374
	    );
375
376
	    /* COMBINED JAVASCRIPT FILE EXISTS */
377
	    clearstatcache(); // needed to get accurate file_exists() results
378
	    $this->assertFileExists($combinedFilePath,
379
	        'combined javascript file exists');
380
381
	    /* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
382
	    $this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
383
	        'combined javascript has correct content');
384
	    $this->assertTrue((strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
385
	        'combined javascript has correct content');
386
387
	    /* COMBINED FILES ARE NOT INCLUDED TWICE */
388
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_b\.js/', $html,
389
	        'combined files are not included twice');
390
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html,
391
	        'combined files are not included twice');
392
393
	    /* NORMAL REQUIREMENTS ARE STILL INCLUDED */
394
	    $this->assertRegExp('/src=".*\/RequirementsTest_a\.js/', $html,
395
	        'normal requirements are still included');
396
397
	    /* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
398
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" async/', $html,
399
	        'normal requirements don\'t have async');
400
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/', $html,
401
	        'normal requirements don\'t have defer');
402
	    $this->assertNotRegExp('/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/', $html,
403
	        'normal requirements don\'t have async/defer');
404
405
	    unlink($combinedFilePath);
406
	}
407
408
	public function testCombinedCss() {
409
		$basePath = $this->getCurrentRelativePath();
410
		/** @var Requirements_Backend $backend */
411
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
412
		$this->setupRequirements($backend);
413
414
		$backend->combineFiles(
415
			'print.css',
416
			array(
417
				$basePath . '/RequirementsTest_print_a.css',
418
				$basePath . '/RequirementsTest_print_b.css'
419
			),
420
			array(
421
			    'media' => 'print'
422
			)
423
		);
424
425
		$html = $backend->includeInHTML(self::$html_template);
426
427
		$this->assertRegExp(
428
			'/href=".*\/print\-94e723d\.css/',
429
			$html,
430
			'Print stylesheets have been combined.'
431
		);
432
		$this->assertRegExp(
433
			'/media="print/',
434
			$html,
435
			'Combined print stylesheet retains the media parameter'
436
		);
437
438
		// Test that combining a file multiple times doesn't trigger an error
439
		/** @var Requirements_Backend $backend */
440
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
441
		$this->setupRequirements($backend);
442
		$backend->combineFiles(
443
			'style.css',
444
			array(
445
				$basePath . '/RequirementsTest_b.css',
446
				$basePath . '/RequirementsTest_c.css'
447
			)
448
		);
449
		$backend->combineFiles(
450
			'style.css',
451
			array(
452
				$basePath . '/RequirementsTest_b.css',
453
				$basePath . '/RequirementsTest_c.css'
454
			)
455
		);
456
457
		$html = $backend->includeInHTML(self::$html_template);
458
		$this->assertRegExp(
459
			'/href=".*\/style\-bcd90f5\.css/',
460
			$html,
461
			'Stylesheets have been combined.'
462
		);
463
	}
464
465
	public function testBlockedCombinedJavascript() {
466
		$basePath = $this->getCurrentRelativePath();
467
		/** @var Requirements_Backend $backend */
468
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
469
		$this->setupCombinedRequirements($backend);
470
		$combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
471
		$combinedFilePath = AssetStoreTest_SpyStore::base_path() . $combinedFileName;
472
473
		/* BLOCKED COMBINED FILES ARE NOT INCLUDED */
474
		$backend->block('RequirementsTest_bc.js');
475
476
		clearstatcache(); // needed to get accurate file_exists() results
477
		$html = $backend->includeInHTML(self::$html_template);
478
		$this->assertFileNotExists($combinedFilePath);
479
		$this->assertNotRegExp(
480
			'/src=".*\/RequirementsTest_bc\.js/',
481
			$html,
482
			'blocked combined files are not included'
483
		);
484
		$backend->unblock('RequirementsTest_bc.js');
485
486
		/* BLOCKED UNCOMBINED FILES ARE NOT INCLUDED */
487
		$this->setupCombinedRequirements($backend);
488
		$backend->block($basePath .'/RequirementsTest_b.js');
489
		$combinedFileName2 = '/_combinedfiles/RequirementsTest_bc-3748f67.js'; // SHA1 without file b included
490
		$combinedFilePath2 = AssetStoreTest_SpyStore::base_path() . $combinedFileName2;
491
		clearstatcache(); // needed to get accurate file_exists() results
492
		$html = $backend->includeInHTML(self::$html_template);
493
		$this->assertFileExists($combinedFilePath2);
494
		$this->assertTrue(
495
			strpos(file_get_contents($combinedFilePath2), "alert('b')") === false,
496
			'blocked uncombined files are not included'
497
		);
498
		$backend->unblock($basePath . '/RequirementsTest_b.js');
499
500
		/* A SINGLE FILE CAN'T BE INCLUDED IN TWO COMBINED FILES */
501
		$this->setupCombinedRequirements($backend);
502
		clearstatcache(); // needed to get accurate file_exists() results
503
504
		// Exception generated from including invalid file
505
		$this->setExpectedException(
506
			'InvalidArgumentException',
507
			sprintf(
508
				"Requirements_Backend::combine_files(): Already included file(s) %s in combined file '%s'",
509
				$basePath . '/RequirementsTest_c.js',
510
				'RequirementsTest_bc.js'
511
			)
512
		);
513
		$backend->combineFiles(
514
			'RequirementsTest_ac.js',
515
			array(
516
				$basePath . '/RequirementsTest_a.js',
517
				$basePath . '/RequirementsTest_c.js'
518
			)
519
		);
520
	}
521
522
	public function testArgsInUrls() {
523
		$basePath = $this->getCurrentRelativePath();
524
525
		/** @var Requirements_Backend $backend */
526
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
527
		$this->setupRequirements($backend);
528
529
		$backend->javascript($basePath . '/RequirementsTest_a.js?test=1&test=2&test=3');
530
		$backend->css($basePath . '/RequirementsTest_a.css?test=1&test=2&test=3');
531
		$html = $backend->includeInHTML(self::$html_template);
532
533
		/* Javascript has correct path */
534
		$this->assertRegExp(
535
			'/src=".*\/RequirementsTest_a\.js\?m=\d\d+&amp;test=1&amp;test=2&amp;test=3/',
536
			$html,
537
			'javascript has correct path'
538
		);
539
540
		/* CSS has correct path */
541
		$this->assertRegExp(
542
			'/href=".*\/RequirementsTest_a\.css\?m=\d\d+&amp;test=1&amp;test=2&amp;test=3/',
543
			$html,
544
			'css has correct path'
545
		);
546
	}
547
548
	public function testRequirementsBackend() {
549
		$basePath = $this->getCurrentRelativePath();
550
551
		/** @var Requirements_Backend $backend */
552
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
553
		$this->setupRequirements($backend);
554
		$backend->javascript($basePath . '/a.js');
555
556
		$this->assertTrue(count($backend->getJavascript()) == 1,
557
			"There should be only 1 file included in required javascript.");
558
		$this->assertArrayHasKey($basePath . '/a.js', $backend->getJavascript(),
559
			"a.js should be included in required javascript.");
560
561
		$backend->javascript($basePath . '/b.js');
562
		$this->assertTrue(count($backend->getJavascript()) == 2,
563
			"There should be 2 files included in required javascript.");
564
565
		$backend->block($basePath . '/a.js');
566
		$this->assertTrue(count($backend->getJavascript()) == 1,
567
			"There should be only 1 file included in required javascript.");
568
		$this->assertArrayNotHasKey($basePath . '/a.js', $backend->getJavascript(),
569
			"a.js should not be included in required javascript after it has been blocked.");
570
		$this->assertArrayHasKey($basePath . '/b.js', $backend->getJavascript(),
571
			"b.js should be included in required javascript.");
572
573
		$backend->css($basePath . '/a.css');
574
		$this->assertTrue(count($backend->getCSS()) == 1,
575
			"There should be only 1 file included in required css.");
576
		$this->assertArrayHasKey($basePath . '/a.css', $backend->getCSS(),
577
			"a.css should be in required css.");
578
579
		$backend->block($basePath . '/a.css');
580
		$this->assertTrue(count($backend->getCSS()) == 0,
581
			"There should be nothing in required css after file has been blocked.");
582
	}
583
584
	public function testConditionalTemplateRequire() {
585
		$testPath = ltrim(preg_replace('#^' . BASE_PATH . '#', '', dirname(__DIR__)), '/');
586
587
		/** @var Requirements_Backend $backend */
588
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
589
		$this->setupRequirements($backend);
590
		$holder = Requirements::backend();
591
		Requirements::set_backend($backend);
592
		$data = new ArrayData(array(
593
			'FailTest' => true,
594
		));
595
		$data->renderWith('RequirementsTest_Conditionals');
596
		$this->assertFileIncluded($backend, 'css', $testPath .'/css/forms/RequirementsTest_a.css');
597
		$this->assertFileIncluded($backend, 'js',
598
			array(
599
				$testPath .'/javascript/forms/RequirementsTest_b.js',
600
				$testPath .'/javascript/forms/RequirementsTest_c.js'
601
			)
602
		);
603
		$this->assertFileNotIncluded($backend, 'js', $testPath .'/javascript/forms/RequirementsTest_a.js');
604
		$this->assertFileNotIncluded($backend, 'css',
605
			array(
606
				$testPath .'/css/forms/RequirementsTest_b.css',
607
				$testPath .'/css/forms/RequirementsTest_c.css'
608
			)
609
		);
610
		$backend->clear();
611
		$data = new ArrayData(array(
612
			'FailTest' => false,
613
		));
614
		$data->renderWith('RequirementsTest_Conditionals');
615
		$this->assertFileNotIncluded($backend, 'css', $testPath .'/css/forms/RequirementsTest_a.css');
616
		$this->assertFileNotIncluded($backend, 'js',
617
			array(
618
				$testPath .'/javascript/forms/RequirementsTest_b.js',
619
				$testPath .'/javascript/forms/RequirementsTest_c.js'
620
			)
621
		);
622
		$this->assertFileIncluded($backend, 'js', $testPath .'/javascript/forms/RequirementsTest_a.js');
623
		$this->assertFileIncluded($backend, 'css',
624
			array(
625
				$testPath .'/css/forms/RequirementsTest_b.css',
626
				$testPath .'/css/forms/RequirementsTest_c.css'
627
			)
628
		);
629
		Requirements::set_backend($holder);
630
	}
631
632
	public function testJsWriteToBody() {
633
		/** @var Requirements_Backend $backend */
634
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
635
		$this->setupRequirements($backend);
636
		$backend->javascript('http://www.mydomain.com/test.js');
637
638
		// Test matching with HTML5 <header> tags as well
639
		$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
640
641
		$backend->setWriteJavascriptToBody(false);
642
		$html = $backend->includeInHTML($template);
643
		$this->assertContains('<head><script', $html);
644
645
		$backend->setWriteJavascriptToBody(true);
646
		$html = $backend->includeInHTML($template);
647
		$this->assertNotContains('<head><script', $html);
648
		$this->assertContains('</script></body>', $html);
649
	}
650
651
	public function testIncludedJsIsNotCommentedOut() {
652
		$template = '<html><head></head><body><!--<script>alert("commented out");</script>--></body></html>';
653
		/** @var Requirements_Backend $backend */
654
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
655
		$this->setupRequirements($backend);
656
		$backend->javascript($this->getCurrentRelativePath() . '/RequirementsTest_a.js');
657
		$html = $backend->includeInHTML($template);
658
		//wiping out commented-out html
659
		$html = preg_replace('/<!--(.*)-->/Uis', '', $html);
660
		$this->assertContains("RequirementsTest_a.js", $html);
661
	}
662
663
	public function testCommentedOutScriptTagIsIgnored() {
664
		$template = '<html><head></head><body><!--<script>alert("commented out");</script>-->'
665
			. '<h1>more content</h1></body></html>';
666
		/** @var Requirements_Backend $backend */
667
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
668
		$this->setupRequirements($backend);
669
		$backend->setSuffixRequirements(false);
670
		$src = $this->getCurrentRelativePath() . '/RequirementsTest_a.js';
671
		$urlSrc = ControllerTest_ContainerController::join_links(Director::baseURL(), $src);
672
		$backend->javascript($src);
673
		$html = $backend->includeInHTML($template);
674
		$this->assertEquals('<html><head></head><body><!--<script>alert("commented out");</script>-->'
675
			. '<h1>more content</h1><script type="application/javascript" src="' . $urlSrc . '"></script></body></html>', $html);
676
	}
677
678
	public function testForceJsToBottom() {
679
		/** @var Requirements_Backend $backend */
680
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
681
		$this->setupRequirements($backend);
682
		$backend->javascript('http://www.mydomain.com/test.js');
683
		$backend->customScript(
684
<<<'EOS'
685
var globalvar = {
686
	pattern: '\\$custom\\1'
687
};
688
EOS
689
		);
690
691
		// Test matching with HTML5 <header> tags as well
692
		$template = '<html><head></head><body><header>My header</header><p>Body<script></script></p></body></html>';
693
694
		// The expected outputs
695
		$expectedScripts = "<script type=\"application/javascript\" src=\"http://www.mydomain.com/test.js\">"
696
			. "</script><script type=\"application/javascript\">//<![CDATA[\n"
697
			. "var globalvar = {\n\tpattern: '\\\\\$custom\\\\1'\n};\n"
698
			. "//]]></script>";
699
		$JsInHead = "<html><head>$expectedScripts</head><body><header>My header</header><p>Body<script></script></p></body></html>";
700
		$JsInBody = "<html><head></head><body><header>My header</header><p>Body$expectedScripts<script></script></p></body></html>";
701
		$JsAtEnd  = "<html><head></head><body><header>My header</header><p>Body<script></script></p>$expectedScripts</body></html>";
702
703
704
		// Test if the script is before the head tag, not before the body.
705
		// Expected: $JsInHead
706
		$backend->setWriteJavascriptToBody(false);
707
		$backend->setForceJSToBottom(false);
708
		$html = $backend->includeInHTML($template);
709
		$this->assertNotEquals($JsInBody, $html);
710
		$this->assertNotEquals($JsAtEnd, $html);
711
		$this->assertEquals($JsInHead, $html);
712
713
		// Test if the script is before the first <script> tag, not before the body.
714
		// Expected: $JsInBody
715
		$backend->setWriteJavascriptToBody(true);
716
		$backend->setForceJSToBottom(false);
717
		$html = $backend->includeInHTML($template);
718
		$this->assertNotEquals($JsAtEnd, $html);
719
		$this->assertEquals($JsInBody, $html);
720
721
		// Test if the script is placed just before the closing bodytag, with write-to-body false.
722
		// Expected: $JsAtEnd
723
		$backend->setWriteJavascriptToBody(false);
724
		$backend->setForceJSToBottom(true);
725
		$html = $backend->includeInHTML($template);
726
		$this->assertNotEquals($JsInHead, $html);
727
		$this->assertNotEquals($JsInBody, $html);
728
		$this->assertEquals($JsAtEnd, $html);
729
730
		// Test if the script is placed just before the closing bodytag, with write-to-body true.
731
		// Expected: $JsAtEnd
732
		$backend->setWriteJavascriptToBody(true);
733
		$backend->setForceJSToBottom(true);
734
		$html = $backend->includeInHTML($template);
735
		$this->assertNotEquals($JsInHead, $html);
736
		$this->assertNotEquals($JsInBody, $html);
737
		$this->assertEquals($JsAtEnd, $html);
738
	}
739
740
	public function testSuffix() {
741
		$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
742
		$basePath = $this->getCurrentRelativePath();
743
744
		/** @var Requirements_Backend $backend */
745
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
746
		$this->setupRequirements($backend);
747
748
		$backend->javascript($basePath .'/RequirementsTest_a.js');
749
		$backend->javascript($basePath .'/RequirementsTest_b.js?foo=bar&bla=blubb');
750
		$backend->css($basePath .'/RequirementsTest_a.css');
751
		$backend->css($basePath .'/RequirementsTest_b.css?foo=bar&bla=blubb');
752
753
		$backend->setSuffixRequirements(true);
754
		$html = $backend->includeInHTML($template);
755
		$this->assertRegexp('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
756
		$this->assertRegexp('/RequirementsTest_b\.js\?m=[\d]*&amp;foo=bar&amp;bla=blubb"/', $html);
757
		$this->assertRegexp('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
758
		$this->assertRegexp('/RequirementsTest_b\.css\?m=[\d]*&amp;foo=bar&amp;bla=blubb"/', $html);
759
760
		$backend->setSuffixRequirements(false);
761
		$html = $backend->includeInHTML($template);
762
		$this->assertNotContains('RequirementsTest_a.js=', $html);
763
		$this->assertNotRegexp('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
764
		$this->assertNotRegexp('/RequirementsTest_b\.js\?m=[\d]*&amp;foo=bar&amp;bla=blubb"/', $html);
765
		$this->assertNotRegexp('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
766
		$this->assertNotRegexp('/RequirementsTest_b\.css\?m=[\d]*&amp;foo=bar&amp;bla=blubb"/', $html);
767
	}
768
769
	/**
770
	 * Tests that provided files work
771
	 */
772
	public function testProvidedFiles() {
773
		/** @var Requirements_Backend $backend */
774
		$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
775
		$basePath = $this->getCurrentRelativePath();
776
777
		// Test that provided files block subsequent files
778
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
779
		$this->setupRequirements($backend);
780
		$backend->javascript($basePath . '/RequirementsTest_a.js');
781
		$backend->javascript($basePath . '/RequirementsTest_b.js', [
782
			'provides' => [
783
				$basePath . '/RequirementsTest_a.js',
784
				$basePath . '/RequirementsTest_c.js'
785
			]
786
		]);
787
		$backend->javascript($basePath . '/RequirementsTest_c.js');
788
		// Note that _a.js isn't considered provided because it was included
789
		// before it was marked as provided
790
		$this->assertEquals([
791
			$basePath . '/RequirementsTest_c.js' => $basePath . '/RequirementsTest_c.js'
792
		], $backend->getProvidedScripts());
793
		$html = $backend->includeInHTML($template);
794
		$this->assertRegExp('/src=".*\/RequirementsTest_a\.js/', $html);
795
		$this->assertRegExp('/src=".*\/RequirementsTest_b\.js/', $html);
796
		$this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html);
797
798
		// Test that provided files block subsequent combined files
799
		$backend = Injector::inst()->create('SilverStripe\\View\\Requirements_Backend');
800
		$this->setupRequirements($backend);
801
		$backend->combineFiles('combined_a.js', [$basePath . '/RequirementsTest_a.js']);
802
		$backend->javascript($basePath . '/RequirementsTest_b.js', [
803
			'provides' => [
804
				$basePath . '/RequirementsTest_a.js',
805
				$basePath . '/RequirementsTest_c.js'
806
			]
807
		]);
808
		$backend->combineFiles('combined_c.js', [$basePath . '/RequirementsTest_c.js']);
809
		$this->assertEquals([
810
			$basePath . '/RequirementsTest_c.js' => $basePath . '/RequirementsTest_c.js'
811
		], $backend->getProvidedScripts());
812
		$html = $backend->includeInHTML($template);
813
		$this->assertRegExp('/src=".*\/combined_a/', $html);
814
		$this->assertRegExp('/src=".*\/RequirementsTest_b\.js/', $html);
815
		$this->assertNotRegExp('/src=".*\/combined_c/', $html);
816
		$this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html);
817
	}
818
819
	/**
820
	 * Verify that the given backend includes the given files
821
	 *
822
	 * @param Requirements_Backend $backend
823
	 * @param string $type js or css
824
	 * @param array|string $files Files or list of files to check
825
	 */
826
	public function assertFileIncluded($backend, $type, $files) {
827
		$includedFiles = $this->getBackendFiles($backend, $type);
828
829
		if(is_array($files)) {
830
			$failedMatches = array();
831
			foreach ($files as $file) {
832
				if(!array_key_exists($file, $includedFiles)) {
833
					$failedMatches[] = $file;
834
				}
835
			}
836
			$this->assertTrue(
837
				(count($failedMatches) == 0),
838
				"Failed asserting the $type files '"
839
				. implode("', '", $failedMatches)
840
				. "' have exact matches in the required elements:\n'"
841
				. implode("'\n'", array_keys($includedFiles)) . "'"
842
			);
843
		} else {
844
			$this->assertTrue(
845
				(array_key_exists($files, $includedFiles)),
846
				"Failed asserting the $type file '$files' has an exact match in the required elements:\n'"
847
				. implode("'\n'", array_keys($includedFiles)) . "'"
848
			);
849
		}
850
	}
851
852
	public function assertFileNotIncluded($backend, $type, $files) {
853
		$includedFiles = $this->getBackendFiles($backend, $type);
854
		if(is_array($files)) {
855
			$failedMatches = array();
856
			foreach ($files as $file) {
857
				if(array_key_exists($file, $includedFiles)) {
858
					$failedMatches[] = $file;
859
				}
860
			}
861
			$this->assertTrue(
862
				(count($failedMatches) == 0),
863
				"Failed asserting the $type files '"
864
				. implode("', '", $failedMatches)
865
				. "' do not have exact matches in the required elements:\n'"
866
				. implode("'\n'", array_keys($includedFiles)) . "'"
867
			);
868
		} else {
869
			$this->assertFalse(
870
				(array_key_exists($files, $includedFiles)),
871
				"Failed asserting the $type file '$files' does not have an exact match in the required elements:"
872
						. "\n'" . implode("'\n'", array_keys($includedFiles)) . "'"
873
			);
874
		}
875
	}
876
877
878
	/**
879
	 * Get files of the given type from the backend
880
	 *
881
	 * @param Requirements_Backend $backend
882
	 * @param string $type js or css
883
	 * @return array
884
	 */
885
	protected function getBackendFiles($backend, $type) {
886
		$type = strtolower($type);
887
		switch (strtolower($type)) {
888
			case 'css':
889
				return $backend->getCSS();
890
			case 'js':
891
			case 'javascript':
892
			case 'script':
893
				return $backend->getJavascript();
894
		}
895
		return array();
896
	}
897
}
898