feat: declarative and operation-based menu item editing

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01At3V6SjwbMe6eMzVGW3Hs7
This commit is contained in:
2026-09-26 00:14:25 +02:00
co-authored by Claude Opus 5.5
parent 954f683dd2
commit f80cfb9e44
11 changed files with 1357 additions and 9 deletions
@@ -0,0 +1,65 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Menus;
use LIT\MCP\Integrations\WordPress\Menus\MenuOperations;
use LIT\MCP\Tests\Unit\TestCase;
final class MenuOperationsTest extends TestCase
{
/** @return list<array<string, mixed>> Start(1) > Team(2); Kontakt(3) */
private function tree(): array
{
return [
['id' => 1, 'title' => 'Start', 'children' => [['id' => 2, 'title' => 'Team', 'children' => []]]],
['id' => 3, 'title' => 'Kontakt', 'children' => []],
];
}
public function testAddInsertsANewNodeAtThePosition(): void
{
$result = (new MenuOperations())->apply($this->tree(), [
['op' => 'add', 'item' => ['type' => 'custom', 'url' => '/blog', 'title' => 'Blog'], 'position' => 1],
]);
self::assertSame(1, $result[0]['id']);
self::assertSame(['type' => 'custom', 'url' => '/blog', 'title' => 'Blog', 'children' => []], $result[1]);
self::assertSame(3, $result[2]['id']);
}
public function testMoveAndRemoveKeepChildren(): void
{
$result = (new MenuOperations())->apply($this->tree(), [
['op' => 'move', 'id' => 3, 'parent_id' => 1, 'position' => 0],
['op' => 'remove', 'id' => 1],
]);
self::assertSame([3, 2], array_column($result, 'id'));
self::assertSame([], $result[0]['children']);
}
public function testInvalidOperationsAreReportedWithTheirIndex(): void
{
$operations = new MenuOperations();
$unknown = $operations->apply($this->tree(), [['op' => 'move', 'id' => 99]]);
$cycle = $operations->apply($this->tree(), [['op' => 'move', 'id' => 1, 'parent_id' => 2]]);
$badOp = $operations->apply($this->tree(), [['op' => 'rename', 'id' => 1]]);
self::assertArrayHasKey('operations.0', $unknown->get_error_data()['fields']);
self::assertStringContainsString('unter sich selbst', $cycle->get_error_data()['fields']['operations.0']);
self::assertStringContainsString('add, move oder remove', $badOp->get_error_data()['fields']['operations.0']);
}
public function testExistingNodesKeepOnlyTheirIdSoTheWriterKeepsTheirData(): void
{
$result = (new MenuOperations())->apply($this->tree(), [['op' => 'move', 'id' => 2]]);
self::assertSame(
[['id' => 1, 'children' => []], ['id' => 3, 'children' => []], ['id' => 2, 'children' => []]],
$result
);
}
}