feat: VariableDiff beschreibt Variablenänderungen (angelegt, geändert, archiviert, aktiviert) samt Zusammenfassung
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
a7a385d3b3
commit
68248da648
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Design;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* What a variable change does (spec §4 step 4, "diff"): per variable added, changed (with the changed fields label,
|
||||
* value, order, status), archived or activated (status switch; "fields" then lists label/value changes only – archiving
|
||||
* clears the order on its own). lastUpdated is no change of its own. Variables are never removed (ruling V1), so a
|
||||
* variable missing in $after produces no row. Sorted by type, then ID. Pure PHP.
|
||||
*/
|
||||
final class VariableDiff
|
||||
{
|
||||
private const COMPARED = ['label', 'value', 'order', 'status'];
|
||||
|
||||
/**
|
||||
* @return list<array{type: string, id: string, name: string, action: string, fields?: list<string>}>
|
||||
* action: added|changed|archived|activated
|
||||
*/
|
||||
public static function between(VariableBook $before, VariableBook $after): array
|
||||
{
|
||||
$new = $after->data();
|
||||
ksort($new);
|
||||
$rows = [];
|
||||
foreach ($new as $type => $entries) {
|
||||
ksort($entries);
|
||||
foreach ($entries as $id => $is) {
|
||||
$id = (string) $id;
|
||||
$was = $before->entry($type, $id);
|
||||
$row = ['type' => $type, 'id' => $id, 'name' => $is['label'] !== '' ? $is['label'] : $id];
|
||||
if ($was === null) {
|
||||
$rows[] = $row + ['action' => 'added'];
|
||||
continue;
|
||||
}
|
||||
$action = self::action((string) $was['status'], (string) $is['status']);
|
||||
// A status switch is the action itself; archiving clears the order, which is no change of its own.
|
||||
$compared = $action === 'changed' ? self::COMPARED : array_slice(self::COMPARED, 0, 2);
|
||||
$fields = array_values(array_filter(
|
||||
$compared,
|
||||
static fn (string $key): bool => $was[$key] !== $is[$key]
|
||||
));
|
||||
if ($fields !== [] || $action !== 'changed') {
|
||||
$rows[] = $row + ['action' => $action] + ($fields === [] ? [] : ['fields' => $fields]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $rows;
|
||||
}
|
||||
|
||||
/**
|
||||
* One German sentence for the dry run and the change log (spec §4 step 6).
|
||||
*
|
||||
* @param list<array<string, mixed>> $diff
|
||||
*/
|
||||
public static function summary(array $diff): string
|
||||
{
|
||||
$count = array_count_values(array_map(static fn (array $row): string => (string) $row['action'], $diff));
|
||||
$words = [
|
||||
'added' => 'angelegt', 'changed' => 'geändert', 'archived' => 'archiviert', 'activated' => 'aktiviert',
|
||||
];
|
||||
$parts = [];
|
||||
foreach ($words as $action => $word) {
|
||||
if (isset($count[$action])) {
|
||||
$parts[] = $count[$action] . ' ' . $word;
|
||||
}
|
||||
}
|
||||
|
||||
return 'Variablen: ' . ($parts === [] ? 'keine Änderung' : implode(', ', $parts)) . '.';
|
||||
}
|
||||
|
||||
private static function action(string $was, string $is): string
|
||||
{
|
||||
return match (true) {
|
||||
$was === $is => 'changed',
|
||||
$is === 'archived' => 'archived',
|
||||
$was === 'archived' && $is === 'active' => 'activated',
|
||||
default => 'changed',
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design;
|
||||
|
||||
use LIT\MCP\Integrations\Divi\Design\VariableBook;
|
||||
use LIT\MCP\Integrations\Divi\Design\VariableDiff;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class VariableDiffTest extends TestCase
|
||||
{
|
||||
public function testNoChangeIsAnEmptyDiffEvenWhenOnlyLastUpdatedDiffers(): void
|
||||
{
|
||||
$book = VariableBook::fromDivi(VariableBookTest::stored());
|
||||
$touched = $book->with('numbers', 'gvid-f6yq6mayfy', [
|
||||
'label' => 'Eckenradius', 'value' => '8px', 'order' => '1', 'lastUpdated' => '2030-01-01T00:00:00.000Z',
|
||||
]);
|
||||
|
||||
self::assertSame([], VariableDiff::between($book, VariableBook::fromDivi(VariableBookTest::stored())));
|
||||
self::assertSame([], VariableDiff::between($book, $touched));
|
||||
}
|
||||
|
||||
public function testAddedChangedArchivedAndActivated(): void
|
||||
{
|
||||
$before = VariableBook::fromDivi(VariableBookTest::stored());
|
||||
$after = $before
|
||||
->with('fonts', 'gvid-new0000001', ['label' => 'Neu', 'value' => 'Lato', 'order' => '2'])
|
||||
->with('numbers', 'gvid-f6yq6mayfy', ['label' => 'Radius', 'value' => '12px', 'order' => '1'])
|
||||
->with('numbers', 'gvid-5dec72fwsc', ['label' => 'Abstand Karte', 'value' => '2rem', 'order' => '2',
|
||||
'status' => 'archived'])
|
||||
->with('numbers', 'gvid-old0000001', ['label' => 'Alt', 'value' => '1px', 'order' => '3']);
|
||||
|
||||
self::assertSame([
|
||||
['type' => 'fonts', 'id' => 'gvid-new0000001', 'name' => 'Neu', 'action' => 'added'],
|
||||
['type' => 'numbers', 'id' => 'gvid-5dec72fwsc', 'name' => 'Abstand Karte', 'action' => 'archived'],
|
||||
['type' => 'numbers', 'id' => 'gvid-f6yq6mayfy', 'name' => 'Radius', 'action' => 'changed',
|
||||
'fields' => ['label', 'value']],
|
||||
['type' => 'numbers', 'id' => 'gvid-old0000001', 'name' => 'Alt', 'action' => 'activated'],
|
||||
], VariableDiff::between($before, $after));
|
||||
}
|
||||
|
||||
public function testArchivingClearsTheOrderWithoutAFieldEntryButLabelAndValueChangesShow(): void
|
||||
{
|
||||
$before = VariableBook::fromDivi(VariableBookTest::stored());
|
||||
$archived = $before->with('numbers', 'gvid-5dec72fwsc', [
|
||||
'label' => 'Abstand Karte', 'value' => '2rem', 'order' => '', 'status' => 'archived',
|
||||
]);
|
||||
$archivedAndRenamed = $before->with('numbers', 'gvid-5dec72fwsc', [
|
||||
'label' => 'Abstand', 'value' => '2rem', 'order' => '', 'status' => 'archived',
|
||||
]);
|
||||
|
||||
self::assertSame(
|
||||
[['type' => 'numbers', 'id' => 'gvid-5dec72fwsc', 'name' => 'Abstand Karte', 'action' => 'archived']],
|
||||
VariableDiff::between($before, $archived)
|
||||
);
|
||||
self::assertSame(
|
||||
[['type' => 'numbers', 'id' => 'gvid-5dec72fwsc', 'name' => 'Abstand', 'action' => 'archived',
|
||||
'fields' => ['label']]],
|
||||
VariableDiff::between($before, $archivedAndRenamed)
|
||||
);
|
||||
}
|
||||
|
||||
public function testReorderIsAChangeOfTheOrder(): void
|
||||
{
|
||||
$before = VariableBook::fromDivi(VariableBookTest::stored());
|
||||
$after = $before->with('numbers', 'gvid-5dec72fwsc', [
|
||||
'label' => 'Abstand Karte', 'value' => '2rem', 'order' => '1',
|
||||
]);
|
||||
|
||||
self::assertSame(
|
||||
[['type' => 'numbers', 'id' => 'gvid-5dec72fwsc', 'name' => 'Abstand Karte', 'action' => 'changed',
|
||||
'fields' => ['order']]],
|
||||
VariableDiff::between($before, $after)
|
||||
);
|
||||
}
|
||||
|
||||
public function testGradientsAreComparedLikeEveryOtherType(): void
|
||||
{
|
||||
$before = VariableBook::fromDivi(VariableBookTest::stored());
|
||||
$after = $before->with('gradients', 'gvid-grad000001', [
|
||||
'label' => 'Verlauf', 'value' => 'linear-gradient(blue, red)', 'order' => '1',
|
||||
]);
|
||||
|
||||
self::assertSame(
|
||||
[['type' => 'gradients', 'id' => 'gvid-grad000001', 'name' => 'Verlauf', 'action' => 'changed',
|
||||
'fields' => ['value']]],
|
||||
VariableDiff::between($before, $after)
|
||||
);
|
||||
}
|
||||
|
||||
public function testSummaryCountsTheActions(): void
|
||||
{
|
||||
self::assertSame(
|
||||
'Variablen: 1 angelegt, 1 geändert, 1 archiviert.',
|
||||
VariableDiff::summary([['action' => 'added'], ['action' => 'changed'], ['action' => 'archived']])
|
||||
);
|
||||
self::assertSame(
|
||||
'Variablen: 2 geändert, 1 aktiviert.',
|
||||
VariableDiff::summary([['action' => 'activated'], ['action' => 'changed'], ['action' => 'changed']])
|
||||
);
|
||||
self::assertSame('Variablen: keine Änderung.', VariableDiff::summary([]));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user