test: shared Divi fixtures in tests/Support (T1)

- DiviFixtures: markup(), givenPost(), schema(), designSystem(), catalog(),
  dynamicContentRegistry(), maliciousRefs() - moved verbatim out of
  DiviPageLoaderTest, DesignSystemTest, ModuleCatalogTest, DynamicContentMapTest
  and VariableRefTest (its malicious() provider now returns maliciousRefs()).
- ComposeFixture moves to tests/Support.
- DiviWriterFixture: modules(), designReader(), composition(), a recording saver(),
  log() and writer(&$saved, &$failures, unfiltered, loader) for the five tests that
  wired the Divi write stack by hand.
No test cases or assertions changed (1397 / 5834).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-26 17:47:11 +02:00
co-authored by Claude Opus 5.5
parent d2cc8497c9
commit 3d96a41366
24 changed files with 489 additions and 404 deletions
@@ -8,71 +8,16 @@ use InvalidArgumentException;
use LIT\MCP\Integrations\Divi\Compose\Placeholders;
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
use LIT\MCP\Tests\Unit\Integrations\Divi\Compose\ComposeFixture;
use LIT\MCP\Tests\Unit\Integrations\Divi\Design\DesignSystemTest;
use LIT\MCP\Tests\Support\ComposeFixture;
use LIT\MCP\Tests\Support\DiviFixtures;
use LIT\MCP\Tests\Unit\TestCase;
use RuntimeException;
final class DynamicContentMapTest extends TestCase
{
/**
* Shape of DynamicContentOptions::get_options($pageId, 'edit') in Divi 5.2.1 (excerpt of a
* live dump for a page: no post_categories/post_tags – pages have no such taxonomies).
*
* @return array<string, array<string, mixed>>
*/
public static function registry(): array
{
$text = static fn (): array => ['label' => 'x', 'type' => 'text', 'default' => ''];
$option = static fn (string $id, string $group, array $fields): array => [
'id' => $id, 'label' => $id, 'type' => 'text', 'custom' => false, 'group' => $group, 'fields' => $fields,
];
return [
'post_title' => $option('post_title', 'Default', ['before' => $text(), 'after' => $text()]),
'post_excerpt' => $option('post_excerpt', 'Default', [
'before' => $text(), 'after' => $text(), 'words' => $text(), 'read_more_label' => $text(),
]),
'post_date' => $option('post_date', 'Default', [
'before' => $text(),
'after' => $text(),
'date_format' => ['label' => 'x', 'type' => 'select', 'default' => 'default'],
'custom_date_format' => $text(),
]),
'post_author' => $option('post_author', 'Default', [
'before' => $text(),
'after' => $text(),
'name_format' => ['type' => 'select'],
'link' => ['type' => 'yes_no_button'],
]),
'site_title' => $option('site_title', 'Default', ['before' => $text(), 'after' => $text()]),
'post_link_url' => $option('post_link_url', 'Default', []),
'post_link_url_page' => $option('post_link_url_page', 'Default', ['post_id' => ['type' => 'select_post']]),
'post_featured_image' => $option('post_featured_image', 'Default', []),
'post_meta_key' => $option('post_meta_key', 'Standard', [
'before' => $text(),
'after' => $text(),
'select_meta_key' => ['type' => 'select'],
'meta_key' => $text(),
'enable_html' => ['type' => 'yes_no_button'],
]),
'loop_post_title' => $option('loop_post_title', 'Loop', [
'before' => $text(), 'after' => $text(), 'loop_position' => $text(),
]),
'loop_post_meta_key_manual_custom_field' => $option('loop_post_meta_key_manual_custom_field', 'Loop', [
'loop_meta_key' => $text(),
]),
// Odd entries a third-party filter could add.
'broken_fields' => $option('broken_fields', 'Default', ['a' => 'not-an-array', 'b' => ['type' => 3]]),
'no_fields' => ['id' => 'no_fields', 'type' => 'text'],
7 => $option('7', 'Default', []),
'not_an_array' => 'x',
];
}
public function testFlattensTheRegistryToOptionNameAndFieldTypes(): void
{
$map = DynamicContentMap::fromOptions(self::registry());
$map = DynamicContentMap::fromOptions(DiviFixtures::dynamicContentRegistry());
// before/after: rich text (Divi outputs them as HTML; text() would trim "Von ").
self::assertSame(['before' => 'richtext', 'after' => 'richtext'], $map['post_title']);
@@ -92,7 +37,7 @@ final class DynamicContentMapTest extends TestCase
public function testLoopOptionsNeverEnterTheMap(): void
{
$map = DynamicContentMap::fromOptions(self::registry());
$map = DynamicContentMap::fromOptions(DiviFixtures::dynamicContentRegistry());
self::assertSame([], array_filter(array_keys($map), static fn (string $name): bool => str_starts_with($name, 'loop_')));
}
@@ -101,7 +46,7 @@ final class DynamicContentMapTest extends TestCase
{
// The map replaces VariableRef's default: every {{post:…}} placeholder must stay usable
// with the settings it writes (before/after), also where the registry lacks the option.
$map = DynamicContentMap::fromOptions(self::registry());
$map = DynamicContentMap::fromOptions(DiviFixtures::dynamicContentRegistry());
foreach (Placeholders::POST as $option) {
self::assertArrayHasKey($option, $map, $option);
@@ -115,7 +60,7 @@ final class DynamicContentMapTest extends TestCase
self::assertSame('select', $map['post_date']['date_format']);
self::assertSame('text', $map['post_excerpt']['words']);
$placeholders = new Placeholders(DesignSystemTest::sample(), $map);
$placeholders = new Placeholders(DiviFixtures::designSystem(), $map);
foreach (array_keys(Placeholders::POST) as $key) {
$reference = $placeholders->resolve('{{post:' . $key . '}}');
self::assertSame(
@@ -128,7 +73,7 @@ final class DynamicContentMapTest extends TestCase
public function testTheMapWorksInVariableRef(): void
{
$references = new VariableRef(DesignSystemTest::sample(), DynamicContentMap::fromOptions(self::registry()));
$references = new VariableRef(DiviFixtures::designSystem(), DynamicContentMap::fromOptions(DiviFixtures::dynamicContentRegistry()));
$sanitizer = ComposeFixture::sanitizer();
$ref = static fn (string $name, array $settings): string => VariableRef::serialize('content', $name, $settings);
@@ -164,7 +109,7 @@ final class DynamicContentMapTest extends TestCase
$map = (new DynamicContentMap(static function (int $postId) use (&$seen): array {
$seen[] = $postId;
return DynamicContentMapTest::registry();
return DiviFixtures::dynamicContentRegistry();
}))->forPost(42);
self::assertSame([42], $seen);