feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
- Base: wintercms/winter branch 1.2 (full framework) - Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS - Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication - Partials: hero (offline-first), features, modes (offline/nube toggle), screenshots, pricing (3 planes), comparison, FAQ, CTA - Plugin VivesPOS.Site with ContactForm - Dockerfile: PHP 8.2 Apache, port 80, healthcheck - Added winter/wn-pages, blog, sitemap, seo plugins - Active theme set to vivespos
This commit is contained in:
121
modules/cms/tests/classes/AssetTest.php
Normal file
121
modules/cms/tests/classes/AssetTest.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\Asset;
|
||||
use Cms\Classes\Theme;
|
||||
|
||||
class AssetTest extends TestCase
|
||||
{
|
||||
public function testLoad()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
// Valid direct path
|
||||
$this->assertStringContainsString(
|
||||
'console.log(\'script1.js\');',
|
||||
Asset::load($theme, 'js/script1.js')->content
|
||||
);
|
||||
|
||||
// Valid direct subdirectory path
|
||||
$this->assertStringContainsString(
|
||||
'console.log(\'subdir/script1.js\');',
|
||||
Asset::load($theme, 'js/subdir/script1.js')->content
|
||||
);
|
||||
|
||||
// Valid relative path
|
||||
$this->assertStringContainsString(
|
||||
'console.log(\'script2.js\');',
|
||||
Asset::load($theme, 'js/subdir/../script2.js')->content
|
||||
);
|
||||
|
||||
// Invalid theme path
|
||||
$this->assertNull(
|
||||
Asset::load($theme, 'js/invalid.js')
|
||||
);
|
||||
|
||||
// Check that we cannot break out of assets directory
|
||||
$this->assertNull(
|
||||
Asset::load($theme, '../../../../js/helpers/fakeDom.js')
|
||||
);
|
||||
$this->assertNull(
|
||||
Asset::load($theme, '../content/html-content.htm')
|
||||
);
|
||||
|
||||
// Check that we cannot load directories directly
|
||||
$this->assertNull(
|
||||
Asset::load($theme, 'js/subdir')
|
||||
);
|
||||
|
||||
// Check that we definitely cannot load external PHP files
|
||||
$this->assertNull(
|
||||
Asset::load($theme, '../../../../../config/database.php')
|
||||
);
|
||||
}
|
||||
|
||||
public function testGetPath()
|
||||
{
|
||||
// Test some pathing fringe cases
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$assetClass = new Asset($theme);
|
||||
$themeDir = $theme->getPath();
|
||||
|
||||
// Direct paths
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/script1.js'),
|
||||
$assetClass->getFilePath('js/script1.js')
|
||||
);
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/script1.js'),
|
||||
$assetClass->getFilePath('/js/script1.js')
|
||||
);
|
||||
|
||||
// Direct path to a directory
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/subdir'),
|
||||
$assetClass->getFilePath('/js/subdir')
|
||||
);
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/subdir'),
|
||||
$assetClass->getFilePath('/js/subdir/')
|
||||
);
|
||||
|
||||
// Relative paths
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/script2.js'),
|
||||
$assetClass->getFilePath('./js/script2.js')
|
||||
);
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/script2.js'),
|
||||
$assetClass->getFilePath('/js/subdir/../script2.js')
|
||||
);
|
||||
|
||||
// Missing file, but valid directory (allows for new files)
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/missing.js'),
|
||||
$assetClass->getFilePath('/js/missing.js')
|
||||
);
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/missing.js'),
|
||||
$assetClass->getFilePath('js/missing.js')
|
||||
);
|
||||
|
||||
// Missing file and missing directory (new directories are created as needed)
|
||||
$this->assertEquals(
|
||||
str_replace('/', DIRECTORY_SEPARATOR, $themeDir . '/assets/js/missing/missing.js'),
|
||||
$assetClass->getFilePath('/js/missing/missing.js')
|
||||
);
|
||||
|
||||
// Ensure we cannot get paths outside of the assets directory
|
||||
$this->assertFalse(
|
||||
$assetClass->getFilePath('../../../../js/helpers/fakeDom.js')
|
||||
);
|
||||
$this->assertFalse(
|
||||
$assetClass->getFilePath('../content/html-content.htm')
|
||||
);
|
||||
$this->assertFalse(
|
||||
$assetClass->getFilePath('../../../../../config/database.php')
|
||||
);
|
||||
}
|
||||
}
|
||||
217
modules/cms/tests/classes/AutoDatasourceTest.php
Normal file
217
modules/cms/tests/classes/AutoDatasourceTest.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use Exception;
|
||||
use System\Tests\Bootstrap\PluginTestCase;
|
||||
use Cms\Classes\AutoDatasource;
|
||||
use Winter\Storm\Database\MemoryCache;
|
||||
use Winter\Storm\Database\Model;
|
||||
use Winter\Storm\Halcyon\Datasource\DbDatasource;
|
||||
use Winter\Storm\Halcyon\Datasource\FileDatasource;
|
||||
use Winter\Storm\Support\Facades\DB;
|
||||
|
||||
class CmsThemeTemplateFixture extends Model
|
||||
{
|
||||
protected $guarded = [];
|
||||
|
||||
public $timestamps = false;
|
||||
|
||||
public $table = 'cms_theme_templates';
|
||||
}
|
||||
|
||||
class AutoDatasourceTest extends PluginTestCase
|
||||
{
|
||||
/**
|
||||
* Array of model fixtures.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $fixtures = [];
|
||||
|
||||
/**
|
||||
* AutoDatasource object.
|
||||
*
|
||||
* @var Cms\Classes\AutoDatasource;
|
||||
*/
|
||||
public $datasource;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->fixtures = [];
|
||||
|
||||
// Create fixtures of template data
|
||||
$this->fixtures[] = CmsThemeTemplateFixture::create([
|
||||
'source' => 'test',
|
||||
'path' => 'partials/page-partial.htm',
|
||||
'content' => 'AutoDatasource partials/page-partial.htm',
|
||||
'file_size' => 40
|
||||
]);
|
||||
|
||||
$this->fixtures[] = CmsThemeTemplateFixture::create([
|
||||
'source' => 'test',
|
||||
'path' => 'partials/testpost/default.htm',
|
||||
'content' => 'AutoDatasource partials/testpost/default.htm',
|
||||
'file_size' => 44
|
||||
]);
|
||||
|
||||
$this->fixtures[] = CmsThemeTemplateFixture::create([
|
||||
'source' => 'test',
|
||||
'path' => 'partials/subdir/test.htm',
|
||||
'content' => 'AutoDatasource partials/subdir/test.htm',
|
||||
'file_size' => 39,
|
||||
'updated_at' => '2019-06-01 12:00:00'
|
||||
]);
|
||||
|
||||
$this->fixtures[] = CmsThemeTemplateFixture::create([
|
||||
'source' => 'test',
|
||||
'path' => 'partials/nesting/level2.htm',
|
||||
'content' => 'AutoDatasource partials/nesting/level2.htm',
|
||||
'file_size' => 42,
|
||||
'deleted_at' => '2019-01-01 00:00:00'
|
||||
]);
|
||||
|
||||
// Create AutoDatasource
|
||||
$this->datasource = new AutoDatasource([
|
||||
'database' => new DbDatasource('test', 'cms_theme_templates'),
|
||||
'filesystem' => new FileDatasource(
|
||||
base_path('modules/system/tests/fixtures/themes/test'),
|
||||
\App::make('files')
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
foreach ($this->fixtures as $fixture) {
|
||||
$fixture->delete();
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testSelect()
|
||||
{
|
||||
$results = collect($this->datasource->select('partials'))
|
||||
->keyBy('fileName')
|
||||
->toArray();
|
||||
|
||||
// Should be 14 partials in filesystem (tests/fixtures/themes/test), and 1 created directly in database.
|
||||
// 1 of the filesystem partials should be marked deleted in database.
|
||||
$this->assertCount(14, $results);
|
||||
|
||||
// Database-only partial should be available
|
||||
$this->assertArrayHasKey('subdir/test.htm', $results);
|
||||
$this->assertEquals(
|
||||
'AutoDatasource partials/subdir/test.htm',
|
||||
$results['subdir/test.htm']['content']
|
||||
);
|
||||
|
||||
// Two filesystem partials should be overriden by database
|
||||
$this->assertEquals(
|
||||
'AutoDatasource partials/page-partial.htm',
|
||||
$results['page-partial.htm']['content']
|
||||
);
|
||||
$this->assertEquals(
|
||||
'AutoDatasource partials/testpost/default.htm',
|
||||
$results['testpost/default.htm']['content']
|
||||
);
|
||||
|
||||
// One filesystem partial should be marked deleted in database
|
||||
$this->assertArrayNotHasKey('nesting/level2.htm', $results);
|
||||
}
|
||||
|
||||
public function testPathCacheValueShapes()
|
||||
{
|
||||
$pathCache = self::getProtectedProperty($this->datasource, 'pathCache');
|
||||
|
||||
// Database records report their last modified time, deleted records report false
|
||||
$this->assertIsInt($pathCache[0]['partials/subdir/test.htm']);
|
||||
$this->assertEquals(
|
||||
strtotime('2019-06-01 12:00:00'),
|
||||
$pathCache[0]['partials/subdir/test.htm']
|
||||
);
|
||||
$this->assertFalse($pathCache[0]['partials/nesting/level2.htm']);
|
||||
|
||||
// Filesystem records continue to report true so that their mtime is resolved live
|
||||
$this->assertTrue($pathCache[1]['partials/layout-partial.htm']);
|
||||
}
|
||||
|
||||
public function testLastModifiedIsServedFromPathCacheWithoutQuerying()
|
||||
{
|
||||
// The duplicate query cache would otherwise mask a query issued by this call
|
||||
MemoryCache::instance()->flush();
|
||||
|
||||
DB::connection()->flushQueryLog();
|
||||
DB::connection()->enableQueryLog();
|
||||
|
||||
$mtime = $this->datasource->lastModified('partials', 'subdir/test', 'htm');
|
||||
|
||||
$queries = DB::connection()->getQueryLog();
|
||||
DB::connection()->disableQueryLog();
|
||||
|
||||
$this->assertEquals(strtotime('2019-06-01 12:00:00'), $mtime);
|
||||
$this->assertCount(0, $queries, 'lastModified() should not query the database');
|
||||
}
|
||||
|
||||
public function testLastModifiedFallsBackToFilesystemDatasource()
|
||||
{
|
||||
$path = base_path('modules/system/tests/fixtures/themes/test/partials/layout-partial.htm');
|
||||
|
||||
$this->assertEquals(
|
||||
filemtime($path),
|
||||
$this->datasource->lastModified('partials', 'layout-partial', 'htm')
|
||||
);
|
||||
}
|
||||
|
||||
public function testLastModifiedIsStableForNullUpdatedAt()
|
||||
{
|
||||
$this->fixtures[] = CmsThemeTemplateFixture::create([
|
||||
'source' => 'test',
|
||||
'path' => 'partials/no-timestamp.htm',
|
||||
'content' => 'AutoDatasource partials/no-timestamp.htm',
|
||||
'file_size' => 40,
|
||||
'updated_at' => null,
|
||||
]);
|
||||
|
||||
$this->datasource->populateCache(true);
|
||||
|
||||
$pathCache = self::getProtectedProperty($this->datasource, 'pathCache');
|
||||
$cached = $pathCache[0]['partials/no-timestamp.htm'];
|
||||
|
||||
// updated_at is nullable, and Carbon::parse(null) resolves to "now". The value is
|
||||
// resolved once, when the path cache is built, so lastModified() reports it
|
||||
// consistently rather than returning a different result on every call.
|
||||
// Note this does not make the Halcyon cache usable for such records: selectOne()
|
||||
// still resolves their mtime live, so the two disagree and the cache is busted on
|
||||
// every request. That is pre-existing and not addressed here.
|
||||
$this->assertIsInt($cached);
|
||||
$this->assertEquals($cached, $this->datasource->lastModified('partials', 'no-timestamp', 'htm'));
|
||||
}
|
||||
|
||||
public function testLastModifiedThrowsForDeletedPath()
|
||||
{
|
||||
$this->expectException(Exception::class);
|
||||
$this->expectExceptionMessage('partials/nesting/level2.htm is deleted');
|
||||
|
||||
$this->datasource->lastModified('partials', 'nesting/level2', 'htm');
|
||||
}
|
||||
|
||||
public function testLastModifiedReflectsUpdatesMadeThroughTheDatasource()
|
||||
{
|
||||
$before = $this->datasource->lastModified('partials', 'subdir/test', 'htm');
|
||||
|
||||
$this->datasource->update('partials', 'subdir/test', 'htm', 'Updated content');
|
||||
|
||||
$after = $this->datasource->lastModified('partials', 'subdir/test', 'htm');
|
||||
|
||||
// Editing a template must take effect immediately, without clearing the cache
|
||||
$this->assertGreaterThan($before, $after);
|
||||
$this->assertEquals(
|
||||
'Updated content',
|
||||
$this->datasource->selectOne('partials', 'subdir/test', 'htm')['content']
|
||||
);
|
||||
}
|
||||
}
|
||||
333
modules/cms/tests/classes/CmsCompoundObjectTest.php
Normal file
333
modules/cms/tests/classes/CmsCompoundObjectTest.php
Normal file
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\CmsCompoundObject;
|
||||
use Cms\Classes\CmsObject;
|
||||
use Cms\Classes\Theme;
|
||||
use Winter\Storm\Halcyon\Model;
|
||||
|
||||
class TestCmsCompoundObject extends CmsCompoundObject
|
||||
{
|
||||
protected $dirName = 'testobjects';
|
||||
|
||||
protected function parseSettings()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class TestParsedCmsCompoundObject extends CmsCompoundObject
|
||||
{
|
||||
protected $dirName = 'testobjects';
|
||||
}
|
||||
|
||||
class TestTemporaryCmsCompoundObject extends CmsCompoundObject
|
||||
{
|
||||
protected $dirName = 'temporary';
|
||||
|
||||
protected function parseSettings()
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
class CmsCompoundObjectTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Model::clearBootedModels();
|
||||
Model::flushEventListeners();
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Archive.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Post.php';
|
||||
}
|
||||
|
||||
public function testLoadFile()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$obj = TestCmsCompoundObject::load($theme, 'compound.htm');
|
||||
$this->assertStringContainsString("\$controller->data['something'] = 'some value'", $obj->code);
|
||||
$this->assertEquals('<p>This is a paragraph</p>', $obj->markup);
|
||||
$this->assertIsArray($obj->settings);
|
||||
$this->assertArrayHasKey('var', $obj->settings);
|
||||
$this->assertEquals('value', $obj->settings['var']);
|
||||
|
||||
$this->assertArrayHasKey('components', $obj->settings);
|
||||
|
||||
$this->assertArrayHasKey('section', $obj->settings['components']);
|
||||
$this->assertIsArray($obj->settings['components']['section']);
|
||||
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
|
||||
$this->assertEquals(10, $obj->settings['components']['section']['version']);
|
||||
|
||||
$this->assertEquals('value', $obj->var);
|
||||
|
||||
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
|
||||
$this->assertEquals(10, $obj->settings['components']['section']['version']);
|
||||
}
|
||||
|
||||
public function testParseComponentSettings()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$obj = TestCmsCompoundObject::load($theme, 'component.htm');
|
||||
$this->assertArrayHasKey('components', $obj->settings);
|
||||
$this->assertIsArray($obj->settings['components']);
|
||||
$this->assertArrayHasKey('testArchive', $obj->settings['components']);
|
||||
$this->assertArrayHasKey('posts-per-page', $obj->settings['components']['testArchive']);
|
||||
$this->assertEquals(10, $obj->settings['components']['testArchive']['posts-per-page']);
|
||||
}
|
||||
|
||||
public function testHasComponent()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$obj = TestCmsCompoundObject::load($theme, 'components.htm');
|
||||
$this->assertArrayHasKey('components', $obj->settings);
|
||||
|
||||
$this->assertIsArray($obj->settings['components']);
|
||||
$this->assertArrayHasKey('testArchive firstAlias', $obj->settings['components']);
|
||||
$this->assertArrayHasKey('Winter\Tester\Components\Post secondAlias', $obj->settings['components']);
|
||||
|
||||
// Explicit
|
||||
$this->assertEquals('testArchive firstAlias', $obj->hasComponent('testArchive'));
|
||||
$this->assertEquals('Winter\Tester\Components\Post secondAlias', $obj->hasComponent('Winter\Tester\Components\Post'));
|
||||
|
||||
// Resolved
|
||||
$this->assertEquals('testArchive firstAlias', $obj->hasComponent('Winter\Tester\Components\Archive'));
|
||||
$this->assertEquals('Winter\Tester\Components\Post secondAlias', $obj->hasComponent('testPost'));
|
||||
|
||||
// Negative test
|
||||
$this->assertFalse($obj->hasComponent('yooHooBigSummerBlowOut'));
|
||||
$this->assertFalse($obj->hasComponent('Winter\Tester\Components\BigSummer'));
|
||||
}
|
||||
|
||||
public function testGetComponentProperties()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$obj = TestCmsCompoundObject::load($theme, 'components.htm');
|
||||
|
||||
$properties = $obj->getComponentProperties('Winter\Tester\Components\Post');
|
||||
$emptyProperties = $obj->getComponentProperties('Winter\Tester\Components\Archive');
|
||||
$notExistingProperties = $obj->getComponentProperties('This\Is\Not\Component');
|
||||
$this->assertIsArray($properties);
|
||||
$this->assertArrayHasKey('show-featured', $properties);
|
||||
$this->assertTrue((bool)$properties['show-featured']);
|
||||
$this->assertEquals('true', $properties['show-featured']);
|
||||
$this->assertCount(1, $properties);
|
||||
$this->assertCount(0, $emptyProperties);
|
||||
$this->assertCount(0, $notExistingProperties);
|
||||
}
|
||||
|
||||
public function testCache()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$themePath = $theme->getPath();
|
||||
|
||||
/*
|
||||
* Prepare the test file
|
||||
*/
|
||||
|
||||
$srcPath = $themePath . '/testobjects/compound.htm';
|
||||
$this->assertFileExists($srcPath);
|
||||
$testContent = file_get_contents($srcPath);
|
||||
$this->assertNotEmpty($testContent);
|
||||
|
||||
$filePath = $themePath .= '/temporary/testcompound.htm';
|
||||
if (file_exists($filePath)) {
|
||||
@unlink($filePath);
|
||||
}
|
||||
|
||||
$this->assertFileNotExists($filePath);
|
||||
file_put_contents($filePath, $testContent);
|
||||
|
||||
/*
|
||||
* Load the test object to initialize the cache
|
||||
*/
|
||||
|
||||
$obj = TestTemporaryCmsCompoundObject::loadCached($theme, 'testcompound.htm');
|
||||
$this->assertFalse($obj->isLoadedFromCache());
|
||||
$this->assertEquals($testContent, $obj->getContent());
|
||||
$this->assertEquals('testcompound.htm', $obj->getFileName());
|
||||
$this->assertEquals('<p>This is a paragraph</p>', $obj->markup);
|
||||
$this->assertIsArray($obj->settings);
|
||||
$this->assertArrayHasKey('var', $obj->settings);
|
||||
$this->assertEquals('value', $obj->settings['var']);
|
||||
|
||||
$this->assertArrayHasKey('components', $obj->settings);
|
||||
|
||||
$this->assertIsArray($obj->settings['components']['section']);
|
||||
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
|
||||
$this->assertEquals(10, $obj->settings['components']['section']['version']);
|
||||
|
||||
$this->assertEquals('value', $obj->var);
|
||||
$this->assertIsArray($obj->settings['components']['section']);
|
||||
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
|
||||
$this->assertEquals(10, $obj->settings['components']['section']['version']);
|
||||
|
||||
/*
|
||||
* Load the test object again, it should be loaded from the cache this time
|
||||
*/
|
||||
|
||||
CmsObject::clearInternalCache();
|
||||
$obj = TestTemporaryCmsCompoundObject::loadCached($theme, 'testcompound.htm');
|
||||
$this->assertTrue($obj->isLoadedFromCache());
|
||||
$this->assertEquals($testContent, $obj->getContent());
|
||||
$this->assertEquals('testcompound.htm', $obj->getFileName());
|
||||
$this->assertEquals('<p>This is a paragraph</p>', $obj->markup);
|
||||
$this->assertIsArray($obj->settings);
|
||||
$this->assertArrayHasKey('var', $obj->settings);
|
||||
$this->assertEquals('value', $obj->settings['var']);
|
||||
|
||||
$this->assertArrayHasKey('components', $obj->settings);
|
||||
|
||||
$this->assertIsArray($obj->settings['components']['section']);
|
||||
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
|
||||
$this->assertEquals(10, $obj->settings['components']['section']['version']);
|
||||
|
||||
$this->assertEquals('value', $obj->var);
|
||||
$this->assertIsArray($obj->settings['components']['section']);
|
||||
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
|
||||
$this->assertEquals(10, $obj->settings['components']['section']['version']);
|
||||
}
|
||||
|
||||
public function testUndefinedProperty()
|
||||
{
|
||||
$obj = new TestCmsCompoundObject;
|
||||
$this->assertNull($obj->something);
|
||||
}
|
||||
|
||||
public function testSaveMarkup()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$destFilePath = $theme->getPath() . '/testobjects/compound-markup.htm';
|
||||
if (file_exists($destFilePath)) {
|
||||
unlink($destFilePath);
|
||||
}
|
||||
|
||||
$this->assertFileNotExists($destFilePath);
|
||||
|
||||
$obj = TestCmsCompoundObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'markup' => '<p>Hello, world!</p>',
|
||||
'fileName' => 'compound-markup'
|
||||
]);
|
||||
$obj->save();
|
||||
|
||||
$referenceFilePath = base_path() . '/modules/cms/tests/fixtures/reference/compound-markup.htm';
|
||||
$this->assertFileExists($referenceFilePath);
|
||||
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertFileEqualsNormalized($referenceFilePath, $destFilePath);
|
||||
}
|
||||
|
||||
public function testSaveMarkupAndSettings()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$destFilePath = $theme->getPath() . '/testobjects/compound-markup-settings.htm';
|
||||
if (file_exists($destFilePath)) {
|
||||
unlink($destFilePath);
|
||||
}
|
||||
|
||||
$this->assertFileNotExists($destFilePath);
|
||||
|
||||
$obj = TestCmsCompoundObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'settings' => ['var' => 'value'],
|
||||
'markup' => '<p>Hello, world!</p>',
|
||||
'fileName' => 'compound-markup-settings'
|
||||
]);
|
||||
$obj->save();
|
||||
|
||||
$referenceFilePath = base_path() . '/modules/cms/tests/fixtures/reference/compound-markup-settings.htm';
|
||||
$this->assertFileExists($referenceFilePath);
|
||||
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertFileEqualsNormalized($referenceFilePath, $destFilePath);
|
||||
}
|
||||
|
||||
public function testSaveFull()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$destFilePath = $theme->getPath() . '/testobjects/compound.htm';
|
||||
if (file_exists($destFilePath)) {
|
||||
unlink($destFilePath);
|
||||
}
|
||||
|
||||
$this->assertFileNotExists($destFilePath);
|
||||
|
||||
$obj = TestCmsCompoundObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'fileName' => 'compound',
|
||||
'settings' => ['var' => 'value'],
|
||||
'code' => 'function a() {return true;}',
|
||||
'markup' => '<p>Hello, world!</p>'
|
||||
]);
|
||||
$obj->save();
|
||||
|
||||
$referenceFilePath = base_path() . '/modules/cms/tests/fixtures/reference/compound-full.htm';
|
||||
$this->assertFileExists($referenceFilePath);
|
||||
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertFileEqualsNormalized($referenceFilePath, $destFilePath);
|
||||
}
|
||||
|
||||
public function testGetViewBagPopulated()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$obj = TestParsedCmsCompoundObject::load($theme, 'viewbag.htm');
|
||||
$this->assertNull($obj->code);
|
||||
$this->assertEquals('<p>Chop Suey!</p>', $obj->markup);
|
||||
$this->assertIsArray($obj->settings);
|
||||
$this->assertArrayHasKey('var', $obj->settings);
|
||||
$this->assertEquals('value', $obj->settings['var']);
|
||||
|
||||
$this->assertArrayHasKey('components', $obj->settings);
|
||||
|
||||
$this->assertArrayHasKey('viewBag', $obj->settings['components']);
|
||||
$this->assertIsArray($obj->settings['components']['viewBag']);
|
||||
$this->assertArrayHasKey('title', $obj->settings['components']['viewBag']);
|
||||
$this->assertEquals('Toxicity', $obj->settings['components']['viewBag']['title']);
|
||||
|
||||
$viewBag = $obj->getViewBag();
|
||||
$properties = $viewBag->getProperties();
|
||||
$this->assertCount(1, $properties);
|
||||
$this->assertEquals($obj->viewBag, $properties);
|
||||
$this->assertInstanceOf('Cms\Components\ViewBag', $viewBag);
|
||||
$this->assertArrayHasKey('title', $properties);
|
||||
$this->assertEquals('Toxicity', $properties['title']);
|
||||
}
|
||||
|
||||
public function testGetViewBagEmpty()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$obj = TestParsedCmsCompoundObject::load($theme, 'compound.htm');
|
||||
|
||||
$viewBag = $obj->getViewBag();
|
||||
$this->assertInstanceOf('Cms\Components\ViewBag', $viewBag);
|
||||
$properties = $viewBag->getProperties();
|
||||
$this->assertEmpty($properties);
|
||||
$this->assertEquals($obj->viewBag, $properties);
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
protected function assertFileEqualsNormalized($expected, $actual)
|
||||
{
|
||||
$expected = file_get_contents($expected);
|
||||
$expected = preg_replace('~\R~u', PHP_EOL, $expected); // Normalize EOL
|
||||
|
||||
$actual = file_get_contents($actual);
|
||||
$actual = preg_replace('~\R~u', PHP_EOL, $actual); // Normalize EOL
|
||||
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
}
|
||||
46
modules/cms/tests/classes/CmsExceptionTest.php
Normal file
46
modules/cms/tests/classes/CmsExceptionTest.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\CmsException;
|
||||
use Cms\Classes\Router;
|
||||
use Cms\Classes\Theme;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
|
||||
class CmsExceptionTest extends TestCase
|
||||
{
|
||||
//
|
||||
// Tests
|
||||
//
|
||||
|
||||
public function testExceptionMask()
|
||||
{
|
||||
$foreignException = new \Exception('This is a general error');
|
||||
$exceptionMask = new SystemException('This is a system exception');
|
||||
$exceptionMask->setMask($foreignException);
|
||||
|
||||
$this->assertEquals('This is a general error', $exceptionMask->getMessage());
|
||||
}
|
||||
|
||||
public function testCmsExceptionPhp()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$router = new Router($theme);
|
||||
$page = $router->findByUrl('/throw-php');
|
||||
|
||||
$error = [
|
||||
'file' => 'test.php',
|
||||
'line' => 20,
|
||||
];
|
||||
$foreignException = new \Symfony\Component\ErrorHandler\Error\FatalError('This is a general error', 100, $error);
|
||||
$this->setProtectedProperty($foreignException, 'file', "/modules/cms/classes/CodeParser.php(165) : eval()'d code line 7");
|
||||
|
||||
$exception = new CmsException($page, 300);
|
||||
$exception->setMask($foreignException);
|
||||
|
||||
$this->assertEquals($page->getFilePath(), $exception->getFile());
|
||||
$this->assertEquals('PHP Content', $exception->getErrorType());
|
||||
$this->assertEquals('This is a general error', $exception->getMessage());
|
||||
}
|
||||
}
|
||||
122
modules/cms/tests/classes/CmsObjectQueryTest.php
Normal file
122
modules/cms/tests/classes/CmsObjectQueryTest.php
Normal file
@@ -0,0 +1,122 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\Layout;
|
||||
use Cms\Classes\Page;
|
||||
use Winter\Storm\Halcyon\Model;
|
||||
|
||||
class CmsObjectQueryTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Model::clearBootedModels();
|
||||
Model::flushEventListeners();
|
||||
}
|
||||
|
||||
public function testWhere()
|
||||
{
|
||||
$page = Page::where('layout', 'caramba')->first();
|
||||
$this->assertEquals('/no-layout', $page->url);
|
||||
}
|
||||
|
||||
public function testWhereComponent()
|
||||
{
|
||||
$pages = Page::whereComponent('testArchive', 'posts-per-page', '6');
|
||||
$this->assertCount(1, $pages->all());
|
||||
|
||||
$page = $pages->first();
|
||||
$this->assertEquals('/with-components', $page->url);
|
||||
}
|
||||
|
||||
public function testWithComponent()
|
||||
{
|
||||
$pages = Page::withComponent('testArchive')->all();
|
||||
$this->assertCount(2, $pages);
|
||||
foreach ($pages as $page) {
|
||||
$this->assertTrue(!!$page->hasComponent('testArchive'));
|
||||
}
|
||||
}
|
||||
|
||||
public function testWithComponentCallback()
|
||||
{
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Archive.php';
|
||||
|
||||
$pages = Page::withComponent('testArchive', function ($component) {
|
||||
return $component->property('posts-per-page') == '69';
|
||||
})->all();
|
||||
|
||||
$this->assertCount(1, $pages);
|
||||
}
|
||||
|
||||
public function testLists()
|
||||
{
|
||||
// Default theme: test
|
||||
$pages = Page::lists('baseFileName');
|
||||
sort($pages);
|
||||
|
||||
$this->assertEquals([
|
||||
"404",
|
||||
"a/a-page",
|
||||
"ajax-test",
|
||||
"authors",
|
||||
"b/b-page",
|
||||
"blog-archive",
|
||||
"blog-category",
|
||||
"blog-post",
|
||||
"code-namespaces",
|
||||
"code-namespaces-aliases",
|
||||
"component-custom-render",
|
||||
"component-partial",
|
||||
"component-partial-alias-override",
|
||||
"component-partial-nesting",
|
||||
"component-partial-override",
|
||||
"cycle-test",
|
||||
"filters-test",
|
||||
"index",
|
||||
"no-component",
|
||||
"no-component-class",
|
||||
"no-layout",
|
||||
"no-partial",
|
||||
"no-soft-component-class",
|
||||
"optional-full-php-tags",
|
||||
"optional-short-php-tags",
|
||||
"shared-variable",
|
||||
"shared-variable-override",
|
||||
"throw-php",
|
||||
"with-component",
|
||||
"with-components",
|
||||
"with-content",
|
||||
"with-layout",
|
||||
"with-macro",
|
||||
"with-partials",
|
||||
"with-placeholder",
|
||||
"with-soft-component-class",
|
||||
"with-soft-component-class-alias",
|
||||
], $pages);
|
||||
|
||||
$layouts = Layout::lists('baseFileName');
|
||||
sort($layouts);
|
||||
|
||||
$this->assertEquals([
|
||||
"a/a-layout",
|
||||
"ajax-test",
|
||||
"content",
|
||||
"cycle-test",
|
||||
"no-php",
|
||||
"partials",
|
||||
"php-parser-test",
|
||||
"placeholder",
|
||||
"sidebar",
|
||||
], $layouts);
|
||||
}
|
||||
|
||||
public function testListsNonExistentTheme()
|
||||
{
|
||||
$pages = Page::inTheme('NON_EXISTENT_THEME')->lists('baseFileName');
|
||||
$this->assertEmpty($pages);
|
||||
}
|
||||
}
|
||||
343
modules/cms/tests/classes/CmsObjectTest.php
Normal file
343
modules/cms/tests/classes/CmsObjectTest.php
Normal file
@@ -0,0 +1,343 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\CmsObject;
|
||||
use Cms\Classes\Theme;
|
||||
|
||||
class TestCmsObject extends CmsObject
|
||||
{
|
||||
protected $dirName = 'testobjects';
|
||||
|
||||
protected $allowedExtensions = ['htm', 'html'];
|
||||
}
|
||||
|
||||
class TestTemporaryCmsObject extends CmsObject
|
||||
{
|
||||
protected $dirName = 'temporary';
|
||||
}
|
||||
|
||||
class CmsObjectTest extends TestCase
|
||||
{
|
||||
public function testLoad()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$obj = TestCmsObject::load($theme, 'plain.html');
|
||||
$this->assertEquals('<p>This is a test HTML content file.</p>', $obj->getContent());
|
||||
$this->assertEquals('plain.html', $obj->getFileName());
|
||||
|
||||
$path = str_replace('/', DIRECTORY_SEPARATOR, $theme->getPath() . '/testobjects/plain.html');
|
||||
$this->assertEquals($path, $obj->getFilePath());
|
||||
$this->assertEquals(filemtime($path), $obj->mtime);
|
||||
}
|
||||
|
||||
public function testLoadFromSubdirectory()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$obj = TestCmsObject::load($theme, 'subdir/obj.html');
|
||||
$this->assertEquals('<p>This is an object in a subdirectory.</p>', $obj->getContent());
|
||||
$this->assertEquals('subdir/obj.html', $obj->getFileName());
|
||||
|
||||
$path = str_replace('/', DIRECTORY_SEPARATOR, $theme->getPath() . '/testobjects/subdir/obj.html');
|
||||
$this->assertEquals($path, $obj->getFilePath());
|
||||
$this->assertEquals(filemtime($path), $obj->mtime);
|
||||
}
|
||||
|
||||
public function testValidateLoadInvalidTheme()
|
||||
{
|
||||
$theme = Theme::load('none');
|
||||
|
||||
$this->assertNull(TestCmsObject::load($theme, 'plain.html'));
|
||||
}
|
||||
|
||||
public function testValidateLoadInvalidFile()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$this->assertNull(TestCmsObject::load($theme, 'none'));
|
||||
}
|
||||
|
||||
public function testCache()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$themePath = $theme->getPath();
|
||||
|
||||
$filePath = $themePath .= '/temporary/test.htm';
|
||||
if (file_exists($filePath)) {
|
||||
@unlink($filePath);
|
||||
}
|
||||
|
||||
$this->assertFileNotExists($filePath);
|
||||
|
||||
file_put_contents($filePath, '<p>Test content</p>');
|
||||
|
||||
/*
|
||||
* First try - the object should be loaded from the file
|
||||
*/
|
||||
$obj = TestTemporaryCmsObject::loadCached($theme, 'test.htm');
|
||||
$this->assertFalse($obj->isLoadedFromCache());
|
||||
$this->assertEquals('<p>Test content</p>', $obj->getContent());
|
||||
$this->assertEquals('test.htm', $obj->getFileName());
|
||||
$this->assertEquals(filemtime($filePath), $obj->mtime);
|
||||
|
||||
/*
|
||||
* Second try - the object should be loaded from the cache
|
||||
*/
|
||||
CmsObject::clearInternalCache();
|
||||
|
||||
$obj = TestTemporaryCmsObject::loadCached($theme, 'test.htm');
|
||||
$this->assertTrue($obj->isLoadedFromCache());
|
||||
$this->assertEquals('<p>Test content</p>', $obj->getContent());
|
||||
$this->assertEquals('test.htm', $obj->getFileName());
|
||||
$this->assertEquals(filemtime($filePath), $obj->mtime);
|
||||
|
||||
/*
|
||||
* Modify the file. The object should be loaded from the disk and re-cached.
|
||||
*/
|
||||
sleep(1); // Sleep a second in order to have the update file modification time
|
||||
file_put_contents($filePath, '<p>Updated test content</p>');
|
||||
clearstatcache(); // The filemtime() function caches its value within a request, so we should clear its cache.
|
||||
|
||||
CmsObject::clearInternalCache();
|
||||
$obj = TestTemporaryCmsObject::loadCached($theme, 'test.htm');
|
||||
$this->assertFalse($obj->isLoadedFromCache());
|
||||
$this->assertEquals('<p>Updated test content</p>', $obj->getContent());
|
||||
$this->assertEquals(filemtime($filePath), $obj->mtime);
|
||||
|
||||
CmsObject::clearInternalCache();
|
||||
$obj = TestTemporaryCmsObject::loadCached($theme, 'test.htm');
|
||||
$this->assertTrue($obj->isLoadedFromCache());
|
||||
$this->assertEquals('<p>Updated test content</p>', $obj->getContent());
|
||||
$this->assertEquals(filemtime($filePath), $obj->mtime);
|
||||
|
||||
/*
|
||||
* Delete the file. The loadCached() should return null
|
||||
*/
|
||||
@unlink($filePath);
|
||||
$this->assertFileNotExists($filePath);
|
||||
|
||||
CmsObject::clearInternalCache();
|
||||
$obj = TestTemporaryCmsObject::loadCached($theme, 'test.htm');
|
||||
$this->assertNull($obj);
|
||||
}
|
||||
|
||||
public function testFillFillable()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'fileName' => 'mytestobj',
|
||||
'content' => $testContents
|
||||
]);
|
||||
|
||||
$this->assertEquals($testContents, $obj->getContent());
|
||||
$this->assertEquals('mytestobj.htm', $obj->getFileName());
|
||||
}
|
||||
|
||||
public function testFillNotFillable()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'something' => 'mytestobj',
|
||||
'content' => $testContents
|
||||
]);
|
||||
|
||||
$this->assertNull($obj->something);
|
||||
}
|
||||
|
||||
public function testFillInvalidFileNameSymbol()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\ValidationException::class);
|
||||
$this->expectExceptionMessage('Invalid file name');
|
||||
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'fileName' => '@name'
|
||||
]);
|
||||
$obj->save();
|
||||
}
|
||||
|
||||
public function testFillInvalidFileNamePath()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\ValidationException::class);
|
||||
$this->expectExceptionMessage('Invalid file name');
|
||||
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'fileName' => '../somefile'
|
||||
]);
|
||||
$obj->save();
|
||||
}
|
||||
|
||||
public function testFillInvalidFileSlash()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\ValidationException::class);
|
||||
$this->expectExceptionMessage('Invalid file name');
|
||||
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'fileName' => '/somefile'
|
||||
]);
|
||||
$obj->save();
|
||||
}
|
||||
|
||||
public function testFillEmptyFileName()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\ValidationException::class);
|
||||
$this->expectExceptionMessage('The File Name field is required');
|
||||
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'fileName' => ' '
|
||||
]);
|
||||
$obj->save();
|
||||
}
|
||||
|
||||
public function testSave()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$destFilePath = $theme->getPath() . '/testobjects/mytestobj.htm';
|
||||
if (file_exists($destFilePath)) {
|
||||
unlink($destFilePath);
|
||||
}
|
||||
|
||||
$this->assertFileNotExists($destFilePath);
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'fileName' => 'mytestobj',
|
||||
'content' => $testContents
|
||||
]);
|
||||
$obj->save();
|
||||
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertEquals($testContents, file_get_contents($destFilePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testSave
|
||||
*/
|
||||
public function testRename()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$srcFilePath = $theme->getPath() . '/testobjects/mytestobj.htm';
|
||||
$this->assertFileExists($srcFilePath);
|
||||
|
||||
$destFilePath = $theme->getPath() . '/testobjects/anotherobj.htm';
|
||||
if (file_exists($destFilePath)) {
|
||||
unlink($destFilePath);
|
||||
}
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::load($theme, 'mytestobj.htm');
|
||||
$this->assertEquals($testContents, $obj->getContent());
|
||||
|
||||
$obj->fill([
|
||||
'fileName' => 'anotherobj'
|
||||
]);
|
||||
$obj->save();
|
||||
|
||||
$this->assertFileNotExists($srcFilePath);
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertEquals($testContents, file_get_contents($destFilePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testRename
|
||||
*/
|
||||
public function testSaveSameName()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$filePath = $theme->getPath() . '/testobjects/anotherobj.htm';
|
||||
$this->assertFileExists($filePath);
|
||||
|
||||
$testContents = 'new content';
|
||||
$obj = TestCmsObject::load($theme, 'anotherobj.htm');
|
||||
|
||||
$obj->fill([
|
||||
'fileName' => 'anotherobj',
|
||||
'content' => $testContents
|
||||
]);
|
||||
$obj->save();
|
||||
|
||||
$this->assertFileExists($filePath);
|
||||
$this->assertEquals($testContents, file_get_contents($filePath));
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testRename
|
||||
*/
|
||||
public function testRenameToExistingFile()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\ApplicationException::class);
|
||||
$this->expectExceptionMessageMatches('/already\sexists/');
|
||||
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$srcFilePath = $theme->getPath() . '/testobjects/anotherobj.htm';
|
||||
$this->assertFileExists($srcFilePath);
|
||||
|
||||
$destFilePath = $theme->getPath() . '/testobjects/existingobj.htm';
|
||||
if (!file_exists($destFilePath)) {
|
||||
file_put_contents($destFilePath, 'str');
|
||||
}
|
||||
$this->assertFileExists($destFilePath);
|
||||
|
||||
$obj = TestCmsObject::load($theme, 'anotherobj.htm');
|
||||
$obj->fill(['fileName' => 'existingobj']);
|
||||
$obj->save();
|
||||
}
|
||||
|
||||
public function testSaveNewDir()
|
||||
{
|
||||
$theme = Theme::load('apitest');
|
||||
|
||||
$destFilePath = $theme->getPath() . '/testobjects/testsubdir/mytestobj.htm';
|
||||
if (file_exists($destFilePath)) {
|
||||
unlink($destFilePath);
|
||||
}
|
||||
|
||||
$destDirPath = dirname($destFilePath);
|
||||
if (file_exists($destDirPath) && is_dir($destDirPath)) {
|
||||
rmdir($destDirPath);
|
||||
}
|
||||
|
||||
$this->assertFileNotExists($destFilePath);
|
||||
$this->assertFileNotExists($destDirPath);
|
||||
|
||||
$testContents = 'mytestcontent';
|
||||
$obj = TestCmsObject::inTheme($theme);
|
||||
$obj->fill([
|
||||
'fileName' => 'testsubdir/mytestobj.htm',
|
||||
'content' => $testContents
|
||||
]);
|
||||
$obj->save();
|
||||
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertEquals($testContents, file_get_contents($destFilePath));
|
||||
}
|
||||
}
|
||||
322
modules/cms/tests/classes/CodeParserTest.php
Normal file
322
modules/cms/tests/classes/CodeParserTest.php
Normal file
@@ -0,0 +1,322 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\CodeParser;
|
||||
use Cms\Classes\Controller;
|
||||
use Cms\Classes\Layout;
|
||||
use Cms\Classes\LayoutCode;
|
||||
use Cms\Classes\Page;
|
||||
use Cms\Classes\PageCode;
|
||||
use Cms\Classes\Theme;
|
||||
use File;
|
||||
use ReflectionClass;
|
||||
|
||||
class CodeParserTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setup();
|
||||
|
||||
/*
|
||||
* Clear cache
|
||||
*/
|
||||
foreach (File::directories(storage_path() . '/cms/cache') as $directory) {
|
||||
File::deleteDirectory($directory);
|
||||
}
|
||||
}
|
||||
|
||||
public static function getProperty($name)
|
||||
{
|
||||
$class = new ReflectionClass(CodeParser::class);
|
||||
$property = $class->getProperty($name);
|
||||
$property->setAccessible(true);
|
||||
|
||||
return $property;
|
||||
}
|
||||
|
||||
public function testParser()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$layout = Layout::load($theme, 'php-parser-test.htm');
|
||||
$this->assertNotEmpty($layout);
|
||||
|
||||
$parser = new CodeParser($layout);
|
||||
$info = $parser->parse();
|
||||
|
||||
$this->assertIsArray($info);
|
||||
$this->assertArrayHasKey('filePath', $info);
|
||||
$this->assertArrayHasKey('className', $info);
|
||||
$this->assertArrayHasKey('source', $info);
|
||||
|
||||
$this->assertFileExists($info['filePath']);
|
||||
|
||||
$controller = new Controller($theme);
|
||||
$obj = $parser->source(null, $layout, $controller);
|
||||
$this->assertInstanceOf(LayoutCode::class, $obj);
|
||||
|
||||
/*
|
||||
* Test the file contents
|
||||
*/
|
||||
|
||||
$body = preg_replace('/^\s*function/m', 'public function', $layout->code);
|
||||
$expectedContent = '<?php ' . PHP_EOL;
|
||||
|
||||
$expectedContent .= 'class ' . $info['className'] . ' extends ' . LayoutCode::class . PHP_EOL;
|
||||
$expectedContent .= '{' . PHP_EOL;
|
||||
$expectedContent .= $body . PHP_EOL;
|
||||
$expectedContent .= '}' . PHP_EOL;
|
||||
|
||||
$this->assertEquals($expectedContent, file_get_contents($info['filePath']));
|
||||
|
||||
/*
|
||||
* Test caching - the first time the file should be parsed
|
||||
*/
|
||||
|
||||
$this->assertEquals('parser', $info['source']);
|
||||
|
||||
/*
|
||||
* Test caching - the second time the file should be loaded from the request-wide cache
|
||||
*/
|
||||
|
||||
$parser = new CodeParser($layout);
|
||||
$info = $parser->parse();
|
||||
$this->assertIsArray($info);
|
||||
$this->assertEquals('request-cache', $info['source']);
|
||||
$this->assertFileExists($info['filePath']);
|
||||
|
||||
/*
|
||||
* Test caching - reset the request-wide cache and let the parser to load the file from the cache
|
||||
*/
|
||||
|
||||
$property = $this->getProperty('cache');
|
||||
$property->setValue($parser, []);
|
||||
|
||||
$parser = new CodeParser($layout);
|
||||
$info = $parser->parse();
|
||||
$this->assertIsArray($info);
|
||||
$this->assertEquals('cache', $info['source']);
|
||||
$this->assertFileExists($info['filePath']);
|
||||
|
||||
/*
|
||||
* Test caching - the cached data should now be stored in the request-wide cache again
|
||||
*/
|
||||
|
||||
$parser = new CodeParser($layout);
|
||||
$info = $parser->parse();
|
||||
$this->assertIsArray($info);
|
||||
$this->assertEquals('request-cache', $info['source']);
|
||||
$this->assertFileExists($info['filePath']);
|
||||
|
||||
/*
|
||||
* Test caching - update the file modification time and reset the internal cache. The file should be parsed.
|
||||
*/
|
||||
|
||||
$this->assertTrue(@touch($layout->getFilePath()));
|
||||
clearstatcache();
|
||||
$layout = Layout::load($theme, 'php-parser-test.htm');
|
||||
$this->assertNotEmpty($layout);
|
||||
$parser = new CodeParser($layout);
|
||||
$property->setValue($parser, []);
|
||||
|
||||
$info = $parser->parse();
|
||||
$this->assertIsArray($info);
|
||||
$this->assertEquals('parser', $info['source']);
|
||||
$this->assertFileExists($info['filePath']);
|
||||
}
|
||||
|
||||
public function testParseNoPhp()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$layout = Layout::load($theme, 'no-php.htm');
|
||||
$this->assertNotEmpty($layout);
|
||||
|
||||
$parser = new CodeParser($layout);
|
||||
$info = $parser->parse();
|
||||
|
||||
$this->assertIsArray($info);
|
||||
$this->assertArrayHasKey('filePath', $info);
|
||||
$this->assertArrayHasKey('className', $info);
|
||||
$this->assertArrayHasKey('source', $info);
|
||||
|
||||
$this->assertFileExists($info['filePath']);
|
||||
|
||||
$expectedContent = '<?php ' . PHP_EOL;
|
||||
$expectedContent .= 'class ' . $info['className'] . ' extends ' . LayoutCode::class . PHP_EOL;
|
||||
$expectedContent .= '{' . PHP_EOL;
|
||||
$expectedContent .= PHP_EOL;
|
||||
$expectedContent .= '}' . PHP_EOL;
|
||||
|
||||
$this->assertEquals($expectedContent, file_get_contents($info['filePath']));
|
||||
}
|
||||
|
||||
public function testParsePage()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$page = Page::load($theme, 'cycle-test.htm');
|
||||
$this->assertNotEmpty($page);
|
||||
|
||||
$parser = new CodeParser($page);
|
||||
$info = $parser->parse();
|
||||
|
||||
$this->assertIsArray($info);
|
||||
$this->assertArrayHasKey('filePath', $info);
|
||||
$this->assertArrayHasKey('className', $info);
|
||||
$this->assertArrayHasKey('source', $info);
|
||||
|
||||
$this->assertFileExists($info['filePath']);
|
||||
$controller = new Controller($theme);
|
||||
$obj = $parser->source($page, null, $controller);
|
||||
$this->assertInstanceOf(PageCode::class, $obj);
|
||||
|
||||
$body = preg_replace('/^\s*function/m', 'public function', $page->code);
|
||||
$expectedContent = '<?php ' . PHP_EOL;
|
||||
$expectedContent .= 'class ' . $info['className'] . ' extends ' . PageCode::class . PHP_EOL;
|
||||
$expectedContent .= '{' . PHP_EOL;
|
||||
$expectedContent .= $body . PHP_EOL;
|
||||
$expectedContent .= '}' . PHP_EOL;
|
||||
|
||||
$this->assertEquals($expectedContent, file_get_contents($info['filePath']));
|
||||
}
|
||||
|
||||
public function testOptionalPhpTags()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
/*
|
||||
* Test short PHP tags
|
||||
*/
|
||||
|
||||
$page = Page::load($theme, 'optional-short-php-tags.htm');
|
||||
$this->assertNotEmpty($page);
|
||||
|
||||
$parser = new CodeParser($page);
|
||||
$info = $parser->parse();
|
||||
|
||||
$this->assertIsArray($info);
|
||||
$this->assertArrayHasKey('filePath', $info);
|
||||
$this->assertArrayHasKey('className', $info);
|
||||
$this->assertArrayHasKey('source', $info);
|
||||
|
||||
$this->assertFileExists($info['filePath']);
|
||||
$controller = new Controller($theme);
|
||||
$obj = $parser->source($page, null, $controller);
|
||||
$this->assertInstanceOf('\Cms\Classes\PageCode', $obj);
|
||||
|
||||
$body = preg_replace('/^\s*function/m', 'public function', $page->code);
|
||||
$expectedContent = '<?php ' . PHP_EOL;
|
||||
$expectedContent .= 'class ' . $info['className'] . ' extends ' . PageCode::class . PHP_EOL;
|
||||
$expectedContent .= '{' . PHP_EOL;
|
||||
$expectedContent .= $body . PHP_EOL;
|
||||
$expectedContent .= '}' . PHP_EOL;
|
||||
|
||||
$this->assertEquals($expectedContent, file_get_contents($info['filePath']));
|
||||
|
||||
/*
|
||||
* Test full PHP tags
|
||||
*/
|
||||
|
||||
$page = Page::load($theme, 'optional-full-php-tags.htm');
|
||||
$this->assertNotEmpty($page);
|
||||
|
||||
$parser = new CodeParser($page);
|
||||
$info = $parser->parse();
|
||||
|
||||
$this->assertIsArray($info);
|
||||
$this->assertArrayHasKey('filePath', $info);
|
||||
$this->assertArrayHasKey('className', $info);
|
||||
$this->assertArrayHasKey('source', $info);
|
||||
|
||||
$this->assertFileExists($info['filePath']);
|
||||
$controller = new Controller($theme);
|
||||
$obj = $parser->source($page, null, $controller);
|
||||
$this->assertInstanceOf(PageCode::class, $obj);
|
||||
|
||||
$body = preg_replace('/^\s*function/m', 'public function', $page->code);
|
||||
$expectedContent = '<?php ' . PHP_EOL;
|
||||
$expectedContent .= 'class ' . $info['className'] . ' extends ' . PageCode::class . PHP_EOL;
|
||||
$expectedContent .= '{' . PHP_EOL;
|
||||
$expectedContent .= $body . PHP_EOL;
|
||||
$expectedContent .= '}' . PHP_EOL;
|
||||
|
||||
$this->assertEquals($expectedContent, file_get_contents($info['filePath']));
|
||||
}
|
||||
|
||||
// public function testSyntaxErrors()
|
||||
// {
|
||||
// $this->markTestIncomplete('Test PHP parsing errors here.');
|
||||
// }
|
||||
|
||||
public function testNamespaces()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$page = Page::load($theme, 'code-namespaces.htm');
|
||||
$this->assertNotEmpty($page);
|
||||
|
||||
$parser = new CodeParser($page);
|
||||
$info = $parser->parse();
|
||||
|
||||
$this->assertIsArray($info);
|
||||
$this->assertArrayHasKey('filePath', $info);
|
||||
$this->assertArrayHasKey('className', $info);
|
||||
$this->assertArrayHasKey('source', $info);
|
||||
|
||||
$this->assertFileExists($info['filePath']);
|
||||
$controller = new Controller($theme);
|
||||
$obj = $parser->source($page, null, $controller);
|
||||
$this->assertInstanceOf(PageCode::class, $obj);
|
||||
|
||||
$referenceFilePath = base_path() . '/modules/cms/tests/fixtures/reference/namespaces.php.stub';
|
||||
$this->assertFileExists($referenceFilePath);
|
||||
$referenceContents = $this->getContents($referenceFilePath);
|
||||
|
||||
$referenceContents = str_replace('{className}', $info['className'], $referenceContents);
|
||||
|
||||
$this->assertEquals($referenceContents, $this->getContents($info['filePath']));
|
||||
}
|
||||
|
||||
public function testNamespacesAliases()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$page = Page::load($theme, 'code-namespaces-aliases.htm');
|
||||
$this->assertNotEmpty($page);
|
||||
|
||||
$parser = new CodeParser($page);
|
||||
$info = $parser->parse();
|
||||
|
||||
$this->assertIsArray($info);
|
||||
$this->assertArrayHasKey('filePath', $info);
|
||||
$this->assertArrayHasKey('className', $info);
|
||||
$this->assertArrayHasKey('source', $info);
|
||||
|
||||
$this->assertFileExists($info['filePath']);
|
||||
$controller = new Controller($theme);
|
||||
$obj = $parser->source($page, null, $controller);
|
||||
$this->assertInstanceOf(PageCode::class, $obj);
|
||||
|
||||
$referenceFilePath = base_path() . '/modules/cms/tests/fixtures/reference/namespaces-aliases.php.stub';
|
||||
$this->assertFileExists($referenceFilePath);
|
||||
$referenceContents = $this->getContents($referenceFilePath);
|
||||
|
||||
$referenceContents = str_replace('{className}', $info['className'], $referenceContents);
|
||||
|
||||
$this->assertEquals($referenceContents, $this->getContents($info['filePath']));
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
protected function getContents($path)
|
||||
{
|
||||
$content = file_get_contents($path);
|
||||
$content = preg_replace('~\R~u', PHP_EOL, $content); // Normalize EOL
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
142
modules/cms/tests/classes/ComponentManagerTest.php
Normal file
142
modules/cms/tests/classes/ComponentManagerTest.php
Normal file
@@ -0,0 +1,142 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\CodeParser;
|
||||
use Cms\Classes\ComponentManager;
|
||||
use Cms\Classes\Controller;
|
||||
use Cms\Classes\Layout;
|
||||
use Cms\Classes\Page;
|
||||
use Cms\Classes\Theme;
|
||||
|
||||
class ComponentManagerTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Archive.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Post.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/MainMenu.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/ContentBlock.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Comments.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/classes/Users.php';
|
||||
}
|
||||
|
||||
|
||||
public function testListComponents()
|
||||
{
|
||||
$manager = ComponentManager::instance();
|
||||
$components = $manager->listComponents();
|
||||
|
||||
$this->assertArrayHasKey('testArchive', $components);
|
||||
$this->assertArrayHasKey('testPost', $components);
|
||||
}
|
||||
|
||||
public function testListComponentDetails()
|
||||
{
|
||||
$manager = ComponentManager::instance();
|
||||
$components = $manager->listComponentDetails();
|
||||
|
||||
$this->assertArrayHasKey('testArchive', $components);
|
||||
$this->assertArrayHasKey('name', $components['testArchive']);
|
||||
$this->assertArrayHasKey('description', $components['testArchive']);
|
||||
$this->assertEquals('Blog Archive Dummy Component', $components['testArchive']['name']);
|
||||
$this->assertEquals('Displays an archive of blog posts.', $components['testArchive']['description']);
|
||||
|
||||
$this->assertArrayHasKey('testPost', $components);
|
||||
$this->assertArrayHasKey('name', $components['testPost']);
|
||||
$this->assertArrayHasKey('description', $components['testPost']);
|
||||
$this->assertEquals('Blog Post Dummy Component', $components['testPost']['name']);
|
||||
$this->assertEquals('Displays a blog post.', $components['testPost']['description']);
|
||||
}
|
||||
|
||||
public function testGetComponentWithFactoryUsingAutomaticResolution()
|
||||
{
|
||||
$manager = ComponentManager::instance();
|
||||
$components = $manager->listComponentDetails();
|
||||
|
||||
$this->assertArrayHasKey('testComments', $components);
|
||||
$this->assertArrayHasKey('name', $components['testComments']);
|
||||
$this->assertArrayHasKey('description', $components['testComments']);
|
||||
$this->assertEquals('Blog Comments Dummy Component', $components['testComments']['name']);
|
||||
$this->assertEquals('Displays the list of comments on a post.', $components['testComments']['description']);
|
||||
|
||||
$comments = $manager->makeComponent('testComments', $this->spoofPageCode(), []);
|
||||
$users = $comments->getUsers()->getUsers();
|
||||
|
||||
$this->assertArrayHasKey('Art Vandelay', $users);
|
||||
$this->assertArrayHasKey('Carl', $users);
|
||||
$this->assertEquals('Arquitecht and Importer/Exporter', $users['Art Vandelay']);
|
||||
$this->assertEquals('where is he?', $users['Carl']);
|
||||
}
|
||||
|
||||
public function testFindByAlias()
|
||||
{
|
||||
$manager = ComponentManager::instance();
|
||||
|
||||
$component = $manager->resolve('testArchive');
|
||||
$this->assertEquals('\Winter\Tester\Components\Archive', $component);
|
||||
|
||||
$component = $manager->resolve('testPost');
|
||||
$this->assertEquals('\Winter\Tester\Components\Post', $component);
|
||||
}
|
||||
|
||||
public function testHasComponent()
|
||||
{
|
||||
$manager = ComponentManager::instance();
|
||||
$result = $manager->hasComponent('testArchive');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $manager->hasComponent('Winter\Tester\Components\Archive');
|
||||
$this->assertTrue($result);
|
||||
|
||||
$result = $manager->hasComponent('Winter\Tester\Components\Post');
|
||||
$this->assertTrue($result);
|
||||
}
|
||||
|
||||
public function testMakeComponent()
|
||||
{
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Archive.php';
|
||||
|
||||
$pageObj = $this->spoofPageCode();
|
||||
|
||||
// Test a defined property
|
||||
$manager = ComponentManager::instance();
|
||||
$object = $manager->makeComponent('testArchive', $pageObj, ['posts-per-page' => 20]);
|
||||
$this->assertNotNull($object);
|
||||
$this->assertEquals(20, $object->property('posts-per-page'));
|
||||
|
||||
// Test an undefined property with default
|
||||
$object = $manager->makeComponent('testArchive', $pageObj);
|
||||
$this->assertNotNull($object);
|
||||
$this->assertEquals(10, $object->property('posts-per-page'));
|
||||
$this->assertEquals(2020, $object->property('undefined-property', 2020));
|
||||
}
|
||||
|
||||
public function testDefineProperties()
|
||||
{
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Archive.php';
|
||||
$manager = ComponentManager::instance();
|
||||
$object = $manager->makeComponent('testArchive');
|
||||
$details = $object->componentDetails();
|
||||
$this->assertCount(2, $details);
|
||||
$this->assertNotNull($details);
|
||||
$this->assertArrayHasKey('name', $details);
|
||||
$this->assertArrayHasKey('description', $details);
|
||||
$this->assertEquals('Blog Archive Dummy Component', $details['name']);
|
||||
}
|
||||
|
||||
private function spoofPageCode()
|
||||
{
|
||||
// Spoof all the objects we need to make a page object
|
||||
$theme = Theme::load('test');
|
||||
$page = Page::load($theme, 'index.htm');
|
||||
$layout = Layout::load($theme, 'content.htm');
|
||||
$controller = new Controller($theme);
|
||||
$parser = new CodeParser($page);
|
||||
$pageObj = $parser->source($page, $layout, $controller);
|
||||
return $pageObj;
|
||||
}
|
||||
}
|
||||
38
modules/cms/tests/classes/ContentTest.php
Normal file
38
modules/cms/tests/classes/ContentTest.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\Content;
|
||||
use Cms\Classes\Theme;
|
||||
|
||||
class ContentTest extends TestCase
|
||||
{
|
||||
|
||||
public function testMarkdownContent()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$content = Content::load($theme, 'markdown-content.md');
|
||||
|
||||
$this->assertEquals('Be brave, be **bold**, live *italic*', $content->markup);
|
||||
$this->assertEquals("<p>Be brave, be <strong>bold</strong>, live <em>italic</em></p>\n", $content->parsedMarkup);
|
||||
}
|
||||
|
||||
public function testTextContent()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$content = Content::load($theme, 'text-content.txt');
|
||||
|
||||
$this->assertEquals('Pen is <mightier> than the sword, HTML is <richer> than the text', $content->markup);
|
||||
$this->assertEquals('Pen is <mightier> than the sword, HTML is <richer> than the text', $content->parsedMarkup);
|
||||
}
|
||||
|
||||
public function testHtmlContent()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$content = Content::load($theme, 'html-content.htm');
|
||||
|
||||
$this->assertEquals('<a href="#">Stephen Saucier</a> changed his profile picture — <small>7 hrs ago</small></div>', $content->markup);
|
||||
$this->assertEquals('<a href="#">Stephen Saucier</a> changed his profile picture — <small>7 hrs ago</small></div>', $content->parsedMarkup);
|
||||
}
|
||||
}
|
||||
175
modules/cms/tests/classes/ControllerPostbackTest.php
Normal file
175
modules/cms/tests/classes/ControllerPostbackTest.php
Normal file
@@ -0,0 +1,175 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use Cms\Classes\Controller;
|
||||
use Cms\Classes\Theme;
|
||||
use Illuminate\Support\Facades\Request;
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
use Winter\Storm\Halcyon\Model;
|
||||
use Winter\Storm\Support\Facades\Config;
|
||||
|
||||
class ControllerPostbackTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Config::set('cms.themesPath', '/modules/cms/tests/fixtures/themes');
|
||||
Model::clearBootedModels();
|
||||
Model::flushEventListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock Request that simulates an AJAX POST with the given handler.
|
||||
*/
|
||||
protected function configAjaxRequestMock(string $handler, $partials = false)
|
||||
{
|
||||
$requestMock = $this
|
||||
->getMockBuilder('Illuminate\Http\Request')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['ajax', 'method', 'header'])
|
||||
->getMock();
|
||||
|
||||
$map = [
|
||||
['X_WINTER_REQUEST_HANDLER', null, $handler],
|
||||
['X_WINTER_REQUEST_PARTIALS', null, $partials],
|
||||
];
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('ajax')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('method')
|
||||
->will($this->returnValue('POST'));
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('header')
|
||||
->will($this->returnValueMap($map));
|
||||
|
||||
return $requestMock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a mock Request that simulates a non-AJAX POST with _handler in POST data.
|
||||
*/
|
||||
protected function configPostbackRequestMock(string $handler)
|
||||
{
|
||||
$requestMock = $this
|
||||
->getMockBuilder('Illuminate\Http\Request')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['ajax', 'method', 'header', 'post', 'input'])
|
||||
->getMock();
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('ajax')
|
||||
->will($this->returnValue(false));
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('method')
|
||||
->will($this->returnValue('POST'));
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('header')
|
||||
->will($this->returnValue(null));
|
||||
|
||||
$postData = ['_handler' => $handler];
|
||||
$requestMock->expects($this->any())
|
||||
->method('post')
|
||||
->will($this->returnCallback(function ($key = null, $default = null) use ($postData) {
|
||||
if ($key === null) {
|
||||
return $postData;
|
||||
}
|
||||
return $postData[$key] ?? $default;
|
||||
}));
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('input')
|
||||
->will($this->returnCallback(function ($key = null, $default = null) use ($postData) {
|
||||
if ($key === null) {
|
||||
return $postData;
|
||||
}
|
||||
return $postData[$key] ?? $default;
|
||||
}));
|
||||
|
||||
return $requestMock;
|
||||
}
|
||||
|
||||
//
|
||||
// AJAX header path — validates handler name (existing behavior)
|
||||
//
|
||||
|
||||
public function testAjaxPathRejectsInvalidHandlerName(): void
|
||||
{
|
||||
$this->expectException(SystemException::class);
|
||||
$this->expectExceptionMessage('Invalid AJAX handler name: update_onDelete.');
|
||||
|
||||
Request::swap($this->configAjaxRequestMock('update_onDelete'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$controller->run('/ajax-test');
|
||||
}
|
||||
|
||||
public function testAjaxPathAcceptsValidHandlerName(): void
|
||||
{
|
||||
// onTest exists on the ajax-test page, so this should not throw SystemException for invalid name
|
||||
// It may throw for other reasons (missing partials, etc.) but not for handler name validation
|
||||
Request::swap($this->configAjaxRequestMock('onTest', ''));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
|
||||
try {
|
||||
$controller->run('/ajax-test');
|
||||
} catch (SystemException $e) {
|
||||
$this->assertStringNotContainsString('Invalid AJAX handler name', $e->getMessage());
|
||||
}
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
//
|
||||
// Postback _handler path — validates handler name (our fix)
|
||||
//
|
||||
|
||||
public function testPostbackPathRejectsInvalidHandlerName(): void
|
||||
{
|
||||
$this->expectException(SystemException::class);
|
||||
$this->expectExceptionMessage('Invalid AJAX handler name: update_onDelete.');
|
||||
|
||||
Config::set('cms.enableCsrfProtection', false);
|
||||
Request::swap($this->configPostbackRequestMock('update_onDelete'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$controller->run('/ajax-test');
|
||||
}
|
||||
|
||||
public function testPostbackPathRejectsMethodName(): void
|
||||
{
|
||||
$this->expectException(SystemException::class);
|
||||
$this->expectExceptionMessage('Invalid AJAX handler name: execPageCycle.');
|
||||
|
||||
Config::set('cms.enableCsrfProtection', false);
|
||||
Request::swap($this->configPostbackRequestMock('execPageCycle'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$controller->run('/ajax-test');
|
||||
}
|
||||
|
||||
public function testPostbackPathRejectsActionPrefixedHandler(): void
|
||||
{
|
||||
$this->expectException(SystemException::class);
|
||||
$this->expectExceptionMessage('Invalid AJAX handler name: index_onSave.');
|
||||
|
||||
Config::set('cms.enableCsrfProtection', false);
|
||||
Request::swap($this->configPostbackRequestMock('index_onSave'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$controller->run('/ajax-test');
|
||||
}
|
||||
}
|
||||
752
modules/cms/tests/classes/ControllerTest.php
Normal file
752
modules/cms/tests/classes/ControllerTest.php
Normal file
@@ -0,0 +1,752 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use Cms;
|
||||
use Request;
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Classes\Controller;
|
||||
use System\Helpers\View;
|
||||
use Winter\Storm\Halcyon\Model;
|
||||
use Winter\Storm\Support\Facades\Config;
|
||||
|
||||
class ControllerTest extends TestCase
|
||||
{
|
||||
protected string $origThemePath;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
$this->origThemePath = Config::get('cms.themesPath');
|
||||
// Set temporary themes path for tests
|
||||
Config::set('cms.themesPath', '/modules/cms/tests/fixtures/themes');
|
||||
|
||||
Model::clearBootedModels();
|
||||
Model::flushEventListeners();
|
||||
|
||||
View::clearVarCache();
|
||||
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Archive.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Post.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/MainMenu.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/ContentBlock.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Comments.php';
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/classes/Users.php';
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
// Restore original themes path
|
||||
Config::set('cms.themesPath', $this->origThemePath);
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testThemeUrl()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
|
||||
$url = $controller->themeUrl();
|
||||
$this->assertEquals(url('/modules/cms/tests/fixtures/themes/test'), $url);
|
||||
|
||||
$url = $controller->themeUrl('assets/css/style1.css');
|
||||
$this->assertEquals(url('/modules/cms/tests/fixtures/themes/test/assets/css/style1.css'), $url);
|
||||
|
||||
$pathSymbolTests = [
|
||||
'~/modules/cms/tests/fixtures/themes/test/assets/css/style1.css' => '/',
|
||||
'$/fakeauthor/fakeplugin/assets/src/app.js' => '/plugins/',
|
||||
'#/faketheme/assets/css/style1.css' => '/themes/',
|
||||
];
|
||||
foreach ($pathSymbolTests as $symbolizedPath => $urlPrefix) {
|
||||
$url = $controller->themeUrl($symbolizedPath);
|
||||
$this->assertEquals(url(str_replace(substr($symbolizedPath, 0, 2), $urlPrefix, $symbolizedPath)), $url);
|
||||
}
|
||||
}
|
||||
|
||||
public function testThemeCombineAssets(): void
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
|
||||
// Generate a url
|
||||
$url = $controller->themeUrl(['~/modules/cms/tests/fixtures/themes/test/assets/css/style1.css', 'assets/css/style2.css']);
|
||||
$this->assertIsString($url);
|
||||
|
||||
// Grab the cache key from the url
|
||||
$cacheKey = 'combiner.' . str_before(basename($url), '-');
|
||||
|
||||
// Load the cached config
|
||||
$combinerConfig = \Cache::get($cacheKey);
|
||||
$this->assertIsString($combinerConfig);
|
||||
|
||||
// Decode the config
|
||||
$combinerConfig = unserialize(base64_decode($combinerConfig));
|
||||
|
||||
// Assert the result is an array and includes files
|
||||
$this->assertIsArray($combinerConfig);
|
||||
$this->assertArrayHasKey('files', $combinerConfig);
|
||||
$this->assertCount(2, $combinerConfig['files']);
|
||||
|
||||
// Check our input file names against our output file names
|
||||
$files = array_map('basename', $combinerConfig['files']);
|
||||
$this->assertTrue(in_array('style1.css', $files));
|
||||
$this->assertTrue(in_array('style2.css', $files));
|
||||
}
|
||||
|
||||
public function testPageUrl()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
|
||||
$loadUrl = '/filters-test/current-slug';
|
||||
|
||||
$response = $controller->run($loadUrl);
|
||||
|
||||
// Check pageUrl for current page
|
||||
$url = $controller->pageUrl('');
|
||||
$this->assertEquals(url($loadUrl), $url);
|
||||
|
||||
// Check pageUrl for persistent URL parameters
|
||||
$url = $controller->pageUrl('blog-post');
|
||||
$this->assertEquals(url('/blog/post/current-slug'), $url);
|
||||
|
||||
// Check pageUrl for providing values for URL parameters
|
||||
$url = $controller->pageUrl('blog-post', ['url_title' => 'test-slug']);
|
||||
$this->assertEquals(url('/blog/post/test-slug'), $url);
|
||||
|
||||
// Check pageUrl for disabling persistent URL parameters
|
||||
$url = $controller->pageUrl('blog-post', [], false);
|
||||
$this->assertEquals(url('/blog/post/default'), $url);
|
||||
|
||||
// Check pageUrl for disabling persistent URL parameters with the second argument being routePersistence
|
||||
$url = $controller->pageUrl('blog-post', false);
|
||||
$this->assertEquals(url('/blog/post/default'), $url);
|
||||
|
||||
// Check the Twig render results
|
||||
$results = $response->getContent();
|
||||
$lines = explode("\n", str_replace("\r\n", "\n", $results));
|
||||
foreach ($lines as $test) {
|
||||
list($result, $expected) = explode(' -> ', $test);
|
||||
$this->assertEquals($expected, $result);
|
||||
}
|
||||
}
|
||||
|
||||
public function test404()
|
||||
{
|
||||
/*
|
||||
* Test the built-in 404 page
|
||||
*/
|
||||
$theme = Theme::load('apitest');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/some-page-that-doesnt-exist');
|
||||
$this->assertNotEmpty($response);
|
||||
$this->assertInstanceOf('\Illuminate\Http\Response', $response);
|
||||
ob_start();
|
||||
include base_path() . '/modules/cms/views/404.php';
|
||||
$page404Content = ob_get_contents();
|
||||
ob_end_clean();
|
||||
$this->assertEquals($page404Content, $response->getContent());
|
||||
|
||||
/*
|
||||
* Test the theme 404 page
|
||||
*/
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/some-page-that-doesnt-exist');
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
|
||||
$content = $response->getContent();
|
||||
$this->assertIsString($content);
|
||||
$this->assertEquals('<p>Page not found</p>', $content);
|
||||
}
|
||||
|
||||
public function testRoot()
|
||||
{
|
||||
/*
|
||||
* Test the / route and the fallback layout
|
||||
*/
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/');
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
|
||||
$content = $response->getContent();
|
||||
$this->assertIsString($content);
|
||||
$this->assertEquals('<h1>My Webpage</h1>', trim($content));
|
||||
}
|
||||
|
||||
public function testLayoutNotFound()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\SystemException::class);
|
||||
$this->expectExceptionMessageMatches('/is\snot\sfound/');
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/no-layout');
|
||||
}
|
||||
|
||||
public function testExistingLayout()
|
||||
{
|
||||
/*
|
||||
* Test existing layout
|
||||
*/
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-layout');
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
|
||||
$content = $response->getContent();
|
||||
$this->assertEquals('<div><p>Hey</p></div>', $content);
|
||||
}
|
||||
|
||||
public function testPartials()
|
||||
{
|
||||
/*
|
||||
* Test partials referred in the layout and page
|
||||
*/
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-partials')->getContent();
|
||||
$this->assertEquals('<div>LAYOUT PARTIAL<p>Hey PAGE PARTIAL Homer Simpson A partial</p></div>', $response);
|
||||
}
|
||||
|
||||
public function testChildThemePartials()
|
||||
{
|
||||
/*
|
||||
* Test partials referred in the layout and page
|
||||
*/
|
||||
$theme = Theme::load('childtest');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-partials')->getContent();
|
||||
$this->assertEquals('<div>LAYOUT PARTIAL<p>Hey PAGE PARTIAL Homer Simpson A child partial</p></div>', $response);
|
||||
}
|
||||
|
||||
public function testContent()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-content')->getContent();
|
||||
$this->assertEquals('<div>LAYOUT CONTENT<p>Hey PAGE CONTENT A content</p></div>', $response);
|
||||
}
|
||||
|
||||
public function testChildThemeContent()
|
||||
{
|
||||
$theme = Theme::load('childtest');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-content')->getContent();
|
||||
$this->assertEquals('<div>LAYOUT CONTENT<p>Hey PAGE CONTENT A child content</p></div>', $response);
|
||||
}
|
||||
|
||||
public function testBlocks()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-placeholder')->getContent();
|
||||
$this->assertEquals("<div>LAYOUT CONTENT <span>BLOCK\n DEFAULT</span> <p>Hey PAGE CONTENT</p></div>SECOND BLOCK", $response);
|
||||
}
|
||||
|
||||
public function testChildThemeBlocks()
|
||||
{
|
||||
$theme = Theme::load('childtest');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-placeholder')->getContent();
|
||||
$this->assertEquals("<div>LAYOUT CONTENT <span>BLOCK\n DEFAULT</span> <p>Hey PAGE CONTENT</p></div>SECOND BLOCK", $response);
|
||||
}
|
||||
|
||||
public function testLayoutInSubdirectory()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/apage')->getContent();
|
||||
$this->assertEquals("<div>LAYOUT CONTENT <h1>This page is a subdirectory</h1></div>", $response);
|
||||
}
|
||||
|
||||
public function testPartialNotFound()
|
||||
{
|
||||
$this->expectException(\Twig\Error\RuntimeError::class);
|
||||
$this->expectExceptionMessageMatches('/is\snot\sfound/');
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/no-partial')->getContent();
|
||||
}
|
||||
|
||||
public function testChildThemePartialNotFound()
|
||||
{
|
||||
$this->expectException(\Twig\Error\RuntimeError::class);
|
||||
$this->expectExceptionMessageMatches('/is\snot\sfound/');
|
||||
|
||||
$theme = Theme::load('childtest');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/no-partial')->getContent();
|
||||
}
|
||||
|
||||
public function testPageLifeCycle()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/cycle-test')->getContent();
|
||||
$this->assertEquals('12345', $response);
|
||||
}
|
||||
|
||||
public function testChildThemePageLifeCycle()
|
||||
{
|
||||
$theme = Theme::load('childtest');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/cycle-test')->getContent();
|
||||
$this->assertEquals('12345', $response);
|
||||
}
|
||||
|
||||
protected function configAjaxRequestMock($handler, $partials = false)
|
||||
{
|
||||
$requestMock = $this
|
||||
->getMockBuilder('Illuminate\Http\Request')
|
||||
->disableOriginalConstructor()
|
||||
->setMethods(['ajax', 'method', 'header'])
|
||||
->getMock();
|
||||
|
||||
$map = [
|
||||
['X_WINTER_REQUEST_HANDLER', null, $handler],
|
||||
['X_WINTER_REQUEST_PARTIALS', null, $partials],
|
||||
];
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('ajax')
|
||||
->will($this->returnValue(true));
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('method')
|
||||
->will($this->returnValue('POST'));
|
||||
|
||||
$requestMock->expects($this->any())
|
||||
->method('header')
|
||||
->will($this->returnValueMap($map));
|
||||
|
||||
return $requestMock;
|
||||
}
|
||||
|
||||
public function testAjaxHandlerNotFound()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\SystemException::class);
|
||||
$this->expectExceptionMessage('AJAX handler \'onNoHandler\' was not found.');
|
||||
|
||||
Request::swap($this->configAjaxRequestMock('onNoHandler', ''));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$controller->run('/ajax-test');
|
||||
}
|
||||
|
||||
public function testAjaxInvalidHandlerName()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\SystemException::class);
|
||||
$this->expectExceptionMessage('Invalid AJAX handler name: delete.');
|
||||
|
||||
Request::swap($this->configAjaxRequestMock('delete'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$controller->run('/ajax-test');
|
||||
}
|
||||
|
||||
public function testAjaxInvalidPartial()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\SystemException::class);
|
||||
$this->expectExceptionMessage('Invalid partial name: p:artial.');
|
||||
|
||||
Request::swap($this->configAjaxRequestMock('onTest', 'p:artial'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$controller->run('/ajax-test');
|
||||
}
|
||||
|
||||
public function testAjaxPartialNotFound()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\SystemException::class);
|
||||
$this->expectExceptionMessage('The partial \'partial\' is not found.');
|
||||
|
||||
Request::swap($this->configAjaxRequestMock('onTest', 'partial'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$controller->run('/ajax-test');
|
||||
}
|
||||
|
||||
public function testPageAjax()
|
||||
{
|
||||
Request::swap($this->configAjaxRequestMock('onTest', 'ajax-result'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/ajax-test');
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
|
||||
|
||||
$content = $response->getOriginalContent();
|
||||
$this->assertIsArray($content);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertCount(1, $content);
|
||||
$this->assertArrayHasKey('ajax-result', $content);
|
||||
$this->assertEquals('page', $content['ajax-result']);
|
||||
}
|
||||
|
||||
public function testLayoutAjax()
|
||||
{
|
||||
Request::swap($this->configAjaxRequestMock('onTestLayout', 'ajax-result'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/ajax-test');
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
|
||||
|
||||
$content = $response->getOriginalContent();
|
||||
$this->assertIsArray($content);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertCount(1, $content);
|
||||
$this->assertArrayHasKey('ajax-result', $content);
|
||||
$this->assertEquals('layout-test', $content['ajax-result']);
|
||||
}
|
||||
|
||||
public function testAjaxMultiplePartials()
|
||||
{
|
||||
Request::swap($this->configAjaxRequestMock('onTest', 'ajax-result&ajax-second-result'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/ajax-test');
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
|
||||
|
||||
$content = $response->getOriginalContent();
|
||||
$this->assertIsArray($content);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertCount(2, $content);
|
||||
$this->assertArrayHasKey('ajax-result', $content);
|
||||
$this->assertArrayHasKey('ajax-second-result', $content);
|
||||
$this->assertEquals('page', $content['ajax-result']);
|
||||
$this->assertEquals('second', $content['ajax-second-result']);
|
||||
}
|
||||
|
||||
public function testBasicComponents()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-component')->getContent();
|
||||
$page = self::getProtectedProperty($controller, 'page');
|
||||
$this->assertArrayHasKey('testArchive', $page->components);
|
||||
|
||||
$component = $page->components['testArchive'];
|
||||
$details = $component->componentDetails();
|
||||
|
||||
$content = <<<ESC
|
||||
<div>LAYOUT CONTENT<p>This page uses components.</p>
|
||||
<h3>Lorum ipsum</h3>
|
||||
<p>Post Content #1</p>
|
||||
<h3>La Playa Nudista</h3>
|
||||
<p>Second Post Content</p>
|
||||
</div>
|
||||
ESC;
|
||||
|
||||
$this->assertEquals(str_replace(PHP_EOL, "\n", $content), $response);
|
||||
$this->assertEquals(69, $component->property('posts-per-page'));
|
||||
$this->assertEquals('Blog Archive Dummy Component', $details['name']);
|
||||
$this->assertEquals('Displays an archive of blog posts.', $details['description']);
|
||||
}
|
||||
|
||||
public function testComponentAliases()
|
||||
{
|
||||
include_once base_path() . '/modules/system/tests/fixtures/plugins/winter/tester/components/Archive.php';
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-components')->getContent();
|
||||
$page = self::getProtectedProperty($controller, 'page');
|
||||
|
||||
$this->assertArrayHasKey('firstAlias', $page->components);
|
||||
$this->assertArrayHasKey('secondAlias', $page->components);
|
||||
|
||||
$component = $page->components['firstAlias'];
|
||||
$component2 = $page->components['secondAlias'];
|
||||
|
||||
$content = <<<ESC
|
||||
<div>LAYOUT CONTENT<p>This page uses components.</p>
|
||||
<h3>Lorum ipsum</h3>
|
||||
<p>Post Content #1</p>
|
||||
<h3>La Playa Nudista</h3>
|
||||
<p>Second Post Content</p>
|
||||
</div>
|
||||
ESC;
|
||||
|
||||
$this->assertEquals(str_replace(PHP_EOL, "\n", $content), $response);
|
||||
$this->assertEquals(6, $component->property('posts-per-page'));
|
||||
$this->assertEquals(9, $component2->property('posts-per-page'));
|
||||
}
|
||||
|
||||
public function testComponentAjax()
|
||||
{
|
||||
Request::swap($this->configAjaxRequestMock('testArchive::onTestAjax', 'ajax-result'));
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-component');
|
||||
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
|
||||
|
||||
$content = $response->getOriginalContent();
|
||||
$this->assertIsArray($content);
|
||||
$this->assertEquals(200, $response->getStatusCode());
|
||||
$this->assertCount(1, $content);
|
||||
$this->assertArrayHasKey('ajax-result', $content);
|
||||
$this->assertEquals('page', $content['ajax-result']);
|
||||
}
|
||||
|
||||
public function testComponentClassNotFound()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\SystemException::class);
|
||||
$this->expectExceptionMessageMatches('/is\snot\sregistered\sfor\sthe\scomponent/');
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/no-component-class')->getContent();
|
||||
}
|
||||
|
||||
public function testSoftComponentClassNotFound()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/no-soft-component-class')->getContent();
|
||||
|
||||
$this->assertEquals('<p>Hey</p>', $response);
|
||||
}
|
||||
|
||||
public function testSoftComponentClassFound()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-soft-component-class')->getContent();
|
||||
$page = $controller->getPage();
|
||||
$this->assertArrayHasKey('testArchive', $page->components);
|
||||
|
||||
$component = $page->components['testArchive'];
|
||||
$details = $component->componentDetails();
|
||||
|
||||
$content = <<<ESC
|
||||
<div>LAYOUT CONTENT<p>This page uses components.</p>
|
||||
<h3>Lorum ipsum</h3>
|
||||
<p>Post Content #1</p>
|
||||
<h3>La Playa Nudista</h3>
|
||||
<p>Second Post Content</p>
|
||||
</div>
|
||||
ESC;
|
||||
|
||||
$this->assertEquals(str_replace(PHP_EOL, "\n", $content), $response);
|
||||
$this->assertEquals(69, $component->property('posts-per-page'));
|
||||
$this->assertEquals('Blog Archive Dummy Component', $details['name']);
|
||||
$this->assertEquals('Displays an archive of blog posts.', $details['description']);
|
||||
}
|
||||
|
||||
public function testSoftComponentWithAliasClassFound()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-soft-component-class-alias')->getContent();
|
||||
$page = $controller->getPage();
|
||||
$this->assertArrayHasKey('someAlias', $page->components);
|
||||
|
||||
$component = $page->components['someAlias'];
|
||||
$details = $component->componentDetails();
|
||||
|
||||
$content = <<<ESC
|
||||
<div>LAYOUT CONTENT<p>This page uses components.</p>
|
||||
<h3>Lorum ipsum</h3>
|
||||
<p>Post Content #1</p>
|
||||
<h3>La Playa Nudista</h3>
|
||||
<p>Second Post Content</p>
|
||||
</div>
|
||||
ESC;
|
||||
|
||||
$this->assertEquals(str_replace(PHP_EOL, "\n", $content), $response);
|
||||
$this->assertEquals(69, $component->property('posts-per-page'));
|
||||
$this->assertEquals('Blog Archive Dummy Component', $details['name']);
|
||||
$this->assertEquals('Displays an archive of blog posts.', $details['description']);
|
||||
}
|
||||
|
||||
public function testComponentNotFound()
|
||||
{
|
||||
//
|
||||
// This test should probably be throwing an exception... -sg
|
||||
//
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/no-component')->getContent();
|
||||
|
||||
$this->assertEquals('<p>Hey</p>', $response);
|
||||
}
|
||||
|
||||
public function testComponentPartial()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/component-partial')->getContent();
|
||||
|
||||
$this->assertEquals('<p>DEFAULT MARKUP: I am a post yay</p>', $response);
|
||||
}
|
||||
|
||||
public function testComponentPartialAliasOverride()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/component-partial-alias-override')->getContent();
|
||||
|
||||
//
|
||||
// Testing case sensitivity
|
||||
//
|
||||
// Component alias: overRide1
|
||||
// Target path: partials\override1\default.htm
|
||||
//
|
||||
$this->assertEquals('<p>I am an override alias partial! Yay</p>', $response);
|
||||
}
|
||||
|
||||
public function testComponentPartialOverride()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/component-partial-override')->getContent();
|
||||
|
||||
//
|
||||
// Testing case sensitivity
|
||||
//
|
||||
// Component code: testPost
|
||||
// Target path: partials\testpost\default.htm
|
||||
//
|
||||
$this->assertEquals('<p>I am an override partial! Yay</p>', $response);
|
||||
}
|
||||
|
||||
public function testComponentPartialNesting()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/component-partial-nesting')->getContent();
|
||||
|
||||
$content = <<<ESC
|
||||
<h1>Level 1</h1>
|
||||
<ul>
|
||||
<strong>Home</strong>
|
||||
<strong>Blog</strong>
|
||||
<strong>About</strong>
|
||||
<strong>Contact</strong>
|
||||
<strong>Home</strong>
|
||||
<strong>Blog</strong>
|
||||
<strong>About</strong>
|
||||
<strong>Contact</strong>
|
||||
<strong>Home</strong>
|
||||
<strong>Blog</strong>
|
||||
<strong>About</strong>
|
||||
<strong>Contact</strong>
|
||||
</ul>
|
||||
|
||||
<h1>Level 2</h1>
|
||||
<p>DEFAULT MARKUP: I am a post yay</p><p>I am another post, deep down</p>
|
||||
|
||||
<h1>Level 3</h1>
|
||||
<h4>DEFAULT MARKUP: Menu</h4>
|
||||
<ul>
|
||||
<li>DEFAULT: Home</li>
|
||||
<li>DEFAULT: Blog</li>
|
||||
<li>DEFAULT: About</li>
|
||||
<li>DEFAULT: Contact</li>
|
||||
</ul>
|
||||
<p>Insert post here</p>
|
||||
ESC;
|
||||
|
||||
$this->assertEquals(str_replace(PHP_EOL, "\n", $content), $response);
|
||||
}
|
||||
|
||||
public function testComponentWithOnRender()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/component-custom-render')->getContent();
|
||||
|
||||
$content = <<<ESC
|
||||
Pass
|
||||
Custom output: Would you look over Picasso's shoulder
|
||||
Custom output: And tell him about his brush strokes?
|
||||
ESC;
|
||||
$this->assertEquals(str_replace(PHP_EOL, "\n", $content), $response);
|
||||
}
|
||||
|
||||
public function testMacro()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/with-macro')->getContent();
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'<p><a href="' . Cms::url('/') . '">with-macro.htm</a><strong>with-macro.htm</strong></p>',
|
||||
$response
|
||||
);
|
||||
}
|
||||
|
||||
public function testSharedVariable()
|
||||
{
|
||||
$this->app['view']->share('winterStatus', 'Is Awesome');
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/shared-variable')->getContent();
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'<p>Winter Is Awesome</p>',
|
||||
$response
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'<p>/shared-variable</p>',
|
||||
$response
|
||||
);
|
||||
}
|
||||
|
||||
public function testSharedVariableCannotOverrideSystemGlobals()
|
||||
{
|
||||
$this->app['view']->share('winterStatus', 'Is Awesome');
|
||||
|
||||
// This override should not apply and change the page URL in the fixture template
|
||||
$this->app['view']->share('this', [
|
||||
'page' => [
|
||||
'url' => '/overriden',
|
||||
],
|
||||
]);
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/shared-variable')->getContent();
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'<p>Winter Is Awesome</p>',
|
||||
$response
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'<p>/shared-variable</p>',
|
||||
$response
|
||||
);
|
||||
}
|
||||
|
||||
public function testSharedVariableCanBeOverriddenLocally()
|
||||
{
|
||||
$this->app['view']->share('winterStatus', 'Is Awesome');
|
||||
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller($theme);
|
||||
$response = $controller->run('/shared-variable-override')->getContent();
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'<p>Winter Is Coming</p>',
|
||||
$response
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'<p>/shared-variable-override</p>',
|
||||
$response
|
||||
);
|
||||
}
|
||||
}
|
||||
33
modules/cms/tests/classes/PageTest.php
Normal file
33
modules/cms/tests/classes/PageTest.php
Normal file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\Controller;
|
||||
use Cms\Classes\Page;
|
||||
use Cms\Classes\Theme;
|
||||
|
||||
class PageTest extends TestCase
|
||||
{
|
||||
public function testResolveMenuItem()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
$controller = new Controller;
|
||||
|
||||
$item = (object) [
|
||||
'type' => 'cms-page',
|
||||
'reference' => 'index',
|
||||
];
|
||||
|
||||
// Check to make sure that resolved menuItems for the provided URL are considered active
|
||||
// with or without a trailing slash
|
||||
$url = $controller->pageUrl($item->reference);
|
||||
$trailingUrl = $url . '/';
|
||||
|
||||
$result = Page::resolveMenuItem($item, $url, $theme);
|
||||
$this->assertTrue($result['isActive']);
|
||||
|
||||
$result = Page::resolveMenuItem($item, $trailingUrl, $theme);
|
||||
$this->assertTrue($result['isActive']);
|
||||
}
|
||||
}
|
||||
61
modules/cms/tests/classes/PartialStackTest.php
Normal file
61
modules/cms/tests/classes/PartialStackTest.php
Normal file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\PartialStack;
|
||||
|
||||
class PartialStackTest extends TestCase
|
||||
{
|
||||
|
||||
public function testStackPartials()
|
||||
{
|
||||
$stack = new PartialStack;
|
||||
|
||||
/*
|
||||
* Stack em up
|
||||
*/
|
||||
$stack->stackPartial();
|
||||
$stack->addComponent('override1', 'Winter\Tester\Components\MainMenu');
|
||||
$stack->addComponent('override2', 'Winter\Tester\Components\ContentBlock');
|
||||
|
||||
$stack->stackPartial();
|
||||
$stack->addComponent('override3', 'Winter\Tester\Components\Post');
|
||||
$stack->addComponent('post', 'Winter\Tester\Components\Post');
|
||||
|
||||
$stack->stackPartial();
|
||||
$stack->addComponent('mainMenu', 'Winter\Tester\Components\MainMenu');
|
||||
|
||||
/*
|
||||
* Knock em down
|
||||
*/
|
||||
$this->assertEquals('Winter\Tester\Components\MainMenu', $stack->getComponent('mainMenu'));
|
||||
$this->assertEquals('Winter\Tester\Components\MainMenu', $stack->getComponent('override1'));
|
||||
|
||||
$stack->unstackPartial();
|
||||
|
||||
$this->assertNull($stack->getComponent('mainMenu'));
|
||||
$this->assertEquals('Winter\Tester\Components\ContentBlock', $stack->getComponent('override2'));
|
||||
$this->assertEquals('Winter\Tester\Components\Post', $stack->getComponent('override3'));
|
||||
|
||||
$stack->unstackPartial();
|
||||
|
||||
$this->assertNull($stack->getComponent('mainMenu'));
|
||||
$this->assertNull($stack->getComponent('post'));
|
||||
$this->assertEquals('Winter\Tester\Components\MainMenu', $stack->getComponent('override1'));
|
||||
|
||||
$stack->unstackPartial();
|
||||
|
||||
$this->assertNull($stack->getComponent('post'));
|
||||
$this->assertNull($stack->getComponent('mainMenu'));
|
||||
$this->assertNull($stack->getComponent('override1'));
|
||||
$this->assertNull($stack->getComponent('override2'));
|
||||
$this->assertNull($stack->getComponent('override3'));
|
||||
}
|
||||
|
||||
public function testEmptyStack()
|
||||
{
|
||||
$stack = new PartialStack;
|
||||
$this->assertNull($stack->getComponent('xxx'));
|
||||
}
|
||||
}
|
||||
183
modules/cms/tests/classes/RouterTest.php
Normal file
183
modules/cms/tests/classes/RouterTest.php
Normal file
@@ -0,0 +1,183 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\Router;
|
||||
use Cms\Classes\Theme;
|
||||
use ReflectionClass;
|
||||
|
||||
class RouterTest extends TestCase
|
||||
{
|
||||
protected static $theme = null;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
self::$theme = Theme::load('test');
|
||||
}
|
||||
|
||||
protected static function getMethod($name)
|
||||
{
|
||||
$class = new ReflectionClass('\Cms\Classes\Router');
|
||||
$method = $class->getMethod($name);
|
||||
$method->setAccessible(true);
|
||||
return $method;
|
||||
}
|
||||
|
||||
public static function getProperty($name)
|
||||
{
|
||||
$class = new ReflectionClass('\Cms\Classes\Router');
|
||||
$property = $class->getProperty($name);
|
||||
$property->setAccessible(true);
|
||||
return $property;
|
||||
}
|
||||
|
||||
public function testLoadUrlMap()
|
||||
{
|
||||
$method = self::getMethod('loadUrlMap');
|
||||
$property = self::getProperty('urlMap');
|
||||
$router = new Router(self::$theme);
|
||||
|
||||
/*
|
||||
* The first time the map should be loaded from the disk
|
||||
*/
|
||||
$value = $method->invoke($router);
|
||||
$this->assertFalse($value);
|
||||
$map = $property->getValue($router);
|
||||
|
||||
$this->assertIsArray($map);
|
||||
$this->assertGreaterThanOrEqual(4, count($map));
|
||||
|
||||
/*
|
||||
* The second time the map should be loaded from the disk
|
||||
*/
|
||||
$value = $method->invoke($router);
|
||||
$this->assertTrue($value);
|
||||
$map = $property->getValue($router);
|
||||
$this->assertIsArray($map);
|
||||
$this->assertGreaterThanOrEqual(4, count($map));
|
||||
}
|
||||
|
||||
public function testUrlListCaching()
|
||||
{
|
||||
$router = new Router(self::$theme);
|
||||
$method = self::getMethod('getCachedUrlFileName');
|
||||
$urlList = [];
|
||||
|
||||
/*
|
||||
* The first time the page should be loaded from the disk.
|
||||
*/
|
||||
$result = $method->invokeArgs($router, ['/', &$urlList]);
|
||||
$this->assertNull($result);
|
||||
|
||||
/*
|
||||
* Resolve the page to initialize the cache
|
||||
*/
|
||||
$page = $router->findByUrl('/');
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('index.htm', $page->getFileName());
|
||||
|
||||
/*
|
||||
* The second time the page should be loaded from the cache.
|
||||
*/
|
||||
$result = $method->invokeArgs($router, ['/', &$urlList]);
|
||||
$this->assertEquals('index.htm', $result);
|
||||
|
||||
/*
|
||||
* Clear the cache
|
||||
*/
|
||||
$router->clearCache();
|
||||
$result = $method->invokeArgs($router, ['/', &$urlList]);
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
public function testFindPageByUrl()
|
||||
{
|
||||
$router = new Router(self::$theme);
|
||||
$page = $router->findByUrl('/');
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('index.htm', $page->getFileName());
|
||||
|
||||
$page = $router->findByUrl('blog/post');
|
||||
$this->assertEmpty($page);
|
||||
|
||||
$page = $router->findByUrl('blog/post/my-post-title');
|
||||
$parameters = $router->getParameters();
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('blog-post.htm', $page->getFileName());
|
||||
$this->assertCount(1, $parameters);
|
||||
$this->assertArrayHasKey('url_title', $parameters);
|
||||
$this->assertEquals('my-post-title', $parameters['url_title']);
|
||||
|
||||
// Test cached
|
||||
$page = $router->findByUrl('blog/post/my-post-title');
|
||||
$parameters = $router->getParameters();
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('blog-post.htm', $page->getFileName());
|
||||
$this->assertCount(1, $parameters);
|
||||
$this->assertArrayHasKey('url_title', $parameters);
|
||||
$this->assertEquals('my-post-title', $parameters['url_title']);
|
||||
|
||||
$page = $router->findByUrl('AuthOrs');
|
||||
$parameters = $router->getParameters();
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('authors.htm', $page->getFileName());
|
||||
$this->assertCount(1, $parameters);
|
||||
$this->assertArrayHasKey('author_id', $parameters);
|
||||
$this->assertEquals('no-author', $parameters['author_id']);
|
||||
|
||||
$page = $router->findByUrl('AuthOrs/test');
|
||||
$this->assertEmpty($page);
|
||||
|
||||
$page = $router->findByUrl('AuthOrs/test/12');
|
||||
$this->assertEmpty($page);
|
||||
|
||||
$page = $router->findByUrl('AuthOrs/44/');
|
||||
$parameters = $router->getParameters();
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('authors.htm', $page->getFileName());
|
||||
$this->assertCount(1, $parameters);
|
||||
$this->assertArrayHasKey('author_id', $parameters);
|
||||
$this->assertEquals('44', $parameters['author_id']);
|
||||
|
||||
$page = $router->findByUrl('blog/archive-page');
|
||||
$parameters = $router->getParameters();
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('blog-archive.htm', $page->getFileName());
|
||||
$this->assertCount(1, $parameters);
|
||||
|
||||
$page = $router->findByUrl('blog/category-page');
|
||||
$parameters = $router->getParameters();
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('blog-category.htm', $page->getFileName());
|
||||
$this->assertCount(1, $parameters);
|
||||
$this->assertEquals(array_keys($parameters)[0], 'category_name');
|
||||
$this->assertEmpty($parameters[array_keys($parameters)[0]]);
|
||||
|
||||
$page = $router->findByUrl('blog/category-page/categoryName');
|
||||
$parameters = $router->getParameters();
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('blog-category.htm', $page->getFileName());
|
||||
$this->assertCount(1, $parameters);
|
||||
|
||||
$page = $router->findByUrl('blog/category-page/categoryName/subCategoryName');
|
||||
$parameters = $router->getParameters();
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('blog-category.htm', $page->getFileName());
|
||||
$this->assertCount(1, $parameters);
|
||||
}
|
||||
|
||||
public function testFindPageFromSubdirectory()
|
||||
{
|
||||
$router = new Router(self::$theme);
|
||||
$page = $router->findByUrl('/apage');
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('a/a-page.htm', $page->getFileName());
|
||||
|
||||
$page = $router->findByUrl('/bpage');
|
||||
$this->assertNotEmpty($page);
|
||||
$this->assertEquals('b/b-page.htm', $page->getFileName());
|
||||
}
|
||||
}
|
||||
177
modules/cms/tests/classes/ThemeTest.php
Normal file
177
modules/cms/tests/classes/ThemeTest.php
Normal file
@@ -0,0 +1,177 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Models\ThemeData;
|
||||
use Config;
|
||||
use Event;
|
||||
use Winter\Storm\Exception\ApplicationException;
|
||||
|
||||
class ThemeTest extends TestCase
|
||||
{
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Config::set('cms.activeTheme', 'test');
|
||||
Config::set('cms.themesPath', '/modules/cms/tests/fixtures/themes');
|
||||
|
||||
Event::flush('cms.theme.getActiveTheme');
|
||||
Theme::resetCache();
|
||||
}
|
||||
|
||||
protected function countThemePages($path)
|
||||
{
|
||||
$result = 0;
|
||||
$it = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($path));
|
||||
$it->setMaxDepth(1);
|
||||
$it->rewind();
|
||||
|
||||
while ($it->valid()) {
|
||||
if (!$it->isDot() && !$it->isDir() && $it->getExtension() == 'htm') {
|
||||
$result++;
|
||||
}
|
||||
|
||||
$it->next();
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
public function testGetPath()
|
||||
{
|
||||
if (PHP_OS_FAMILY === 'Windows') {
|
||||
$this->markTestIncomplete('Need to fix Windows testing here');
|
||||
}
|
||||
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$this->assertEquals(base_path('modules/cms/tests/fixtures/themes/test'), $theme->getPath());
|
||||
}
|
||||
|
||||
public function testListPages()
|
||||
{
|
||||
$theme = Theme::load('test');
|
||||
|
||||
$pageCollection = $theme->listPages();
|
||||
$pages = array_values($pageCollection->all());
|
||||
$this->assertIsArray($pages);
|
||||
|
||||
$expectedPageNum = $this->countThemePages(base_path() . '/modules/cms/tests/fixtures/themes/test/pages');
|
||||
$this->assertCount($expectedPageNum, $pages);
|
||||
|
||||
$this->assertInstanceOf('\Cms\Classes\Page', $pages[0]);
|
||||
$this->assertNotEmpty($pages[0]->url);
|
||||
$this->assertInstanceOf('\Cms\Classes\Page', $pages[1]);
|
||||
$this->assertNotEmpty($pages[1]->url);
|
||||
}
|
||||
|
||||
public function testGetActiveTheme()
|
||||
{
|
||||
$activeTheme = Theme::getActiveTheme();
|
||||
|
||||
$this->assertNotNull($activeTheme);
|
||||
$this->assertEquals('test', $activeTheme->getDirName());
|
||||
}
|
||||
|
||||
public function testNoActiveTheme()
|
||||
{
|
||||
$this->expectException(\Winter\Storm\Exception\SystemException::class);
|
||||
$this->expectExceptionMessage('The active theme is not set.');
|
||||
|
||||
Config::set('cms.activeTheme', null);
|
||||
Theme::getActiveTheme();
|
||||
}
|
||||
|
||||
public function testApiTheme()
|
||||
{
|
||||
Event::flush('cms.theme.getActiveTheme');
|
||||
Event::listen('cms.theme.getActiveTheme', function () {
|
||||
return 'apitest';
|
||||
});
|
||||
|
||||
$activeTheme = Theme::getActiveTheme();
|
||||
$this->assertNotNull($activeTheme);
|
||||
$this->assertEquals('apitest', $activeTheme->getDirName());
|
||||
}
|
||||
|
||||
public function testChildThemeConfig()
|
||||
{
|
||||
Config::set('cms.activeTheme', 'childtest');
|
||||
|
||||
$theme = Theme::getActiveTheme();
|
||||
$config = $theme->getConfig();
|
||||
|
||||
$this->assertArrayHasKey('parent', $config);
|
||||
$this->assertEquals('test', $config['parent']);
|
||||
}
|
||||
|
||||
public function testChildThemeAssetUrl()
|
||||
{
|
||||
Config::set('cms.activeTheme', 'childtest');
|
||||
|
||||
$theme = Theme::getActiveTheme();
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'modules/cms/tests/fixtures/themes/test/assets/css/style1.css',
|
||||
$theme->assetUrl('assets/css/style1.css')
|
||||
);
|
||||
|
||||
$this->assertStringContainsString(
|
||||
'modules/cms/tests/fixtures/themes/childtest/assets/css/style2.css',
|
||||
$theme->assetUrl('assets/css/style2.css')
|
||||
);
|
||||
}
|
||||
|
||||
public function dirNameValidityProvider(): array
|
||||
{
|
||||
return [
|
||||
// Valid names
|
||||
'lowercase' => ['mytheme', true],
|
||||
'uppercase' => ['MYTHEME', true],
|
||||
'mixed case' => ['MyTheme', true],
|
||||
'with digits' => ['theme123', true],
|
||||
'with hyphens' => ['my-theme', true],
|
||||
'with underscores' => ['my_theme', true],
|
||||
'all allowed' => ['My-Theme_123', true],
|
||||
'single char' => ['a', true],
|
||||
|
||||
// Invalid names
|
||||
'empty string' => ['', false],
|
||||
'dot' => ['.', false],
|
||||
'dot dot' => ['..', false],
|
||||
'path traversal' => ['../etc', false],
|
||||
'forward slash' => ['foo/bar', false],
|
||||
'backslash' => ['foo\\bar', false],
|
||||
'spaces' => ['my theme', false],
|
||||
'special chars' => ['theme!@#', false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider dirNameValidityProvider
|
||||
*/
|
||||
public function testIsValidDirName(string $dirName, bool $expected): void
|
||||
{
|
||||
$this->assertSame($expected, Theme::isValidDirName($dirName));
|
||||
}
|
||||
|
||||
public function testLoadRejectsPathTraversal()
|
||||
{
|
||||
$this->expectException(ApplicationException::class);
|
||||
|
||||
Theme::load('../../etc');
|
||||
}
|
||||
|
||||
public function testResetCacheClearsThemeData()
|
||||
{
|
||||
$themeData = new ThemeData(['theme' => 'test']);
|
||||
self::setProtectedProperty($themeData, 'instances', ['test' => $themeData]);
|
||||
|
||||
Theme::resetCache();
|
||||
|
||||
$this->assertEmpty(self::getProtectedProperty($themeData, 'instances'));
|
||||
}
|
||||
}
|
||||
165
modules/cms/tests/classes/TwigExtensionTest.php
Normal file
165
modules/cms/tests/classes/TwigExtensionTest.php
Normal file
@@ -0,0 +1,165 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Classes;
|
||||
|
||||
use Cms\Twig\Extension;
|
||||
use Cms\Classes\Controller;
|
||||
|
||||
use System\Classes\Asset\PackageManager;
|
||||
use System\Tests\Bootstrap\TestCase;
|
||||
use Winter\Storm\Exception\SystemException;
|
||||
use Winter\Storm\Support\Facades\File;
|
||||
|
||||
class TwigExtensionTest extends TestCase
|
||||
{
|
||||
private const VITE_FIXTURE_PACKAGE = 'theme-assettest';
|
||||
private const VITE_FIXTURE_THEME_PATH = '/modules/system/tests/fixtures/themes/assettest';
|
||||
private const VITE_HOT_URL = 'http://localhost:5173';
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
$hotFile = base_path(self::VITE_FIXTURE_THEME_PATH . '/assets/dist/hot');
|
||||
if (File::exists($hotFile)) {
|
||||
File::delete($hotFile);
|
||||
}
|
||||
$distDir = base_path(self::VITE_FIXTURE_THEME_PATH . '/assets/dist');
|
||||
if (File::isDirectory($distDir) && count(File::files($distDir)) === 0 && count(File::directories($distDir)) === 0) {
|
||||
File::deleteDirectory($distDir);
|
||||
}
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testPartialFunction()
|
||||
{
|
||||
$extension = new Extension;
|
||||
$controller = Controller::getController() ?: new Controller;
|
||||
$extension->setController($controller);
|
||||
|
||||
$this->assertFalse($extension->partialFunction('invalid-partial-file', [], false));
|
||||
|
||||
$this->expectException(SystemException::class);
|
||||
$this->expectExceptionMessageMatches('/is\snot\sfound/');
|
||||
$this->assertFalse($extension->partialFunction('invalid-partial-file', [], true));
|
||||
}
|
||||
|
||||
public function testContentFunction()
|
||||
{
|
||||
$extension = new Extension;
|
||||
$controller = Controller::getController() ?: new Controller;
|
||||
$extension->setController($controller);
|
||||
|
||||
$this->assertFalse($extension->contentFunction('invalid-content-file', [], false));
|
||||
|
||||
$this->expectException(SystemException::class);
|
||||
$this->expectExceptionMessageMatches('/is\snot\sfound/');
|
||||
$this->assertFalse($extension->contentFunction('invalid-content-file', [], true));
|
||||
}
|
||||
|
||||
public function testStylesTagEmitsCssAndViteCss(): void
|
||||
{
|
||||
[$extension, $controller] = $this->buildExtensionWithViteAssets([
|
||||
'assets/css/theme.css',
|
||||
'assets/javascript/theme.js',
|
||||
]);
|
||||
$controller->addCss('plain.css');
|
||||
|
||||
// This is exactly the call StylesNode::compile() writes into the compiled `{% styles %}` tag
|
||||
$output = (string) $extension->assetsFunction('css');
|
||||
|
||||
$this->assertStringContainsString('plain.css', $output);
|
||||
$this->assertStringContainsString('assets/css/theme.css', $output);
|
||||
$this->assertStringNotContainsString('assets/javascript/theme.js', $output);
|
||||
}
|
||||
|
||||
public function testScriptsTagEmitsJsAndViteJs(): void
|
||||
{
|
||||
[$extension, $controller] = $this->buildExtensionWithViteAssets([
|
||||
'assets/css/theme.css',
|
||||
'assets/javascript/theme.js',
|
||||
]);
|
||||
$controller->addJs('plain.js');
|
||||
|
||||
// Mirrors what ScriptsNode::compile() emits for the `{% scripts %}` tag
|
||||
$output = (string) $extension->assetsFunction('js');
|
||||
|
||||
$this->assertStringContainsString('plain.js', $output);
|
||||
$this->assertStringContainsString('assets/javascript/theme.js', $output);
|
||||
$this->assertStringNotContainsString('assets/css/theme.css', $output);
|
||||
}
|
||||
|
||||
public function testStylesAndScriptsTagsDoNotCrossLeak(): void
|
||||
{
|
||||
// Reverse entrypoint order to confirm filtering doesn't depend on array order
|
||||
[$extension] = $this->buildExtensionWithViteAssets([
|
||||
'assets/javascript/theme.js',
|
||||
'assets/css/theme.css',
|
||||
]);
|
||||
|
||||
$stylesOutput = (string) $extension->assetsFunction('css');
|
||||
$scriptsOutput = (string) $extension->assetsFunction('js');
|
||||
|
||||
$this->assertStringContainsString('assets/css/theme.css', $stylesOutput);
|
||||
$this->assertStringNotContainsString('assets/javascript/theme.js', $stylesOutput);
|
||||
|
||||
$this->assertStringContainsString('assets/javascript/theme.js', $scriptsOutput);
|
||||
$this->assertStringNotContainsString('assets/css/theme.css', $scriptsOutput);
|
||||
}
|
||||
|
||||
public function testStylesTagOmitsViteWhenNoCssEntrypoints(): void
|
||||
{
|
||||
// JS-only vite registration; the styles tag must contribute no vite output for it
|
||||
[$extension] = $this->buildExtensionWithViteAssets([
|
||||
'assets/javascript/theme.js',
|
||||
]);
|
||||
|
||||
$output = (string) $extension->assetsFunction('css');
|
||||
|
||||
$this->assertStringNotContainsString('@vite/client', $output);
|
||||
$this->assertStringNotContainsString('assets/javascript/theme.js', $output);
|
||||
}
|
||||
|
||||
/**
|
||||
* Boots a Cms\Twig\Extension wired up to a fresh Controller that has the
|
||||
* given vite entrypoints registered against the assettest fixture package.
|
||||
* The fixture's hot file is written so Vite::tags() emits deterministic
|
||||
* dev-server tags (no manifest required).
|
||||
*
|
||||
* @return array{0: Extension, 1: Controller}
|
||||
*/
|
||||
private function buildExtensionWithViteAssets(array $entrypoints): array
|
||||
{
|
||||
$themePath = base_path(self::VITE_FIXTURE_THEME_PATH);
|
||||
if (!File::isDirectory($themePath)) {
|
||||
$this->markTestSkipped('Vite test fixture is missing at ' . self::VITE_FIXTURE_THEME_PATH);
|
||||
}
|
||||
|
||||
// PackageManager's lazy init() touches Theme::all() and Halcyon model events, which
|
||||
// can blow up when prior tests in the full suite have left datasource/plugin state in
|
||||
// an inconsistent shape. Skip gracefully — same pattern as the existing
|
||||
// ViteInstallTest — so this test still asserts something useful in isolation.
|
||||
try {
|
||||
$packageManager = PackageManager::instance();
|
||||
} catch (\Throwable $e) {
|
||||
$this->markTestSkipped('PackageManager could not initialise in this environment: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
// registerPackage silently no-ops when re-registering the same name + config.
|
||||
$packageManager->registerPackage(
|
||||
self::VITE_FIXTURE_PACKAGE,
|
||||
$themePath . '/vite.config.mjs',
|
||||
'vite'
|
||||
);
|
||||
|
||||
File::ensureDirectoryExists($themePath . '/assets/dist');
|
||||
File::put($themePath . '/assets/dist/hot', self::VITE_HOT_URL);
|
||||
|
||||
$controller = new Controller();
|
||||
$controller->addVite($entrypoints, self::VITE_FIXTURE_PACKAGE);
|
||||
|
||||
$extension = new Extension();
|
||||
$extension->setController($controller);
|
||||
|
||||
return [$extension, $controller];
|
||||
}
|
||||
}
|
||||
253
modules/cms/tests/controllers/IndexPermissionTest.php
Normal file
253
modules/cms/tests/controllers/IndexPermissionTest.php
Normal file
@@ -0,0 +1,253 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Controllers;
|
||||
|
||||
use Backend\Tests\Fixtures\Models\UserFixture;
|
||||
use Cms\Controllers\Index;
|
||||
use System\Tests\Bootstrap\PluginTestCase;
|
||||
use Winter\Storm\Auth\AuthorizationException;
|
||||
|
||||
class IndexPermissionTest extends PluginTestCase
|
||||
{
|
||||
protected Index $controller;
|
||||
|
||||
protected static array $allTypes = ['page', 'partial', 'layout', 'content', 'asset'];
|
||||
|
||||
protected static array $permissionMap = [
|
||||
'page' => 'cms.manage_pages',
|
||||
'partial' => 'cms.manage_partials',
|
||||
'layout' => 'cms.manage_layouts',
|
||||
'content' => 'cms.manage_content',
|
||||
'asset' => 'cms.manage_assets',
|
||||
];
|
||||
|
||||
//
|
||||
// getRelevantPermissionForType mapping
|
||||
//
|
||||
|
||||
/**
|
||||
* @dataProvider permissionMappingProvider
|
||||
*/
|
||||
public function testGetRelevantPermissionForType(string $type, string $expectedPermission): void
|
||||
{
|
||||
$this->actingAs((new UserFixture)->asSuperUser());
|
||||
$controller = new Index;
|
||||
|
||||
$result = self::callProtectedMethod($controller, 'getRelevantPermissionForType', [$type]);
|
||||
$this->assertEquals($expectedPermission, $result);
|
||||
}
|
||||
|
||||
public static function permissionMappingProvider(): array
|
||||
{
|
||||
return [
|
||||
'page maps to cms.manage_pages' => ['page', 'cms.manage_pages'],
|
||||
'partial maps to cms.manage_partials' => ['partial', 'cms.manage_partials'],
|
||||
'layout maps to cms.manage_layouts' => ['layout', 'cms.manage_layouts'],
|
||||
'content maps to cms.manage_content' => ['content', 'cms.manage_content'],
|
||||
'asset maps to cms.manage_assets' => ['asset', 'cms.manage_assets'],
|
||||
];
|
||||
}
|
||||
|
||||
//
|
||||
// validateRequestType — allowed access
|
||||
//
|
||||
|
||||
/**
|
||||
* @dataProvider allowedAccessProvider
|
||||
*/
|
||||
public function testValidateRequestTypeAllowed(string $type, array $permissions): void
|
||||
{
|
||||
$user = new UserFixture;
|
||||
foreach ($permissions as $permission) {
|
||||
$user->withPermission($permission, true);
|
||||
}
|
||||
$this->actingAs($user);
|
||||
$controller = new Index;
|
||||
|
||||
// Should not throw
|
||||
self::callProtectedMethod($controller, 'validateRequestType', [$type]);
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
public static function allowedAccessProvider(): array
|
||||
{
|
||||
return [
|
||||
'pages user can access page' => ['page', ['cms.manage_pages']],
|
||||
'partials user can access partial' => ['partial', ['cms.manage_partials']],
|
||||
'layouts user can access layout' => ['layout', ['cms.manage_layouts']],
|
||||
'content user can access content' => ['content', ['cms.manage_content']],
|
||||
'assets user can access asset' => ['asset', ['cms.manage_assets']],
|
||||
'all permissions can access page' => ['page', [
|
||||
'cms.manage_pages', 'cms.manage_partials', 'cms.manage_layouts',
|
||||
'cms.manage_content', 'cms.manage_assets',
|
||||
]],
|
||||
'all permissions can access asset' => ['asset', [
|
||||
'cms.manage_pages', 'cms.manage_partials', 'cms.manage_layouts',
|
||||
'cms.manage_content', 'cms.manage_assets',
|
||||
]],
|
||||
];
|
||||
}
|
||||
|
||||
public function testSuperuserCanAccessAllTypes(): void
|
||||
{
|
||||
$this->actingAs((new UserFixture)->asSuperUser());
|
||||
$controller = new Index;
|
||||
|
||||
foreach (self::$allTypes as $type) {
|
||||
self::callProtectedMethod($controller, 'validateRequestType', [$type]);
|
||||
}
|
||||
$this->assertTrue(true);
|
||||
}
|
||||
|
||||
//
|
||||
// validateRequestType — denied access
|
||||
//
|
||||
|
||||
/**
|
||||
* @dataProvider deniedAccessProvider
|
||||
*/
|
||||
public function testValidateRequestTypeDenied(string $grantedPermission, string $deniedType): void
|
||||
{
|
||||
$user = (new UserFixture)->withPermission($grantedPermission, true);
|
||||
$this->actingAs($user);
|
||||
$controller = new Index;
|
||||
|
||||
$this->expectException(AuthorizationException::class);
|
||||
self::callProtectedMethod($controller, 'validateRequestType', [$deniedType]);
|
||||
}
|
||||
|
||||
public static function deniedAccessProvider(): array
|
||||
{
|
||||
$cases = [];
|
||||
foreach (self::$permissionMap as $grantedType => $grantedPermission) {
|
||||
foreach (self::$allTypes as $targetType) {
|
||||
if ($targetType === $grantedType) {
|
||||
continue;
|
||||
}
|
||||
$cases["$grantedType user denied $targetType"] = [$grantedPermission, $targetType];
|
||||
}
|
||||
}
|
||||
return $cases;
|
||||
}
|
||||
|
||||
//
|
||||
// Constructor widget registration
|
||||
//
|
||||
|
||||
public function testSuperuserGetsAllWidgets(): void
|
||||
{
|
||||
$this->actingAs((new UserFixture)->asSuperUser());
|
||||
$controller = new Index;
|
||||
|
||||
$this->assertNotNull($controller->widget->pageList ?? null, 'pageList should be registered');
|
||||
$this->assertNotNull($controller->widget->partialList ?? null, 'partialList should be registered');
|
||||
$this->assertNotNull($controller->widget->layoutList ?? null, 'layoutList should be registered');
|
||||
$this->assertNotNull($controller->widget->contentList ?? null, 'contentList should be registered');
|
||||
$this->assertNotNull($controller->widget->assetList ?? null, 'assetList should be registered');
|
||||
$this->assertNotNull($controller->widget->componentList ?? null, 'componentList should be registered');
|
||||
}
|
||||
|
||||
public function testPagesOnlyUserGetsOnlyPageList(): void
|
||||
{
|
||||
$user = (new UserFixture)->withPermission('cms.manage_pages', true);
|
||||
$this->actingAs($user);
|
||||
$controller = new Index;
|
||||
|
||||
$this->assertNotNull($controller->widget->pageList ?? null, 'pageList should be registered');
|
||||
$this->assertNull($controller->widget->partialList ?? null, 'partialList should not be registered');
|
||||
$this->assertNull($controller->widget->layoutList ?? null, 'layoutList should not be registered');
|
||||
$this->assertNull($controller->widget->contentList ?? null, 'contentList should not be registered');
|
||||
$this->assertNull($controller->widget->assetList ?? null, 'assetList should not be registered');
|
||||
// Pages user should get componentList since components are usable in pages
|
||||
$this->assertNotNull($controller->widget->componentList ?? null, 'componentList should be registered for pages user');
|
||||
}
|
||||
|
||||
public function testAssetsOnlyUserGetsOnlyAssetList(): void
|
||||
{
|
||||
$user = (new UserFixture)->withPermission('cms.manage_assets', true);
|
||||
$this->actingAs($user);
|
||||
$controller = new Index;
|
||||
|
||||
$this->assertNull($controller->widget->pageList ?? null, 'pageList should not be registered');
|
||||
$this->assertNull($controller->widget->partialList ?? null, 'partialList should not be registered');
|
||||
$this->assertNull($controller->widget->layoutList ?? null, 'layoutList should not be registered');
|
||||
$this->assertNull($controller->widget->contentList ?? null, 'contentList should not be registered');
|
||||
$this->assertNotNull($controller->widget->assetList ?? null, 'assetList should be registered');
|
||||
$this->assertNull($controller->widget->componentList ?? null, 'componentList should not be registered for assets-only user');
|
||||
}
|
||||
|
||||
public function testPagesAndLayoutsUserGetsComponentList(): void
|
||||
{
|
||||
$user = (new UserFixture)
|
||||
->withPermission('cms.manage_pages', true)
|
||||
->withPermission('cms.manage_layouts', true);
|
||||
$this->actingAs($user);
|
||||
$controller = new Index;
|
||||
|
||||
$this->assertNotNull($controller->widget->pageList ?? null, 'pageList should be registered');
|
||||
$this->assertNull($controller->widget->partialList ?? null, 'partialList should not be registered');
|
||||
$this->assertNotNull($controller->widget->layoutList ?? null, 'layoutList should be registered');
|
||||
$this->assertNull($controller->widget->contentList ?? null, 'contentList should not be registered');
|
||||
$this->assertNull($controller->widget->assetList ?? null, 'assetList should not be registered');
|
||||
$this->assertNotNull($controller->widget->componentList ?? null, 'componentList should be registered');
|
||||
}
|
||||
|
||||
public function testContentOnlyUserDoesNotGetComponentList(): void
|
||||
{
|
||||
$user = (new UserFixture)->withPermission('cms.manage_content', true);
|
||||
$this->actingAs($user);
|
||||
$controller = new Index;
|
||||
|
||||
$this->assertNotNull($controller->widget->contentList ?? null, 'contentList should be registered');
|
||||
$this->assertNull($controller->widget->componentList ?? null, 'componentList should not be registered for content-only user');
|
||||
}
|
||||
|
||||
//
|
||||
// makeTemplateFormWidget — permission checks
|
||||
//
|
||||
|
||||
/**
|
||||
* @dataProvider deniedAccessProvider
|
||||
*/
|
||||
public function testMakeTemplateFormWidgetDenied(string $grantedPermission, string $deniedType): void
|
||||
{
|
||||
$user = (new UserFixture)->withPermission($grantedPermission, true);
|
||||
$this->actingAs($user);
|
||||
$controller = new Index;
|
||||
|
||||
$template = self::callProtectedMethod($controller, 'createTemplate', [$deniedType]);
|
||||
|
||||
$this->expectException(AuthorizationException::class);
|
||||
self::callProtectedMethod($controller, 'makeTemplateFormWidget', [$deniedType, $template]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider allowedAccessProvider
|
||||
*/
|
||||
public function testMakeTemplateFormWidgetAllowed(string $type, array $permissions): void
|
||||
{
|
||||
$user = new UserFixture;
|
||||
foreach ($permissions as $permission) {
|
||||
$user->withPermission($permission, true);
|
||||
}
|
||||
$this->actingAs($user);
|
||||
$controller = new Index;
|
||||
|
||||
$template = self::callProtectedMethod($controller, 'createTemplate', [$type]);
|
||||
$widget = self::callProtectedMethod($controller, 'makeTemplateFormWidget', [$type, $template]);
|
||||
|
||||
$this->assertInstanceOf(\Backend\Widgets\Form::class, $widget);
|
||||
}
|
||||
|
||||
public function testMakeTemplateFormWidgetSuperuserAllTypes(): void
|
||||
{
|
||||
$this->actingAs((new UserFixture)->asSuperUser());
|
||||
$controller = new Index;
|
||||
|
||||
foreach (self::$allTypes as $type) {
|
||||
$template = self::callProtectedMethod($controller, 'createTemplate', [$type]);
|
||||
$widget = self::callProtectedMethod($controller, 'makeTemplateFormWidget', [$type, $template]);
|
||||
$this->assertInstanceOf(\Backend\Widgets\Form::class, $widget, "Superuser should be able to create form widget for $type");
|
||||
}
|
||||
}
|
||||
}
|
||||
7
modules/cms/tests/fixtures/reference/compound-full.htm
vendored
Normal file
7
modules/cms/tests/fixtures/reference/compound-full.htm
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
var = "value"
|
||||
==
|
||||
<?php
|
||||
function a() {return true;}
|
||||
?>
|
||||
==
|
||||
<p>Hello, world!</p>
|
||||
3
modules/cms/tests/fixtures/reference/compound-markup-settings.htm
vendored
Normal file
3
modules/cms/tests/fixtures/reference/compound-markup-settings.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
var = "value"
|
||||
==
|
||||
<p>Hello, world!</p>
|
||||
1
modules/cms/tests/fixtures/reference/compound-markup.htm
vendored
Normal file
1
modules/cms/tests/fixtures/reference/compound-markup.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>Hello, world!</p>
|
||||
9
modules/cms/tests/fixtures/reference/namespaces-aliases.php.stub
vendored
Normal file
9
modules/cms/tests/fixtures/reference/namespaces-aliases.php.stub
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
use Cms\Classes\Theme as MyTheme;
|
||||
use Cms\Classes\Router as MyRouter;
|
||||
class {className} extends Cms\Classes\PageCode
|
||||
{
|
||||
public function onStart() {
|
||||
$this['pageStartVar'] = 3;
|
||||
}
|
||||
}
|
||||
9
modules/cms/tests/fixtures/reference/namespaces.php.stub
vendored
Normal file
9
modules/cms/tests/fixtures/reference/namespaces.php.stub
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Classes\Router;
|
||||
class {className} extends Cms\Classes\PageCode
|
||||
{
|
||||
public function onStart() {
|
||||
$this['pageStartVar'] = 3;
|
||||
}
|
||||
}
|
||||
2
modules/cms/tests/fixtures/themes/apitest/.gitignore
vendored
Normal file
2
modules/cms/tests/fixtures/themes/apitest/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*.htm
|
||||
testobjects/*.htm
|
||||
0
modules/cms/tests/fixtures/themes/childtest/assets/css/style2.css
vendored
Normal file
0
modules/cms/tests/fixtures/themes/childtest/assets/css/style2.css
vendored
Normal file
1
modules/cms/tests/fixtures/themes/childtest/content/a/a-content.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/childtest/content/a/a-content.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
A child content
|
||||
1
modules/cms/tests/fixtures/themes/childtest/partials/a/a-partial.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/childtest/partials/a/a-partial.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
A child partial
|
||||
2
modules/cms/tests/fixtures/themes/childtest/theme.yaml
vendored
Normal file
2
modules/cms/tests/fixtures/themes/childtest/theme.yaml
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
name: ChildTest
|
||||
parent: test
|
||||
0
modules/cms/tests/fixtures/themes/test/assets/css/style1.css
vendored
Normal file
0
modules/cms/tests/fixtures/themes/test/assets/css/style1.css
vendored
Normal file
0
modules/cms/tests/fixtures/themes/test/assets/css/style2.css
vendored
Normal file
0
modules/cms/tests/fixtures/themes/test/assets/css/style2.css
vendored
Normal file
BIN
modules/cms/tests/fixtures/themes/test/assets/images/winter.png
vendored
Normal file
BIN
modules/cms/tests/fixtures/themes/test/assets/images/winter.png
vendored
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 7.3 KiB |
1
modules/cms/tests/fixtures/themes/test/assets/js/script1.js
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/assets/js/script1.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
console.log('script1.js');
|
||||
1
modules/cms/tests/fixtures/themes/test/assets/js/script2.js
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/assets/js/script2.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
console.log('script2.js');
|
||||
1
modules/cms/tests/fixtures/themes/test/assets/js/subdir/script1.js
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/assets/js/subdir/script1.js
vendored
Normal file
@@ -0,0 +1 @@
|
||||
console.log('subdir/script1.js');
|
||||
1
modules/cms/tests/fixtures/themes/test/content/a/a-content.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/content/a/a-content.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
A content
|
||||
1
modules/cms/tests/fixtures/themes/test/content/html-content.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/content/html-content.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<a href="#">Stephen Saucier</a> changed his profile picture — <small>7 hrs ago</small></div>
|
||||
1
modules/cms/tests/fixtures/themes/test/content/layout-content.txt
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/content/layout-content.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
LAYOUT CONTENT
|
||||
1
modules/cms/tests/fixtures/themes/test/content/markdown-content.md
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/content/markdown-content.md
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Be brave, be **bold**, live *italic*
|
||||
1
modules/cms/tests/fixtures/themes/test/content/page-content.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/content/page-content.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
PAGE CONTENT
|
||||
1
modules/cms/tests/fixtures/themes/test/content/text-content.txt
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/content/text-content.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Pen is <mightier> than the sword, HTML is <richer> than the text
|
||||
1
modules/cms/tests/fixtures/themes/test/layouts/a/a-layout.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/layouts/a/a-layout.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<div>LAYOUT CONTENT {{ page() }}</div>
|
||||
10
modules/cms/tests/fixtures/themes/test/layouts/ajax-test.htm
vendored
Normal file
10
modules/cms/tests/fixtures/themes/test/layouts/ajax-test.htm
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
description = "This layout tests the AJAX events"
|
||||
==
|
||||
function onTest() {
|
||||
$this['var'] = 'layout';
|
||||
}
|
||||
function onTestLayout() {
|
||||
$this['var'] = 'layout-test';
|
||||
}
|
||||
==
|
||||
{{ page() }}
|
||||
1
modules/cms/tests/fixtures/themes/test/layouts/content.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/layouts/content.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<div>{{ content('layout-content.txt') }}{{ page() }}</div>
|
||||
15
modules/cms/tests/fixtures/themes/test/layouts/cycle-test.htm
vendored
Normal file
15
modules/cms/tests/fixtures/themes/test/layouts/cycle-test.htm
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
description = "This layout tests the page life cycle"
|
||||
==
|
||||
function onStart() {
|
||||
$this['layoutStartVar'] = 1;
|
||||
}
|
||||
|
||||
function onBeforePageStart() {
|
||||
$this['layoutBeforePageStartVar'] = 2;
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
$this['layoutEndVar'] = 5;
|
||||
}
|
||||
==
|
||||
{{ page() }}
|
||||
1
modules/cms/tests/fixtures/themes/test/layouts/no-php.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/layouts/no-php.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<div>{{ page() }}</div>
|
||||
1
modules/cms/tests/fixtures/themes/test/layouts/partials.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/layouts/partials.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<div>{{ partial('layout-partial') }}{{ page() }}</div>
|
||||
10
modules/cms/tests/fixtures/themes/test/layouts/php-parser-test.htm
vendored
Normal file
10
modules/cms/tests/fixtures/themes/test/layouts/php-parser-test.htm
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
description = "Layout for testing the CMS PHP parser"
|
||||
==
|
||||
function onStart() {
|
||||
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
}
|
||||
==
|
||||
{{ layoutStartVar }}{{ layoutBeforePageStartVar }}{{ pageStartVar }}{{ pageEndVar }}{{ layoutEndVar }}
|
||||
1
modules/cms/tests/fixtures/themes/test/layouts/placeholder.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/layouts/placeholder.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<div>LAYOUT CONTENT <span>{% placeholder test default %}DEFAULT{% endplaceholder %}</span> {{ page() }}</div>{% placeholder second %}
|
||||
1
modules/cms/tests/fixtures/themes/test/layouts/sidebar.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/layouts/sidebar.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<div>{% page %}</div>
|
||||
3
modules/cms/tests/fixtures/themes/test/pages/404.htm
vendored
Normal file
3
modules/cms/tests/fixtures/themes/test/pages/404.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
url = "404"
|
||||
==
|
||||
<p>Page not found</p>
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/a/a-page.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/a/a-page.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/apage"
|
||||
layout = "a/a-layout"
|
||||
==
|
||||
<h1>This page is a subdirectory</h1>
|
||||
8
modules/cms/tests/fixtures/themes/test/pages/ajax-test.htm
vendored
Normal file
8
modules/cms/tests/fixtures/themes/test/pages/ajax-test.htm
vendored
Normal file
@@ -0,0 +1,8 @@
|
||||
url = "/ajax-test"
|
||||
layout = "ajax-test"
|
||||
==
|
||||
function onTest() {
|
||||
$this['var'] = 'page';
|
||||
}
|
||||
==
|
||||
{{ layoutStartVar }}{{ layoutBeforePageStartVar }}{{ pageStartVar }}{{ pageEndVar }}{{ layoutEndVar }}
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/authors.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/authors.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/authors/:author_id?no-author|^[0-9]+$"
|
||||
==
|
||||
<h1>Authors page</h1>
|
||||
<p>This is the Authors page</p>
|
||||
3
modules/cms/tests/fixtures/themes/test/pages/b/b-page.htm
vendored
Normal file
3
modules/cms/tests/fixtures/themes/test/pages/b/b-page.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
url = "/bpage"
|
||||
==
|
||||
<h1>This page is a subdirectory</h1>
|
||||
3
modules/cms/tests/fixtures/themes/test/pages/b/c/c-page.htm
vendored
Normal file
3
modules/cms/tests/fixtures/themes/test/pages/b/c/c-page.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
url = "/cpage"
|
||||
==
|
||||
<h1>This page is a subdirectory</h1>
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/blog-archive.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/blog-archive.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/blog/archive-page/:page?1"
|
||||
==
|
||||
<h1>Blog Archive</h1>
|
||||
<p>Hi there!</p>
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/blog-category.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/blog-category.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/blog/category-page/:category_name?*"
|
||||
==
|
||||
<h1>Blog category</h1>
|
||||
<p>This is a blog category page</p>
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/blog-post.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/blog-post.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/blog/post/:url_title"
|
||||
==
|
||||
<h1>Blog post</h1>
|
||||
<p>This is a blog post page</p>
|
||||
13
modules/cms/tests/fixtures/themes/test/pages/code-namespaces-aliases.htm
vendored
Normal file
13
modules/cms/tests/fixtures/themes/test/pages/code-namespaces-aliases.htm
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
url = "/code-namespaces"
|
||||
==
|
||||
<?php
|
||||
|
||||
use Cms\Classes\Theme as MyTheme;
|
||||
use Cms\Classes\Router as MyRouter;
|
||||
|
||||
function onStart() {
|
||||
$this['pageStartVar'] = 3;
|
||||
}
|
||||
?>
|
||||
==
|
||||
<p>Page</p>
|
||||
13
modules/cms/tests/fixtures/themes/test/pages/code-namespaces.htm
vendored
Normal file
13
modules/cms/tests/fixtures/themes/test/pages/code-namespaces.htm
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
url = "/code-namespaces"
|
||||
==
|
||||
<?php
|
||||
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Classes\Router;
|
||||
|
||||
function onStart() {
|
||||
$this['pageStartVar'] = 3;
|
||||
}
|
||||
?>
|
||||
==
|
||||
<p>Page</p>
|
||||
10
modules/cms/tests/fixtures/themes/test/pages/component-custom-render.htm
vendored
Normal file
10
modules/cms/tests/fixtures/themes/test/pages/component-custom-render.htm
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
url = "/component-custom-render"
|
||||
|
||||
[Winter\Tester\Components\ContentBlock theblock]
|
||||
==
|
||||
|
||||
{% component 'theblock' %}
|
||||
|
||||
{% component 'theblock' output="Would you look over Picasso's shoulder" %}
|
||||
|
||||
{% component 'theblock' output='And tell him about his brush strokes?' %}
|
||||
5
modules/cms/tests/fixtures/themes/test/pages/component-partial-alias-override.htm
vendored
Normal file
5
modules/cms/tests/fixtures/themes/test/pages/component-partial-alias-override.htm
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
url = "/component-partial-alias-override"
|
||||
|
||||
[Winter\Tester\Components\Post overRide1]
|
||||
==
|
||||
{% component 'overRide1' %}
|
||||
3
modules/cms/tests/fixtures/themes/test/pages/component-partial-nesting.htm
vendored
Normal file
3
modules/cms/tests/fixtures/themes/test/pages/component-partial-nesting.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
url = "/component-partial-nesting"
|
||||
==
|
||||
{% partial 'nesting/level1' %}
|
||||
5
modules/cms/tests/fixtures/themes/test/pages/component-partial-override.htm
vendored
Normal file
5
modules/cms/tests/fixtures/themes/test/pages/component-partial-override.htm
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
url = "/component-partial-override"
|
||||
|
||||
[testPost]
|
||||
==
|
||||
{% component 'testPost' %}
|
||||
5
modules/cms/tests/fixtures/themes/test/pages/component-partial.htm
vendored
Normal file
5
modules/cms/tests/fixtures/themes/test/pages/component-partial.htm
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
url = "/component-partial"
|
||||
|
||||
[Winter\Tester\Components\Post post]
|
||||
==
|
||||
{% component 'post' %}
|
||||
12
modules/cms/tests/fixtures/themes/test/pages/cycle-test.htm
vendored
Normal file
12
modules/cms/tests/fixtures/themes/test/pages/cycle-test.htm
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
url = "/cycle-test"
|
||||
layout = "cycle-test"
|
||||
==
|
||||
function onStart() {
|
||||
$this['pageStartVar'] = 3;
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
$this['pageEndVar'] = 4;
|
||||
}
|
||||
==
|
||||
{{ layoutStartVar }}{{ layoutBeforePageStartVar }}{{ pageStartVar }}{{ pageEndVar }}{{ layoutEndVar }}
|
||||
13
modules/cms/tests/fixtures/themes/test/pages/filters-test.htm
vendored
Normal file
13
modules/cms/tests/fixtures/themes/test/pages/filters-test.htm
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
url = "/filters-test/:url_title?"
|
||||
==
|
||||
{# Check pageUrl for current page #}
|
||||
{{ '' | page }} -> {{ '/filters-test/current-slug' | app }}
|
||||
{# Check pageUrl for persistent URL parameters #}
|
||||
{{ 'blog-post' | page }} -> {{ '/blog/post/current-slug' | app }}
|
||||
{# Check pageUrl for providing values for URL parameters #}
|
||||
{{ 'blog-post' | page({url_title: 'test-slug'}) }} -> {{ '/blog/post/test-slug' | app }}
|
||||
{# Check pageUrl for disabling persistent URL parameters #}
|
||||
{{ 'blog-post' | page({}, false) }} -> {{ '/blog/post/default' | app }}
|
||||
{# Check pageUrl for disabling persistent URL parameters with the second argument being routePersistence #}
|
||||
{{ 'blog-post' | page(false) }} -> {{ '/blog/post/default' | app }}
|
||||
|
||||
3
modules/cms/tests/fixtures/themes/test/pages/index.htm
vendored
Normal file
3
modules/cms/tests/fixtures/themes/test/pages/index.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
url = "/"
|
||||
==
|
||||
<h1>My Webpage</h1>
|
||||
5
modules/cms/tests/fixtures/themes/test/pages/no-component-class.htm
vendored
Normal file
5
modules/cms/tests/fixtures/themes/test/pages/no-component-class.htm
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
url = "/no-component-class"
|
||||
|
||||
[PeterPan\Nevernever\Land noComponentExist]
|
||||
==
|
||||
<p>Hey</p>
|
||||
3
modules/cms/tests/fixtures/themes/test/pages/no-component.htm
vendored
Normal file
3
modules/cms/tests/fixtures/themes/test/pages/no-component.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
url = "/no-component"
|
||||
==
|
||||
<p>Hey</p>{% component 'noComponentExist' %}
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/no-layout.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/no-layout.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/no-layout"
|
||||
layout = "caramba"
|
||||
==
|
||||
<p>Hey</p>
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/no-partial.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/no-partial.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/no-partial"
|
||||
==
|
||||
<p>Hey</p>
|
||||
{% partial 'caramba' %}
|
||||
5
modules/cms/tests/fixtures/themes/test/pages/no-soft-component-class.htm
vendored
Normal file
5
modules/cms/tests/fixtures/themes/test/pages/no-soft-component-class.htm
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
url = "/no-soft-component-class"
|
||||
|
||||
[@PeterPan\Nevernever\Land noComponentExist]
|
||||
==
|
||||
<p>Hey</p>
|
||||
14
modules/cms/tests/fixtures/themes/test/pages/optional-full-php-tags.htm
vendored
Normal file
14
modules/cms/tests/fixtures/themes/test/pages/optional-full-php-tags.htm
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
url = "/cycle-test"
|
||||
layout = "cycle-test"
|
||||
==
|
||||
<?php
|
||||
function onStart() {
|
||||
$this['pageStartVar'] = 3;
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
$this['pageEndVar'] = 4;
|
||||
}
|
||||
?>
|
||||
==
|
||||
{{ layoutStartVar }}{{ layoutBeforePageStartVar }}{{ pageStartVar }}{{ pageEndVar }}{{ layoutEndVar }}
|
||||
14
modules/cms/tests/fixtures/themes/test/pages/optional-short-php-tags.htm
vendored
Normal file
14
modules/cms/tests/fixtures/themes/test/pages/optional-short-php-tags.htm
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
url = "/cycle-test"
|
||||
layout = "cycle-test"
|
||||
==
|
||||
<?
|
||||
function onStart() {
|
||||
$this['pageStartVar'] = 3;
|
||||
}
|
||||
|
||||
function onEnd() {
|
||||
$this['pageEndVar'] = 4;
|
||||
}
|
||||
?>
|
||||
==
|
||||
{{ layoutStartVar }}{{ layoutBeforePageStartVar }}{{ pageStartVar }}{{ pageEndVar }}{{ layoutEndVar }}
|
||||
10
modules/cms/tests/fixtures/themes/test/pages/shared-variable-override.htm
vendored
Normal file
10
modules/cms/tests/fixtures/themes/test/pages/shared-variable-override.htm
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
url = '/shared-variable-override'
|
||||
==
|
||||
<?php
|
||||
function onStart() {
|
||||
$this['winterStatus'] = 'Is Coming';
|
||||
}
|
||||
==
|
||||
<p>Winter {{ winterStatus }}</p>
|
||||
|
||||
<p>{{ this.page.url }}</p>
|
||||
5
modules/cms/tests/fixtures/themes/test/pages/shared-variable.htm
vendored
Normal file
5
modules/cms/tests/fixtures/themes/test/pages/shared-variable.htm
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
url = '/shared-variable'
|
||||
==
|
||||
<p>Winter {{ winterStatus }}</p>
|
||||
|
||||
<p>{{ this.page.url }}</p>
|
||||
7
modules/cms/tests/fixtures/themes/test/pages/throw-php.htm
vendored
Normal file
7
modules/cms/tests/fixtures/themes/test/pages/throw-php.htm
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
url = "/throw-php"
|
||||
==
|
||||
function onStart() {
|
||||
test();
|
||||
}
|
||||
==
|
||||
<h1>This page will throw an exception</h1>
|
||||
11
modules/cms/tests/fixtures/themes/test/pages/with-component.htm
vendored
Normal file
11
modules/cms/tests/fixtures/themes/test/pages/with-component.htm
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
url = "/with-component"
|
||||
layout = "content"
|
||||
|
||||
[testArchive]
|
||||
posts-per-page = "69"
|
||||
==
|
||||
<p>This page uses components.</p>
|
||||
{% for post in testArchive.posts %}
|
||||
<h3>{{ post.title }}</h3>
|
||||
<p>{{ post.content }}</p>
|
||||
{% endfor %}
|
||||
14
modules/cms/tests/fixtures/themes/test/pages/with-components.htm
vendored
Normal file
14
modules/cms/tests/fixtures/themes/test/pages/with-components.htm
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
url = "/with-components"
|
||||
layout = "content"
|
||||
|
||||
[testArchive firstAlias]
|
||||
posts-per-page = "6"
|
||||
|
||||
[Winter\Tester\Components\Archive secondAlias]
|
||||
posts-per-page = "9"
|
||||
==
|
||||
<p>This page uses components.</p>
|
||||
{% for post in secondAlias.posts %}
|
||||
<h3>{{ post.title }}</h3>
|
||||
<p>{{ post.content }}</p>
|
||||
{% endfor %}
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/with-content.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/with-content.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/with-content"
|
||||
layout = "content"
|
||||
==
|
||||
<p>Hey {% content "page-content.htm" %} {% content "a/a-content.htm" %}</p>
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/with-layout.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/with-layout.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/with-layout"
|
||||
layout = "sidebar"
|
||||
==
|
||||
<p>Hey</p>
|
||||
6
modules/cms/tests/fixtures/themes/test/pages/with-macro.htm
vendored
Normal file
6
modules/cms/tests/fixtures/themes/test/pages/with-macro.htm
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
url = "/with-macro"
|
||||
==
|
||||
{% macro pageTest(_context) -%}
|
||||
<a href="{{ 'index' | page }}">{{ this.page.fileName }}</a>
|
||||
{%- endmacro pageTest %}
|
||||
<p>{{ _self.pageTest() }}<strong>{{ this.page.fileName }}</strong></p>
|
||||
4
modules/cms/tests/fixtures/themes/test/pages/with-partials.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/pages/with-partials.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
url = "/with-partials"
|
||||
layout = "partials"
|
||||
==
|
||||
<p>Hey {% partial "page-partial" firstName="Homer" lastName="Simpson" %} {% partial "a/a-partial" %}</p>
|
||||
11
modules/cms/tests/fixtures/themes/test/pages/with-placeholder.htm
vendored
Normal file
11
modules/cms/tests/fixtures/themes/test/pages/with-placeholder.htm
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
url = "/with-placeholder"
|
||||
layout = "placeholder"
|
||||
==
|
||||
{% put test %}
|
||||
BLOCK
|
||||
{% default %}
|
||||
{% endput %}
|
||||
{% put second %}
|
||||
SECOND BLOCK
|
||||
{% endput %}
|
||||
<p>Hey PAGE CONTENT</p>
|
||||
11
modules/cms/tests/fixtures/themes/test/pages/with-soft-component-class-alias.htm
vendored
Normal file
11
modules/cms/tests/fixtures/themes/test/pages/with-soft-component-class-alias.htm
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
url = "/with-soft-component-class-alias"
|
||||
layout = "content"
|
||||
|
||||
[@testArchive someAlias]
|
||||
posts-per-page = "69"
|
||||
==
|
||||
<p>This page uses components.</p>
|
||||
{% for post in someAlias.posts %}
|
||||
<h3>{{ post.title }}</h3>
|
||||
<p>{{ post.content }}</p>
|
||||
{% endfor %}
|
||||
11
modules/cms/tests/fixtures/themes/test/pages/with-soft-component-class.htm
vendored
Normal file
11
modules/cms/tests/fixtures/themes/test/pages/with-soft-component-class.htm
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
url = "/with-soft-component-class"
|
||||
layout = "content"
|
||||
|
||||
[@testArchive]
|
||||
posts-per-page = "69"
|
||||
==
|
||||
<p>This page uses components.</p>
|
||||
{% for post in testArchive.posts %}
|
||||
<h3>{{ post.title }}</h3>
|
||||
<p>{{ post.content }}</p>
|
||||
{% endfor %}
|
||||
1
modules/cms/tests/fixtures/themes/test/partials/a/a-partial.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/partials/a/a-partial.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
A partial
|
||||
1
modules/cms/tests/fixtures/themes/test/partials/ajax-result.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/partials/ajax-result.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
{{ var }}
|
||||
1
modules/cms/tests/fixtures/themes/test/partials/ajax-second-result.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/partials/ajax-second-result.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
second
|
||||
1
modules/cms/tests/fixtures/themes/test/partials/layout-partial.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/partials/layout-partial.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
LAYOUT PARTIAL
|
||||
6
modules/cms/tests/fixtures/themes/test/partials/nesting/level1.htm
vendored
Normal file
6
modules/cms/tests/fixtures/themes/test/partials/nesting/level1.htm
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[Winter\Tester\Components\MainMenu override2]
|
||||
[Winter\Tester\Components\Post override3]
|
||||
==
|
||||
<h1>Level 1</h1>
|
||||
{% component 'override2' %}
|
||||
{% component 'override3' %}
|
||||
6
modules/cms/tests/fixtures/themes/test/partials/nesting/level2.htm
vendored
Normal file
6
modules/cms/tests/fixtures/themes/test/partials/nesting/level2.htm
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
[Winter\Tester\Components\Post post]
|
||||
[Winter\Tester\Components\Post override4]
|
||||
==
|
||||
<h1>Level 2</h1>
|
||||
{% component 'post' %}
|
||||
{% component 'override4' %}
|
||||
4
modules/cms/tests/fixtures/themes/test/partials/nesting/level3.htm
vendored
Normal file
4
modules/cms/tests/fixtures/themes/test/partials/nesting/level3.htm
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
[Winter\Tester\Components\MainMenu mainMenu]
|
||||
==
|
||||
<h1>Level 3</h1>
|
||||
{% component 'mainMenu' %}
|
||||
1
modules/cms/tests/fixtures/themes/test/partials/override1/default.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/partials/override1/default.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>I am an override alias partial! Yay</p>
|
||||
7
modules/cms/tests/fixtures/themes/test/partials/override2/default.htm
vendored
Normal file
7
modules/cms/tests/fixtures/themes/test/partials/override2/default.htm
vendored
Normal file
@@ -0,0 +1,7 @@
|
||||
<ul>
|
||||
{% partial __SELF__ ~ "::items" items=__SELF__.menuItems %}
|
||||
{% partial __SELF__ ~ "::items" items=__SELF__.menuItems %}
|
||||
{% partial __SELF__ ~ "::items" items=__SELF__.menuItems %}
|
||||
</ul>
|
||||
|
||||
{% partial 'nesting/level2' %}
|
||||
3
modules/cms/tests/fixtures/themes/test/partials/override2/items.htm
vendored
Normal file
3
modules/cms/tests/fixtures/themes/test/partials/override2/items.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
{% for item in items %}
|
||||
<strong>{{ item }}</strong>
|
||||
{% endfor %}
|
||||
1
modules/cms/tests/fixtures/themes/test/partials/override3/default.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/partials/override3/default.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>Insert post here</p>
|
||||
3
modules/cms/tests/fixtures/themes/test/partials/override4/default.htm
vendored
Normal file
3
modules/cms/tests/fixtures/themes/test/partials/override4/default.htm
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
<p>I am another post, deep down</p>
|
||||
|
||||
{% partial 'nesting/level3' %}
|
||||
1
modules/cms/tests/fixtures/themes/test/partials/page-partial.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/partials/page-partial.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
PAGE PARTIAL {{ firstName }} {{ lastName }}
|
||||
1
modules/cms/tests/fixtures/themes/test/partials/testpost/default.htm
vendored
Normal file
1
modules/cms/tests/fixtures/themes/test/partials/testpost/default.htm
vendored
Normal file
@@ -0,0 +1 @@
|
||||
<p>I am an override partial! Yay</p>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user