feat: Divi save pipeline – tree_rev lock, kses-safe save, revision, flags, cache, ToolResult
DiviSaver/DiviWriter/DiviComposition/DiviChange, WpValueSanitizer (S4), WpMediaResolver, DiviFlags (S11), DynamicContentMap from Divi's live registry (S22), S31 wiring, S35 control strip at apply() entry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
<?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\Compose\TreeEditor;
|
||||
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
||||
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\Unit\Integrations\Divi\Compose\ComposeFixture;
|
||||
use LIT\MCP\Tests\Unit\Integrations\Divi\Design\DesignSystemTest;
|
||||
use LIT\MCP\Tests\Unit\Integrations\Divi\Schema\ModuleCatalogTest;
|
||||
use LIT\MCP\Tests\Unit\TestCase;
|
||||
use Mockery;
|
||||
|
||||
final class DiviCompositionTest extends TestCase
|
||||
{
|
||||
/** @var list<int> */
|
||||
private array $registryCalls = [];
|
||||
|
||||
private function composition(): DiviComposition
|
||||
{
|
||||
$modules = Mockery::mock(DiviModules::class);
|
||||
$modules->allows('catalog')->andReturn(ModuleCatalogTest::sampleCatalog());
|
||||
$modules->allows('builderVersion')->andReturn('5.2.1');
|
||||
$design = Mockery::mock(DesignSystemReader::class);
|
||||
$design->allows('read')->andReturn(DesignSystemTest::sample());
|
||||
$registry = new DynamicContentMap(function (int $postId): array {
|
||||
$this->registryCalls[] = $postId;
|
||||
|
||||
return DynamicContentMapTest::registry();
|
||||
});
|
||||
|
||||
return new DiviComposition($modules, $design, ComposeFixture::sanitizer(), ComposeFixture::media(), $registry);
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
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::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());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user