Files
vivespos-landing/modules/cms/tests/classes/ThemeTest.php
avives 1f72193a64
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run
feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
- 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
2026-08-21 19:29:00 -06:00

178 lines
5.1 KiB
PHP

<?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'));
}
}