Files
WP-LIT-MCP/tests/Unit/Integrations/Divi/Design/PresetComposerTest.php
T

215 lines
9.1 KiB
PHP

<?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, [
'name' => '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']);
}
/** Final review M-5: the ability resolves name/rename; the composer only takes "name" as given. */
public function testOnlyTheResolvedNameCounts(): void
{
$existing = self::book()->items(new PresetScope('module', 'divi/button'))['aaaaaaaaaa'];
$kept = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), $existing, [
'rename' => 'Ignoriert',
], 'items[0]', null);
$missing = self::composer()->compose(self::book(), new PresetScope('module', 'divi/button'), null, [
'rename' => 'Ignoriert',
], 'items[0]', null);
self::assertSame('CTA primär', $kept['item']['name']);
self::assertSame('Name des neuen Presets fehlt', $missing['errors']['items[0].name']);
}
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']);
}
}