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:
154
modules/cms/tests/models/ThemeDataTest.php
Normal file
154
modules/cms/tests/models/ThemeDataTest.php
Normal file
@@ -0,0 +1,154 @@
|
||||
<?php
|
||||
|
||||
namespace Cms\Tests\Models;
|
||||
|
||||
use Cms\Classes\Theme;
|
||||
use Cms\Models\ThemeData;
|
||||
use Config;
|
||||
use Event;
|
||||
use System\Tests\Bootstrap\PluginTestCase;
|
||||
use Winter\Storm\Database\MemoryCache;
|
||||
use Winter\Storm\Support\Facades\DB;
|
||||
|
||||
class ThemeDataTest extends PluginTestCase
|
||||
{
|
||||
/**
|
||||
* @var Theme A theme fixture that declares customization fields
|
||||
*/
|
||||
protected $theme;
|
||||
|
||||
public function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
Config::set('cms.activeTheme', 'themedata');
|
||||
Config::set('cms.themesPath', '/modules/cms/tests/fixtures/themes');
|
||||
|
||||
Event::flush('cms.theme.getActiveTheme');
|
||||
Theme::resetCache();
|
||||
|
||||
$this->theme = Theme::load('themedata');
|
||||
}
|
||||
|
||||
public function tearDown(): void
|
||||
{
|
||||
ThemeData::flushCache('themedata');
|
||||
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the queries made against the theme data table while running the callback.
|
||||
*/
|
||||
protected function countQueries(callable $callback): int
|
||||
{
|
||||
// Identical queries are deduplicated in memory for the lifetime of a request, which
|
||||
// would otherwise hide the query this cache is meant to avoid on the next request
|
||||
MemoryCache::instance()->flush();
|
||||
|
||||
DB::connection()->flushQueryLog();
|
||||
DB::connection()->enableQueryLog();
|
||||
|
||||
$callback();
|
||||
|
||||
$queries = DB::connection()->getQueryLog();
|
||||
DB::connection()->disableQueryLog();
|
||||
|
||||
return count(array_filter($queries, function ($query) {
|
||||
return str_contains($query['query'], 'cms_theme_data');
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures the record exists and the persistent cache is populated, then drops only the
|
||||
* in memory cache, simulating the state at the start of a subsequent request.
|
||||
*/
|
||||
protected function primeCache(): void
|
||||
{
|
||||
ThemeData::forTheme($this->theme);
|
||||
ThemeData::flushCache();
|
||||
|
||||
ThemeData::forTheme($this->theme);
|
||||
ThemeData::flushCache();
|
||||
}
|
||||
|
||||
public function testForThemeIsCachedAcrossRequests()
|
||||
{
|
||||
$this->primeCache();
|
||||
|
||||
$queries = $this->countQueries(function () {
|
||||
ThemeData::forTheme($this->theme);
|
||||
});
|
||||
|
||||
$this->assertEquals(0, $queries, 'Theme data should be served from the cache');
|
||||
}
|
||||
|
||||
public function testForThemeCreatesRecordOnlyOnce()
|
||||
{
|
||||
ThemeData::forTheme($this->theme);
|
||||
ThemeData::flushCache();
|
||||
ThemeData::forTheme($this->theme);
|
||||
ThemeData::flushCache();
|
||||
ThemeData::forTheme($this->theme);
|
||||
|
||||
$this->assertEquals(1, ThemeData::where('theme', 'themedata')->count());
|
||||
}
|
||||
|
||||
public function testDefaultValuesAreAppliedToCachedRecords()
|
||||
{
|
||||
$this->primeCache();
|
||||
|
||||
// Defaults are applied by afterFetch(), which must still run for cached rows
|
||||
$this->assertEquals('Winter', ThemeData::forTheme($this->theme)->site_name);
|
||||
}
|
||||
|
||||
public function testAfterSaveInvalidatesCache()
|
||||
{
|
||||
$this->primeCache();
|
||||
|
||||
$themeData = ThemeData::forTheme($this->theme);
|
||||
$themeData->site_name = 'Updated';
|
||||
$themeData->save();
|
||||
|
||||
// Only drop the in memory cache; the persistent cache must have been invalidated
|
||||
// by afterSave(), otherwise theme customizations would not take effect
|
||||
ThemeData::flushCache();
|
||||
|
||||
$this->assertEquals('Updated', ThemeData::forTheme($this->theme)->site_name);
|
||||
}
|
||||
|
||||
public function testAfterDeleteInvalidatesCache()
|
||||
{
|
||||
$this->primeCache();
|
||||
|
||||
$themeData = ThemeData::forTheme($this->theme);
|
||||
$themeData->site_name = 'Updated';
|
||||
$themeData->save();
|
||||
|
||||
$this->theme->removeCustomData();
|
||||
ThemeData::flushCache();
|
||||
|
||||
// A new record should be created rather than the deleted one being served
|
||||
$this->assertNull(ThemeData::forTheme($this->theme)->site_name);
|
||||
$this->assertEquals(1, ThemeData::where('theme', 'themedata')->count());
|
||||
}
|
||||
|
||||
public function testDynamicAttributesSurviveTheCache()
|
||||
{
|
||||
$themeData = ThemeData::forTheme($this->theme);
|
||||
$themeData->site_name = 'From cache';
|
||||
$themeData->save();
|
||||
|
||||
ThemeData::flushCache();
|
||||
ThemeData::forTheme($this->theme);
|
||||
ThemeData::flushCache();
|
||||
|
||||
$queries = $this->countQueries(function () {
|
||||
// Dynamic attributes live in the jsonable `data` column and are expanded by
|
||||
// afterFetch(), which runs during hydration rather than during the query
|
||||
$this->assertEquals('From cache', ThemeData::forTheme($this->theme)->site_name);
|
||||
});
|
||||
|
||||
$this->assertEquals(0, $queries);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user