feat: ThemeOptionDiff – Diff und Zusammenfassung der Theme-Optionen
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
d9c4bc3751
commit
8740e9b8dc
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Integrations\Divi\Design;
|
||||
|
||||
defined('ABSPATH') || exit;
|
||||
|
||||
/**
|
||||
* What a theme-options change does (spec §4 step 4, "diff"): per key added (not set before), changed or reset
|
||||
* (removed afterwards – Divi falls back to its default). Values are compared strictly, so false (Divi's default
|
||||
* font) and a type change (1 versus "1") count. before/after carry the raw values, null where the key is not set.
|
||||
* In the order of ThemeOptionSpec::KEYS. Pure PHP.
|
||||
*/
|
||||
final class ThemeOptionDiff
|
||||
{
|
||||
/**
|
||||
* @return list<array{key: string, label: string, action: string, before: mixed, after: mixed}>
|
||||
* action: added|changed|reset
|
||||
*/
|
||||
public static function between(ThemeOptionBook $before, ThemeOptionBook $after): array
|
||||
{
|
||||
$rows = [];
|
||||
foreach (ThemeOptionSpec::KEYS as $key) {
|
||||
$action = match (true) {
|
||||
! $before->has($key) && $after->has($key) => 'added',
|
||||
$before->has($key) && ! $after->has($key) => 'reset',
|
||||
$before->get($key) !== $after->get($key) => 'changed',
|
||||
default => null,
|
||||
};
|
||||
if ($action !== null) {
|
||||
$rows[] = [
|
||||
'key' => $key,
|
||||
'label' => ThemeOptionSpec::definition($key)['label'],
|
||||
'action' => $action,
|
||||
'before' => $before->get($key),
|
||||
'after' => $after->get($key),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
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));
|
||||
$parts = [];
|
||||
foreach (['added' => 'gesetzt', 'changed' => 'geändert', 'reset' => 'zurückgesetzt'] as $action => $word) {
|
||||
if (isset($count[$action])) {
|
||||
$parts[] = $count[$action] . ' ' . $word;
|
||||
}
|
||||
}
|
||||
|
||||
return 'Theme-Optionen: ' . ($parts === [] ? 'keine Änderung' : implode(', ', $parts)) . '.';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design;
|
||||
|
||||
use LIT\MCP\Integrations\Divi\Design\ThemeOptionBook;
|
||||
use LIT\MCP\Integrations\Divi\Design\ThemeOptionDiff;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
final class ThemeOptionDiffTest extends TestCase
|
||||
{
|
||||
public function testNoChangeIsAnEmptyDiffEvenWhenTheOrderOfTheInputDiffers(): void
|
||||
{
|
||||
$book = ThemeOptionBook::fromDivi(ThemeOptionBookTest::stored());
|
||||
$same = ThemeOptionBook::fromDivi(array_reverse(ThemeOptionBookTest::stored(), true));
|
||||
|
||||
self::assertSame([], ThemeOptionDiff::between($book, $same));
|
||||
self::assertSame([], ThemeOptionDiff::between(ThemeOptionBook::fromDivi([]), ThemeOptionBook::fromDivi([])));
|
||||
}
|
||||
|
||||
public function testAddedChangedAndResetInTheOrderOfTheKeys(): void
|
||||
{
|
||||
$before = ThemeOptionBook::fromDivi(ThemeOptionBookTest::stored());
|
||||
$after = $before
|
||||
->with('content_width', 1200)
|
||||
->with('body_font_size', 18)
|
||||
->with('body_font', 'Roboto')
|
||||
->without('body_header_style');
|
||||
|
||||
self::assertSame([
|
||||
['key' => 'body_font', 'label' => 'Fließtext-Schrift', 'action' => 'changed',
|
||||
'before' => 'Lato', 'after' => 'Roboto'],
|
||||
['key' => 'body_font_size', 'label' => 'Fließtext-Schriftgröße', 'action' => 'changed',
|
||||
'before' => 16, 'after' => 18],
|
||||
['key' => 'body_header_style', 'label' => 'Überschrift-Schriftstil', 'action' => 'reset',
|
||||
'before' => 'uppercase', 'after' => null],
|
||||
['key' => 'content_width', 'label' => 'Inhaltsbreite', 'action' => 'added',
|
||||
'before' => null, 'after' => 1200],
|
||||
], ThemeOptionDiff::between($before, $after));
|
||||
}
|
||||
|
||||
public function testFalseIsAValueOfItsOwn(): void
|
||||
{
|
||||
$unset = ThemeOptionBook::fromDivi([]);
|
||||
$default = ThemeOptionBook::fromDivi(['heading_font' => false]);
|
||||
$font = ThemeOptionBook::fromDivi(['heading_font' => 'Lato']);
|
||||
|
||||
self::assertSame(
|
||||
[['key' => 'heading_font', 'label' => 'Überschrift-Schrift', 'action' => 'added',
|
||||
'before' => null, 'after' => false]],
|
||||
ThemeOptionDiff::between($unset, $default)
|
||||
);
|
||||
self::assertSame(
|
||||
[['key' => 'heading_font', 'label' => 'Überschrift-Schrift', 'action' => 'changed',
|
||||
'before' => false, 'after' => 'Lato']],
|
||||
ThemeOptionDiff::between($default, $font)
|
||||
);
|
||||
self::assertSame(
|
||||
[['key' => 'heading_font', 'label' => 'Überschrift-Schrift', 'action' => 'reset',
|
||||
'before' => false, 'after' => null]],
|
||||
ThemeOptionDiff::between($default, $unset)
|
||||
);
|
||||
}
|
||||
|
||||
public function testATypeChangeCountsAsAChange(): void
|
||||
{
|
||||
$int = ThemeOptionBook::fromDivi(['gutter_width' => 1, 'body_header_height' => 1]);
|
||||
$text = ThemeOptionBook::fromDivi(['gutter_width' => '1', 'body_header_height' => 1.0]);
|
||||
|
||||
self::assertSame(
|
||||
[
|
||||
['key' => 'body_header_height', 'label' => 'Überschrift-Zeilenhöhe', 'action' => 'changed',
|
||||
'before' => 1, 'after' => 1.0],
|
||||
['key' => 'gutter_width', 'label' => 'Spaltenabstand', 'action' => 'changed',
|
||||
'before' => 1, 'after' => '1'],
|
||||
],
|
||||
ThemeOptionDiff::between($int, $text)
|
||||
);
|
||||
}
|
||||
|
||||
public function testSummaryCountsPerAction(): void
|
||||
{
|
||||
$before = ThemeOptionBook::fromDivi(ThemeOptionBookTest::stored());
|
||||
$after = $before->with('content_width', 1200)->with('body_font_size', 18)->with('body_font', 'Roboto')
|
||||
->without('body_header_style');
|
||||
|
||||
self::assertSame(
|
||||
'Theme-Optionen: 1 gesetzt, 2 geändert, 1 zurückgesetzt.',
|
||||
ThemeOptionDiff::summary(ThemeOptionDiff::between($before, $after))
|
||||
);
|
||||
self::assertSame(
|
||||
'Theme-Optionen: 1 gesetzt.',
|
||||
ThemeOptionDiff::summary(ThemeOptionDiff::between(
|
||||
ThemeOptionBook::fromDivi([]),
|
||||
ThemeOptionBook::fromDivi(['content_width' => 1200])
|
||||
))
|
||||
);
|
||||
self::assertSame('Theme-Optionen: keine Änderung.', ThemeOptionDiff::summary([]));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user