diff --git a/src/Integrations/Divi/Design/ColorDiff.php b/src/Integrations/Divi/Design/ColorDiff.php new file mode 100644 index 0000000..b60877e --- /dev/null +++ b/src/Integrations/Divi/Design/ColorDiff.php @@ -0,0 +1,86 @@ +}> + * 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> $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', + }; + } +} diff --git a/tests/Unit/Integrations/Divi/Design/ColorDiffTest.php b/tests/Unit/Integrations/Divi/Design/ColorDiffTest.php new file mode 100644 index 0000000..98e4759 --- /dev/null +++ b/tests/Unit/Integrations/Divi/Design/ColorDiffTest.php @@ -0,0 +1,79 @@ +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([])); + } +}