Files
michaelschiemer/tests/Unit/Domain/Cms/Rendering/ContentRendererTest.php
Michael Schiemer 2d53270056 feat(cms,asset): add comprehensive test suite and finalize modules
- Add comprehensive test suite for CMS and Asset modules using Pest Framework
- Implement ContentTypeService::delete() protection against deletion of in-use content types
- Add CannotDeleteContentTypeInUseException for better error handling
- Fix DerivatPipelineRegistry::getAllPipelines() to handle object uniqueness correctly
- Fix VariantName::getScale() to correctly parse scales with file extensions
- Update CMS module documentation with new features, exceptions, and test coverage
- Add CmsTestHelpers and AssetTestHelpers for test data factories
- Fix BlockTypeRegistry to be immutable after construction
- Update ContentTypeService to check for associated content before deletion
- Improve BlockRendererRegistry initialization

Test coverage:
- Value Objects: All CMS and Asset value objects
- Services: ContentService, ContentTypeService, SlugGenerator, BlockValidator, ContentLocalizationService, AssetService, DeduplicationService, MetadataExtractor
- Repositories: All database repositories with mocked connections
- Rendering: Block renderers and ContentRenderer
- Controllers: API endpoints for both modules

254 tests passing, 38 remaining (mostly image processing pipeline tests)
2025-11-10 02:12:28 +01:00

162 lines
5.9 KiB
PHP

<?php
declare(strict_types=1);
use App\Domain\Cms\Entities\Content;
use App\Domain\Cms\Enums\ContentStatus;
use App\Domain\Cms\Rendering\BlockRendererRegistry;
use App\Domain\Cms\Rendering\DefaultBlockRenderer;
use App\Domain\Cms\Rendering\HeroBlockRenderer;
use App\Domain\Cms\Rendering\TextBlockRenderer;
use App\Domain\Cms\Services\ContentLocalizationService;
use App\Domain\Cms\Services\ContentRenderer;
use App\Domain\Cms\ValueObjects\BlockId;
use App\Domain\Cms\ValueObjects\BlockType;
use App\Domain\Cms\ValueObjects\ContentBlocks;
use App\Domain\Cms\ValueObjects\ContentId;
use App\Domain\Cms\ValueObjects\ContentSlug;
use App\Domain\Cms\ValueObjects\ContentTypeId;
use App\Domain\Cms\ValueObjects\Locale;
use App\Framework\Core\ValueObjects\Timestamp;
use App\Framework\DateTime\SystemClock;
use App\Framework\DI\DefaultContainer;
use App\Framework\Filesystem\FileStorage;
use App\Framework\Cache\Cache;
use App\Framework\Cache\Driver\NullCache;
use App\Framework\Cache\GeneralCache;
use App\Framework\Core\PathProvider;
use App\Framework\Serializer\Serializer;
use App\Framework\View\ComponentCache;
use App\Framework\View\ComponentRenderer;
use App\Framework\View\Loading\TemplateLoader;
use App\Framework\View\TemplateProcessor;
use Tests\Support\CmsTestHelpers;
describe('ContentRenderer', function () {
beforeEach(function () {
$this->clock = new SystemClock();
$this->blockRegistry = new BlockRendererRegistry();
// Create minimal real TemplateLoader for testing
$pathProvider = new PathProvider(__DIR__ . '/../../../../../');
$nullCacheDriver = new NullCache();
$serializer = Mockery::mock(Serializer::class);
$serializer->shouldReceive('serialize')->andReturnUsing(fn($data) => serialize($data));
$serializer->shouldReceive('unserialize')->andReturnUsing(fn($data) => unserialize($data));
$cache = new GeneralCache($nullCacheDriver, $serializer);
$templateLoader = new TemplateLoader(
pathProvider: $pathProvider,
cache: $cache,
discoveryRegistry: null,
templates: [],
templatePath: '/src/Framework/View/templates',
cacheEnabled: false
);
$componentCache = new ComponentCache('/tmp/test-cache');
// Create minimal real TemplateProcessor for testing
$container = new DefaultContainer();
$templateProcessor = new TemplateProcessor(
astTransformers: [],
stringProcessors: [],
container: $container,
chainOptimizer: null,
compiledTemplateCache: null,
performanceTracker: null
);
// Create real FileStorage for testing
$fileStorage = new FileStorage(
basePath: sys_get_temp_dir() . '/test-components'
);
$this->componentRenderer = new ComponentRenderer(
$templateProcessor,
$componentCache,
$templateLoader,
$fileStorage
);
// Create real ContentLocalizationService for testing
$contentTranslationRepository = Mockery::mock(\App\Domain\Cms\Repositories\ContentTranslationRepository::class);
$this->localizationService = new ContentLocalizationService(
$contentTranslationRepository,
$this->clock
);
$this->defaultRenderer = new DefaultBlockRenderer();
$this->renderer = new ContentRenderer(
$this->blockRegistry,
$this->componentRenderer,
$this->localizationService,
$this->defaultRenderer
);
});
it('renders content with multiple blocks', function () {
$content = CmsTestHelpers::createContent($this->clock);
// ContentLocalizationService is real instance, no need to mock
$html = $this->renderer->render($content);
expect($html)->toBeString();
expect($html)->not->toBeEmpty();
});
it('uses default renderer for unregistered block types', function () {
$blocks = ContentBlocks::fromArray([
[
'id' => 'custom-1',
'type' => 'custom-block',
'data' => ['key' => 'value'],
],
]);
$content = CmsTestHelpers::createContent($this->clock, blocks: $blocks);
// ContentLocalizationService is real instance, no need to mock
$html = $this->renderer->render($content);
expect($html)->toBeString();
expect($html)->not->toBeEmpty();
});
it('uses registered renderer for block type', function () {
$this->blockRegistry->registerForType('hero', new HeroBlockRenderer());
$content = CmsTestHelpers::createContent($this->clock);
// ContentLocalizationService is real instance, no need to mock
$html = $this->renderer->render($content);
expect($html)->toBeString();
expect($html)->not->toBeEmpty();
});
it('renders blocks to component data array', function () {
$this->blockRegistry->registerForType('hero', new HeroBlockRenderer());
$this->blockRegistry->registerForType('text', new TextBlockRenderer());
$content = CmsTestHelpers::createContent($this->clock);
// ContentLocalizationService is real instance, no need to mock
$componentData = $this->renderer->renderBlocksToComponentData($content);
expect($componentData)->toBeArray();
expect($componentData)->toHaveCount(2);
expect($componentData[0]['component'])->toBe('cms/hero');
expect($componentData[1]['component'])->toBe('cms/text');
});
it('uses provided locale for rendering', function () {
$content = CmsTestHelpers::createContent($this->clock);
$locale = Locale::german();
// ContentLocalizationService is real instance, no need to mock
$html = $this->renderer->render($content, $locale);
expect($html)->toBeString();
expect($html)->not->toBeEmpty();
});
});