Completed
Push — 4.0 ( 1c72d6...11b15f )
by Damian
32s
created

RequirementsTest::testArgsInUrls()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 24
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 14
nc 1
nop 0
dl 0
loc 24
rs 8.9713
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\View\Tests;
4
5
use InvalidArgumentException;
6
use SilverStripe\Core\Injector\Injector;
7
use SilverStripe\Dev\SapphireTest;
8
use SilverStripe\View\Requirements;
9
use SilverStripe\View\ArrayData;
10
use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
11
use SilverStripe\View\Requirements_Backend;
12
use SilverStripe\Core\Manifest\ResourceURLGenerator;
13
use SilverStripe\Control\SimpleResourceURLGenerator;
14
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
 * @skipUpgrade
19
 */
20
class RequirementsTest extends SapphireTest
21
{
22
23
    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...
24
25
    protected function setUp()
26
    {
27
        parent::setUp();
28
        TestAssetStore::activate('RequirementsTest'); // Set backend root to /RequirementsTest
29
    }
30
31
    protected function tearDown()
32
    {
33
        TestAssetStore::reset();
34
        parent::tearDown();
35
    }
36
37
    public function testExternalUrls()
38
    {
39
        /** @var Requirements_Backend $backend */
40
        $backend = Injector::inst()->create(Requirements_Backend::class);
41
        $backend->setCombinedFilesEnabled(true);
42
43
        $backend->javascript('http://www.mydomain.com/test.js');
44
        $backend->javascript('https://www.mysecuredomain.com/test.js');
45
        $backend->javascript('//scheme-relative.example.com/test.js');
46
        $backend->javascript('http://www.mydomain.com:3000/test.js');
47
        $backend->css('http://www.mydomain.com/test.css');
48
        $backend->css('https://www.mysecuredomain.com/test.css');
49
        $backend->css('//scheme-relative.example.com/test.css');
50
        $backend->css('http://www.mydomain.com:3000/test.css');
51
52
        $html = $backend->includeInHTML(self::$html_template);
53
54
        $this->assertContains('http://www.mydomain.com/test.js', $html, 'Load external javascript URL');
55
        $this->assertContains('https://www.mysecuredomain.com/test.js', $html, 'Load external secure javascript URL');
56
        $this->assertContains('//scheme-relative.example.com/test.js', $html, 'Load external scheme-relative JS');
57
        $this->assertContains('http://www.mydomain.com:3000/test.js', $html, 'Load external with port');
58
        $this->assertContains('http://www.mydomain.com/test.css', $html, 'Load external CSS URL');
59
        $this->assertContains('https://www.mysecuredomain.com/test.css', $html, 'Load external secure CSS URL');
60
        $this->assertContains('//scheme-relative.example.com/test.css', $html, 'Load scheme-relative CSS URL');
61
        $this->assertContains('http://www.mydomain.com:3000/test.css', $html, 'Load external with port');
62
    }
63
64
    /**
65
     * Setup new backend
66
     *
67
     * @param Requirements_Backend $backend
68
     */
69
    protected function setupRequirements($backend)
70
    {
71
        // Flush requirements
72
        $backend->clear();
73
        $backend->clearCombinedFiles();
74
        $backend->setCombinedFilesFolder('_combinedfiles');
75
        $backend->setMinifyCombinedFiles(false);
76
        $backend->setCombinedFilesEnabled(true);
77
        Requirements::flush();
78
    }
79
80
    /**
81
     * Setup combined and non-combined js with the backend
82
     *
83
     * @param Requirements_Backend $backend
84
     */
85
    protected function setupCombinedRequirements($backend)
86
    {
87
        $basePath = $this->getThemeRoot();
88
        $this->setupRequirements($backend);
89
90
        // require files normally (e.g. called from a FormField instance)
91
        $backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
92
        $backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
93
        $backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
94
95
        // require two of those files as combined includes
96
        $backend->combineFiles(
97
            'RequirementsTest_bc.js',
98
            array(
99
                $basePath . '/javascript/RequirementsTest_b.js',
100
                $basePath . '/javascript/RequirementsTest_c.js'
101
            )
102
        );
103
    }
104
105
    /**
106
     * Setup combined files with the backend
107
     *
108
     * @param Requirements_Backend $backend
109
     */
110
    protected function setupCombinedNonrequiredRequirements($backend)
111
    {
112
        $basePath = $this->getThemeRoot();
113
        $this->setupRequirements($backend);
114
115
        // require files as combined includes
116
        $backend->combineFiles(
117
            'RequirementsTest_bc.js',
118
            array(
119
                $basePath . '/javascript/RequirementsTest_b.js',
120
                $basePath . '/javascript/RequirementsTest_c.js'
121
            )
122
        );
123
    }
124
125
    /**
126
     * @param Requirements_Backend $backend
127
     * @param bool                 $async
128
     * @param bool                 $defer
129
     */
130
    protected function setupCombinedRequirementsJavascriptAsyncDefer($backend, $async, $defer)
131
    {
132
        $basePath = $this->getThemeRoot();
133
        $this->setupRequirements($backend);
134
135
        // require files normally (e.g. called from a FormField instance)
136
        $backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
137
        $backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
138
        $backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
139
140
        // require two of those files as combined includes
141
        $backend->combineFiles(
142
            'RequirementsTest_bc.js',
143
            array(
144
                $basePath . '/javascript/RequirementsTest_b.js',
145
                $basePath . '/javascript/RequirementsTest_c.js'
146
            ),
147
            array(
148
                'async' => $async,
149
                'defer' => $defer,
150
            )
151
        );
152
    }
153
154
    public function testCustomType()
155
    {
156
        /** @var Requirements_Backend $backend */
157
        $backend = Injector::inst()->create(Requirements_Backend::class);
158
        $basePath = $this->getThemeRoot();
159
        $this->setupRequirements($backend);
160
161
        // require files normally (e.g. called from a FormField instance)
162
        $backend->javascript(
163
            $basePath . '/javascript/RequirementsTest_a.js',
164
            [
165
            'type' => 'application/json'
166
            ]
167
        );
168
        $backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
169
        $result = $backend->includeInHTML(self::$html_template);
170
        $this->assertRegExp(
171
            '#<script type="application/json" src=".*/javascript/RequirementsTest_a.js#',
172
            $result
173
        );
174
        $this->assertRegExp(
175
            '#<script type="application/javascript" src=".*/javascript/RequirementsTest_b.js#',
176
            $result
177
        );
178
    }
179
180
    public function testCombinedJavascript()
181
    {
182
        /** @var Requirements_Backend $backend */
183
        $backend = Injector::inst()->create(Requirements_Backend::class);
184
        $backend->setCombinedFilesEnabled(true);
185
        $this->setupCombinedRequirements($backend);
186
187
        $combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
188
        $combinedFilePath = TestAssetStore::base_path() . $combinedFileName;
189
190
        $html = $backend->includeInHTML(self::$html_template);
191
192
        /* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
193
        $this->assertRegExp(
194
            '/src=".*' . preg_quote($combinedFileName, '/') . '/',
195
            $html,
196
            'combined javascript file is included in html header'
197
        );
198
199
        /* COMBINED JAVASCRIPT FILE EXISTS */
200
        $this->assertTrue(
201
            file_exists($combinedFilePath),
202
            'combined javascript file exists'
203
        );
204
205
        /* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
206
        $this->assertContains(
207
            "alert('b')",
208
            file_get_contents($combinedFilePath),
209
            'combined javascript has correct content'
210
        );
211
        $this->assertContains(
212
            "alert('c')",
213
            file_get_contents($combinedFilePath),
214
            'combined javascript has correct content'
215
        );
216
217
        /* COMBINED FILES ARE NOT INCLUDED TWICE */
218
        $this->assertNotRegExp(
219
            '/src=".*\/RequirementsTest_b\.js/',
220
            $html,
221
            'combined files are not included twice'
222
        );
223
        $this->assertNotRegExp(
224
            '/src=".*\/RequirementsTest_c\.js/',
225
            $html,
226
            'combined files are not included twice'
227
        );
228
229
        /* NORMAL REQUIREMENTS ARE STILL INCLUDED */
230
        $this->assertRegExp(
231
            '/src=".*\/RequirementsTest_a\.js/',
232
            $html,
233
            'normal requirements are still included'
234
        );
235
236
        // Then do it again, this time not requiring the files beforehand
237
        unlink($combinedFilePath);
238
        /** @var Requirements_Backend $backend */
239
        $backend = Injector::inst()->create(Requirements_Backend::class);
240
        $this->setupCombinedNonrequiredRequirements($backend);
241
        $html = $backend->includeInHTML(self::$html_template);
242
243
        /* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
244
        $this->assertRegExp(
245
            '/src=".*' . preg_quote($combinedFileName, '/') . '/',
246
            $html,
247
            'combined javascript file is included in html header'
248
        );
249
250
        /* COMBINED JAVASCRIPT FILE EXISTS */
251
        $this->assertTrue(
252
            file_exists($combinedFilePath),
253
            'combined javascript file exists'
254
        );
255
256
        /* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
257
        $this->assertContains(
258
            "alert('b')",
259
            file_get_contents($combinedFilePath),
260
            'combined javascript has correct content'
261
        );
262
        $this->assertContains(
263
            "alert('c')",
264
            file_get_contents($combinedFilePath),
265
            'combined javascript has correct content'
266
        );
267
268
        /* COMBINED FILES ARE NOT INCLUDED TWICE */
269
        $this->assertNotRegExp(
270
            '/src=".*\/RequirementsTest_b\.js/',
271
            $html,
272
            'combined files are not included twice'
273
        );
274
        $this->assertNotRegExp(
275
            '/src=".*\/RequirementsTest_c\.js/',
276
            $html,
277
            'combined files are not included twice'
278
        );
279
    }
280
281
    public function testCombinedJavascriptAsyncDefer()
282
    {
283
        /** @var Requirements_Backend $backend */
284
        $backend = Injector::inst()->create(Requirements_Backend::class);
285
286
        $this->setupCombinedRequirementsJavascriptAsyncDefer($backend, true, false);
287
288
        $combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
289
        $combinedFilePath = TestAssetStore::base_path() . $combinedFileName;
290
291
        $html = $backend->includeInHTML(false, self::$html_template);
292
293
        /* ASYNC IS INCLUDED IN SCRIPT TAG */
294
        $this->assertRegExp(
295
            '/src=".*' . preg_quote($combinedFileName, '/') . '" async/',
296
            $html,
297
            'async is included in script tag'
298
        );
299
300
        /* DEFER IS NOT INCLUDED IN SCRIPT TAG */
301
        $this->assertNotContains('defer', $html, 'defer is not included');
302
303
        /* COMBINED JAVASCRIPT FILE EXISTS */
304
        clearstatcache(); // needed to get accurate file_exists() results
305
        $this->assertFileExists(
306
            $combinedFilePath,
307
            'combined javascript file exists'
308
        );
309
310
        /* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
311
        $this->assertContains(
312
            "alert('b')",
313
            file_get_contents($combinedFilePath),
314
            'combined javascript has correct content'
315
        );
316
        $this->assertContains(
317
            "alert('c')",
318
            file_get_contents($combinedFilePath),
319
            'combined javascript has correct content'
320
        );
321
322
        /* COMBINED FILES ARE NOT INCLUDED TWICE */
323
        $this->assertNotRegExp(
324
            '/src=".*\/RequirementsTest_b\.js/',
325
            $html,
326
            'combined files are not included twice'
327
        );
328
        $this->assertNotRegExp(
329
            '/src=".*\/RequirementsTest_c\.js/',
330
            $html,
331
            'combined files are not included twice'
332
        );
333
334
        /* NORMAL REQUIREMENTS ARE STILL INCLUDED */
335
        $this->assertRegExp(
336
            '/src=".*\/RequirementsTest_a\.js/',
337
            $html,
338
            'normal requirements are still included'
339
        );
340
341
        /* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
342
        $this->assertNotRegExp(
343
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
344
            $html,
345
            'normal requirements don\'t have async'
346
        );
347
        $this->assertNotRegExp(
348
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
349
            $html,
350
            'normal requirements don\'t have defer'
351
        );
352
        $this->assertNotRegExp(
353
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
354
            $html,
355
            'normal requirements don\'t have async/defer'
356
        );
357
358
        // setup again for testing defer
359
        unlink($combinedFilePath);
360
        /** @var Requirements_Backend $backend */
361
        $backend = Injector::inst()->create(Requirements_Backend::class);
362
363
        $this->setupCombinedRequirementsJavascriptAsyncDefer($backend, false, true);
364
365
        $html = $backend->includeInHTML(self::$html_template);
366
367
        /* DEFER IS INCLUDED IN SCRIPT TAG */
368
        $this->assertRegExp(
369
            '/src=".*' . preg_quote($combinedFileName, '/') . '" defer/',
370
            $html,
371
            'defer is included in script tag'
372
        );
373
374
        /* ASYNC IS NOT INCLUDED IN SCRIPT TAG */
375
        $this->assertNotContains('async', $html, 'async is not included');
376
377
        /* COMBINED JAVASCRIPT FILE EXISTS */
378
        clearstatcache(); // needed to get accurate file_exists() results
379
        $this->assertFileExists(
380
            $combinedFilePath,
381
            'combined javascript file exists'
382
        );
383
384
        /* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
385
        $this->assertContains(
386
            "alert('b')",
387
            file_get_contents($combinedFilePath),
388
            'combined javascript has correct content'
389
        );
390
        $this->assertContains(
391
            "alert('c')",
392
            file_get_contents($combinedFilePath),
393
            'combined javascript has correct content'
394
        );
395
396
        /* COMBINED FILES ARE NOT INCLUDED TWICE */
397
        $this->assertNotRegExp(
398
            '/src=".*\/RequirementsTest_b\.js/',
399
            $html,
400
            'combined files are not included twice'
401
        );
402
        $this->assertNotRegExp(
403
            '/src=".*\/RequirementsTest_c\.js/',
404
            $html,
405
            'combined files are not included twice'
406
        );
407
408
        /* NORMAL REQUIREMENTS ARE STILL INCLUDED */
409
        $this->assertRegExp(
410
            '/src=".*\/RequirementsTest_a\.js/',
411
            $html,
412
            'normal requirements are still included'
413
        );
414
415
        /* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
416
        $this->assertNotRegExp(
417
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
418
            $html,
419
            'normal requirements don\'t have async'
420
        );
421
        $this->assertNotRegExp(
422
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
423
            $html,
424
            'normal requirements don\'t have defer'
425
        );
426
        $this->assertNotRegExp(
427
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
428
            $html,
429
            'normal requirements don\'t have async/defer'
430
        );
431
432
        // setup again for testing async and defer
433
        unlink($combinedFilePath);
434
        /** @var Requirements_Backend $backend */
435
        $backend = Injector::inst()->create(Requirements_Backend::class);
436
437
        $this->setupCombinedRequirementsJavascriptAsyncDefer($backend, true, true);
438
439
        $html = $backend->includeInHTML(self::$html_template);
440
441
        /* ASYNC/DEFER IS INCLUDED IN SCRIPT TAG */
442
        $this->assertRegExp(
443
            '/src=".*' . preg_quote($combinedFileName, '/') . '" async="async" defer="defer"/',
444
            $html,
445
            'async and defer are included in script tag'
446
        );
447
448
        /* COMBINED JAVASCRIPT FILE EXISTS */
449
        clearstatcache(); // needed to get accurate file_exists() results
450
        $this->assertFileExists(
451
            $combinedFilePath,
452
            'combined javascript file exists'
453
        );
454
455
        /* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
456
        $this->assertContains(
457
            "alert('b')",
458
            file_get_contents($combinedFilePath),
459
            'combined javascript has correct content'
460
        );
461
        $this->assertContains(
462
            "alert('c')",
463
            file_get_contents($combinedFilePath),
464
            'combined javascript has correct content'
465
        );
466
467
        /* COMBINED FILES ARE NOT INCLUDED TWICE */
468
        $this->assertNotRegExp(
469
            '/src=".*\/RequirementsTest_b\.js/',
470
            $html,
471
            'combined files are not included twice'
472
        );
473
        $this->assertNotRegExp(
474
            '/src=".*\/RequirementsTest_c\.js/',
475
            $html,
476
            'combined files are not included twice'
477
        );
478
479
        /* NORMAL REQUIREMENTS ARE STILL INCLUDED */
480
        $this->assertRegExp(
481
            '/src=".*\/RequirementsTest_a\.js/',
482
            $html,
483
            'normal requirements are still included'
484
        );
485
486
        /* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
487
        $this->assertNotRegExp(
488
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
489
            $html,
490
            'normal requirements don\'t have async'
491
        );
492
        $this->assertNotRegExp(
493
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
494
            $html,
495
            'normal requirements don\'t have defer'
496
        );
497
        $this->assertNotRegExp(
498
            '/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
499
            $html,
500
            'normal requirements don\'t have async/defer'
501
        );
502
503
        unlink($combinedFilePath);
504
    }
505
506
    public function testCombinedCss()
507
    {
508
        $basePath = $this->getThemeRoot();
509
        /** @var Requirements_Backend $backend */
510
        $backend = Injector::inst()->create(Requirements_Backend::class);
511
        $this->setupRequirements($backend);
512
513
        $backend->combineFiles(
514
            'print.css',
515
            array(
516
                $basePath . '/css/RequirementsTest_print_a.css',
517
                $basePath . '/css/RequirementsTest_print_b.css'
518
            ),
519
            array(
520
                'media' => 'print'
521
            )
522
        );
523
524
        $html = $backend->includeInHTML(self::$html_template);
525
526
        $this->assertRegExp(
527
            '/href=".*\/print\-94e723d\.css/',
528
            $html,
529
            'Print stylesheets have been combined.'
530
        );
531
        $this->assertRegExp(
532
            '/media="print/',
533
            $html,
534
            'Combined print stylesheet retains the media parameter'
535
        );
536
537
        // Test that combining a file multiple times doesn't trigger an error
538
        /** @var Requirements_Backend $backend */
539
        $backend = Injector::inst()->create(Requirements_Backend::class);
540
        $this->setupRequirements($backend);
541
        $backend->combineFiles(
542
            'style.css',
543
            array(
544
                $basePath . '/css/RequirementsTest_b.css',
545
                $basePath . '/css/RequirementsTest_c.css'
546
            )
547
        );
548
        $backend->combineFiles(
549
            'style.css',
550
            array(
551
                $basePath . '/css/RequirementsTest_b.css',
552
                $basePath . '/css/RequirementsTest_c.css'
553
            )
554
        );
555
556
        $html = $backend->includeInHTML(self::$html_template);
557
        $this->assertRegExp(
558
            '/href=".*\/style\-bcd90f5\.css/',
559
            $html,
560
            'Stylesheets have been combined.'
561
        );
562
    }
563
564
    public function testBlockedCombinedJavascript()
565
    {
566
        $basePath = $this->getThemeRoot();
567
        /** @var Requirements_Backend $backend */
568
        $backend = Injector::inst()->create(Requirements_Backend::class);
569
        $this->setupCombinedRequirements($backend);
570
        $combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
571
        $combinedFilePath = TestAssetStore::base_path() . $combinedFileName;
572
573
        /* BLOCKED COMBINED FILES ARE NOT INCLUDED */
574
        $backend->block('RequirementsTest_bc.js');
575
576
        clearstatcache(); // needed to get accurate file_exists() results
577
        $html = $backend->includeInHTML(self::$html_template);
578
        $this->assertFileNotExists($combinedFilePath);
579
        $this->assertNotRegExp(
580
            '/src=".*\/RequirementsTest_bc\.js/',
581
            $html,
582
            'blocked combined files are not included'
583
        );
584
        $backend->unblock('RequirementsTest_bc.js');
585
586
        /* BLOCKED UNCOMBINED FILES ARE NOT INCLUDED */
587
        $this->setupCombinedRequirements($backend);
588
        $backend->block($basePath .'/javascript/RequirementsTest_b.js');
589
        $combinedFileName2 = '/_combinedfiles/RequirementsTest_bc-3748f67.js'; // SHA1 without file b included
590
        $combinedFilePath2 = TestAssetStore::base_path() . $combinedFileName2;
591
        clearstatcache(); // needed to get accurate file_exists() results
592
        $backend->includeInHTML(self::$html_template);
593
        $this->assertFileExists($combinedFilePath2);
594
        $this->assertNotContains(
595
            "alert('b')",
596
            file_get_contents($combinedFilePath2),
597
            'blocked uncombined files are not included'
598
        );
599
        $backend->unblock($basePath . '/javascript/RequirementsTest_b.js');
600
601
        /* A SINGLE FILE CAN'T BE INCLUDED IN TWO COMBINED FILES */
602
        $this->setupCombinedRequirements($backend);
603
        clearstatcache(); // needed to get accurate file_exists() results
604
605
        // Exception generated from including invalid file
606
        $this->expectException(InvalidArgumentException::class);
607
        $this->expectExceptionMessage(sprintf(
608
            "Requirements_Backend::combine_files(): Already included file(s) %s in combined file '%s'",
609
            $basePath . '/javascript/RequirementsTest_c.js',
610
            'RequirementsTest_bc.js'
611
        ));
612
        $backend->combineFiles(
613
            'RequirementsTest_ac.js',
614
            array(
615
                $basePath . '/javascript/RequirementsTest_a.js',
616
                $basePath . '/javascript/RequirementsTest_c.js'
617
            )
618
        );
619
    }
620
621
    public function testArgsInUrls()
622
    {
623
        $basePath = $this->getThemeRoot();
624
625
        /** @var Requirements_Backend $backend */
626
        $backend = Injector::inst()->create(Requirements_Backend::class);
627
        $this->setupRequirements($backend);
628
629
        $backend->javascript($basePath . '/javascript/RequirementsTest_a.js?test=1&test=2&test=3');
630
        $backend->css($basePath . '/css/RequirementsTest_a.css?test=1&test=2&test=3');
631
        $html = $backend->includeInHTML(self::$html_template);
632
633
        /* Javascript has correct path */
634
        $this->assertRegExp(
635
            '/src=".*\/RequirementsTest_a\.js\?test=1&amp;test=2&amp;test=3&amp;m=\d\d+/',
636
            $html,
637
            'javascript has correct path'
638
        );
639
640
        /* CSS has correct path */
641
        $this->assertRegExp(
642
            '/href=".*\/RequirementsTest_a\.css\?test=1&amp;test=2&amp;test=3&amp;m=\d\d+/',
643
            $html,
644
            'css has correct path'
645
        );
646
    }
647
648
    public function testRequirementsBackend()
649
    {
650
        $basePath = $this->getThemeRoot();
651
652
        /** @var Requirements_Backend $backend */
653
        $backend = Injector::inst()->create(Requirements_Backend::class);
654
        $this->setupRequirements($backend);
655
        $backend->javascript($basePath . '/a.js');
656
657
        $this->assertCount(
658
            1,
659
            $backend->getJavascript(),
660
            "There should be only 1 file included in required javascript."
661
        );
662
        $this->assertArrayHasKey(
663
            $basePath . '/a.js',
664
            $backend->getJavascript(),
665
            "a.js should be included in required javascript."
666
        );
667
668
        $backend->javascript($basePath . '/b.js');
669
        $this->assertCount(
670
            2,
671
            $backend->getJavascript(),
672
            "There should be 2 files included in required javascript."
673
        );
674
675
        $backend->block($basePath . '/a.js');
676
        $this->assertCount(
677
            1,
678
            $backend->getJavascript(),
679
            "There should be only 1 file included in required javascript."
680
        );
681
        $this->assertArrayNotHasKey(
682
            $basePath . '/a.js',
683
            $backend->getJavascript(),
684
            "a.js should not be included in required javascript after it has been blocked."
685
        );
686
        $this->assertArrayHasKey(
687
            $basePath . '/b.js',
688
            $backend->getJavascript(),
689
            "b.js should be included in required javascript."
690
        );
691
692
        $backend->css($basePath . '/a.css');
693
        $this->assertCount(
694
            1,
695
            $backend->getCSS(),
696
            "There should be only 1 file included in required css."
697
        );
698
        $this->assertArrayHasKey(
699
            $basePath . '/a.css',
700
            $backend->getCSS(),
701
            "a.css should be in required css."
702
        );
703
704
        $backend->block($basePath . '/a.css');
705
        $this->assertCount(
706
            0,
707
            $backend->getCSS(),
708
            "There should be nothing in required css after file has been blocked."
709
        );
710
    }
711
712
    public function testAppendAndBlockWithModuleResourceLoader()
713
    {
714
        $basePath = $this->getThemeRoot();
0 ignored issues
show
Unused Code introduced by
The assignment to $basePath is dead and can be removed.
Loading history...
715
716
        /** @var Requirements_Backend $backend */
717
        $backend = Injector::inst()->create(Requirements_Backend::class);
718
        $this->setupRequirements($backend);
719
720
        // Note: assumes that client/styles/debug.css is "exposed"
721
        $backend->css('silverstripe/framework:client/styles/debug.css');
722
        $this->assertCount(
723
            1,
724
            $backend->getCSS(),
725
            'Module resource can be loaded via resources reference'
726
        );
727
728
        $backend->block('silverstripe/framework:client/styles/debug.css');
729
        $this->assertCount(
730
            0,
731
            $backend->getCSS(),
732
            'Module resource can be blocked via resources reference'
733
        );
734
    }
735
736
    public function testConditionalTemplateRequire()
737
    {
738
        $testPath = $this->getThemeRoot();
739
740
        /** @var Requirements_Backend $backend */
741
        $backend = Injector::inst()->create(Requirements_Backend::class);
742
        $this->setupRequirements($backend);
743
        $holder = Requirements::backend();
744
        Requirements::set_backend($backend);
745
        $data = new ArrayData(
746
            array(
747
            'FailTest' => true,
748
            )
749
        );
750
751
        // Note: SSViewer theme automatically registered due to 'templates' directory
752
        $data->renderWith('RequirementsTest_Conditionals');
753
        $this->assertFileIncluded($backend, 'css', $testPath .'/css/RequirementsTest_a.css');
754
        $this->assertFileIncluded(
755
            $backend,
756
            'js',
757
            array(
758
                $testPath .'/javascript/RequirementsTest_b.js',
759
                $testPath .'/javascript/RequirementsTest_c.js'
760
            )
761
        );
762
        $this->assertFileNotIncluded($backend, 'js', $testPath .'/javascript/RequirementsTest_a.js');
763
        $this->assertFileNotIncluded(
764
            $backend,
765
            'css',
766
            array(
767
                $testPath .'/css/RequirementsTest_b.css',
768
                $testPath .'/css/RequirementsTest_c.css'
769
            )
770
        );
771
        $backend->clear();
772
        $data = new ArrayData(
773
            array(
774
            'FailTest' => false,
775
            )
776
        );
777
        $data->renderWith('RequirementsTest_Conditionals');
778
        $this->assertFileNotIncluded($backend, 'css', $testPath .'/css/RequirementsTest_a.css');
779
        $this->assertFileNotIncluded(
780
            $backend,
781
            'js',
782
            array(
783
                $testPath .'/javascript/RequirementsTest_b.js',
784
                $testPath .'/javascript/RequirementsTest_c.js'
785
            )
786
        );
787
        $this->assertFileIncluded($backend, 'js', $testPath .'/javascript/RequirementsTest_a.js');
788
        $this->assertFileIncluded(
789
            $backend,
790
            'css',
791
            array(
792
                $testPath .'/css/RequirementsTest_b.css',
793
                $testPath .'/css/RequirementsTest_c.css'
794
            )
795
        );
796
        Requirements::set_backend($holder);
797
    }
798
799
    public function testJsWriteToBody()
800
    {
801
        /** @var Requirements_Backend $backend */
802
        $backend = Injector::inst()->create(Requirements_Backend::class);
803
        $this->setupRequirements($backend);
804
        $backend->javascript('http://www.mydomain.com/test.js');
805
806
        // Test matching with HTML5 <header> tags as well
807
        $template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
808
809
        $backend->setWriteJavascriptToBody(false);
810
        $html = $backend->includeInHTML($template);
811
        $this->assertContains('<head><script', $html);
812
813
        $backend->setWriteJavascriptToBody(true);
814
        $html = $backend->includeInHTML($template);
815
        $this->assertNotContains('<head><script', $html);
816
        $this->assertContains("</script>\n</body>", $html);
817
    }
818
819
    public function testIncludedJsIsNotCommentedOut()
820
    {
821
        $template = '<html><head></head><body><!--<script>alert("commented out");</script>--></body></html>';
822
        /** @var Requirements_Backend $backend */
823
        $backend = Injector::inst()->create(Requirements_Backend::class);
824
        $this->setupRequirements($backend);
825
        $backend->javascript($this->getThemeRoot() . '/javascript/RequirementsTest_a.js');
826
        $html = $backend->includeInHTML($template);
827
        //wiping out commented-out html
828
        $html = preg_replace('/<!--(.*)-->/Uis', '', $html);
829
        $this->assertContains("RequirementsTest_a.js", $html);
830
    }
831
832
    public function testCommentedOutScriptTagIsIgnored()
833
    {
834
        /// Disable nonce
835
        $urlGenerator = new SimpleResourceURLGenerator();
836
        Injector::inst()->registerService($urlGenerator, ResourceURLGenerator::class);
837
838
        $template = '<html><head></head><body><!--<script>alert("commented out");</script>-->'
839
            . '<h1>more content</h1></body></html>';
840
        /** @var Requirements_Backend $backend */
841
        $backend = Injector::inst()->create(Requirements_Backend::class);
842
        $this->setupRequirements($backend);
843
844
        $src = $this->getThemeRoot() . '/javascript/RequirementsTest_a.js';
845
        $backend->javascript($src);
846
        $html = $backend->includeInHTML($template);
847
        $urlSrc = $urlGenerator->urlForResource($src);
848
        $this->assertEquals(
849
            '<html><head></head><body><!--<script>alert("commented out");</script>-->'
850
            . '<h1>more content</h1><script type="application/javascript" src="' . $urlSrc
851
            . "\"></script>\n</body></html>",
852
            $html
853
        );
854
    }
855
856
    public function testForceJsToBottom()
857
    {
858
        /** @var Requirements_Backend $backend */
859
        $backend = Injector::inst()->create(Requirements_Backend::class);
860
        $this->setupRequirements($backend);
861
        $backend->javascript('http://www.mydomain.com/test.js');
862
        $backend->customScript(
863
            <<<'EOS'
864
var globalvar = {
865
	pattern: '\\$custom\\1'
866
};
867
EOS
868
        );
869
870
        // Test matching with HTML5 <header> tags as well
871
        $template = '<html><head></head><body><header>My header</header><p>Body<script></script></p></body></html>';
872
873
        // The expected outputs
874
        $expectedScripts = "<script type=\"application/javascript\" src=\"http://www.mydomain.com/test.js\"></script>\n"
875
            . "<script type=\"application/javascript\">//<![CDATA[\n"
876
            . "var globalvar = {\n\tpattern: '\\\\\$custom\\\\1'\n};\n"
877
            . "//]]></script>\n";
878
        $JsInHead = "<html><head>$expectedScripts</head><body><header>My header</header><p>Body<script></script></p></body></html>";
879
        $JsInBody = "<html><head></head><body><header>My header</header><p>Body$expectedScripts<script></script></p></body></html>";
880
        $JsAtEnd  = "<html><head></head><body><header>My header</header><p>Body<script></script></p>$expectedScripts</body></html>";
881
882
883
        // Test if the script is before the head tag, not before the body.
884
        // Expected: $JsInHead
885
        $backend->setWriteJavascriptToBody(false);
886
        $backend->setForceJSToBottom(false);
887
        $html = $backend->includeInHTML($template);
888
        $this->assertNotEquals($JsInBody, $html);
889
        $this->assertNotEquals($JsAtEnd, $html);
890
        $this->assertEquals($JsInHead, $html);
891
892
        // Test if the script is before the first <script> tag, not before the body.
893
        // Expected: $JsInBody
894
        $backend->setWriteJavascriptToBody(true);
895
        $backend->setForceJSToBottom(false);
896
        $html = $backend->includeInHTML($template);
897
        $this->assertNotEquals($JsAtEnd, $html);
898
        $this->assertEquals($JsInBody, $html);
899
900
        // Test if the script is placed just before the closing bodytag, with write-to-body false.
901
        // Expected: $JsAtEnd
902
        $backend->setWriteJavascriptToBody(false);
903
        $backend->setForceJSToBottom(true);
904
        $html = $backend->includeInHTML($template);
905
        $this->assertNotEquals($JsInHead, $html);
906
        $this->assertNotEquals($JsInBody, $html);
907
        $this->assertEquals($JsAtEnd, $html);
908
909
        // Test if the script is placed just before the closing bodytag, with write-to-body true.
910
        // Expected: $JsAtEnd
911
        $backend->setWriteJavascriptToBody(true);
912
        $backend->setForceJSToBottom(true);
913
        $html = $backend->includeInHTML($template);
914
        $this->assertNotEquals($JsInHead, $html);
915
        $this->assertNotEquals($JsInBody, $html);
916
        $this->assertEquals($JsAtEnd, $html);
917
    }
918
919
    public function testSuffix()
920
    {
921
        /// Disable nonce
922
        $urlGenerator = new SimpleResourceURLGenerator();
923
        Injector::inst()->registerService($urlGenerator, ResourceURLGenerator::class);
924
925
        $template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
926
        $basePath = $this->getThemeRoot();
927
928
        /** @var Requirements_Backend $backend */
929
        $backend = Injector::inst()->create(Requirements_Backend::class);
930
        $this->setupRequirements($backend);
931
932
        $backend->javascript($basePath .'/javascript/RequirementsTest_a.js');
933
        $backend->javascript($basePath .'/javascript/RequirementsTest_b.js?foo=bar&bla=blubb');
934
        $backend->css($basePath .'/css/RequirementsTest_a.css');
935
        $backend->css($basePath .'/css/RequirementsTest_b.css?foo=bar&bla=blubb');
936
937
        $urlGenerator->setNonceStyle('mtime');
938
        $html = $backend->includeInHTML($template);
939
        $this->assertRegExp('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
940
        $this->assertRegExp('/RequirementsTest_b\.js\?foo=bar&amp;bla=blubb&amp;m=[\d]*"/', $html);
941
        $this->assertRegExp('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
942
        $this->assertRegExp('/RequirementsTest_b\.css\?foo=bar&amp;bla=blubb&amp;m=[\d]*"/', $html);
943
944
        $urlGenerator->setNonceStyle(null);
945
        $html = $backend->includeInHTML($template);
946
        $this->assertNotContains('RequirementsTest_a.js=', $html);
947
        $this->assertNotRegExp('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
948
        $this->assertNotRegExp('/RequirementsTest_b\.js\?foo=bar&amp;bla=blubb&amp;m=[\d]*"/', $html);
949
        $this->assertNotRegExp('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
950
        $this->assertNotRegExp('/RequirementsTest_b\.css\?foo=bar&amp;bla=blubb&amp;m=[\d]*"/', $html);
951
    }
952
953
    /**
954
     * Tests that provided files work
955
     */
956
    public function testProvidedFiles()
957
    {
958
        /** @var Requirements_Backend $backend */
959
        $template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
960
        $basePath = $this->getThemeRoot();
961
962
        // Test that provided files block subsequent files
963
        $backend = Injector::inst()->create(Requirements_Backend::class);
964
        $this->setupRequirements($backend);
965
        $backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
966
        $backend->javascript(
967
            $basePath . '/javascript/RequirementsTest_b.js',
968
            [
969
            'provides' => [
970
                $basePath . '/javascript/RequirementsTest_a.js',
971
                $basePath . '/javascript/RequirementsTest_c.js'
972
            ]
973
            ]
974
        );
975
        $backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
976
        // Note that _a.js isn't considered provided because it was included
977
        // before it was marked as provided
978
        $this->assertEquals(
979
            [
980
            $basePath . '/javascript/RequirementsTest_c.js' => $basePath . '/javascript/RequirementsTest_c.js'
981
            ],
982
            $backend->getProvidedScripts()
983
        );
984
        $html = $backend->includeInHTML($template);
985
        $this->assertRegExp('/src=".*\/RequirementsTest_a\.js/', $html);
986
        $this->assertRegExp('/src=".*\/RequirementsTest_b\.js/', $html);
987
        $this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html);
988
989
        // Test that provided files block subsequent combined files
990
        $backend = Injector::inst()->create(Requirements_Backend::class);
991
        $this->setupRequirements($backend);
992
        $backend->combineFiles('combined_a.js', [$basePath . '/javascript/RequirementsTest_a.js']);
993
        $backend->javascript(
994
            $basePath . '/javascript/RequirementsTest_b.js',
995
            [
996
            'provides' => [
997
                $basePath . '/javascript/RequirementsTest_a.js',
998
                $basePath . '/javascript/RequirementsTest_c.js'
999
            ]
1000
            ]
1001
        );
1002
        $backend->combineFiles('combined_c.js', [$basePath . '/javascript/RequirementsTest_c.js']);
1003
        $this->assertEquals(
1004
            [
1005
            $basePath . '/javascript/RequirementsTest_c.js' => $basePath . '/javascript/RequirementsTest_c.js'
1006
            ],
1007
            $backend->getProvidedScripts()
1008
        );
1009
        $html = $backend->includeInHTML($template);
1010
        $this->assertRegExp('/src=".*\/combined_a/', $html);
1011
        $this->assertRegExp('/src=".*\/RequirementsTest_b\.js/', $html);
1012
        $this->assertNotRegExp('/src=".*\/combined_c/', $html);
1013
        $this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html);
1014
    }
1015
1016
    /**
1017
     * Verify that the given backend includes the given files
1018
     *
1019
     * @param Requirements_Backend $backend
1020
     * @param string               $type    js or css
1021
     * @param array|string         $files   Files or list of files to check
1022
     */
1023
    public function assertFileIncluded($backend, $type, $files)
1024
    {
1025
        $includedFiles = $this->getBackendFiles($backend, $type);
1026
1027
        if (is_array($files)) {
1028
            $failedMatches = array();
1029
            foreach ($files as $file) {
1030
                if (!array_key_exists($file, $includedFiles)) {
1031
                    $failedMatches[] = $file;
1032
                }
1033
            }
1034
            $this->assertCount(
1035
                0,
1036
                $failedMatches,
1037
                "Failed asserting the $type files '"
1038
                . implode("', '", $failedMatches)
1039
                . "' have exact matches in the required elements:\n'"
1040
                . implode("'\n'", array_keys($includedFiles)) . "'"
1041
            );
1042
        } else {
1043
            $this->assertArrayHasKey(
1044
                $files,
1045
                $includedFiles,
1046
                "Failed asserting the $type file '$files' has an exact match in the required elements:\n'"
1047
                . implode("'\n'", array_keys($includedFiles)) . "'"
1048
            );
1049
        }
1050
    }
1051
1052
    public function assertFileNotIncluded($backend, $type, $files)
1053
    {
1054
        $includedFiles = $this->getBackendFiles($backend, $type);
1055
        if (is_array($files)) {
1056
            $failedMatches = array();
1057
            foreach ($files as $file) {
1058
                if (array_key_exists($file, $includedFiles)) {
1059
                    $failedMatches[] = $file;
1060
                }
1061
            }
1062
            $this->assertCount(
1063
                0,
1064
                $failedMatches,
1065
                "Failed asserting the $type files '"
1066
                . implode("', '", $failedMatches)
1067
                . "' do not have exact matches in the required elements:\n'"
1068
                . implode("'\n'", array_keys($includedFiles)) . "'"
1069
            );
1070
        } else {
1071
            $this->assertArrayNotHasKey(
1072
                $files,
1073
                $includedFiles,
1074
                "Failed asserting the $type file '$files' does not have an exact match in the required elements:"
1075
                        . "\n'" . implode("'\n'", array_keys($includedFiles)) . "'"
1076
            );
1077
        }
1078
    }
1079
1080
1081
    /**
1082
     * Get files of the given type from the backend
1083
     *
1084
     * @param  Requirements_Backend $backend
1085
     * @param  string               $type    js or css
1086
     * @return array
1087
     */
1088
    protected function getBackendFiles($backend, $type)
1089
    {
1090
        $type = strtolower($type);
1091
        switch (strtolower($type)) {
1092
            case 'css':
1093
                return $backend->getCSS();
1094
            case 'js':
1095
            case 'javascript':
1096
            case 'script':
1097
                return $backend->getJavascript();
1098
        }
1099
        return array();
1100
    }
1101
1102
    /**
1103
     * Get base directory of theme to use for this test
1104
     *
1105
     * @return string
1106
     */
1107
    protected function getThemeRoot()
1108
    {
1109
        return $this->getCurrentRelativePath() . '/SSViewerTest';
1110
    }
1111
}
1112