feat: ColorDiff beschreibt Farbänderungen (angelegt, geändert, deaktiviert, 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
2428801bc9
commit
39a5782562
@@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Integrations\Divi\Design;
|
||||||
|
|
||||||
|
defined('ABSPATH') || exit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What a color change does (spec §4 step 4, "diff"): per color added, changed (with the changed fields), deactivated
|
||||||
|
* or activated (status change; "fields" lists further changed fields), removed (only a restore can drop a color).
|
||||||
|
* lastUpdated and usedInPosts are no change of their own. Sorted by ID. Pure PHP.
|
||||||
|
*/
|
||||||
|
final class ColorDiff
|
||||||
|
{
|
||||||
|
private const COMPARED = ['color', 'folder', 'label', 'status'];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return list<array{id: string, name: string, action: string, fields?: list<string>}>
|
||||||
|
* action: added|changed|deactivated|activated|removed
|
||||||
|
*/
|
||||||
|
public static function between(ColorBook $before, ColorBook $after): array
|
||||||
|
{
|
||||||
|
$ids = array_unique(array_merge(array_keys($before->data()), array_keys($after->data())));
|
||||||
|
sort($ids);
|
||||||
|
$rows = [];
|
||||||
|
foreach ($ids as $id) {
|
||||||
|
$id = (string) $id;
|
||||||
|
$was = $before->entry($id);
|
||||||
|
$is = $after->entry($id);
|
||||||
|
if ($was === null || $is === null) {
|
||||||
|
$book = $is === null ? $before : $after;
|
||||||
|
$rows[] = ['id' => $id, 'name' => $book->label($id), 'action' => $is === null ? 'removed' : 'added'];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$action = self::action($was['status'], $is['status']);
|
||||||
|
// A status switch is the action itself; "fields" then names only the other changes.
|
||||||
|
$skip = $action === 'changed' ? [] : ['status'];
|
||||||
|
$fields = array_values(array_filter(
|
||||||
|
self::COMPARED,
|
||||||
|
static fn (string $key): bool => $was[$key] !== $is[$key] && ! in_array($key, $skip, true)
|
||||||
|
));
|
||||||
|
$row = ['id' => $id, 'name' => $after->label($id), 'action' => $action];
|
||||||
|
if ($fields !== []) {
|
||||||
|
$row['fields'] = $fields;
|
||||||
|
}
|
||||||
|
if ($fields !== [] || $action !== 'changed') {
|
||||||
|
$rows[] = $row;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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', 'deactivated' => 'deaktiviert',
|
||||||
|
'activated' => 'aktiviert', 'removed' => 'gelöscht',
|
||||||
|
];
|
||||||
|
$parts = [];
|
||||||
|
foreach ($words as $action => $word) {
|
||||||
|
if (isset($count[$action])) {
|
||||||
|
$parts[] = $count[$action] . ' ' . $word;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'Farben: ' . ($parts === [] ? 'keine Änderung' : implode(', ', $parts)) . '.';
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function action(string $was, string $is): string
|
||||||
|
{
|
||||||
|
return match (true) {
|
||||||
|
$was === $is => 'changed',
|
||||||
|
$is === 'inactive' => 'deactivated',
|
||||||
|
$is === 'active' => 'activated',
|
||||||
|
default => 'changed',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design;
|
||||||
|
|
||||||
|
use LIT\MCP\Integrations\Divi\Design\ColorBook;
|
||||||
|
use LIT\MCP\Integrations\Divi\Design\ColorDiff;
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
final class ColorDiffTest extends TestCase
|
||||||
|
{
|
||||||
|
public function testNoChangeIsAnEmptyDiffEvenWhenOnlyDerivedFieldsDiffer(): void
|
||||||
|
{
|
||||||
|
$book = ColorBook::fromDivi(ColorBookTest::stored());
|
||||||
|
$touched = $book->with('gcid-q9swzy97df', [
|
||||||
|
'color' => '#5b3a29', 'label' => 'Holz dunkel', 'lastUpdated' => '2030-01-01T00:00:00.000Z',
|
||||||
|
'usedInPosts' => [7],
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertSame([], ColorDiff::between($book, ColorBook::fromDivi(ColorBookTest::stored())));
|
||||||
|
self::assertSame([], ColorDiff::between($book, $touched));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAddedChangedDeactivatedActivatedAndRemoved(): void
|
||||||
|
{
|
||||||
|
$before = ColorBook::fromDivi(ColorBookTest::stored());
|
||||||
|
$after = $before
|
||||||
|
->with('gcid-new0000001', ['color' => '#abc', 'label' => 'Neu'])
|
||||||
|
->with('gcid-q9swzy97df', ['color' => '#000000', 'label' => 'Holz dunkler'])
|
||||||
|
->with('gcid-aaaaaaaaaa', ['color' => '#111111', 'label' => 'Doppelt', 'status' => 'inactive'])
|
||||||
|
->with('gcid-bbbbbbbbbb', ['color' => '#222222', 'label' => 'doppelt', 'status' => 'active'])
|
||||||
|
->with('gcid-989stx15zc', ['color' => '#333333', 'label' => 'Holz hell', 'status' => 'temporary']);
|
||||||
|
|
||||||
|
self::assertSame([
|
||||||
|
['id' => 'gcid-989stx15zc', 'name' => 'Holz hell', 'action' => 'changed', 'fields' => ['color', 'status']],
|
||||||
|
['id' => 'gcid-aaaaaaaaaa', 'name' => 'Doppelt', 'action' => 'deactivated'],
|
||||||
|
['id' => 'gcid-bbbbbbbbbb', 'name' => 'doppelt', 'action' => 'activated'],
|
||||||
|
['id' => 'gcid-new0000001', 'name' => 'Neu', 'action' => 'added'],
|
||||||
|
['id' => 'gcid-q9swzy97df', 'name' => 'Holz dunkler', 'action' => 'changed',
|
||||||
|
'fields' => ['color', 'label']],
|
||||||
|
], ColorDiff::between($before, $after));
|
||||||
|
|
||||||
|
$restored = ColorBook::fromDivi(array_diff_key(ColorBookTest::stored(), ['gcid-aaaaaaaaaa' => 1]));
|
||||||
|
self::assertSame(
|
||||||
|
[['id' => 'gcid-aaaaaaaaaa', 'name' => 'Doppelt', 'action' => 'removed']],
|
||||||
|
ColorDiff::between($before, $restored)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testStatusSwitchWithFurtherChangesNamesTheOtherFields(): void
|
||||||
|
{
|
||||||
|
$before = ColorBook::fromDivi(ColorBookTest::stored());
|
||||||
|
$after = $before->with('gcid-aaaaaaaaaa', [
|
||||||
|
'color' => '#999999', 'label' => 'Doppelt', 'folder' => 'Marke', 'status' => 'inactive',
|
||||||
|
]);
|
||||||
|
|
||||||
|
self::assertSame(
|
||||||
|
[['id' => 'gcid-aaaaaaaaaa', 'name' => 'Doppelt', 'action' => 'deactivated',
|
||||||
|
'fields' => ['color', 'folder']]],
|
||||||
|
ColorDiff::between($before, $after)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testSummaryCountsTheActions(): void
|
||||||
|
{
|
||||||
|
self::assertSame(
|
||||||
|
'Farben: 1 angelegt, 2 geändert, 1 deaktiviert.',
|
||||||
|
ColorDiff::summary([
|
||||||
|
['action' => 'added'], ['action' => 'changed'], ['action' => 'changed'], ['action' => 'deactivated'],
|
||||||
|
])
|
||||||
|
);
|
||||||
|
self::assertSame(
|
||||||
|
'Farben: 1 geändert, 1 aktiviert, 1 gelöscht.',
|
||||||
|
ColorDiff::summary([['action' => 'removed'], ['action' => 'activated'], ['action' => 'changed']])
|
||||||
|
);
|
||||||
|
self::assertSame('Farben: keine Änderung.', ColorDiff::summary([]));
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user