From 68248da648bd6df1179a476579891acb50546879 Mon Sep 17 00:00:00 2001 From: Jan-Niclas Loosen Date: Mon, 19 Oct 2026 14:01:02 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20VariableDiff=20beschreibt=20Variablen?= =?UTF-8?q?=C3=A4nderungen=20(angelegt,=20ge=C3=A4ndert,=20archiviert,=20a?= =?UTF-8?q?ktiviert)=20samt=20Zusammenfassung?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Sonnet 5.5 --- src/Integrations/Divi/Design/VariableDiff.php | 84 ++++++++++++++ .../Divi/Design/VariableDiffTest.php | 104 ++++++++++++++++++ 2 files changed, 188 insertions(+) create mode 100644 src/Integrations/Divi/Design/VariableDiff.php create mode 100644 tests/Unit/Integrations/Divi/Design/VariableDiffTest.php diff --git a/src/Integrations/Divi/Design/VariableDiff.php b/src/Integrations/Divi/Design/VariableDiff.php new file mode 100644 index 0000000..52278cb --- /dev/null +++ b/src/Integrations/Divi/Design/VariableDiff.php @@ -0,0 +1,84 @@ +}> + * 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> $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', + }; + } +} diff --git a/tests/Unit/Integrations/Divi/Design/VariableDiffTest.php b/tests/Unit/Integrations/Divi/Design/VariableDiffTest.php new file mode 100644 index 0000000..5458d08 --- /dev/null +++ b/tests/Unit/Integrations/Divi/Design/VariableDiffTest.php @@ -0,0 +1,104 @@ +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([])); + } +}