Files
WP-LIT-MCP/tests/Unit/Integrations/Divi/Pages/DiviCompositionTest.php
T

242 lines
9.7 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Pages;
use LIT\MCP\Integrations\Divi\BlockTree\AttrMap;
use LIT\MCP\Integrations\Divi\BlockTree\Block;
use LIT\MCP\Integrations\Divi\BlockTree\Document;
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
use LIT\MCP\Integrations\Divi\Compose\StoredNodeGuard;
use LIT\MCP\Integrations\Divi\Compose\TreeEditor;
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter;
use LIT\MCP\Integrations\Divi\Pages\DiviComposition;
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
use LIT\MCP\Integrations\Divi\Schema\DiviModules;
use LIT\MCP\Tests\Support\ComposeFixture;
use LIT\MCP\Tests\Support\DiviFixtures;
use LIT\MCP\Tests\Support\DiviWriterFixture;
use LIT\MCP\Tests\Unit\TestCase;
use Mockery;
final class DiviCompositionTest extends TestCase
{
/** @var list<int> */
private array $registryCalls = [];
private function composition(?DesignSystemWriter $presets = null): DiviComposition
{
$modules = Mockery::mock(DiviModules::class);
$modules->allows('catalog')->andReturn(DiviFixtures::catalog());
$modules->allows('builderVersion')->andReturn('5.2.1');
$design = Mockery::mock(DesignSystemReader::class);
$design->allows('read')->andReturn(DiviFixtures::designSystem());
$registry = new DynamicContentMap(function (int $postId): array {
$this->registryCalls[] = $postId;
return DiviFixtures::dynamicContentRegistry();
});
return new DiviComposition(
$modules,
$design,
ComposeFixture::sanitizer(),
ComposeFixture::media(),
$registry,
ComposeFixture::links(),
presetSource: $presets
);
}
/** The preset source of the live wiring: Divi's stored presets (raw format) and the option groups per module. */
private function presetSource(int $reads = 1): DesignSystemWriter
{
$writer = Mockery::mock(DesignSystemWriter::class);
$writer->expects('presets')->times($reads)->andReturn([
'module' => ['divi/button' => ['default' => 'p-1', 'items' => [
'p-1' => ['id' => 'p-1', 'name' => 'Standard', 'moduleName' => 'divi/button'],
'p-2' => ['id' => 'p-2', 'name' => 'CTA primär', 'moduleName' => 'divi/button'],
]]],
'group' => ['divi/font' => ['default' => '', 'items' => [
'g-1' => ['id' => 'g-1', 'name' => 'Fließtext groß', 'groupName' => 'divi/font'],
]]],
]);
$writer->allows('groupsOf')->andReturnUsing(
static fn (string $module): array => $module === 'divi/button'
? ['button.decoration.font.font' => 'divi/font']
: []
);
return $writer;
}
private static function siteTitle(): string
{
return VariableRef::serialize('content', 'site_title', ['before' => '', 'after' => '']);
}
public function testOneLiveMapIsSharedByTheFullAndTheShortForm(): void
{
$composition = $this->composition()->forPost(7);
// Full form (AttrWriter).
$full = $composition->attrWriter()->apply(
'divi/text',
[],
['content.innerContent' => ['desktop' => self::siteTitle()]],
'attrs'
);
self::assertSame([], $full['errors']);
self::assertSame(self::siteTitle(), AttrMap::desktop($full['attrs'], 'content.innerContent'));
// Short form through the Composer (ShortformWriter) – same map, same AttrWriter.
$short = $composition->composer()->compose(
[['type' => 'button', 'text' => self::siteTitle()]],
Block::create('divi/column'),
'nodes'
);
self::assertSame([], $short['errors']);
self::assertStringContainsString('site_title', json_encode($short['blocks'][0]->attrs(), JSON_THROW_ON_ERROR));
self::assertSame([7], $this->registryCalls, 'the registry is read once per post');
self::assertSame($composition->attrWriter(), $composition->attrWriter());
self::assertSame($composition->placeholders(), $composition->placeholders());
self::assertSame($composition->references(), $composition->references());
}
/** E13a: the composition hands its link targets to Placeholders; the result still passes ValueCleaner. */
public function testInternalLinksUseTheLinkTargets(): void
{
$composer = $this->composition()->forPost(7)->composer();
$column = Block::create('divi/column');
$page = $composer->compose([['type' => 'button', 'text' => 'Kontakt', 'url' => '{{link:839}}']], $column, 'nodes');
self::assertSame([], $page['errors']);
self::assertStringContainsString(
'post_link_url_page',
json_encode($page['blocks'][0]->attrs(), JSON_THROW_ON_ERROR)
);
$missing = $composer->compose([['type' => 'button', 'url' => '{{link:999}}']], $column, 'nodes');
self::assertStringContainsString('Eintrag 999', $missing['errors']['nodes[0].url']);
}
public function testWithoutAPostTheDefaultMapApplies(): void
{
$composition = $this->composition();
$result = $composition->attrWriter()->apply(
'divi/text',
[],
['content.innerContent' => ['desktop' => self::siteTitle()]],
'attrs'
);
self::assertStringContainsString('Unbekannter Name "site_title"', $result['errors']['attrs.content.innerContent']);
self::assertSame([], $this->registryCalls);
}
public function testForPostIsAFreshCompositionPerPost(): void
{
$base = $this->composition();
$seven = $base->forPost(7);
$eight = $base->forPost(8);
self::assertNotSame($base, $seven);
self::assertNotSame($seven->placeholders(), $eight->placeholders());
self::assertNotSame($base->placeholders(), $seven->placeholders());
self::assertNotSame($seven->references(), $eight->references());
self::assertNotSame($base->references(), $seven->references());
self::assertSame([7, 8], $this->registryCalls);
}
public function testRulesEditorAndWarnings(): void
{
$composition = $this->composition();
self::assertNull($composition->rules()->violation(Block::create('divi/column'), 'divi/button'));
self::assertNotNull($composition->rules()->violation(null, 'divi/button'));
self::assertSame([], $composition->warnings()->collect(NodeIndex::build(new Document()), []));
self::assertInstanceOf(TreeEditor::class, $composition->editor());
}
public function testStoredGuardSharesTheCleanerAndReferences(): void
{
$composition = DiviWriterFixture::composition(DiviWriterFixture::designReader())->forPost(7);
$code = Block::create('divi/code', ['builderVersion' => '5.2.1']);
self::assertInstanceOf(StoredNodeGuard::class, $composition->storedGuard());
self::assertSame(
[],
$composition->storedGuard()->problems(Block::create('divi/text', ['builderVersion' => '5.2.1']))
);
self::assertNotSame([], $composition->storedGuard()->problems($code), 'the editor sanitizer is used');
self::assertSame(
[],
DiviWriterFixture::composition(DiviWriterFixture::designReader(), true)->storedGuard()->problems($code)
);
}
public function testPresetStackAndGroupsComeFromTheWritersBookAndAreReadOnce(): void
{
$composer = $this->composition($this->presetSource())->forPost(7)->composer();
$result = $composer->compose([
['type' => 'button', 'text' => 'Eins', 'preset' => ['CTA primär', 'Standard'], 'group_presets' => ['font' => 'g-1']],
['type' => 'button', 'text' => 'Zwei', 'preset' => 'p-2'],
], Block::create('divi/column'), 'nodes');
self::assertSame([], $result['errors']);
self::assertSame(['p-2', 'p-1'], $result['blocks'][0]->attrs()['modulePreset']);
self::assertSame(['p-2'], $result['blocks'][1]->attrs()['modulePreset']);
self::assertSame(
['button.decoration.font.font' => ['groupName' => 'divi/font', 'presetIds' => ['g-1']]],
NodeSummary::groupPresets($result['blocks'][0])
);
}
public function testThePresetBookIsNotReadWithoutAPresetInTheInput(): void
{
$writer = Mockery::mock(DesignSystemWriter::class);
$writer->shouldNotReceive('presets');
$writer->shouldNotReceive('groupsOf');
$result = $this->composition($writer)->forPost(7)->composer()->compose(
[['type' => 'button', 'text' => 'Los']],
Block::create('divi/column'),
'nodes'
);
self::assertSame([], $result['errors']);
}
public function testWithoutAPresetSourceNoPresetExists(): void
{
$result = $this->composition()->forPost(7)->composer()->compose(
[['type' => 'button', 'text' => 'Los', 'preset' => 'Standard']],
Block::create('divi/column'),
'nodes'
);
self::assertSame(
'unbekanntes Preset; vorhanden für divi/button: (keine)',
$result['errors']['nodes[0].preset']
);
}
public function testOnePresetAssignmentPerBoundComposition(): void
{
$base = $this->composition($this->presetSource(0));
$seven = $base->forPost(7);
self::assertSame($seven->presets(), $seven->presets());
self::assertNotSame($seven->presets(), $base->forPost(8)->presets());
self::assertNotSame($base->presets(), $seven->presets());
}
}