feat: Protokoll tab as a core-style list table
The change summary is the wide primary column (key "title", so it cannot
be hidden) with the tool's human label below and row actions "Revision
ansehen" / "Objekt öffnen". Objects get human names ("Seite: Kontakt",
"Menü: …", "Einstellungen: Titel der Website"; deleted/trashed marked),
primed per page with one query each for posts, menus and users. Times are
relative within 24 h. Non-ok results show a badge and a marked row instead
of an Ergebnis column. Page size and columns via Screen Options; the tool
filter shows labels grouped by integration, dates have visible labels,
and an active filter offers "Filter zurücksetzen" (also in the empty state).
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:
@@ -0,0 +1,167 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\Admin;
|
||||
|
||||
use Brain\Monkey\Functions;
|
||||
use LIT\MCP\Admin\LogObjects;
|
||||
use LIT\MCP\Tests\Unit\TestCase;
|
||||
use WP_Post;
|
||||
use WP_Term;
|
||||
|
||||
final class LogObjectsTest extends TestCase
|
||||
{
|
||||
/** @var array<int, WP_Post> */
|
||||
private array $posts = [];
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
Functions\stubEscapeFunctions();
|
||||
Functions\stubTranslationFunctions();
|
||||
Functions\when('post_type_exists')->alias(static fn (string $type): bool => in_array($type, ['page', 'post', 'attachment'], true));
|
||||
Functions\when('get_post_type_object')->alias(static fn (string $type): object => (object) [
|
||||
'labels' => (object) ['singular_name' => ['page' => 'Seite', 'post' => 'Beitrag'][$type] ?? $type],
|
||||
]);
|
||||
Functions\when('get_post')->alias(fn ($id) => $this->posts[(int) $id] ?? null);
|
||||
Functions\when('get_edit_post_link')->alias(
|
||||
static fn ($id, $context = 'display'): string => 'https://kunde.de/wp-admin/post.php?post=' . $id . '&action=edit'
|
||||
);
|
||||
Functions\when('admin_url')->alias(static fn (string $path = ''): string => 'https://kunde.de/wp-admin/' . $path);
|
||||
Functions\when('current_user_can')->justReturn(true);
|
||||
Functions\when('get_registered_nav_menus')->justReturn(['primary-menu' => 'Hauptmenü']);
|
||||
}
|
||||
|
||||
public function testAPageShowsItsTypeLabelAndLinkedTitle(): void
|
||||
{
|
||||
$this->posts[834] = new WP_Post(['ID' => 834, 'post_type' => 'page', 'post_status' => 'publish', 'post_title' => '[Test] <b>Kontakt</b>']);
|
||||
|
||||
$object = (new LogObjects())->describe('page', '834');
|
||||
|
||||
self::assertSame(
|
||||
'Seite: <a href="https://kunde.de/wp-admin/post.php?post=834&action=edit">[Test] <b>Kontakt</b></a>',
|
||||
$object['html']
|
||||
);
|
||||
self::assertSame('https://kunde.de/wp-admin/post.php?post=834&action=edit', $object['url']);
|
||||
}
|
||||
|
||||
public function testAnUntitledPostSaysSo(): void
|
||||
{
|
||||
$this->posts[5] = new WP_Post(['ID' => 5, 'post_type' => 'post', 'post_status' => 'draft', 'post_title' => '']);
|
||||
|
||||
self::assertStringContainsString('>(kein Titel)</a>', (new LogObjects())->describe('post', '5')['html']);
|
||||
}
|
||||
|
||||
public function testNoLinkWhenTheUserMayNotEditThePost(): void
|
||||
{
|
||||
Functions\when('get_edit_post_link')->justReturn(null);
|
||||
$this->posts[834] = new WP_Post(['ID' => 834, 'post_type' => 'page', 'post_status' => 'publish', 'post_title' => 'Kontakt']);
|
||||
|
||||
self::assertSame(['html' => 'Seite: Kontakt', 'url' => ''], (new LogObjects())->describe('page', '834'));
|
||||
}
|
||||
|
||||
public function testATrashedPostIsNamedButNotLinked(): void
|
||||
{
|
||||
$this->posts[783] = new WP_Post(['ID' => 783, 'post_type' => 'page', 'post_status' => 'trash', 'post_title' => 'LIT Smoke Seite']);
|
||||
|
||||
self::assertSame(
|
||||
['html' => 'Seite: LIT Smoke Seite (im Papierkorb)', 'url' => ''],
|
||||
(new LogObjects())->describe('page', '783')
|
||||
);
|
||||
}
|
||||
|
||||
public function testADeletedPostShowsItsIdAsDeleted(): void
|
||||
{
|
||||
self::assertSame(['html' => 'Seite: #834 (gelöscht)', 'url' => ''], (new LogObjects())->describe('page', '834'));
|
||||
}
|
||||
|
||||
public function testAnAttachmentIsAMediumLinkedToTheMediaEditScreen(): void
|
||||
{
|
||||
$this->posts[830] = new WP_Post(['ID' => 830, 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_title' => 'Massivholzmoebel']);
|
||||
|
||||
$object = (new LogObjects())->describe('attachment', '830');
|
||||
|
||||
self::assertSame(
|
||||
'Medium: <a href="https://kunde.de/wp-admin/post.php?post=830&action=edit">Massivholzmoebel</a>',
|
||||
$object['html']
|
||||
);
|
||||
self::assertSame('Medium: #799 (gelöscht)', (new LogObjects())->describe('attachment', '799')['html']);
|
||||
}
|
||||
|
||||
public function testANavMenuIsLinkedToTheMenusScreen(): void
|
||||
{
|
||||
Functions\when('get_terms')->justReturn([new WP_Term(['term_id' => 21, 'name' => 'Haupt<menü>', 'taxonomy' => 'nav_menu'])]);
|
||||
$objects = new LogObjects();
|
||||
$objects->prime([['object_type' => 'nav_menu', 'object_id' => '21'], ['object_type' => 'nav_menu', 'object_id' => '22']]);
|
||||
|
||||
self::assertSame(
|
||||
[
|
||||
'html' => 'Menü: <a href="https://kunde.de/wp-admin/nav-menus.php?action=edit&menu=21">Haupt<menü></a>',
|
||||
'url' => 'https://kunde.de/wp-admin/nav-menus.php?action=edit&menu=21',
|
||||
],
|
||||
$objects->describe('nav_menu', '21')
|
||||
);
|
||||
self::assertSame(['html' => 'Menü: #22 (gelöscht)', 'url' => ''], $objects->describe('nav_menu', '22'));
|
||||
}
|
||||
|
||||
public function testANavMenuIsNotLinkedWithoutTheMenuCapability(): void
|
||||
{
|
||||
Functions\when('current_user_can')->justReturn(false);
|
||||
Functions\when('get_terms')->justReturn([new WP_Term(['term_id' => 21, 'name' => 'Hauptmenü', 'taxonomy' => 'nav_menu'])]);
|
||||
$objects = new LogObjects();
|
||||
$objects->prime([['object_type' => 'nav_menu', 'object_id' => '21']]);
|
||||
|
||||
self::assertSame(['html' => 'Menü: Hauptmenü', 'url' => ''], $objects->describe('nav_menu', '21'));
|
||||
}
|
||||
|
||||
public function testAMenuLocationShowsItsRegisteredLabel(): void
|
||||
{
|
||||
self::assertSame(['html' => 'Menüposition: Hauptmenü', 'url' => ''], (new LogObjects())->describe('menu_location', 'primary-menu'));
|
||||
self::assertSame(
|
||||
['html' => 'Menüposition: <code>footer<x></code>', 'url' => ''],
|
||||
(new LogObjects())->describe('menu_location', 'footer<x>')
|
||||
);
|
||||
}
|
||||
|
||||
public function testOptionsShowHumanLabelsAndUnknownKeysAsCode(): void
|
||||
{
|
||||
self::assertSame(
|
||||
['html' => 'Einstellungen: Titel der Website, Untertitel', 'url' => ''],
|
||||
(new LogObjects())->describe('option', 'blogname,blogdescription')
|
||||
);
|
||||
self::assertSame(
|
||||
['html' => 'Einstellung: <code>foo<b></code>', 'url' => ''],
|
||||
(new LogObjects())->describe('option', 'foo<b>')
|
||||
);
|
||||
}
|
||||
|
||||
public function testAnUnknownObjectTypeIsShownAsCode(): void
|
||||
{
|
||||
self::assertSame(['html' => '<code>shop_thing 42</code>', 'url' => ''], (new LogObjects())->describe('shop_thing', '42'));
|
||||
self::assertSame(['html' => '–', 'url' => ''], (new LogObjects())->describe('', ''));
|
||||
}
|
||||
|
||||
public function testPrimeLoadsAllPostsAndMenusOfThePageInOneGoEach(): void
|
||||
{
|
||||
Functions\expect('_prime_post_caches')->once()->with([834, 830], false, false);
|
||||
Functions\expect('get_terms')->once()->with(['taxonomy' => 'nav_menu', 'include' => [21], 'hide_empty' => false])->andReturn([]);
|
||||
|
||||
(new LogObjects())->prime([
|
||||
['object_type' => 'page', 'object_id' => '834'],
|
||||
['object_type' => 'attachment', 'object_id' => '830'],
|
||||
['object_type' => 'page', 'object_id' => '834'],
|
||||
['object_type' => 'nav_menu', 'object_id' => '21'],
|
||||
['object_type' => 'option', 'object_id' => 'blogname'],
|
||||
['object_type' => 'page', 'object_id' => 'x'],
|
||||
]);
|
||||
}
|
||||
|
||||
public function testPrimeQueriesNothingWithoutPostsOrMenus(): void
|
||||
{
|
||||
Functions\expect('_prime_post_caches')->never();
|
||||
Functions\expect('get_terms')->never();
|
||||
|
||||
(new LogObjects())->prime([['object_type' => 'option', 'object_id' => 'blogname']]);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user