Passed
Pull Request — 4 (#10134)
by Sergey
08:26
created

RequirementsTest::setUp()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 10
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

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