feat: lit/page-render-summary – rendered content as structured excerpt
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,67 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Render;
|
||||
|
||||
use Brain\Monkey\Functions;
|
||||
use LIT\MCP\Integrations\WordPress\Render\PageRenderer;
|
||||
use LIT\MCP\Integrations\WordPress\Render\PageRenderSummaryAbility;
|
||||
use LIT\MCP\Integrations\WordPress\Render\RenderSummary;
|
||||
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
|
||||
use LIT\MCP\Tests\Unit\TestCase;
|
||||
use Mockery;
|
||||
use WP_Post;
|
||||
|
||||
final class PageRenderSummaryAbilityTest extends TestCase
|
||||
{
|
||||
private const HTML = '<div class="et_pb_section"><h1>Willkommen</h1><h3>Leistungen</h3>'
|
||||
. '<p>Seit 1987 <a href="/kontakt">in Köln</a>.</p><ul><li>Beratung</li></ul>'
|
||||
. '<img src="https://k.test/a.jpg" alt="Team"><img src="https://k.test/b.jpg"><a href="#">Mehr</a></div>';
|
||||
|
||||
public function testSummarizesHtml(): void
|
||||
{
|
||||
$summary = RenderSummary::fromHtml(self::HTML);
|
||||
|
||||
self::assertSame([['level' => 1, 'text' => 'Willkommen'], ['level' => 3, 'text' => 'Leistungen']], $summary['headings']);
|
||||
self::assertSame(['Seit 1987 in Köln.', 'Beratung'], $summary['texts']);
|
||||
self::assertSame(['text' => 'in Köln', 'href' => '/kontakt'], $summary['links'][0]);
|
||||
self::assertSame(
|
||||
[['src' => 'https://k.test/a.jpg', 'alt' => 'Team'], ['src' => 'https://k.test/b.jpg', 'alt' => null]],
|
||||
$summary['images']
|
||||
);
|
||||
self::assertSame(
|
||||
[
|
||||
'Sprung in der Überschriftenhierarchie: h1 → h3 ("Leistungen").',
|
||||
'Bild ohne Alt-Text: https://k.test/b.jpg',
|
||||
'Link ohne Ziel: "Mehr".',
|
||||
],
|
||||
$summary['warnings']
|
||||
);
|
||||
self::assertSame([], RenderSummary::fromHtml('')['headings']);
|
||||
self::assertCount(2, RenderSummary::fromHtml(str_repeat('<p>x</p>', 5), 2)['texts']);
|
||||
}
|
||||
|
||||
public function testRendersTheEntryAndAddsNotes(): void
|
||||
{
|
||||
$post = new WP_Post(['ID' => 7, 'post_type' => 'page', 'post_password' => 'geheim']);
|
||||
Functions\when('get_post')->justReturn($post);
|
||||
Functions\when('current_user_can')->justReturn(true);
|
||||
Functions\when('get_permalink')->justReturn('https://k.test/start/');
|
||||
$schema = Mockery::mock(ContentSchema::class);
|
||||
$schema->allows('isManaged')->andReturn(true);
|
||||
$renderer = Mockery::mock(PageRenderer::class);
|
||||
$renderer->expects('render')->with($post)->andReturn(self::HTML);
|
||||
$ability = new PageRenderSummaryAbility($schema, $renderer);
|
||||
|
||||
self::assertTrue($ability->permission(['post_id' => 7]));
|
||||
$result = $ability->execute(['post_id' => 7]);
|
||||
|
||||
self::assertSame(7, $result['post_id']);
|
||||
self::assertSame('https://k.test/start/', $result['url']);
|
||||
self::assertCount(2, $result['headings']);
|
||||
self::assertCount(2, $result['notes']);
|
||||
self::assertTrue($ability->isDirect());
|
||||
self::assertTrue($ability->annotations()['readonly']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\WordPress\Render;
|
||||
|
||||
use Brain\Monkey\Filters;
|
||||
use Brain\Monkey\Functions;
|
||||
use LIT\MCP\Integrations\WordPress\Render\PageRenderer;
|
||||
use LIT\MCP\Tests\Unit\TestCase;
|
||||
use RuntimeException;
|
||||
use WP_Post;
|
||||
|
||||
final class PageRendererTest extends TestCase
|
||||
{
|
||||
protected function tearDown(): void
|
||||
{
|
||||
unset($GLOBALS['post']);
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
public function testRendersTheContentFilterAndRestoresTheGlobalPost(): void
|
||||
{
|
||||
$previous = new WP_Post(['ID' => 1]);
|
||||
$GLOBALS['post'] = $previous;
|
||||
$post = new WP_Post(['ID' => 7, 'post_content' => '<!-- wp:divi/section /-->']);
|
||||
$setup = [];
|
||||
Functions\when('setup_postdata')->alias(static function (WP_Post $p) use (&$setup): bool {
|
||||
$setup[] = $p->ID;
|
||||
|
||||
return true;
|
||||
});
|
||||
Filters\expectApplied('the_content')->once()->with('<!-- wp:divi/section /-->')->andReturnUsing(
|
||||
static function () use ($post): string {
|
||||
echo '<p>echoed</p>';
|
||||
|
||||
return $GLOBALS['post'] === $post ? '<div class="et_pb_section"></div>' : 'wrong post';
|
||||
}
|
||||
);
|
||||
|
||||
$html = (new PageRenderer())->render($post);
|
||||
|
||||
self::assertSame('<p>echoed</p><div class="et_pb_section"></div>', $html);
|
||||
self::assertSame($previous, $GLOBALS['post']);
|
||||
self::assertSame([7, 1], $setup);
|
||||
}
|
||||
|
||||
public function testRestoresEvenWhenRenderingThrows(): void
|
||||
{
|
||||
Functions\when('setup_postdata')->justReturn(true);
|
||||
Functions\expect('wp_reset_postdata')->once();
|
||||
Filters\expectApplied('the_content')->once()->andReturnUsing(static function (): string {
|
||||
throw new RuntimeException('module crashed');
|
||||
});
|
||||
|
||||
try {
|
||||
(new PageRenderer())->render(new WP_Post(['ID' => 7]));
|
||||
self::fail('exception expected');
|
||||
} catch (RuntimeException) {
|
||||
self::assertArrayHasKey('post', $GLOBALS);
|
||||
self::assertNull($GLOBALS['post']);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user