silverstripe-framework/tests/php/View/RequirementsTest.php

1424 lines
53 KiB
PHP
Raw Normal View History

<?php
2016-10-14 03:30:05 +02:00
namespace SilverStripe\View\Tests;
use InvalidArgumentException;
use SilverStripe\Control\Director;
use SilverStripe\Core\Injector\Injector;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\i18n\i18n;
use SilverStripe\View\Requirements;
use SilverStripe\View\ArrayData;
2018-02-14 04:24:12 +01:00
use Silverstripe\Assets\Dev\TestAssetStore;
2016-10-14 03:30:05 +02:00
use SilverStripe\View\Requirements_Backend;
use SilverStripe\Core\Manifest\ResourceURLGenerator;
use SilverStripe\Control\SimpleResourceURLGenerator;
use SilverStripe\Core\Config\Config;
2022-11-28 07:16:31 +01:00
use SilverStripe\Dev\Deprecation;
use SilverStripe\View\SSViewer;
use SilverStripe\View\ThemeResourceLoader;
class RequirementsTest extends SapphireTest
{
/**
* @var ThemeResourceLoader
*/
protected $oldThemeResourceLoader = null;
static $html_template = '<html><head></head><body></body></html>';
protected function setUp(): void
{
parent::setUp();
Director::config()->set('alternate_base_folder', __DIR__ . '/SSViewerTest');
Director::config()->set('alternate_base_url', 'http://www.mysite.com/basedir/');
Director::config()->set('alternate_public_dir', 'public'); // Enforce public dir
// Add public as a theme in itself
SSViewer::set_themes([SSViewer::PUBLIC_THEME, SSViewer::DEFAULT_THEME]);
TestAssetStore::activate('RequirementsTest'); // Set backend root to /RequirementsTest
$this->oldThemeResourceLoader = ThemeResourceLoader::inst();
}
protected function tearDown(): void
{
ThemeResourceLoader::set_instance($this->oldThemeResourceLoader);
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->javascript('http://www.mydomain.com:3000/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');
$backend->css('http://www.mydomain.com:3000/test.css');
$html = $backend->includeInHTML(self::$html_template);
$this->assertStringContainsString('http://www.mydomain.com/test.js', $html, 'Load external javascript URL');
$this->assertStringContainsString('https://www.mysecuredomain.com/test.js', $html, 'Load external secure javascript URL');
$this->assertStringContainsString('//scheme-relative.example.com/test.js', $html, 'Load external scheme-relative JS');
$this->assertStringContainsString('http://www.mydomain.com:3000/test.js', $html, 'Load external with port');
$this->assertStringContainsString('http://www.mydomain.com/test.css', $html, 'Load external CSS URL');
$this->assertStringContainsString('https://www.mysecuredomain.com/test.css', $html, 'Load external secure CSS URL');
$this->assertStringContainsString('//scheme-relative.example.com/test.css', $html, 'Load scheme-relative CSS URL');
$this->assertStringContainsString('http://www.mydomain.com:3000/test.css', $html, 'Load external with port');
}
public function testResolveCSSReferencesDisabled()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
Config::forClass(get_class($backend))->set('resolve_relative_css_refs', false);
$backend->combineFiles(
'RequirementsTest_pc.css',
[
'css/RequirementsTest_d.css',
'css/deep/deeper/RequirementsTest_p.css'
]
);
$backend->includeInHTML(self::$html_template);
// we get the file path here
$allCSS = $backend->getCSS();
$this->assertCount(
1,
$allCSS,
'only one combined file'
);
$files = array_keys($allCSS);
$combinedFileName = $files[0];
$combinedFileName = str_replace('/' . ASSETS_DIR . '/', '/', $combinedFileName);
$combinedFilePath = TestAssetStore::base_path() . $combinedFileName;
$content = file_get_contents($combinedFilePath);
/* DISABLED COMBINED CSS URL RESOLVER IGNORED ONE DOT */
$this->assertStringContainsString(
".p0 { background: url(./zero.gif); }",
$content,
'disabled combined css url resolver ignored one dot'
);
/* DISABLED COMBINED CSS URL RESOLVER IGNORED DOUBLE-DOT */
$this->assertStringContainsString(
".p1 { background: url(../one.gif); }",
$content,
'disabled combined css url resolver ignored double-dot'
);
}
public function testResolveCSSReferences()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
Config::forClass(get_class($backend))->set('resolve_relative_css_refs', true);
$backend->combineFiles(
'RequirementsTest_pc.css',
[
'css/RequirementsTest_d.css',
'css/deep/deeper/RequirementsTest_p.css'
]
);
$backend->includeInHTML(self::$html_template);
// we get the file path here
$allCSS = $backend->getCSS();
$this->assertCount(
1,
$allCSS,
'only one combined file'
);
$files = array_keys($allCSS);
$combinedFileName = $files[0];
$combinedFileName = str_replace('/' . ASSETS_DIR . '/', '/', $combinedFileName);
$combinedFilePath = TestAssetStore::base_path() . $combinedFileName;
/* COMBINED JAVASCRIPT FILE EXISTS */
$this->assertTrue(
file_exists($combinedFilePath),
'combined css file exists'
);
$content = file_get_contents($combinedFilePath);
/* COMBINED CSS URL RESOLVER IGNORE FULL URLS */
$this->assertStringContainsString(
".url { background: url(http://example.com/zero.gif); }",
$content,
'combined css url resolver ignore full urls'
);
/* COMBINED CSS URL RESOLVER DECODED ONE DOT */
$this->assertStringContainsString(
".p0 { background: url(/css/deep/deeper/zero.gif); }",
$content,
'combined css url resolver decoded one dot'
);
/* COMBINED CSS URL RESOLVER DECODED NO DOTS */
$this->assertStringContainsString(
".p0-plain { background: url(/css/deep/deeper/zero.gif); }",
$content,
'combined css url resolver decoded no dots'
);
/* COMBINED CSS URL RESOLVER DAMAGED A QUERYSTRING */
$this->assertStringContainsString(
".p0-qs { background: url(/css/deep/deeper/zero.gif?some=param); }",
$content,
'combined css url resolver damaged a querystring'
);
/* COMBINED CSS URL RESOLVER DECODED ONE DOT WITH SINGLE QUOTES */
$this->assertStringContainsString(
".p0sq { background: url('/css/deep/deeper/zero-sq.gif'); }",
$content,
'combined css url resolver decoded one dot with single quotes'
);
/* COMBINED CSS URL RESOLVER DECODED ONE DOT WITH DOUBLE QUOTES */
$this->assertStringContainsString(
".p0dq { background: url(\"/css/deep/deeper/zero-dq.gif\"); }",
$content,
'combined css url resolver decoded one dot with double quotes'
);
/* COMBINED CSS URL RESOLVER DECODED ONE DOT WITH DOUBLE QUOTES AND SPACES NEW LINE */
$this->assertStringContainsString(
"\n \"/css/deep/deeper/zero-dq-nls.gif\"\n",
$content,
'combined css url resolver decoded one dot with double quotes and spaces new line'
);
/* COMBINED CSS URL RESOLVER DECODED ONE DOT WITH DOUBLE QUOTES NEW LINE */
$this->assertStringContainsString(
"\"/css/deep/deeper/zero-dq-nl.gif\"",
$content,
'combined css url resolver decoded one dot with double quotes new line'
);
/* COMBINED CSS URL RESOLVER DECODED ONE DOT WITH DOUBLE QUOTES NEW LINE WITH SPACES */
$this->assertStringContainsString(
"\"/css/deep/deeper/zero-dq-nls.gif\"",
$content,
'combined css url resolver decoded one dot with double quotes new line with spaces'
);
/* COMBINED CSS URL RESOLVER DECODED 1 DOUBLE-DOT */
$this->assertStringContainsString(
".p1 { background: url(/css/deep/one.gif); }",
$content,
'combined css url resolver decoded 1 double-dot'
);
/* COMBINED CSS URL RESOLVER DECODED 2 DOUBLE-DOT */
$this->assertStringContainsString(
".p2 { background: url(/css/two.gif); }",
$content,
'combined css url resolver decoded 2 double-dot'
);
/* COMBINED CSS URL RESOLVER DECODED 2 DOUBLE-DOT SINGLE QUOTES */
$this->assertStringContainsString(
".p2sq { background: url('/css/two-sq.gif'); }",
$content,
'combined css url resolver decoded 2 double-dot single quotes'
);
/* COMBINED CSS URL RESOLVER DECODED 2 DOUBLE-DOT DOUBLE QUOTES */
$this->assertStringContainsString(
".p2dq { background: url(\"/css/two-dq.gif\"); }",
$content,
'combined css url resolver decoded 2 double-dot double quotes'
);
/* COMBINED CSS URL RESOLVER SHOULD NOT TOUCH ABSOLUTE PATH */
$this->assertStringContainsString(
".p2abs { background: url(/foo/bar/../../two-abs.gif); }",
$content,
'combined css url resolver should not touch absolute path'
);
/* COMBINED CSS URL RESOLVER SHOULD NOT TOUCH ABSOLUTE PATH ON NEW LINE */
$this->assertStringContainsString(
"\n /foo/bar/../../two-abs-ln.gif\n",
$content,
'combined css url resolver should not touch absolute path on new line'
);
/* COMBINED CSS URL RESOLVER DECODED 3 DOUBLE-DOT */
$this->assertStringContainsString(
".p3 { background: url(/three.gif); }",
$content,
'combined css url resolver decoded 3 double-dot'
);
/* COMBINED CSS URL RESOLVER DECODED 4 DOUBLE-DOT WHEN ONLY 3 LEVELS AVAILABLE*/
$this->assertStringContainsString(
".p4 { background: url(/four.gif); }",
$content,
'combined css url resolver decoded 4 double-dot when only 3 levels available'
);
/* COMBINED CSS URL RESOLVER MODIFIED AN ARBITRARY VALUE */
$this->assertStringContainsString(
".weird { content: \"./keepme.gif\"; }",
$content,
'combined css url resolver modified an arbitrary value'
);
}
/**
* Setup new backend
*
* @param Requirements_Backend $backend
*/
protected function setupRequirements($backend)
{
// Flush requirements
$backend->clear();
$backend->clearCombinedFiles();
$backend->setCombinedFilesFolder('_combinedfiles');
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 8f65e5653211240650eaa4fa65bb83b45aae6d58 Author: Ingo Schommer <me@chillu.com> Date: Thu Jun 22 22:25:50 2017 +1200 Fixed upgrade guide spelling commit 76f95944fa89b0b540704b8d744329f690f9698c 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 9379834cb4b2e5177a2600049feec05bf111c16b Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 15:50:47 2017 +1200 BUG Fix nesting bug in Kernel commit 188ce35d82599360c40f0f2de29579c56fb90761 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 15:14:51 2017 +1200 BUG fix db bootstrapping issues commit 7ed4660e7a63915e8e974deeaba9807bc4d38b0d Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 14:49:07 2017 +1200 BUG Fix issue in DetailedErrorFormatter commit 738f50c497166f81ccbe3f40fbcff895ce71f82f Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 11:49:19 2017 +1200 Upgrading notes on mysite/_config.php commit 6279d28e5e455916f902a2f963c014d8899f7fc7 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 11:43:28 2017 +1200 Update developer documentation commit 5c90d53a84ef0139c729396949a7857fae60436f Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 10:48:44 2017 +1200 Update installer to not use global databaseConfig commit f9b2ba4755371f08bd95f6908ac612fcbb7ca205 Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 21:04:39 2017 +1200 Fix behat issues commit 5b59a912b60282b4dad4ef10ed3b97c5d0a761ac Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 17:07:11 2017 +1200 Move HTTPApplication to SilverStripe\Control namespace commit e2c4a18f637bdd3d276619554de60ee8b4d95ced 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 5d235e64f341d6251bfe9f4833f15cc8593c5034 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 d88d4ed4e48291cb65407f222f190064b1f1deeb Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 16:39:43 2017 +1200 API Refactor AppKernel into CoreKernel commit f7946aec3391139ae1b4029c353c327a36552b36 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 16:00:40 2017 +1200 Docs and minor cleanup commit 12bd31f9366327650b5c0c0f96cd0327d44faf0a 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 bba979114624247cf463cf2a8c9e4be9a7c3a772 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 2a10c2397bdc53001013f607b5d38087ce6c0730 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 17:42:42 2017 +1200 Fixed ORM tests commit d75a8d1d93398af4bd0432df9e4bc6295c15a3fe Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 17:15:07 2017 +1200 FIx i18n tests commit 06364af3c379c931889c4cc34dd920fee3db204a Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 16:59:34 2017 +1200 Fix controller namespace Move states to sub namespace commit 2a278e2953d2dbb19f78d91c919048e1fc935436 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 12:49:45 2017 +1200 Fix forms namespace commit b65c21241bee019730027071d815dbf7571197a4 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 15 18:56:48 2017 +1200 Update API usages commit d1d4375c95a264a6b63cbaefc2c1d12f808bfd82 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 b220534f06732db4fa940d8724c2a85c0ba2495a Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 22:05:57 2017 +1200 Move app nesting to a test state helper commit 603704165c08d0c1c81fd5e6bb9506326eeee17b Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 21:46:04 2017 +1200 Restore kernel stack to fix multi-level nesting commit 2f6336a15bf79dc8c2edd44cec1931da2dd51c28 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 17:23:21 2017 +1200 API Implement kernel nesting commit fc7188da7d6ad6785354bab61f08700454c81d91 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:43:13 2017 +1200 Fix core tests commit a0ae7235148fffd71f2f02d1fe7fe45bf3aa39eb Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:23:52 2017 +1200 Fix manifest tests commit ca033952513633e182040d3d13e1caa9000ca184 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:00:00 2017 +1200 API Move extension management into test state commit c66d4339777663a8a04661fea32a0cf35b95d20f 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 f26ae75c6ecaafa0dec1093264e0187191e6764d Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 12 18:04:34 2017 +1200 Implement basic CLI application object commit 001d5596621404892de0a5413392379eff990641 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 de079c041dacd96bc4f4b66421fa2b2cc4c320f8 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 12:50:45 +02:00
$backend->setCombinedFilesEnabled(true);
Requirements::flush();
}
/**
* Setup combined and non-combined js with the backend
*
* @param Requirements_Backend $backend
*/
protected function setupCombinedRequirements($backend)
{
$this->setupRequirements($backend);
// require files normally (e.g. called from a FormField instance)
$backend->javascript('javascript/RequirementsTest_a.js');
$backend->javascript('javascript/RequirementsTest_b.js');
$backend->javascript('javascript/RequirementsTest_c.js');
// Public resources may or may not be specified with `public/` prefix
$backend->javascript('javascript/RequirementsTest_d.js');
$backend->javascript('public/javascript/RequirementsTest_e.js');
// require two of those files as combined includes
$backend->combineFiles(
'RequirementsTest_bc.js',
[
'javascript/RequirementsTest_b.js',
'javascript/RequirementsTest_c.js'
]
);
}
/**
* Setup combined files with the backend
*
* @param Requirements_Backend $backend
*/
protected function setupCombinedNonrequiredRequirements($backend)
{
$this->setupRequirements($backend);
// require files as combined includes
$backend->combineFiles(
'RequirementsTest_bc.js',
[
'javascript/RequirementsTest_b.js',
'javascript/RequirementsTest_c.js'
]
);
}
/**
* @param Requirements_Backend $backend
* @param bool $async
* @param bool $defer
*/
protected function setupCombinedRequirementsJavascriptAsyncDefer($backend, $async, $defer)
{
$this->setupRequirements($backend);
// require files normally (e.g. called from a FormField instance)
$backend->javascript('javascript/RequirementsTest_a.js');
$backend->javascript('javascript/RequirementsTest_b.js');
$backend->javascript('javascript/RequirementsTest_c.js');
// require two of those files as combined includes
$backend->combineFiles(
'RequirementsTest_bc.js',
[
'javascript/RequirementsTest_b.js',
'javascript/RequirementsTest_c.js'
],
[
'async' => $async,
'defer' => $defer,
]
);
}
public function testCustomType()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
// require files normally (e.g. called from a FormField instance)
$backend->javascript(
'javascript/RequirementsTest_a.js',
[ 'type' => 'application/json' ]
);
$backend->javascript('javascript/RequirementsTest_b.js');
$result = $backend->includeInHTML(self::$html_template);
$this->assertMatchesRegularExpression(
'#<script type="application/json" src=".*/javascript/RequirementsTest_a.js#',
$result
);
$this->assertMatchesRegularExpression(
'#<script type="application/javascript" src=".*/javascript/RequirementsTest_b.js#',
$result
);
}
public function testCombinedJavascript()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
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 8f65e5653211240650eaa4fa65bb83b45aae6d58 Author: Ingo Schommer <me@chillu.com> Date: Thu Jun 22 22:25:50 2017 +1200 Fixed upgrade guide spelling commit 76f95944fa89b0b540704b8d744329f690f9698c 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 9379834cb4b2e5177a2600049feec05bf111c16b Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 15:50:47 2017 +1200 BUG Fix nesting bug in Kernel commit 188ce35d82599360c40f0f2de29579c56fb90761 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 15:14:51 2017 +1200 BUG fix db bootstrapping issues commit 7ed4660e7a63915e8e974deeaba9807bc4d38b0d Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 14:49:07 2017 +1200 BUG Fix issue in DetailedErrorFormatter commit 738f50c497166f81ccbe3f40fbcff895ce71f82f Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 11:49:19 2017 +1200 Upgrading notes on mysite/_config.php commit 6279d28e5e455916f902a2f963c014d8899f7fc7 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 11:43:28 2017 +1200 Update developer documentation commit 5c90d53a84ef0139c729396949a7857fae60436f Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 22 10:48:44 2017 +1200 Update installer to not use global databaseConfig commit f9b2ba4755371f08bd95f6908ac612fcbb7ca205 Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 21:04:39 2017 +1200 Fix behat issues commit 5b59a912b60282b4dad4ef10ed3b97c5d0a761ac Author: Damian Mooyman <damian@silverstripe.com> Date: Wed Jun 21 17:07:11 2017 +1200 Move HTTPApplication to SilverStripe\Control namespace commit e2c4a18f637bdd3d276619554de60ee8b4d95ced 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 5d235e64f341d6251bfe9f4833f15cc8593c5034 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 d88d4ed4e48291cb65407f222f190064b1f1deeb Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 16:39:43 2017 +1200 API Refactor AppKernel into CoreKernel commit f7946aec3391139ae1b4029c353c327a36552b36 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 20 16:00:40 2017 +1200 Docs and minor cleanup commit 12bd31f9366327650b5c0c0f96cd0327d44faf0a 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 bba979114624247cf463cf2a8c9e4be9a7c3a772 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 2a10c2397bdc53001013f607b5d38087ce6c0730 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 17:42:42 2017 +1200 Fixed ORM tests commit d75a8d1d93398af4bd0432df9e4bc6295c15a3fe Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 17:15:07 2017 +1200 FIx i18n tests commit 06364af3c379c931889c4cc34dd920fee3db204a Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 16:59:34 2017 +1200 Fix controller namespace Move states to sub namespace commit 2a278e2953d2dbb19f78d91c919048e1fc935436 Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 19 12:49:45 2017 +1200 Fix forms namespace commit b65c21241bee019730027071d815dbf7571197a4 Author: Damian Mooyman <damian@silverstripe.com> Date: Thu Jun 15 18:56:48 2017 +1200 Update API usages commit d1d4375c95a264a6b63cbaefc2c1d12f808bfd82 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 b220534f06732db4fa940d8724c2a85c0ba2495a Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 22:05:57 2017 +1200 Move app nesting to a test state helper commit 603704165c08d0c1c81fd5e6bb9506326eeee17b Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 21:46:04 2017 +1200 Restore kernel stack to fix multi-level nesting commit 2f6336a15bf79dc8c2edd44cec1931da2dd51c28 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 17:23:21 2017 +1200 API Implement kernel nesting commit fc7188da7d6ad6785354bab61f08700454c81d91 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:43:13 2017 +1200 Fix core tests commit a0ae7235148fffd71f2f02d1fe7fe45bf3aa39eb Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:23:52 2017 +1200 Fix manifest tests commit ca033952513633e182040d3d13e1caa9000ca184 Author: Damian Mooyman <damian@silverstripe.com> Date: Tue Jun 13 15:00:00 2017 +1200 API Move extension management into test state commit c66d4339777663a8a04661fea32a0cf35b95d20f 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 f26ae75c6ecaafa0dec1093264e0187191e6764d Author: Damian Mooyman <damian@silverstripe.com> Date: Mon Jun 12 18:04:34 2017 +1200 Implement basic CLI application object commit 001d5596621404892de0a5413392379eff990641 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 de079c041dacd96bc4f4b66421fa2b2cc4c320f8 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 12:50:45 +02:00
$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->assertMatchesRegularExpression(
2022-04-14 03:12:59 +02:00
'/src=".*' . preg_quote($combinedFileName ?? '', '/') . '/',
$html,
'combined javascript file is included in html header'
);
/* COMBINED JAVASCRIPT FILE EXISTS */
$this->assertTrue(
2022-04-14 03:12:59 +02:00
file_exists($combinedFilePath ?? ''),
'combined javascript file exists'
);
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertStringContainsString(
"alert('b')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
$this->assertStringContainsString(
"alert('c')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertMatchesRegularExpression(
'/src=".*\/RequirementsTest_a\.js/',
$html,
'normal requirements are still included'
);
// Then do it again, this time not requiring the files beforehand
2022-04-14 03:12:59 +02:00
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->assertMatchesRegularExpression(
2022-04-14 03:12:59 +02:00
'/src=".*' . preg_quote($combinedFileName ?? '', '/') . '/',
$html,
'combined javascript file is included in html header'
);
/* COMBINED JAVASCRIPT FILE EXISTS */
$this->assertTrue(
2022-04-14 03:12:59 +02:00
file_exists($combinedFilePath ?? ''),
'combined javascript file exists'
);
/* COMBINED JAVASCRIPT HAS CORRECT CONTENT */
$this->assertStringContainsString(
"alert('b')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
$this->assertStringContainsString(
"alert('c')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertDoesNotMatchRegularExpression(
'/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;
2022-11-15 06:20:54 +01:00
$html = $backend->includeInHTML(self::$html_template);
/* ASYNC IS INCLUDED IN SCRIPT TAG */
$this->assertMatchesRegularExpression(
2022-04-14 03:12:59 +02:00
'/src=".*' . preg_quote($combinedFileName ?? '', '/') . '" async/',
$html,
'async is included in script tag'
);
/* DEFER IS NOT INCLUDED IN SCRIPT TAG */
$this->assertStringNotContainsString('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->assertStringContainsString(
"alert('b')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
$this->assertStringContainsString(
"alert('c')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertMatchesRegularExpression(
'/src=".*\/RequirementsTest_a\.js/',
$html,
'normal requirements are still included'
);
/* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
$html,
'normal requirements don\'t have async'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
$html,
'normal requirements don\'t have defer'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
$html,
'normal requirements don\'t have async/defer'
);
// setup again for testing defer
2022-04-14 03:12:59 +02:00
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->assertMatchesRegularExpression(
2022-04-14 03:12:59 +02:00
'/src=".*' . preg_quote($combinedFileName ?? '', '/') . '" defer/',
$html,
'defer is included in script tag'
);
/* ASYNC IS NOT INCLUDED IN SCRIPT TAG */
$this->assertStringNotContainsString('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->assertStringContainsString(
"alert('b')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
$this->assertStringContainsString(
"alert('c')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertMatchesRegularExpression(
'/src=".*\/RequirementsTest_a\.js/',
$html,
'normal requirements are still included'
);
/* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
$html,
'normal requirements don\'t have async'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
$html,
'normal requirements don\'t have defer'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
$html,
'normal requirements don\'t have async/defer'
);
// setup again for testing async and defer
2022-04-14 03:12:59 +02:00
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->assertMatchesRegularExpression(
2022-04-14 03:12:59 +02:00
'/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->assertStringContainsString(
"alert('b')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
$this->assertStringContainsString(
"alert('c')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath ?? ''),
'combined javascript has correct content'
);
/* COMBINED FILES ARE NOT INCLUDED TWICE */
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_b\.js/',
$html,
'combined files are not included twice'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_c\.js/',
$html,
'combined files are not included twice'
);
/* NORMAL REQUIREMENTS ARE STILL INCLUDED */
$this->assertMatchesRegularExpression(
'/src=".*\/RequirementsTest_a\.js/',
$html,
'normal requirements are still included'
);
/* NORMAL REQUIREMENTS DON'T HAVE ASYNC/DEFER */
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async/',
$html,
'normal requirements don\'t have async'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" defer/',
$html,
'normal requirements don\'t have defer'
);
$this->assertDoesNotMatchRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?m=\d+" async defer/',
$html,
'normal requirements don\'t have async/defer'
);
2022-04-14 03:12:59 +02:00
unlink($combinedFilePath ?? '');
}
public function testCombinedCss()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->combineFiles(
'print.css',
[
'css/RequirementsTest_print_a.css',
'css/RequirementsTest_print_b.css',
'css/RequirementsTest_print_d.css',
'public/css/RequirementsTest_print_e.css',
],
[
'media' => 'print'
]
);
$html = $backend->includeInHTML(self::$html_template);
$this->assertMatchesRegularExpression(
'/href=".*\/print\-69ce614\.css/',
$html,
'Print stylesheets have been combined.'
);
$this->assertMatchesRegularExpression(
'/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',
[
'css/RequirementsTest_b.css',
'css/RequirementsTest_c.css',
'css/RequirementsTest_d.css',
'public/css/RequirementsTest_e.css',
]
);
$backend->combineFiles(
'style.css',
[
'css/RequirementsTest_b.css',
'css/RequirementsTest_c.css',
'css/RequirementsTest_d.css',
'public/css/RequirementsTest_e.css',
]
);
$html = $backend->includeInHTML(self::$html_template);
$this->assertMatchesRegularExpression(
'/href=".*\/style\-8011538\.css/',
$html,
'Stylesheets have been combined.'
);
}
public function testBlockedCombinedJavascript()
{
/** @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->assertFileDoesNotExist($combinedFilePath);
$this->assertDoesNotMatchRegularExpression(
'/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('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->assertStringNotContainsString(
"alert('b')",
2022-04-14 03:12:59 +02:00
file_get_contents($combinedFilePath2 ?? ''),
'blocked uncombined files are not included'
);
$backend->unblock('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'",
'javascript/RequirementsTest_c.js',
'RequirementsTest_bc.js'
));
$backend->combineFiles(
'RequirementsTest_ac.js',
[
'javascript/RequirementsTest_a.js',
'javascript/RequirementsTest_c.js'
]
);
}
public function testArgsInUrls()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
2020-05-13 06:07:31 +02:00
$generator = Injector::inst()->get(ResourceURLGenerator::class);
$generator->setNonceStyle('mtime');
$backend->javascript('javascript/RequirementsTest_a.js?test=1&test=2&test=3');
$backend->css('css/RequirementsTest_a.css?test=1&test=2&test=3');
$html = $backend->includeInHTML(self::$html_template);
/* Javascript has correct path */
$this->assertMatchesRegularExpression(
'/src=".*\/RequirementsTest_a\.js\?test=1&amp;test=2&amp;test=3&amp;m=\d\d+/',
$html,
'javascript has correct path'
);
/* CSS has correct path */
$this->assertMatchesRegularExpression(
'/href=".*\/RequirementsTest_a\.css\?test=1&amp;test=2&amp;test=3&amp;m=\d\d+/',
$html,
'css has correct path'
);
}
public function testRequirementsBackend()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript('a.js');
$this->assertCount(
1,
$backend->getJavascript(),
"There should be only 1 file included in required javascript."
);
$this->assertArrayHasKey(
'a.js',
$backend->getJavascript(),
"a.js should be included in required javascript."
);
$backend->javascript('b.js');
$this->assertCount(
2,
$backend->getJavascript(),
"There should be 2 files included in required javascript."
);
$backend->block('a.js');
$this->assertCount(
1,
$backend->getJavascript(),
"There should be only 1 file included in required javascript."
);
$this->assertArrayNotHasKey(
'a.js',
$backend->getJavascript(),
"a.js should not be included in required javascript after it has been blocked."
);
$this->assertArrayHasKey(
'b.js',
$backend->getJavascript(),
"b.js should be included in required javascript."
);
$backend->css('a.css');
$this->assertCount(
1,
$backend->getCSS(),
"There should be only 1 file included in required css."
);
$this->assertArrayHasKey(
'a.css',
$backend->getCSS(),
"a.css should be in required css."
);
$backend->block('a.css');
$this->assertCount(
0,
$backend->getCSS(),
"There should be nothing in required css after file has been blocked."
);
}
public function testAppendAndBlockWithModuleResourceLoader()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
// Note: assumes that client/styles/debug.css is "exposed"
$backend->css('silverstripe/framework:client/styles/debug.css');
$this->assertCount(
1,
$backend->getCSS(),
'Module resource can be loaded via resources reference'
);
$backend->block('silverstripe/framework:client/styles/debug.css');
$this->assertCount(
0,
$backend->getCSS(),
'Module resource can be blocked via resources reference'
);
}
public function testConditionalTemplateRequire()
{
// Set /SSViewerTest and /SSViewerTest/public as themes
SSViewer::set_themes([
'/',
SSViewer::PUBLIC_THEME
]);
ThemeResourceLoader::set_instance(new ThemeResourceLoader(__DIR__ . '/SSViewerTest'));
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$holder = Requirements::backend();
Requirements::set_backend($backend);
$data = new ArrayData([
'FailTest' => true,
]);
$data->renderWith('RequirementsTest_Conditionals');
$this->assertFileIncluded($backend, 'css', 'css/RequirementsTest_a.css');
$this->assertFileIncluded(
$backend,
'js',
[
'javascript/RequirementsTest_b.js',
'javascript/RequirementsTest_c.js'
]
);
$this->assertFileNotIncluded($backend, 'js', 'javascript/RequirementsTest_a.js');
$this->assertFileNotIncluded(
$backend,
'css',
[
'css/RequirementsTest_b.css',
'css/RequirementsTest_c.css'
]
);
$backend->clear();
$data = new ArrayData(
[
'FailTest' => false,
]
);
$data->renderWith('RequirementsTest_Conditionals');
$this->assertFileNotIncluded($backend, 'css', 'css/RequirementsTest_a.css');
$this->assertFileNotIncluded(
$backend,
'js',
[
'javascript/RequirementsTest_b.js',
'javascript/RequirementsTest_c.js',
]
);
$this->assertFileIncluded($backend, 'js', 'javascript/RequirementsTest_a.js');
$this->assertFileIncluded(
$backend,
'css',
[
'css/RequirementsTest_b.css',
'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->assertStringContainsString('<head><script', $html);
$backend->setWriteJavascriptToBody(true);
$html = $backend->includeInHTML($template);
$this->assertStringNotContainsString('<head><script', $html);
$this->assertStringContainsString("</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('javascript/RequirementsTest_a.js');
$html = $backend->includeInHTML($template);
//wiping out commented-out html
2022-04-14 03:12:59 +02:00
$html = preg_replace('/<!--(.*)-->/Uis', '', $html ?? '');
$this->assertStringContainsString("RequirementsTest_a.js", $html);
}
public function testCommentedOutScriptTagIsIgnored()
{
/// Disable nonce
2017-10-03 03:21:24 +02:00
$urlGenerator = new SimpleResourceURLGenerator();
Injector::inst()->registerService($urlGenerator, ResourceURLGenerator::class);
$template = '<html><head></head><body><!--<script>alert("commented out");</script>-->'
. '<h1>more content</h1></body></html>';
2017-10-03 03:21:24 +02:00
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$src = 'javascript/RequirementsTest_a.js';
$backend->javascript($src);
$html = $backend->includeInHTML($template);
2017-10-03 03:21:24 +02:00
$urlSrc = $urlGenerator->urlForResource($src);
$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()
{
/// Disable nonce
$urlGenerator = new SimpleResourceURLGenerator();
Injector::inst()->registerService($urlGenerator, ResourceURLGenerator::class);
$template = '<html><head></head><body><header>My header</header><p>Body</p></body></html>';
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript('javascript/RequirementsTest_a.js');
$backend->javascript('javascript/RequirementsTest_b.js?foo=bar&bla=blubb');
$backend->css('css/RequirementsTest_a.css');
$backend->css('css/RequirementsTest_b.css?foo=bar&bla=blubb');
$urlGenerator->setNonceStyle('mtime');
$html = $backend->includeInHTML($template);
$this->assertMatchesRegularExpression('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
$this->assertMatchesRegularExpression('/RequirementsTest_b\.js\?foo=bar&amp;bla=blubb&amp;m=[\d]*"/', $html);
$this->assertMatchesRegularExpression('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
$this->assertMatchesRegularExpression('/RequirementsTest_b\.css\?foo=bar&amp;bla=blubb&amp;m=[\d]*"/', $html);
$urlGenerator->setNonceStyle(null);
$html = $backend->includeInHTML($template);
$this->assertStringNotContainsString('RequirementsTest_a.js=', $html);
$this->assertDoesNotMatchRegularExpression('/RequirementsTest_a\.js\?m=[\d]*"/', $html);
$this->assertDoesNotMatchRegularExpression('/RequirementsTest_b\.js\?foo=bar&amp;bla=blubb&amp;m=[\d]*"/', $html);
$this->assertDoesNotMatchRegularExpression('/RequirementsTest_a\.css\?m=[\d]*"/', $html);
$this->assertDoesNotMatchRegularExpression('/RequirementsTest_b\.css\?foo=bar&amp;bla=blubb&amp;m=[\d]*"/', $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>';
// Test that provided files block subsequent files
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript('javascript/RequirementsTest_a.js');
$backend->javascript(
'javascript/RequirementsTest_b.js',
[
'provides' => [
'javascript/RequirementsTest_a.js',
'javascript/RequirementsTest_c.js',
],
]
);
$backend->javascript('javascript/RequirementsTest_c.js');
// Note that _a.js isn't considered provided because it was included
// before it was marked as provided
$this->assertEquals(
[
'javascript/RequirementsTest_c.js' => 'javascript/RequirementsTest_c.js'
],
$backend->getProvidedScripts()
);
$html = $backend->includeInHTML($template);
$this->assertMatchesRegularExpression('/src=".*\/RequirementsTest_a\.js/', $html);
$this->assertMatchesRegularExpression('/src=".*\/RequirementsTest_b\.js/', $html);
$this->assertDoesNotMatchRegularExpression('/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', ['javascript/RequirementsTest_a.js']);
$backend->javascript(
'javascript/RequirementsTest_b.js',
[
'provides' => [
'javascript/RequirementsTest_a.js',
'javascript/RequirementsTest_c.js'
]
]
);
$backend->combineFiles('combined_c.js', ['javascript/RequirementsTest_c.js']);
$this->assertEquals(
[
'javascript/RequirementsTest_c.js' => 'javascript/RequirementsTest_c.js'
],
$backend->getProvidedScripts()
);
$html = $backend->includeInHTML($template);
$this->assertMatchesRegularExpression('/src=".*\/combined_a/', $html);
$this->assertMatchesRegularExpression('/src=".*\/RequirementsTest_b\.js/', $html);
$this->assertDoesNotMatchRegularExpression('/src=".*\/combined_c/', $html);
$this->assertDoesNotMatchRegularExpression('/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 = [];
foreach ($files as $file) {
2022-04-14 03:12:59 +02:00
if (!array_key_exists($file, $includedFiles ?? [])) {
$failedMatches[] = $file;
}
}
$this->assertCount(
0,
$failedMatches,
"Failed asserting the $type files '"
. implode("', '", $failedMatches)
. "' have exact matches in the required elements:\n'"
2022-04-14 03:12:59 +02:00
. implode("'\n'", array_keys($includedFiles ?? [])) . "'"
);
} else {
$this->assertArrayHasKey(
$files,
$includedFiles,
"Failed asserting the $type file '$files' has an exact match in the required elements:\n'"
2022-04-14 03:12:59 +02:00
. implode("'\n'", array_keys($includedFiles ?? [])) . "'"
);
}
}
public function assertFileNotIncluded($backend, $type, $files)
{
$includedFiles = $this->getBackendFiles($backend, $type);
if (is_array($files)) {
$failedMatches = [];
foreach ($files as $file) {
2022-04-14 03:12:59 +02:00
if (array_key_exists($file, $includedFiles ?? [])) {
$failedMatches[] = $file;
}
}
$this->assertCount(
0,
$failedMatches,
"Failed asserting the $type files '"
. implode("', '", $failedMatches)
. "' do not have exact matches in the required elements:\n'"
2022-04-14 03:12:59 +02:00
. implode("'\n'", array_keys($includedFiles ?? [])) . "'"
);
} else {
$this->assertArrayNotHasKey(
$files,
$includedFiles,
"Failed asserting the $type file '$files' does not have an exact match in the required elements:"
2022-04-14 03:12:59 +02:00
. "\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)
{
2022-04-14 03:12:59 +02:00
$type = strtolower($type ?? '');
switch (strtolower($type ?? '')) {
case 'css':
return $backend->getCSS();
case 'js':
case 'javascript':
case 'script':
return $backend->getJavascript();
}
return [];
}
public function testAddI18nJavascript()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->add_i18n_javascript('i18n');
$actual = $backend->getJavascript();
// English and English US should always be loaded no matter what
$this->assertArrayHasKey('i18n/en.js', $actual);
$this->assertArrayHasKey('i18n/en_US.js', $actual);
$this->assertArrayHasKey('i18n/en-us.js', $actual);
}
public function testAddI18nJavascriptWithDefaultLocale()
{
i18n::config()->set('default_locale', 'fr_CA');
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->add_i18n_javascript('i18n');
$actual = $backend->getJavascript();
$this->assertArrayHasKey('i18n/en.js', $actual);
$this->assertArrayHasKey('i18n/en_US.js', $actual);
$this->assertArrayHasKey('i18n/en-us.js', $actual);
// Default locale should be loaded
$this->assertArrayHasKey('i18n/fr.js', $actual);
$this->assertArrayHasKey('i18n/fr_CA.js', $actual);
$this->assertArrayHasKey('i18n/fr-ca.js', $actual);
}
public function testAddI18nJavascriptWithMemberLocale()
{
i18n::set_locale('en_GB');
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->add_i18n_javascript('i18n');
$actual = $backend->getJavascript();
// The current member's Locale as defined by i18n::get_locale should be loaded
$this->assertArrayHasKey('i18n/en.js', $actual);
$this->assertArrayHasKey('i18n/en_US.js', $actual);
$this->assertArrayHasKey('i18n/en-us.js', $actual);
$this->assertArrayHasKey('i18n/en-gb.js', $actual);
$this->assertArrayHasKey('i18n/en_GB.js', $actual);
}
public function testAddI18nJavascriptWithMissingLocale()
{
i18n::set_locale('fr_BE');
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->add_i18n_javascript('i18n');
$actual = $backend->getJavascript();
// We don't have a file for French Belgium. Regular french should be loaded anyway.
$this->assertArrayHasKey('i18n/en.js', $actual);
$this->assertArrayHasKey('i18n/en_US.js', $actual);
$this->assertArrayHasKey('i18n/en-us.js', $actual);
$this->assertArrayHasKey('i18n/fr.js', $actual);
}
public function testSriAttributes()
{
/** @var Requirements_Backend $backend */
$backend = Injector::inst()->create(Requirements_Backend::class);
$this->setupRequirements($backend);
$backend->javascript('javascript/RequirementsTest_a.js', ['integrity' => 'abc', 'crossorigin' => 'use-credentials']);
$backend->css('css/RequirementsTest_a.css', null, ['integrity' => 'def', 'crossorigin' => 'anonymous']);
$html = $backend->includeInHTML(self::$html_template);
/* Javascript has correct attributes */
$this->assertMatchesRegularExpression(
'#<script type="application/javascript" src=".*/javascript/RequirementsTest_a.js.*" integrity="abc" crossorigin="use-credentials"#',
$html,
'javascript has correct sri attributes'
);
/* CSS has correct attributes */
$this->assertMatchesRegularExpression(
'#<link .*href=".*/RequirementsTest_a\.css.*" integrity="def" crossorigin="anonymous"#',
$html,
'css has correct sri attributes'
);
}
}