Completed
Push — master ( f6cae8...f7ba78 )
by Daniel
23s
created

RequirementsTest   A

Complexity

Total Complexity 33

Size/Duplication

Total Lines 844
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 8

Importance

Changes 1
Bugs 0 Features 1
Metric Value
c 1
b 0
f 1
dl 0
loc 844
rs 9.3999
wmc 33
lcom 1
cbo 8

23 Methods

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