Files

201 lines
8.9 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\BlockTree\Json;
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
use LIT\MCP\Integrations\Divi\BlockTree\Parser;
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
use LIT\MCP\Integrations\Divi\Design\VariableDetachRewrite;
use PHPUnit\Framework\TestCase;
final class VariableDetachRewriteTest extends TestCase
{
private const RADIUS = 'gvid-radius0001';
private const CLAIM = 'gvid-claim00001';
/** A whole reference to global variable $id, as the Divi Builder stores it. */
public static function ref(string $id, array $settings = []): string
{
return VariableRef::serialize('content', $id, $settings);
}
/** A breakpoint map with one desktop value. */
private static function desktop(mixed $value): array
{
return ['desktop' => ['value' => $value]];
}
/**
* @param list<array{0: string, 1: array<string, mixed>}> $blocks name and attributes, in the order of the column
* @return NodeIndex Blocks below s0.r0.c0 are m0, m1, … (g0, … for instances, x0, … for other blocks).
*/
private static function index(array $blocks): NodeIndex
{
$inner = '';
foreach ($blocks as [$name, $attrs]) {
$inner .= sprintf('<!-- wp:%s %s /-->', $name, Json::encode($attrs));
}
return NodeIndex::build((new Parser())->parse(
'<!-- wp:divi/section --><!-- wp:divi/row --><!-- wp:divi/column -->' . $inner
. '<!-- /wp:divi/column --><!-- /wp:divi/row --><!-- /wp:divi/section -->'
));
}
private static function node(NodeIndex $index, string $id): Block
{
$block = $index->find('s0.r0.c0.' . $id);
self::assertNotNull($block);
return $block;
}
public function testReplacesWholeNumberReferencesAtAnyDepth(): void
{
$index = self::index([['divi/button', [
'button' => ['decoration' => ['border' => self::desktop([
'radius' => ['topLeft' => self::ref(self::RADIUS), 'topRight' => self::ref(self::RADIUS)],
])]],
'module' => ['decoration' => ['spacing' => self::desktop(['padding' => ['top' => '10px']])]],
]]]);
$attrs = (new VariableDetachRewrite(self::RADIUS, '8px', 'numbers'))->rewrite(self::node($index, 'm0'));
self::assertSame(
['topLeft' => '8px', 'topRight' => '8px'],
$attrs['button']['decoration']['border']['desktop']['value']['radius'] ?? null
);
self::assertSame('10px', $attrs['module']['decoration']['spacing']['desktop']['value']['padding']['top']);
}
public function testAStringsVariableKeepsBeforeAndAfterOfTheReference(): void
{
$index = self::index([
['divi/text', ['content' => ['innerContent' => self::desktop(
self::ref(self::CLAIM, ['before' => 'Jetzt: ', 'after' => '!'])
)]]],
['divi/text', ['content' => ['innerContent' => self::desktop(self::ref(self::CLAIM))]]],
]);
$rewrite = new VariableDetachRewrite(self::CLAIM, 'Gut beraten', 'strings');
self::assertSame(
'Jetzt: Gut beraten!',
$rewrite->rewrite(self::node($index, 'm0'))['content']['innerContent']['desktop']['value'] ?? null
);
self::assertSame(
'Gut beraten',
$rewrite->rewrite(self::node($index, 'm1'))['content']['innerContent']['desktop']['value'] ?? null
);
}
public function testOtherTypesIgnoreBeforeAndAfter(): void
{
self::assertSame(
'Open Sans',
VariableDetachRewrite::replacement(self::ref('gvid-font000001', ['before' => 'x']), 'Open Sans', 'fonts')
);
self::assertSame('a1b', VariableDetachRewrite::replacement(self::ref(self::CLAIM, [
'before' => 'a', 'after' => 'b',
]), '1', 'strings'));
}
public function testFontsAndOtherReferencesStayUntouched(): void
{
$color = VariableRef::serialize('color', 'gcid-primary-color', []);
$dynamic = VariableRef::serialize('content', 'post_title', []);
$index = self::index([
['divi/heading', [
'title' => ['decoration' => ['font' => ['font' => self::desktop([
'family' => self::ref('gvid-font000001'),
'color' => $color,
])]]],
'content' => ['innerContent' => self::desktop($dynamic)],
]],
['divi/text', ['content' => ['innerContent' => self::desktop(self::ref(self::RADIUS))]]],
['core/paragraph', ['content' => self::ref('gvid-font000001')]],
]);
$rewrite = new VariableDetachRewrite('gvid-font000001', 'Open Sans', 'fonts');
$attrs = $rewrite->rewrite(self::node($index, 'm0'));
self::assertSame(
['family' => 'Open Sans', 'color' => $color],
$attrs['title']['decoration']['font']['font']['desktop']['value'] ?? null
);
self::assertSame($dynamic, $attrs['content']['innerContent']['desktop']['value']);
self::assertNull($rewrite->rewrite(self::node($index, 'm1')), 'another variable');
self::assertNull($rewrite->rewrite(self::node($index, 'x0')), 'not a Divi block');
}
public function testEmbeddedReferencesStayAndAreNoted(): void
{
$embedded = 'Ab ' . self::ref(self::RADIUS) . ' Rundung';
$index = self::index([
['divi/text', [
'content' => ['innerContent' => self::desktop($embedded)],
'module' => ['decoration' => ['border' => self::desktop([
'radius' => ['topLeft' => self::ref(self::RADIUS)],
])]],
]],
['divi/text', ['content' => ['innerContent' => self::desktop($embedded)]]],
]);
$rewrite = new VariableDetachRewrite(self::RADIUS, '8px', 'numbers');
$first = self::node($index, 'm0');
$attrs = $rewrite->rewrite($first);
self::assertSame($embedded, $attrs['content']['innerContent']['desktop']['value'] ?? null);
$first->setAttrs((array) $attrs);
self::assertStringContainsString('eingebettet', (string) $rewrite->leftover($first));
self::assertStringContainsString('bleibt unverändert', (string) $rewrite->leftover($first));
self::assertNotNull($rewrite->leftover(self::node($index, 'm1')), 'embedded only: still reported');
self::assertNull($rewrite->rewrite(self::node($index, 'm1')), 'only embedded: nothing to replace');
self::assertNull(
(new VariableDetachRewrite(self::CLAIM, 'x', 'strings'))->leftover($first),
'another variable is not reported'
);
}
public function testUnsafeBeforeAndAfterLeaveTheBlockWholeAndReportIt(): void
{
$index = self::index([
['divi/text', ['content' => ['innerContent' => self::desktop(self::ref(self::CLAIM, ['before' => '<b>']))]]],
['divi/text', ['content' => ['innerContent' => self::desktop(
// Built by hand: VariableRef::serialize() itself refuses a marker in the settings.
'$variable(' . Json::encode(['type' => 'content', 'value' => [
'name' => self::CLAIM, 'settings' => ['after' => 'a $variable(b']
]]) . ')$'
)]]],
['divi/text', ['content' => ['innerContent' => self::desktop(self::ref(self::CLAIM, ['before' => '<b>']))]]],
]);
$strict = new VariableDetachRewrite(self::CLAIM, 'x', 'strings');
$allowed = new VariableDetachRewrite(self::CLAIM, 'x', 'strings', true);
self::assertNull($strict->rewrite(self::node($index, 'm0')));
self::assertStringContainsString('Markup', (string) $strict->leftover(self::node($index, 'm0')));
self::assertNull($allowed->rewrite(self::node($index, 'm1')), 'a marker is never inlined');
self::assertNotNull($allowed->leftover(self::node($index, 'm1')));
$third = self::node($index, 'm2');
$attrs = (array) $allowed->rewrite($third);
self::assertSame('<b>x', $attrs['content']['innerContent']['desktop']['value'] ?? null);
$third->setAttrs($attrs);
self::assertNull($allowed->leftover($third));
}
public function testInstancesThatMentionTheIdConcernTheRewrite(): void
{
$index = self::index([
['divi/global-layout', ['globalModule' => '1058', 'blockName' => 'divi/text', 'localAttrs' => [
'content' => ['innerContent' => self::desktop(self::ref(self::RADIUS))],
]]],
['divi/global-layout', ['globalModule' => '1059', 'blockName' => 'divi/text']],
]);
$rewrite = new VariableDetachRewrite(self::RADIUS, '8px', 'numbers');
self::assertTrue($rewrite->concernsInstance(self::node($index, 'g0')));
self::assertFalse($rewrite->concernsInstance(self::node($index, 'g1')));
}
}