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)
This commit is contained in:
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Domain\Cms\Rendering\BlockRendererInterface;
|
||||
use App\Domain\Cms\Rendering\BlockRendererRegistry;
|
||||
use App\Domain\Cms\Rendering\HeroBlockRenderer;
|
||||
use App\Domain\Cms\Rendering\TextBlockRenderer;
|
||||
|
||||
describe('BlockRendererRegistry', function () {
|
||||
it('can register renderer for specific type', function () {
|
||||
$registry = new BlockRendererRegistry();
|
||||
$renderer = new HeroBlockRenderer();
|
||||
|
||||
$registry->registerForType('hero', $renderer);
|
||||
|
||||
expect($registry->hasRenderer('hero'))->toBeTrue();
|
||||
expect($registry->getRenderer('hero'))->toBe($renderer);
|
||||
});
|
||||
|
||||
it('can register multiple renderers', function () {
|
||||
$registry = new BlockRendererRegistry();
|
||||
$heroRenderer = new HeroBlockRenderer();
|
||||
$textRenderer = new TextBlockRenderer();
|
||||
|
||||
$registry->registerForType('hero', $heroRenderer);
|
||||
$registry->registerForType('text', $textRenderer);
|
||||
|
||||
expect($registry->getRenderer('hero'))->toBe($heroRenderer);
|
||||
expect($registry->getRenderer('text'))->toBe($textRenderer);
|
||||
});
|
||||
|
||||
it('returns null for unregistered block type', function () {
|
||||
$registry = new BlockRendererRegistry();
|
||||
|
||||
expect($registry->getRenderer('unknown'))->toBeNull();
|
||||
expect($registry->hasRenderer('unknown'))->toBeFalse();
|
||||
});
|
||||
|
||||
it('can find renderer by supports method', function () {
|
||||
$registry = new BlockRendererRegistry();
|
||||
$heroRenderer = new HeroBlockRenderer();
|
||||
|
||||
$registry->register($heroRenderer);
|
||||
|
||||
$found = $registry->getRenderer('hero');
|
||||
expect($found)->toBe($heroRenderer);
|
||||
});
|
||||
|
||||
it('can get all registered renderers', function () {
|
||||
$registry = new BlockRendererRegistry();
|
||||
$heroRenderer = new HeroBlockRenderer();
|
||||
$textRenderer = new TextBlockRenderer();
|
||||
|
||||
$registry->registerForType('hero', $heroRenderer);
|
||||
$registry->registerForType('text', $textRenderer);
|
||||
|
||||
$all = $registry->getAllRenderers();
|
||||
|
||||
expect($all)->toHaveCount(2);
|
||||
expect($all['hero'])->toBe($heroRenderer);
|
||||
expect($all['text'])->toBe($textRenderer);
|
||||
});
|
||||
|
||||
it('overwrites renderer when registering same type twice', function () {
|
||||
$registry = new BlockRendererRegistry();
|
||||
$renderer1 = new HeroBlockRenderer();
|
||||
$renderer2 = new HeroBlockRenderer();
|
||||
|
||||
$registry->registerForType('hero', $renderer1);
|
||||
$registry->registerForType('hero', $renderer2);
|
||||
|
||||
expect($registry->getRenderer('hero'))->toBe($renderer2);
|
||||
});
|
||||
});
|
||||
|
||||
161
tests/Unit/Domain/Cms/Rendering/ContentRendererTest.php
Normal file
161
tests/Unit/Domain/Cms/Rendering/ContentRendererTest.php
Normal file
@@ -0,0 +1,161 @@
|
||||
<?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();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
66
tests/Unit/Domain/Cms/Rendering/DefaultBlockRendererTest.php
Normal file
66
tests/Unit/Domain/Cms/Rendering/DefaultBlockRendererTest.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Domain\Cms\Rendering\DefaultBlockRenderer;
|
||||
use App\Domain\Cms\ValueObjects\BlockData;
|
||||
use App\Domain\Cms\ValueObjects\BlockId;
|
||||
use App\Domain\Cms\ValueObjects\BlockSettings;
|
||||
use App\Domain\Cms\ValueObjects\BlockType;
|
||||
use App\Domain\Cms\ValueObjects\ContentBlock;
|
||||
|
||||
describe('DefaultBlockRenderer', function () {
|
||||
it('renders any block type as generic block', function () {
|
||||
$renderer = new DefaultBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::fromString('custom-block'),
|
||||
blockId: BlockId::fromString('custom-1'),
|
||||
data: BlockData::fromArray(['key' => 'value'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result)->toHaveKeys(['component', 'data']);
|
||||
expect($result['component'])->toBe('cms/block');
|
||||
expect($result['data']['blockType'])->toBe('custom-block');
|
||||
expect($result['data']['blockId'])->toBe('custom-1');
|
||||
expect($result['data']['data'])->toBe(['key' => 'value']);
|
||||
});
|
||||
|
||||
it('renders block with settings', function () {
|
||||
$renderer = new DefaultBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::fromString('custom-block'),
|
||||
blockId: BlockId::fromString('custom-1'),
|
||||
data: BlockData::fromArray(['key' => 'value']),
|
||||
settings: BlockSettings::fromArray(['setting' => 'value'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['settings'])->toBe(['setting' => 'value']);
|
||||
});
|
||||
|
||||
it('renders block without settings', function () {
|
||||
$renderer = new DefaultBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::fromString('custom-block'),
|
||||
blockId: BlockId::fromString('custom-1'),
|
||||
data: BlockData::fromArray(['key' => 'value'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['settings'])->toBe([]);
|
||||
});
|
||||
|
||||
it('supports all block types', function () {
|
||||
$renderer = new DefaultBlockRenderer();
|
||||
|
||||
expect($renderer->supports('hero'))->toBeTrue();
|
||||
expect($renderer->supports('text'))->toBeTrue();
|
||||
expect($renderer->supports('custom-block'))->toBeTrue();
|
||||
expect($renderer->supports('any-type'))->toBeTrue();
|
||||
});
|
||||
});
|
||||
|
||||
103
tests/Unit/Domain/Cms/Rendering/HeroBlockRendererTest.php
Normal file
103
tests/Unit/Domain/Cms/Rendering/HeroBlockRendererTest.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Domain\Cms\Rendering\HeroBlockRenderer;
|
||||
use App\Domain\Cms\ValueObjects\BlockData;
|
||||
use App\Domain\Cms\ValueObjects\BlockId;
|
||||
use App\Domain\Cms\ValueObjects\BlockSettings;
|
||||
use App\Domain\Cms\ValueObjects\BlockType;
|
||||
use App\Domain\Cms\ValueObjects\ContentBlock;
|
||||
|
||||
describe('HeroBlockRenderer', function () {
|
||||
it('renders hero block with title', function () {
|
||||
$renderer = new HeroBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::hero(),
|
||||
blockId: BlockId::fromString('hero-1'),
|
||||
data: BlockData::fromArray(['title' => 'Hero Title'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result)->toHaveKeys(['component', 'data']);
|
||||
expect($result['component'])->toBe('cms/hero');
|
||||
expect($result['data']['title'])->toBe('Hero Title');
|
||||
});
|
||||
|
||||
it('renders hero block with all fields', function () {
|
||||
$renderer = new HeroBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::hero(),
|
||||
blockId: BlockId::fromString('hero-1'),
|
||||
data: BlockData::fromArray([
|
||||
'title' => 'Hero Title',
|
||||
'subtitle' => 'Hero Subtitle',
|
||||
'backgroundImage' => 'img-123',
|
||||
'ctaText' => 'Click Me',
|
||||
'ctaLink' => '/signup',
|
||||
]),
|
||||
settings: BlockSettings::fromArray([
|
||||
'fullWidth' => true,
|
||||
'padding' => 'large',
|
||||
])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['title'])->toBe('Hero Title');
|
||||
expect($result['data']['subtitle'])->toBe('Hero Subtitle');
|
||||
expect($result['data']['backgroundImage'])->toBe('img-123');
|
||||
expect($result['data']['ctaText'])->toBe('Click Me');
|
||||
expect($result['data']['ctaLink'])->toBe('/signup');
|
||||
expect($result['data']['fullWidth'])->toBeTrue();
|
||||
expect($result['data']['padding'])->toBe('large');
|
||||
});
|
||||
|
||||
it('handles snake_case field names', function () {
|
||||
$renderer = new HeroBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::hero(),
|
||||
blockId: BlockId::fromString('hero-1'),
|
||||
data: BlockData::fromArray([
|
||||
'title' => 'Hero Title',
|
||||
'background_image' => 'img-123',
|
||||
'cta_text' => 'Click Me',
|
||||
'cta_link' => '/signup',
|
||||
]),
|
||||
settings: BlockSettings::fromArray(['full_width' => true])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['backgroundImage'])->toBe('img-123');
|
||||
expect($result['data']['ctaText'])->toBe('Click Me');
|
||||
expect($result['data']['ctaLink'])->toBe('/signup');
|
||||
expect($result['data']['fullWidth'])->toBeTrue();
|
||||
});
|
||||
|
||||
it('provides default values for missing fields', function () {
|
||||
$renderer = new HeroBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::hero(),
|
||||
blockId: BlockId::fromString('hero-1'),
|
||||
data: BlockData::fromArray(['title' => 'Hero Title'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['title'])->toBe('Hero Title');
|
||||
expect($result['data']['subtitle'])->toBeNull();
|
||||
expect($result['data']['fullWidth'])->toBeFalse();
|
||||
expect($result['data']['padding'])->toBe('medium');
|
||||
});
|
||||
|
||||
it('supports hero block type', function () {
|
||||
$renderer = new HeroBlockRenderer();
|
||||
|
||||
expect($renderer->supports('hero'))->toBeTrue();
|
||||
expect($renderer->supports('text'))->toBeFalse();
|
||||
expect($renderer->supports('image'))->toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
128
tests/Unit/Domain/Cms/Rendering/ImageBlockRendererTest.php
Normal file
128
tests/Unit/Domain/Cms/Rendering/ImageBlockRendererTest.php
Normal file
@@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Domain\Cms\Rendering\ImageBlockRenderer;
|
||||
use App\Domain\Cms\ValueObjects\BlockData;
|
||||
use App\Domain\Cms\ValueObjects\BlockId;
|
||||
use App\Domain\Cms\ValueObjects\BlockSettings;
|
||||
use App\Domain\Cms\ValueObjects\BlockType;
|
||||
use App\Domain\Cms\ValueObjects\ContentBlock;
|
||||
|
||||
describe('ImageBlockRenderer', function () {
|
||||
it('renders image block with imageId', function () {
|
||||
$renderer = new ImageBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::image(),
|
||||
blockId: BlockId::fromString('image-1'),
|
||||
data: BlockData::fromArray(['imageId' => 'img-123'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result)->toHaveKeys(['component', 'data']);
|
||||
expect($result['component'])->toBe('cms/image');
|
||||
expect($result['data']['imageId'])->toBe('img-123');
|
||||
});
|
||||
|
||||
it('renders image block with imageUrl', function () {
|
||||
$renderer = new ImageBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::image(),
|
||||
blockId: BlockId::fromString('image-1'),
|
||||
data: BlockData::fromArray(['imageUrl' => 'https://example.com/image.jpg'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['imageUrl'])->toBe('https://example.com/image.jpg');
|
||||
});
|
||||
|
||||
it('handles snake_case field names', function () {
|
||||
$renderer = new ImageBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::image(),
|
||||
blockId: BlockId::fromString('image-1'),
|
||||
data: BlockData::fromArray(['image_id' => 'img-123', 'image_url' => 'https://example.com/image.jpg'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['imageId'])->toBe('img-123');
|
||||
expect($result['data']['imageUrl'])->toBe('https://example.com/image.jpg');
|
||||
});
|
||||
|
||||
it('renders image block with caption and alt', function () {
|
||||
$renderer = new ImageBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::image(),
|
||||
blockId: BlockId::fromString('image-1'),
|
||||
data: BlockData::fromArray([
|
||||
'imageId' => 'img-123',
|
||||
'caption' => 'Image caption',
|
||||
'alt' => 'Alt text',
|
||||
])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['caption'])->toBe('Image caption');
|
||||
expect($result['data']['alt'])->toBe('Alt text');
|
||||
});
|
||||
|
||||
it('uses caption as alt when alt is missing', function () {
|
||||
$renderer = new ImageBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::image(),
|
||||
blockId: BlockId::fromString('image-1'),
|
||||
data: BlockData::fromArray([
|
||||
'imageId' => 'img-123',
|
||||
'caption' => 'Image caption',
|
||||
])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['alt'])->toBe('Image caption');
|
||||
});
|
||||
|
||||
it('renders image block with width and height settings', function () {
|
||||
$renderer = new ImageBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::image(),
|
||||
blockId: BlockId::fromString('image-1'),
|
||||
data: BlockData::fromArray(['imageId' => 'img-123']),
|
||||
settings: BlockSettings::fromArray(['width' => 800, 'height' => 600])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['width'])->toBe(800);
|
||||
expect($result['data']['height'])->toBe(600);
|
||||
});
|
||||
|
||||
it('provides default values for missing fields', function () {
|
||||
$renderer = new ImageBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::image(),
|
||||
blockId: BlockId::fromString('image-1'),
|
||||
data: BlockData::fromArray(['imageId' => 'img-123'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['imageId'])->toBe('img-123');
|
||||
expect($result['data']['imageUrl'])->toBeNull();
|
||||
expect($result['data']['caption'])->toBeNull();
|
||||
expect($result['data']['alt'])->toBe('');
|
||||
});
|
||||
|
||||
it('supports image block type', function () {
|
||||
$renderer = new ImageBlockRenderer();
|
||||
|
||||
expect($renderer->supports('image'))->toBeTrue();
|
||||
expect($renderer->supports('hero'))->toBeFalse();
|
||||
expect($renderer->supports('text'))->toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
96
tests/Unit/Domain/Cms/Rendering/TextBlockRendererTest.php
Normal file
96
tests/Unit/Domain/Cms/Rendering/TextBlockRendererTest.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
use App\Domain\Cms\Rendering\TextBlockRenderer;
|
||||
use App\Domain\Cms\ValueObjects\BlockData;
|
||||
use App\Domain\Cms\ValueObjects\BlockId;
|
||||
use App\Domain\Cms\ValueObjects\BlockSettings;
|
||||
use App\Domain\Cms\ValueObjects\BlockType;
|
||||
use App\Domain\Cms\ValueObjects\ContentBlock;
|
||||
|
||||
describe('TextBlockRenderer', function () {
|
||||
it('renders text block with content', function () {
|
||||
$renderer = new TextBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::text(),
|
||||
blockId: BlockId::fromString('text-1'),
|
||||
data: BlockData::fromArray(['content' => 'Text content'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result)->toHaveKeys(['component', 'data']);
|
||||
expect($result['component'])->toBe('cms/text');
|
||||
expect($result['data']['content'])->toBe('Text content');
|
||||
});
|
||||
|
||||
it('renders text block with alignment', function () {
|
||||
$renderer = new TextBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::text(),
|
||||
blockId: BlockId::fromString('text-1'),
|
||||
data: BlockData::fromArray([
|
||||
'content' => 'Text content',
|
||||
'alignment' => 'center',
|
||||
])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['content'])->toBe('Text content');
|
||||
expect($result['data']['alignment'])->toBe('center');
|
||||
});
|
||||
|
||||
it('renders text block with maxWidth setting', function () {
|
||||
$renderer = new TextBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::text(),
|
||||
blockId: BlockId::fromString('text-1'),
|
||||
data: BlockData::fromArray(['content' => 'Text content']),
|
||||
settings: BlockSettings::fromArray(['maxWidth' => 800])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['maxWidth'])->toBe(800);
|
||||
});
|
||||
|
||||
it('handles snake_case maxWidth setting', function () {
|
||||
$renderer = new TextBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::text(),
|
||||
blockId: BlockId::fromString('text-1'),
|
||||
data: BlockData::fromArray(['content' => 'Text content']),
|
||||
settings: BlockSettings::fromArray(['max_width' => 800])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['maxWidth'])->toBe(800);
|
||||
});
|
||||
|
||||
it('provides default values for missing fields', function () {
|
||||
$renderer = new TextBlockRenderer();
|
||||
$block = ContentBlock::create(
|
||||
type: BlockType::text(),
|
||||
blockId: BlockId::fromString('text-1'),
|
||||
data: BlockData::fromArray(['content' => 'Text content'])
|
||||
);
|
||||
|
||||
$result = $renderer->render($block);
|
||||
|
||||
expect($result['data']['content'])->toBe('Text content');
|
||||
expect($result['data']['alignment'])->toBe('left');
|
||||
expect($result['data']['maxWidth'])->toBeNull();
|
||||
});
|
||||
|
||||
it('supports text block type', function () {
|
||||
$renderer = new TextBlockRenderer();
|
||||
|
||||
expect($renderer->supports('text'))->toBeTrue();
|
||||
expect($renderer->supports('hero'))->toBeFalse();
|
||||
expect($renderer->supports('image'))->toBeFalse();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user