silverstripe-framework/tests/php/View/RequirementsTest.php
Damian Mooyman 3873e4ba00 API Refactor bootstrap, request handling
See https://github.com/silverstripe/silverstripe-framework/pull/7037
and https://github.com/silverstripe/silverstripe-framework/issues/6681

Squashed commit of the following:

commit 8f65e56532
Author: Ingo Schommer <me@chillu.com>
Date:   Thu Jun 22 22:25:50 2017 +1200

    Fixed upgrade guide spelling

commit 76f95944fa
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 22 16:38:34 2017 +1200

    BUG Fix non-test class manifest including sapphiretest / functionaltest

commit 9379834cb4
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 22 15:50:47 2017 +1200

    BUG Fix nesting bug in Kernel

commit 188ce35d82
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 22 15:14:51 2017 +1200

    BUG fix db bootstrapping issues

commit 7ed4660e7a
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 22 14:49:07 2017 +1200

    BUG Fix issue in DetailedErrorFormatter

commit 738f50c497
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 22 11:49:19 2017 +1200

    Upgrading notes on mysite/_config.php

commit 6279d28e5e
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 22 11:43:28 2017 +1200

    Update developer documentation

commit 5c90d53a84
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 22 10:48:44 2017 +1200

    Update installer to not use global databaseConfig

commit f9b2ba4755
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Wed Jun 21 21:04:39 2017 +1200

    Fix behat issues

commit 5b59a912b6
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Wed Jun 21 17:07:11 2017 +1200

    Move HTTPApplication to SilverStripe\Control namespace

commit e2c4a18f63
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Wed Jun 21 16:29:03 2017 +1200

    More documentation
    Fix up remaining tests
    Refactor temp DB into TempDatabase class so it’s available outside of unit tests.

commit 5d235e64f3
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Wed Jun 21 12:13:15 2017 +1200

    API HTTPRequestBuilder::createFromEnvironment() now cleans up live globals
    BUG Fix issue with SSViewer
    Fix Security / View tests

commit d88d4ed4e4
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 20 16:39:43 2017 +1200

    API Refactor AppKernel into CoreKernel

commit f7946aec33
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 20 16:00:40 2017 +1200

    Docs and minor cleanup

commit 12bd31f936
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 20 15:34:34 2017 +1200

    API Remove OutputMiddleware
    API Move environment / global / ini management into Environment class
    API Move getTempFolder into TempFolder class
    API Implement HTTPRequestBuilder / CLIRequestBuilder
    BUG Restore SS_ALLOWED_HOSTS check in original location
    API CoreKernel now requires $basePath to be passed in
    API Refactor installer.php to use application to bootstrap
    API move memstring conversion globals to Convert
    BUG Fix error in CoreKernel nesting not un-nesting itself properly.

commit bba9791146
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Mon Jun 19 18:07:53 2017 +1200

    API Create HTTPMiddleware and standardise middleware for request handling

commit 2a10c2397b
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Mon Jun 19 17:42:42 2017 +1200

    Fixed ORM tests

commit d75a8d1d93
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Mon Jun 19 17:15:07 2017 +1200

    FIx i18n tests

commit 06364af3c3
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Mon Jun 19 16:59:34 2017 +1200

    Fix controller namespace
    Move states to sub namespace

commit 2a278e2953
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Mon Jun 19 12:49:45 2017 +1200

    Fix forms namespace

commit b65c21241b
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 15 18:56:48 2017 +1200

    Update API usages

commit d1d4375c95
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Thu Jun 15 18:41:44 2017 +1200

    API Refactor $flush into HTPPApplication
    API Enforce health check in Controller::pushCurrent()
    API Better global backup / restore
    Updated Director::test() to use new API

commit b220534f06
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 13 22:05:57 2017 +1200

    Move app nesting to a test state helper

commit 603704165c
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 13 21:46:04 2017 +1200

    Restore kernel stack to fix multi-level nesting

commit 2f6336a15b
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 13 17:23:21 2017 +1200

    API Implement kernel nesting

commit fc7188da7d
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 13 15:43:13 2017 +1200

    Fix core tests

commit a0ae723514
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 13 15:23:52 2017 +1200

    Fix manifest tests

commit ca03395251
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 13 15:00:00 2017 +1200

    API Move extension management into test state

commit c66d433977
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Tue Jun 13 14:10:59 2017 +1200

    API Refactor SapphireTest state management into SapphireTestState
    API Remove Injector::unregisterAllObjects()
    API Remove FakeController

commit f26ae75c6e
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Mon Jun 12 18:04:34 2017 +1200

    Implement basic CLI application object

commit 001d559662
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Mon Jun 12 17:39:38 2017 +1200

    Remove references to SapphireTest::is_running_test()
    Upgrade various code

commit de079c041d
Author: Damian Mooyman <damian@silverstripe.com>
Date:   Wed Jun 7 18:07:33 2017 +1200

    API Implement APP object
    API Refactor of Session
2017-06-22 22:50:45 +12:00

1075 lines
40 KiB
PHP

<?php
namespace SilverStripe\View\Tests;
use InvalidArgumentException;
use SilverStripe\Control\Controller;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\Control\Director;
use SilverStripe\View\Requirements;
use SilverStripe\View\ArrayData;
use SilverStripe\Assets\Tests\Storage\AssetStoreTest\TestAssetStore;
use SilverStripe\View\Requirements_Backend;
/**
* @todo Test that order of combine_files() is correct
* @todo Figure out how to clear the modified state of Requirements class - might affect other tests.
*/
class RequirementsTest extends SapphireTest
{
static $html_template = '<html><head></head><body></body></html>';
protected function setUp()
{
parent::setUp();
TestAssetStore::activate('RequirementsTest'); // Set backend root to /RequirementsTest
}
protected function tearDown()
{
TestAssetStore::reset();
parent::tearDown();
}
public function testExternalUrls()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$backend->setCombinedFilesEnabled(true);
$backend->javascript('http://www.mydomain.com/test.js');
$backend->javascript('https://www.mysecuredomain.com/test.js');
$backend->javascript('//scheme-relative.example.com/test.js');
$backend->css('http://www.mydomain.com/test.css');
$backend->css('https://www.mysecuredomain.com/test.css');
$backend->css('//scheme-relative.example.com/test.css');
$html = $backend->includeInHTML(self::$html_template);
$this->assertTrue(
(strpos($html, 'http://www.mydomain.com/test.js') !== false),
'Load external javascript URL'
);
$this->assertTrue(
(strpos($html, 'https://www.mysecuredomain.com/test.js') !== false),
'Load external secure javascript URL'
);
$this->assertTrue(
(strpos($html, '//scheme-relative.example.com/test.js') !== false),
'Load external scheme-relative javascript URL'
);
$this->assertTrue(
(strpos($html, 'http://www.mydomain.com/test.css') !== false),
'Load external CSS URL'
);
$this->assertTrue(
(strpos($html, 'https://www.mysecuredomain.com/test.css') !== false),
'Load external secure CSS URL'
);
$this->assertTrue(
(strpos($html, '//scheme-relative.example.com/test.css') !== false),
'Load scheme-relative CSS URL'
);
}
/**
* Setup new backend
*
* @param Requirements_Backend $backend
*/
protected function setupRequirements($backend)
{
// Flush requirements
$backend->clear();
$backend->clearCombinedFiles();
$backend->setCombinedFilesFolder('_combinedfiles');
$backend->setMinifyCombinedFiles(false);
$backend->setCombinedFilesEnabled(true);
Requirements::flush();
}
/**
* Setup combined and non-combined js with the backend
*
* @param Requirements_Backend $backend
*/
protected function setupCombinedRequirements($backend)
{
$basePath = $this->getThemeRoot();
$this->setupRequirements($backend);
// require files normally (e.g. called from a FormField instance)
$backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
$backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
$backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
// require two of those files as combined includes
$backend->combineFiles(
'RequirementsTest_bc.js',
array(
$basePath . '/javascript/RequirementsTest_b.js',
$basePath . '/javascript/RequirementsTest_c.js'
)
);
}
/**
* Setup combined files with the backend
*
* @param Requirements_Backend $backend
*/
protected function setupCombinedNonrequiredRequirements($backend)
{
$basePath = $this->getThemeRoot();
$this->setupRequirements($backend);
// require files as combined includes
$backend->combineFiles(
'RequirementsTest_bc.js',
array(
$basePath . '/javascript/RequirementsTest_b.js',
$basePath . '/javascript/RequirementsTest_c.js'
)
);
}
/**
* @param Requirements_Backend $backend
* @param bool $async
* @param bool $defer
*/
protected function setupCombinedRequirementsJavascriptAsyncDefer($backend, $async, $defer)
{
$basePath = $this->getThemeRoot();
$this->setupRequirements($backend);
// require files normally (e.g. called from a FormField instance)
$backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
$backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
$backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
// require two of those files as combined includes
$backend->combineFiles(
'RequirementsTest_bc.js',
array(
$basePath . '/javascript/RequirementsTest_b.js',
$basePath . '/javascript/RequirementsTest_c.js'
),
array(
'async' => $async,
'defer' => $defer,
)
);
}
public function testCustomType()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$basePath = $this->getThemeRoot();
$this->setupRequirements($backend);
// require files normally (e.g. called from a FormField instance)
$backend->javascript(
$basePath . '/javascript/RequirementsTest_a.js',
[
'type' => 'application/json'
]
);
$backend->javascript($basePath . '/javascript/RequirementsTest_b.js');
$result = $backend->includeInHTML(self::$html_template);
$this->assertRegExp(
'#<script type="application/json" src=".*/javascript/RequirementsTest_a.js#',
$result
);
$this->assertRegExp(
'#<script type="application/javascript" src=".*/javascript/RequirementsTest_b.js#',
$result
);
}
public function testCombinedJavascript()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$backend->setCombinedFilesEnabled(true);
$this->setupCombinedRequirements($backend);
$combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
$combinedFilePath = TestAssetStore::base_path() . $combinedFileName;
$html = $backend->includeInHTML(self::$html_template);
/* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
$this->assertRegExp(
'/src=".*' . preg_quote($combinedFileName, '/') . '/',
$html,
'combined javascript file is included in html header'
);
/* COMBINED JAVASCRIPT FILE EXISTS */
$this->assertTrue(
file_exists($combinedFilePath),
'combined javascript file exists'
);
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
'combined javascript has correct content'
);
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertRegExp(
'/src=".*\/RequirementsTest_a\.js/',
$html,
'normal requirements are still included'
);
// Then do it again, this time not requiring the files beforehand
unlink($combinedFilePath);
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupCombinedNonrequiredRequirements($backend);
$html = $backend->includeInHTML(self::$html_template);
/* COMBINED JAVASCRIPT FILE IS INCLUDED IN HTML HEADER */
$this->assertRegExp(
'/src=".*' . preg_quote($combinedFileName, '/') . '/',
$html,
'combined javascript file is included in html header'
);
/* COMBINED JAVASCRIPT FILE EXISTS */
$this->assertTrue(
file_exists($combinedFilePath),
'combined javascript file exists'
);
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
'combined javascript has correct content'
);
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
}
public function testCombinedJavascriptAsyncDefer()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupCombinedRequirementsJavascriptAsyncDefer($backend, true, false);
$combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
$combinedFilePath = TestAssetStore::base_path() . $combinedFileName;
$html = $backend->includeInHTML(false, self::$html_template);
/* ASYNC IS INCLUDED IN SCRIPT TAG */
$this->assertRegExp(
'/src=".*' . preg_quote($combinedFileName, '/') . '" async/',
$html,
'async is included in script tag'
);
/* DEFER IS NOT INCLUDED IN SCRIPT TAG */
$this->assertNotContains('defer', $html, 'defer is not included');
/* COMBINED JAVASCRIPT FILE EXISTS */
clearstatcache(); // needed to get accurate file_exists() results
$this->assertFileExists(
$combinedFilePath,
'combined javascript file exists'
);
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
'combined javascript has correct content'
);
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertRegExp(
'/src=".*\/RequirementsTest_a\.js/',
$html,
'normal requirements are still included'
);
/* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
$html,
'normal requirements don\'t have async'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
$html,
'normal requirements don\'t have defer'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
$html,
'normal requirements don\'t have async/defer'
);
// setup again for testing defer
unlink($combinedFilePath);
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupCombinedRequirementsJavascriptAsyncDefer($backend, false, true);
$html = $backend->includeInHTML(self::$html_template);
/* DEFER IS INCLUDED IN SCRIPT TAG */
$this->assertRegExp(
'/src=".*' . preg_quote($combinedFileName, '/') . '" defer/',
$html,
'defer is included in script tag'
);
/* ASYNC IS NOT INCLUDED IN SCRIPT TAG */
$this->assertNotContains('async', $html, 'async is not included');
/* COMBINED JAVASCRIPT FILE EXISTS */
clearstatcache(); // needed to get accurate file_exists() results
$this->assertFileExists(
$combinedFilePath,
'combined javascript file exists'
);
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
'combined javascript has correct content'
);
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertRegExp(
'/src=".*\/RequirementsTest_a\.js/',
$html,
'normal requirements are still included'
);
/* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
$html,
'normal requirements don\'t have async'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
$html,
'normal requirements don\'t have defer'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
$html,
'normal requirements don\'t have async/defer'
);
// setup again for testing async and defer
unlink($combinedFilePath);
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupCombinedRequirementsJavascriptAsyncDefer($backend, true, true);
$html = $backend->includeInHTML(self::$html_template);
/* ASYNC/DEFER IS INCLUDED IN SCRIPT TAG */
$this->assertRegExp(
'/src=".*' . preg_quote($combinedFileName, '/') . '" async="async" defer="defer"/',
$html,
'async and defer are included in script tag'
);
/* COMBINED JAVASCRIPT FILE EXISTS */
clearstatcache(); // needed to get accurate file_exists() results
$this->assertFileExists(
$combinedFilePath,
'combined javascript file exists'
);
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('b')") !== false),
'combined javascript has correct content'
);
$this->assertTrue(
(strpos(file_get_contents($combinedFilePath), "alert('c')") !== false),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertRegExp(
'/src=".*\/RequirementsTest_a\.js/',
$html,
'normal requirements are still included'
);
/* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
$html,
'normal requirements don\'t have async'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
$html,
'normal requirements don\'t have defer'
);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
$html,
'normal requirements don\'t have async/defer'
);
unlink($combinedFilePath);
}
public function testCombinedCss()
{
$basePath = $this->getThemeRoot();
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->combineFiles(
'print.css',
array(
$basePath . '/css/RequirementsTest_print_a.css',
$basePath . '/css/RequirementsTest_print_b.css'
),
array(
'media' => 'print'
)
);
$html = $backend->includeInHTML(self::$html_template);
$this->assertRegExp(
'/href=".*\/print\-94e723d\.css/',
$html,
'Print stylesheets have been combined.'
);
$this->assertRegExp(
'/media="print/',
$html,
'Combined print stylesheet retains the media parameter'
);
// Test that combining a file multiple times doesn't trigger an error
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->combineFiles(
'style.css',
array(
$basePath . '/css/RequirementsTest_b.css',
$basePath . '/css/RequirementsTest_c.css'
)
);
$backend->combineFiles(
'style.css',
array(
$basePath . '/css/RequirementsTest_b.css',
$basePath . '/css/RequirementsTest_c.css'
)
);
$html = $backend->includeInHTML(self::$html_template);
$this->assertRegExp(
'/href=".*\/style\-bcd90f5\.css/',
$html,
'Stylesheets have been combined.'
);
}
public function testBlockedCombinedJavascript()
{
$basePath = $this->getThemeRoot();
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupCombinedRequirements($backend);
$combinedFileName = '/_combinedfiles/RequirementsTest_bc-2a55d56.js';
$combinedFilePath = TestAssetStore::base_path() . $combinedFileName;
/* BLOCKED COMBINED FILES ARE NOT INCLUDED */
$backend->block('RequirementsTest_bc.js');
clearstatcache(); // needed to get accurate file_exists() results
$html = $backend->includeInHTML(self::$html_template);
$this->assertFileNotExists($combinedFilePath);
$this->assertNotRegExp(
'/src=".*\/RequirementsTest_bc\.js/',
$html,
'blocked combined files are not included'
);
$backend->unblock('RequirementsTest_bc.js');
/* BLOCKED UNCOMBINED FILES ARE NOT INCLUDED */
$this->setupCombinedRequirements($backend);
$backend->block($basePath .'/javascript/RequirementsTest_b.js');
$combinedFileName2 = '/_combinedfiles/RequirementsTest_bc-3748f67.js'; // SHA1 without file b included
$combinedFilePath2 = TestAssetStore::base_path() . $combinedFileName2;
clearstatcache(); // needed to get accurate file_exists() results
$backend->includeInHTML(self::$html_template);
$this->assertFileExists($combinedFilePath2);
$this->assertTrue(
strpos(file_get_contents($combinedFilePath2), "alert('b')") === false,
'blocked uncombined files are not included'
);
$backend->unblock($basePath . '/javascript/RequirementsTest_b.js');
/* A SINGLE FILE CAN'T BE INCLUDED IN TWO COMBINED FILES */
$this->setupCombinedRequirements($backend);
clearstatcache(); // needed to get accurate file_exists() results
// Exception generated from including invalid file
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage(sprintf(
"Requirements_Backend::combine_files(): Already included file(s) %s in combined file '%s'",
$basePath . '/javascript/RequirementsTest_c.js',
'RequirementsTest_bc.js'
));
$backend->combineFiles(
'RequirementsTest_ac.js',
array(
$basePath . '/javascript/RequirementsTest_a.js',
$basePath . '/javascript/RequirementsTest_c.js'
)
);
}
public function testArgsInUrls()
{
$basePath = $this->getThemeRoot();
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript($basePath . '/javascript/RequirementsTest_a.js?test=1&test=2&test=3');
$backend->css($basePath . '/css/RequirementsTest_a.css?test=1&test=2&test=3');
$html = $backend->includeInHTML(self::$html_template);
/* Javascript has correct path */
$this->assertRegExp(
'/src=".*\/RequirementsTest_a\.js\?m=\d\d+&amp;test=1&amp;test=2&amp;test=3/',
$html,
'javascript has correct path'
);
/* CSS has correct path */
$this->assertRegExp(
'/href=".*\/RequirementsTest_a\.css\?m=\d\d+&amp;test=1&amp;test=2&amp;test=3/',
$html,
'css has correct path'
);
}
public function testRequirementsBackend()
{
$basePath = $this->getThemeRoot();
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript($basePath . '/a.js');
$this->assertTrue(
count($backend->getJavascript()) == 1,
"There should be only 1 file included in required javascript."
);
$this->assertArrayHasKey(
$basePath . '/a.js',
$backend->getJavascript(),
"a.js should be included in required javascript."
);
$backend->javascript($basePath . '/b.js');
$this->assertTrue(
count($backend->getJavascript()) == 2,
"There should be 2 files included in required javascript."
);
$backend->block($basePath . '/a.js');
$this->assertTrue(
count($backend->getJavascript()) == 1,
"There should be only 1 file included in required javascript."
);
$this->assertArrayNotHasKey(
$basePath . '/a.js',
$backend->getJavascript(),
"a.js should not be included in required javascript after it has been blocked."
);
$this->assertArrayHasKey(
$basePath . '/b.js',
$backend->getJavascript(),
"b.js should be included in required javascript."
);
$backend->css($basePath . '/a.css');
$this->assertTrue(
count($backend->getCSS()) == 1,
"There should be only 1 file included in required css."
);
$this->assertArrayHasKey(
$basePath . '/a.css',
$backend->getCSS(),
"a.css should be in required css."
);
$backend->block($basePath . '/a.css');
$this->assertTrue(
count($backend->getCSS()) == 0,
"There should be nothing in required css after file has been blocked."
);
}
public function testConditionalTemplateRequire()
{
$testPath = $this->getThemeRoot();
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$holder = Requirements::backend();
Requirements::set_backend($backend);
$data = new ArrayData(
array(
'FailTest' => true,
)
);
// Note: SSViewer theme automatically registered due to 'templates' directory
$data->renderWith('RequirementsTest_Conditionals');
$this->assertFileIncluded($backend, 'css', $testPath .'/css/RequirementsTest_a.css');
$this->assertFileIncluded(
$backend,
'js',
array(
$testPath .'/javascript/RequirementsTest_b.js',
$testPath .'/javascript/RequirementsTest_c.js'
)
);
$this->assertFileNotIncluded($backend, 'js', $testPath .'/javascript/RequirementsTest_a.js');
$this->assertFileNotIncluded(
$backend,
'css',
array(
$testPath .'/css/RequirementsTest_b.css',
$testPath .'/css/RequirementsTest_c.css'
)
);
$backend->clear();
$data = new ArrayData(
array(
'FailTest' => false,
)
);
$data->renderWith('RequirementsTest_Conditionals');
$this->assertFileNotIncluded($backend, 'css', $testPath .'/css/RequirementsTest_a.css');
$this->assertFileNotIncluded(
$backend,
'js',
array(
$testPath .'/javascript/RequirementsTest_b.js',
$testPath .'/javascript/RequirementsTest_c.js'
)
);
$this->assertFileIncluded($backend, 'js', $testPath .'/javascript/RequirementsTest_a.js');
$this->assertFileIncluded(
$backend,
'css',
array(
$testPath .'/css/RequirementsTest_b.css',
$testPath .'/css/RequirementsTest_c.css'
)
);
Requirements::set_backend($holder);
}
public function testJsWriteToBody()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript('http://www.mydomain.com/test.js');
// Test matching with HTML5 <header> tags as well
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
$backend->setWriteJavascriptToBody(false);
$html = $backend->includeInHTML($template);
$this->assertContains('<head><script', $html);
$backend->setWriteJavascriptToBody(true);
$html = $backend->includeInHTML($template);
$this->assertNotContains('<head><script', $html);
$this->assertContains("</script>\n</body>", $html);
}
public function testIncludedJsIsNotCommentedOut()
{
$template = '<html><head></head><body><!--<script>alert("commented out");</script>--></body></html>';
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript($this->getThemeRoot() . '/javascript/RequirementsTest_a.js');
$html = $backend->includeInHTML($template);
//wiping out commented-out html
$html = preg_replace('/<!--(.*)-->/Uis', '', $html);
$this->assertContains("RequirementsTest_a.js", $html);
}
public function testCommentedOutScriptTagIsIgnored()
{
$template = '<html><head></head><body><!--<script>alert("commented out");</script>-->'
. '<h1>more content</h1></body></html>';
/**
* @var Requirements_Backend $backend
*/
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->setSuffixRequirements(false);
$src = $this->getThemeRoot() . '/javascript/RequirementsTest_a.js';
$urlSrc = Controller::join_links(Director::baseURL(), $src);
$backend->javascript($src);
$html = $backend->includeInHTML($template);
$this->assertEquals(
'<html><head></head><body><!--<script>alert("commented out");</script>-->'
. '<h1>more content</h1><script type="application/javascript" src="' . $urlSrc
. "\"></script>\n</body></html>",
$html
);
}
public function testForceJsToBottom()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript('http://www.mydomain.com/test.js');
$backend->customScript(
<<<'EOS'
var globalvar = {
pattern: '\\$custom\\1'
};
EOS
);
// Test matching with HTML5 <header> tags as well
$template = '<html><head></head><body><header>My header</header><p>Body<script></script></p></body></html>';
// The expected outputs
$expectedScripts = "<script type=\"application/javascript\" src=\"http://www.mydomain.com/test.js\"></script>\n"
. "<script type=\"application/javascript\">//<![CDATA[\n"
. "var globalvar = {\n\tpattern: '\\\\\$custom\\\\1'\n};\n"
. "//]]></script>\n";
$JsInHead = "<html><head>$expectedScripts</head><body><header>My header</header><p>Body<script></script></p></body></html>";
$JsInBody = "<html><head></head><body><header>My header</header><p>Body$expectedScripts<script></script></p></body></html>";
$JsAtEnd = "<html><head></head><body><header>My header</header><p>Body<script></script></p>$expectedScripts</body></html>";
// Test if the script is before the head tag, not before the body.
// Expected: $JsInHead
$backend->setWriteJavascriptToBody(false);
$backend->setForceJSToBottom(false);
$html = $backend->includeInHTML($template);
$this->assertNotEquals($JsInBody, $html);
$this->assertNotEquals($JsAtEnd, $html);
$this->assertEquals($JsInHead, $html);
// Test if the script is before the first <script> tag, not before the body.
// Expected: $JsInBody
$backend->setWriteJavascriptToBody(true);
$backend->setForceJSToBottom(false);
$html = $backend->includeInHTML($template);
$this->assertNotEquals($JsAtEnd, $html);
$this->assertEquals($JsInBody, $html);
// Test if the script is placed just before the closing bodytag, with write-to-body false.
// Expected: $JsAtEnd
$backend->setWriteJavascriptToBody(false);
$backend->setForceJSToBottom(true);
$html = $backend->includeInHTML($template);
$this->assertNotEquals($JsInHead, $html);
$this->assertNotEquals($JsInBody, $html);
$this->assertEquals($JsAtEnd, $html);
// Test if the script is placed just before the closing bodytag, with write-to-body true.
// Expected: $JsAtEnd
$backend->setWriteJavascriptToBody(true);
$backend->setForceJSToBottom(true);
$html = $backend->includeInHTML($template);
$this->assertNotEquals($JsInHead, $html);
$this->assertNotEquals($JsInBody, $html);
$this->assertEquals($JsAtEnd, $html);
}
public function testSuffix()
{
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
$basePath = $this->getThemeRoot();
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript($basePath .'/javascript/RequirementsTest_a.js');
$backend->javascript($basePath .'/javascript/RequirementsTest_b.js?foo=bar&bla=blubb');
$backend->css($basePath .'/css/RequirementsTest_a.css');
$backend->css($basePath .'/css/RequirementsTest_b.css?foo=bar&bla=blubb');
$backend->setSuffixRequirements(true);
$html = $backend->includeInHTML($template);
$this->assertRegExp('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
$this->assertRegExp('/RequirementsTest_b\.js\?m=[\d]*&amp;foo=bar&amp;bla=blubb"/', $html);
$this->assertRegExp('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
$this->assertRegExp('/RequirementsTest_b\.css\?m=[\d]*&amp;foo=bar&amp;bla=blubb"/', $html);
$backend->setSuffixRequirements(false);
$html = $backend->includeInHTML($template);
$this->assertNotContains('RequirementsTest_a.js=', $html);
$this->assertNotRegExp('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
$this->assertNotRegExp('/RequirementsTest_b\.js\?m=[\d]*&amp;foo=bar&amp;bla=blubb"/', $html);
$this->assertNotRegExp('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
$this->assertNotRegExp('/RequirementsTest_b\.css\?m=[\d]*&amp;foo=bar&amp;bla=blubb"/', $html);
}
/**
* Tests that provided files work
*/
public function testProvidedFiles()
{
/** @var Requirements_Backend $backend */
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
$basePath = $this->getThemeRoot();
// Test that provided files block subsequent files
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript($basePath . '/javascript/RequirementsTest_a.js');
$backend->javascript(
$basePath . '/javascript/RequirementsTest_b.js',
[
'provides' => [
$basePath . '/javascript/RequirementsTest_a.js',
$basePath . '/javascript/RequirementsTest_c.js'
]
]
);
$backend->javascript($basePath . '/javascript/RequirementsTest_c.js');
// Note that _a.js isn't considered provided because it was included
// before it was marked as provided
$this->assertEquals(
[
$basePath . '/javascript/RequirementsTest_c.js' => $basePath . '/javascript/RequirementsTest_c.js'
],
$backend->getProvidedScripts()
);
$html = $backend->includeInHTML($template);
$this->assertRegExp('/src=".*\/RequirementsTest_a\.js/', $html);
$this->assertRegExp('/src=".*\/RequirementsTest_b\.js/', $html);
$this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html);
// Test that provided files block subsequent combined files
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->combineFiles('combined_a.js', [$basePath . '/javascript/RequirementsTest_a.js']);
$backend->javascript(
$basePath . '/javascript/RequirementsTest_b.js',
[
'provides' => [
$basePath . '/javascript/RequirementsTest_a.js',
$basePath . '/javascript/RequirementsTest_c.js'
]
]
);
$backend->combineFiles('combined_c.js', [$basePath . '/javascript/RequirementsTest_c.js']);
$this->assertEquals(
[
$basePath . '/javascript/RequirementsTest_c.js' => $basePath . '/javascript/RequirementsTest_c.js'
],
$backend->getProvidedScripts()
);
$html = $backend->includeInHTML($template);
$this->assertRegExp('/src=".*\/combined_a/', $html);
$this->assertRegExp('/src=".*\/RequirementsTest_b\.js/', $html);
$this->assertNotRegExp('/src=".*\/combined_c/', $html);
$this->assertNotRegExp('/src=".*\/RequirementsTest_c\.js/', $html);
}
/**
* Verify that the given backend includes the given files
*
* @param Requirements_Backend $backend
* @param string $type js or css
* @param array|string $files Files or list of files to check
*/
public function assertFileIncluded($backend, $type, $files)
{
$includedFiles = $this->getBackendFiles($backend, $type);
if (is_array($files)) {
$failedMatches = array();
foreach ($files as $file) {
if (!array_key_exists($file, $includedFiles)) {
$failedMatches[] = $file;
}
}
$this->assertTrue(
(count($failedMatches) == 0),
"Failed asserting the $type files '"
. implode("', '", $failedMatches)
. "' have exact matches in the required elements:\n'"
. implode("'\n'", array_keys($includedFiles)) . "'"
);
} else {
$this->assertTrue(
(array_key_exists($files, $includedFiles)),
"Failed asserting the $type file '$files' has an exact match in the required elements:\n'"
. implode("'\n'", array_keys($includedFiles)) . "'"
);
}
}
public function assertFileNotIncluded($backend, $type, $files)
{
$includedFiles = $this->getBackendFiles($backend, $type);
if (is_array($files)) {
$failedMatches = array();
foreach ($files as $file) {
if (array_key_exists($file, $includedFiles)) {
$failedMatches[] = $file;
}
}
$this->assertTrue(
(count($failedMatches) == 0),
"Failed asserting the $type files '"
. implode("', '", $failedMatches)
. "' do not have exact matches in the required elements:\n'"
. implode("'\n'", array_keys($includedFiles)) . "'"
);
} else {
$this->assertFalse(
(array_key_exists($files, $includedFiles)),
"Failed asserting the $type file '$files' does not have an exact match in the required elements:"
. "\n'" . implode("'\n'", array_keys($includedFiles)) . "'"
);
}
}
/**
* Get files of the given type from the backend
*
* @param Requirements_Backend $backend
* @param string $type js or css
* @return array
*/
protected function getBackendFiles($backend, $type)
{
$type = strtolower($type);
switch (strtolower($type)) {
case 'css':
return $backend->getCSS();
case 'js':
case 'javascript':
case 'script':
return $backend->getJavascript();
}
return array();
}
/**
* Get base directory of theme to use for this test
*
* @return string
*/
protected function getThemeRoot()
{
return $this->getCurrentRelativePath() . '/SSViewerTest';
}
}