- Fingerprint::content() includes non-whitespace text parts (raw HTML between blocks, inner HTML of core blocks) and opaque blocks with all their bytes; structure() unchanged; find-similar "identical" follows - make-global mode 2 also compares Fingerprint::canonical() bytes of each node - sources and targets: post types page and post only (POST_TYPES) - a rolled-back page N>1 keeps DiviSaver's "Revision X bleibt erhalten" - guide: escaped divi/global-layout re-expanded by Divi's do_blocks in shortcode-module and WooCommerce text output documented as residual risk Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
136 lines
5.5 KiB
PHP
136 lines
5.5 KiB
PHP
<?php
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\BlockTree;
|
||
|
||
use LIT\MCP\Integrations\Divi\BlockTree\Block;
|
||
use LIT\MCP\Integrations\Divi\BlockTree\Fingerprint;
|
||
use LIT\MCP\Integrations\Divi\BlockTree\LibraryContent;
|
||
use LIT\MCP\Integrations\Divi\BlockTree\Parser;
|
||
use LIT\MCP\Tests\Support\DiviFixtures;
|
||
use PHPUnit\Framework\TestCase;
|
||
|
||
final class FingerprintTest extends TestCase
|
||
{
|
||
/** The section of library entry 1055: section > row > column (24_24) > blurb. */
|
||
private static function section(): Block
|
||
{
|
||
$content = DiviFixtures::markup('stage3/library--library-abschnitt.html');
|
||
|
||
return LibraryContent::roots(LibraryContent::load(new Parser(), $content))[0];
|
||
}
|
||
|
||
private static function blurbOf(Block $section): Block
|
||
{
|
||
return $section->children()[0]->children()[0]->children()[0];
|
||
}
|
||
|
||
public function testAdminLabelsAndBuilderVersionDoNotCount(): void
|
||
{
|
||
$a = self::section();
|
||
$b = $a->copy();
|
||
$attrs = $b->attrs();
|
||
$attrs['module']['meta']['adminLabel']['desktop']['value'] = 'Anderes Label';
|
||
$attrs['builderVersion'] = '5.3.0';
|
||
$b->setAttrs($attrs);
|
||
|
||
self::assertSame(16, strlen(Fingerprint::content($a)));
|
||
self::assertSame(Fingerprint::content($a), Fingerprint::content($b));
|
||
self::assertSame(Fingerprint::structure($a), Fingerprint::structure($b));
|
||
}
|
||
|
||
public function testOtherTextsKeepTheStructureButNotTheContent(): void
|
||
{
|
||
$a = self::section();
|
||
$b = $a->copy();
|
||
$blurb = self::blurbOf($b);
|
||
$attrs = $blurb->attrs();
|
||
$attrs['title']['innerContent']['desktop']['value'] = 'Anderer Titel';
|
||
$blurb->setAttrs($attrs);
|
||
|
||
self::assertSame(Fingerprint::structure($a), Fingerprint::structure($b));
|
||
self::assertNotSame(Fingerprint::content($a), Fingerprint::content($b));
|
||
}
|
||
|
||
public function testColumnLayoutAndModuleTypesChangeTheStructure(): void
|
||
{
|
||
$a = self::section();
|
||
$b = $a->copy();
|
||
$column = $b->children()[0]->children()[0];
|
||
$attrs = $column->attrs();
|
||
$attrs['module']['decoration']['sizing']['desktop']['value']['flexType'] = '12_24';
|
||
$column->setAttrs($attrs);
|
||
$c = Block::create('divi/section', [], [Block::create('divi/row', [], [Block::create('divi/column', [], [
|
||
Block::create('divi/text'),
|
||
])])]);
|
||
|
||
self::assertNotSame(Fingerprint::structure($a), Fingerprint::structure($b));
|
||
self::assertNotSame(Fingerprint::structure($a), Fingerprint::structure($c));
|
||
}
|
||
|
||
/** The section of 1055 with $extra inserted at the end of its column (review of Task 15, ruling Q25). */
|
||
private static function sectionWith(string $extra): Block
|
||
{
|
||
$markup = DiviFixtures::markup('stage3/library--library-abschnitt.html');
|
||
$at = strrpos($markup, '<!-- /wp:divi/column -->');
|
||
$markup = substr($markup, 0, (int) $at) . $extra . substr($markup, (int) $at);
|
||
|
||
return LibraryContent::roots(LibraryContent::load(new Parser(), $markup))[0];
|
||
}
|
||
|
||
/** @return array<string, array{string, string}> */
|
||
public static function visibleExtras(): array
|
||
{
|
||
return [
|
||
'raw html between blocks' => ['<p>ONLY ON B</p>', '<p>ONLY ON C</p>'],
|
||
'core paragraph' => [
|
||
'<!-- wp:paragraph --><p>ONLY ON B</p><!-- /wp:paragraph -->',
|
||
'<!-- wp:paragraph --><p>ONLY ON C</p><!-- /wp:paragraph -->',
|
||
],
|
||
'divi 4 shortcode text' => ['[et_pb_text]ONLY ON B[/et_pb_text]', '[et_pb_text]ONLY ON C[/et_pb_text]'],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Ruling Q25: text parts are visible – raw HTML between blocks and the inner HTML of core blocks count
|
||
* for the content, like attributes.
|
||
*
|
||
* @dataProvider visibleExtras
|
||
*/
|
||
public function testVisibleTextPartsCountForTheContent(string $extra, string $other): void
|
||
{
|
||
$plain = self::section();
|
||
|
||
self::assertNotSame(Fingerprint::content($plain), Fingerprint::content(self::sectionWith($extra)));
|
||
self::assertNotSame(
|
||
Fingerprint::content(self::sectionWith($extra)),
|
||
Fingerprint::content(self::sectionWith($other))
|
||
);
|
||
self::assertNotSame(Fingerprint::canonical($plain), Fingerprint::canonical(self::sectionWith($extra)));
|
||
}
|
||
|
||
public function testRawTextKeepsTheStructureAndWhitespaceNeverCounts(): void
|
||
{
|
||
$plain = self::section();
|
||
|
||
self::assertSame(Fingerprint::structure($plain), Fingerprint::structure(self::sectionWith('<p>x</p>')));
|
||
self::assertSame(Fingerprint::content($plain), Fingerprint::content(self::sectionWith("\n \n")));
|
||
self::assertSame(Fingerprint::canonical($plain), Fingerprint::canonical(self::sectionWith("\n\t")));
|
||
}
|
||
|
||
/** Opaque blocks count with all their bytes (a global instance's snapshot included). */
|
||
public function testOpaqueBlocksCountWithAllTheirBytes(): void
|
||
{
|
||
$instance = static fn (string $text): Block => LibraryContent::roots(LibraryContent::load(
|
||
new Parser(),
|
||
'<!-- wp:divi/placeholder --><!-- wp:divi/section --><!-- wp:divi/row --><!-- wp:divi/column -->'
|
||
. '<!-- wp:divi/global-layout {"globalModule":"5","blockName":"divi/text"} --><p>' . $text
|
||
. '</p><!-- /wp:divi/global-layout -->'
|
||
. '<!-- /wp:divi/column --><!-- /wp:divi/row --><!-- /wp:divi/section --><!-- /wp:divi/placeholder -->'
|
||
))[0];
|
||
|
||
self::assertNotSame(Fingerprint::content($instance('a')), Fingerprint::content($instance('b')));
|
||
}
|
||
}
|