feat: {{link:ID}} placeholder via Divi's post_link_url dynamic content

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-27 00:32:59 +02:00
co-authored by Claude Opus 5.5
parent 404f0f34b9
commit 788943e809
11 changed files with 304 additions and 21 deletions
@@ -6,13 +6,21 @@ namespace LIT\MCP\Tests\Unit\Integrations\Divi\Compose;
use InvalidArgumentException;
use LIT\MCP\Integrations\Divi\Compose\Placeholders;
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
use LIT\MCP\Integrations\Divi\Design\DesignSystem;
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
use LIT\MCP\Tests\Support\ComposeFixture;
use LIT\MCP\Tests\Support\DiviFixtures;
use PHPUnit\Framework\TestCase;
final class PlaceholdersTest extends TestCase
{
/** @return array<string, array{string, string}> */
/**
* Rows with a third value true go into a URL field (ruling P4: only the link row – URL mode
* refuses colour references by design).
*
* @return array<string, array{0: string, 1: string, 2?: bool}>
*/
public static function placeholders(): array
{
return [
@@ -31,20 +39,30 @@ final class PlaceholdersTest extends TestCase
'{{field:telefon_mobil}}',
'$variable({"type":"content","value":{"name":"custom_meta_telefon_mobil","settings":{"before":"","after":""}}})$',
],
'internal link' => [
'{{link:839}}',
'$variable({"type":"content","value":{"name":"post_link_url_page","settings":{"post_id":"839"}}})$',
true,
],
'plain text' => ['Hallo Welt', 'Hallo Welt'],
];
}
/** @dataProvider placeholders */
public function testResolvesToDiviVariables(string $input, string $expected): void
public function testResolvesToDiviVariables(string $input, string $expected, bool $url = false): void
{
$placeholders = ComposeFixture::placeholders();
$resolved = $placeholders->resolve($input);
self::assertSame($expected, $resolved);
if ($expected !== $input) {
// What placeholders produce passes the strict checker unchanged (ruling S1/S25).
self::assertSame($resolved, ComposeFixture::references()->canonical($resolved, ComposeFixture::sanitizer()));
// What placeholders produce passes the strict checker unchanged (ruling S1/S25); the live
// registry map knows post_link_url_page, the URL mode allows it.
$references = new VariableRef(
DiviFixtures::designSystem(),
DynamicContentMap::fromOptions(DiviFixtures::dynamicContentRegistry())
);
self::assertSame($resolved, $references->canonical($resolved, ComposeFixture::sanitizer(), $url));
}
}
@@ -60,6 +78,11 @@ final class PlaceholdersTest extends TestCase
'bad field key' => ['{{field:a b}}', 'Ungültiger Feldschlüssel'],
'protected field key' => ['{{field:_edit_lock}}', 'Ungültiger Feldschlüssel'],
'embedded' => ['Seite: {{post:title}}', 'allein im Wert'],
'link without id' => ['{{link:start}}', 'Ungültige Beitrags-ID'],
'link to a missing post' => ['{{link:999}}', 'Eintrag 999'],
'link without link targets' => ['{{link:0}}', 'Ungültige Beitrags-ID'],
'link with a leading zero' => ['{{link:0839}}', 'Ungültige Beitrags-ID'],
'link beyond PHP_INT_MAX' => ['{{link:9999999999999999999}}', 'Ungültige Beitrags-ID'],
];
}
@@ -137,4 +160,12 @@ final class PlaceholdersTest extends TestCase
ComposeFixture::placeholders()->resolveDeep(['color' => '{{color:primary}}', 'size' => '20px'])
);
}
public function testLinksNeedLinkTargets(): void
{
$this->expectException(InvalidArgumentException::class);
$this->expectExceptionMessage('Eintrag 839');
(new Placeholders(DiviFixtures::designSystem()))->resolve('{{link:839}}');
}
}