feat: PresetComposer – Preset aus Kurz-/Vollform, Modul und Gruppen-Presets
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
bb4d9b3baf
commit
cc20100cbc
@@ -27,6 +27,7 @@ final class DesignSystemWriterTest extends TestCase
|
||||
'GlobalPreset::get_data()', 'GlobalPreset::prepare_data()', 'GlobalPreset::validate_preset_count()',
|
||||
'GlobalPreset::get_deleted_preset_ids()', 'GlobalPreset::remove_preset_from_legacy_option()',
|
||||
'GlobalPreset::save_data()', 'Conversion::get_preset_attrs_mapping()', 'Conversion::get_preset_attrs()',
|
||||
'GlobalPreset::get_group_preset_default_attr()', 'ModuleRegistration::get_module_settings()',
|
||||
'UserRole::can_current_user_use_visual_builder()',
|
||||
],
|
||||
array_map(static fn (Call $call): string => $call->label(), $manifest->calls)
|
||||
@@ -48,4 +49,9 @@ final class DesignSystemWriterTest extends TestCase
|
||||
{
|
||||
self::assertFalse((new DesignSystemWriter())->canUseVisualBuilder());
|
||||
}
|
||||
|
||||
public function testWithoutDiviAModuleHasNoGroups(): void
|
||||
{
|
||||
self::assertSame([], (new DesignSystemWriter())->groupsOf('divi/button'));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,198 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design;
|
||||
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\Block;
|
||||
use LIT\MCP\Integrations\Divi\Compose\StoredNodeGuard;
|
||||
use LIT\MCP\Integrations\Divi\Design\PresetBook;
|
||||
use LIT\MCP\Integrations\Divi\Design\PresetComposer;
|
||||
use LIT\MCP\Integrations\Divi\Design\PresetScope;
|
||||
use LIT\MCP\Tests\Support\ComposeFixture;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class PresetComposerTest extends TestCase
|
||||
{
|
||||
private const NOW = 1760000000000;
|
||||
|
||||
/** Divi's mapping in small: decoration paths are preset attributes, innerContent is content. */
|
||||
private static function presetAttrs(): \Closure
|
||||
{
|
||||
return static function (string $module, array $attrs): array {
|
||||
$all = [];
|
||||
foreach ($attrs as $key => $value) {
|
||||
if (is_array($value)) {
|
||||
$kept = array_diff_key($value, ['innerContent' => true]);
|
||||
if ($kept !== []) {
|
||||
$all[$key] = $kept;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ['all' => $all, 'style' => $all, 'render' => []];
|
||||
};
|
||||
}
|
||||
|
||||
private static function composer(bool $unfiltered = true): PresetComposer
|
||||
{
|
||||
return new PresetComposer(
|
||||
ComposeFixture::composer($unfiltered),
|
||||
new StoredNodeGuard(
|
||||
ComposeFixture::cleaner($unfiltered),
|
||||
ComposeFixture::references(),
|
||||
ComposeFixture::sanitizer($unfiltered)
|
||||
),
|
||||
self::presetAttrs(),
|
||||
static fn (string $module): array => [
|
||||
'module.decoration.spacing' => 'divi/spacing',
|
||||
'button.decoration.font' => 'divi/font',
|
||||
],
|
||||
'5.15.1',
|
||||
static fn (): int => self::NOW
|
||||
);
|
||||
}
|
||||
|
||||
private static function book(): PresetBook
|
||||
{
|
||||
return PresetBook::fromDivi(PresetBookTest::stored());
|
||||
}
|
||||
|
||||
public function testNewModulePresetFromAttrs(): void
|
||||
{
|
||||
$result = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), null, [
|
||||
'name' => 'CTA sekundär',
|
||||
'attrs' => ['button.decoration.background' => ['desktop' => ['color' => '#123456']]],
|
||||
], 'items[0]', null);
|
||||
|
||||
self::assertSame([], $result['errors']);
|
||||
$item = $result['item'];
|
||||
self::assertMatchesRegularExpression('/^[0-9a-z]{10}$/', $item['id']);
|
||||
self::assertSame(['module', 'CTA sekundär', 'divi/button', '5.15.1'], [$item['type'], $item['name'],
|
||||
$item['moduleName'], $item['version']]);
|
||||
self::assertSame([self::NOW, self::NOW], [$item['created'], $item['updated']]);
|
||||
self::assertSame('#123456', $item['attrs']['button']['decoration']['background']['desktop']['value']['color']);
|
||||
self::assertSame($item['attrs'], $item['styleAttrs']);
|
||||
self::assertArrayNotHasKey('renderAttrs', $item);
|
||||
}
|
||||
|
||||
public function testContentPathsAreRefused(): void
|
||||
{
|
||||
$result = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), null, [
|
||||
'name' => 'Mit Text',
|
||||
'set' => ['text' => 'Jetzt buchen'],
|
||||
], 'items[0]', null);
|
||||
|
||||
self::assertArrayHasKey('items[0].attrs.button.innerContent', $result['errors']);
|
||||
self::assertStringContainsString('kein Preset-Attribut', $result['errors']['items[0].attrs.button.innerContent']);
|
||||
}
|
||||
|
||||
public function testChangingMergesKeepsIdCreatedAndOrder(): void
|
||||
{
|
||||
$existing = self::book()->items(new PresetScope('module', 'divi/button'))['aaaaaaaaaa'];
|
||||
$existing['created'] = 5;
|
||||
$existing['order'] = 3;
|
||||
$existing['attrs'] = ['button' => ['decoration' => ['border' => ['desktop' => ['value' => ['radius' => '4px']]]]]];
|
||||
|
||||
$result = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), $existing, [
|
||||
'rename' => 'CTA Primär',
|
||||
'attrs' => ['button.decoration.background' => ['desktop' => ['color' => '#000000']]],
|
||||
], 'items[0]', null);
|
||||
|
||||
self::assertSame([], $result['errors']);
|
||||
self::assertSame(['aaaaaaaaaa', 'CTA Primär', 5, self::NOW, 3], [$result['item']['id'], $result['item']['name'],
|
||||
$result['item']['created'], $result['item']['updated'], $result['item']['order']]);
|
||||
self::assertSame('4px', $result['item']['attrs']['button']['decoration']['border']['desktop']['value']['radius']);
|
||||
}
|
||||
|
||||
public function testFromNodeTakesTheDesignButNotTheContent(): void
|
||||
{
|
||||
$node = Block::create('divi/button', [
|
||||
'button' => [
|
||||
'innerContent' => ['desktop' => ['value' => ['text' => 'Hallo']]],
|
||||
'decoration' => ['background' => ['desktop' => ['value' => ['color' => '#abcdef']]]],
|
||||
],
|
||||
]);
|
||||
|
||||
$result = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), null, [
|
||||
'name' => 'Aus Seite',
|
||||
], 'items[0]', $node);
|
||||
|
||||
self::assertSame([], $result['errors']);
|
||||
self::assertSame('#abcdef', $result['item']['attrs']['button']['decoration']['background']['desktop']['value']['color']);
|
||||
self::assertArrayNotHasKey('innerContent', $result['item']['attrs']['button']);
|
||||
}
|
||||
|
||||
public function testEditorCannotCarryBuilderOnlyValuesFromANode(): void
|
||||
{
|
||||
$node = Block::create('divi/button', [
|
||||
'css' => ['desktop' => ['value' => ['mainElement' => 'color:red']]],
|
||||
'button' => ['decoration' => ['background' => ['desktop' => ['value' => ['color' => '#000000']]]]],
|
||||
]);
|
||||
|
||||
$result = self::composer(false)->compose(self::book(), new PresetScope('module', 'divi/button'), null, [
|
||||
'name' => 'Mit CSS',
|
||||
], 'items[0]', $node);
|
||||
|
||||
self::assertArrayHasKey('items[0].from_node', $result['errors']);
|
||||
}
|
||||
|
||||
public function testGroupPresetOnlyTakesItsGroup(): void
|
||||
{
|
||||
$result = self::composer()->compose(self::book(), new PresetScope('group', 'divi/spacing'), null, [
|
||||
'name' => 'Abstand groß',
|
||||
'source_module' => 'button',
|
||||
'attrs' => [
|
||||
'module.decoration.spacing' => ['desktop' => ['padding' => ['top' => '40px']]],
|
||||
'button.decoration.background' => ['desktop' => ['color' => '#000000']],
|
||||
],
|
||||
], 'items[0]', null);
|
||||
|
||||
self::assertSame(
|
||||
'gehört nicht zur Gruppe module.decoration.spacing',
|
||||
$result['errors']['items[0].attrs.button.decoration.background']
|
||||
);
|
||||
self::assertSame(['group', 'divi/spacing', 'module.decoration.spacing', 'divi/button'], [
|
||||
$result['item']['type'], $result['item']['groupName'], $result['item']['groupId'],
|
||||
$result['item']['moduleName'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testNestedGroupPresetsByShortName(): void
|
||||
{
|
||||
$book = self::book()->with(new PresetScope('group', 'divi/font'), [
|
||||
'type' => 'group', 'id' => 'ffffffffff', 'name' => 'Button-Schrift', 'groupName' => 'divi/font',
|
||||
'groupId' => 'button.decoration.font', 'moduleName' => 'divi/button', 'attrs' => [],
|
||||
]);
|
||||
|
||||
$result = self::composer()->compose($book, new PresetScope('module', 'divi/button'), null, [
|
||||
'name' => 'Mit Schrift',
|
||||
'group_presets' => ['font' => 'Button-Schrift'],
|
||||
], 'items[0]', null);
|
||||
|
||||
self::assertSame([], $result['errors']);
|
||||
self::assertSame(
|
||||
['button.decoration.font' => ['presetId' => ['ffffffffff'], 'groupName' => 'divi/font']],
|
||||
$result['item']['groupPresets']
|
||||
);
|
||||
}
|
||||
|
||||
public function testUnknownGroupAndPresetAreErrors(): void
|
||||
{
|
||||
$result = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), null, [
|
||||
'name' => 'X',
|
||||
'group_presets' => ['sizing' => 'Gibt es nicht', 'font' => 'Fehlt'],
|
||||
], 'items[0]', null);
|
||||
|
||||
self::assertStringContainsString('keine Gruppe', $result['errors']['items[0].group_presets.sizing']);
|
||||
self::assertStringContainsString('unbekanntes Gruppen-Preset', $result['errors']['items[0].group_presets.font']);
|
||||
}
|
||||
|
||||
public function testNewPresetNeedsAName(): void
|
||||
{
|
||||
$result = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), null, [],
|
||||
'items[0]', null);
|
||||
|
||||
self::assertSame('Name des neuen Presets fehlt', $result['errors']['items[0].name']);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user