alias(static fn (string $value): string => 'kses(' . $value . ')');
Functions\when('esc_url_raw')->alias(static fn (string $value): string => 'url(' . $value . ')');
}
public function testEditorsGetKsesForRichTextAndCode(): void
{
Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap !== 'unfiltered_html');
$sanitizer = new WpValueSanitizer();
self::assertSame('kses()', $sanitizer->html(''));
self::assertSame('kses()', $sanitizer->code(''));
self::assertFalse($sanitizer->allowsUnfilteredHtml());
}
public function testAdministratorsKeepRichTextAndCodeUnfiltered(): void
{
// Ruling S4: html() depends on unfiltered_html like code() – admin embeds survive updates.
Functions\when('current_user_can')->alias(static fn (string $cap): bool => $cap === 'unfiltered_html');
$sanitizer = new WpValueSanitizer();
self::assertSame('', $sanitizer->html(''));
self::assertSame('', $sanitizer->code(''));
self::assertTrue($sanitizer->allowsUnfilteredHtml());
}
public function testTextAndUrlDoNotDependOnTheRole(): void
{
Functions\when('current_user_can')->justReturn(true);
$sanitizer = new WpValueSanitizer();
self::assertSame('Hallo', $sanitizer->text('Hallo'));
self::assertSame('url(https://x.test/a b)', $sanitizer->url(' https://x.test/a b '));
}
public function testCanReadPostAsksWordPress(): void
{
Functions\when('current_user_can')->alias(static fn (string $cap, int $id = 0): bool => $cap === 'read_post' && $id === 12);
$sanitizer = new WpValueSanitizer();
self::assertTrue($sanitizer->canReadPost(12));
self::assertFalse($sanitizer->canReadPost(13));
}
/** Ruling P10: the full form uses the {{link:}} targets – attachments, trash and unreadable entries are none. */
public function testLinkTargetTypeFollowsTheLinkTargets(): void
{
Functions\when('get_post')->alias(static fn (int $id): ?WP_Post => match ($id) {
12 => new WP_Post(['ID' => 12, 'post_type' => 'page', 'post_status' => 'draft']),
13 => new WP_Post(['ID' => 13, 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_parent' => 12]),
14 => new WP_Post(['ID' => 14, 'post_type' => 'page', 'post_status' => 'trash']),
16 => new WP_Post(['ID' => 16, 'post_type' => 'page', 'post_status' => 'private']),
17 => new WP_Post(['ID' => 17, 'post_type' => 'attachment', 'post_status' => 'inherit', 'post_parent' => 0]),
18 => new WP_Post(['ID' => 18, 'post_type' => 'attachment', 'post_status' => 'trash', 'post_parent' => 0]),
19 => new WP_Post(['ID' => 19, 'post_type' => 'attachment', 'post_status' => 'private', 'post_parent' => 0]),
20 => new WP_Post(['ID' => 20, 'post_type' => 'post', 'post_status' => 'publish']),
default => null,
});
Functions\when('current_user_can')->alias(static fn (string $cap, int $id): bool => $id !== 16);
$sanitizer = new WpValueSanitizer(new WpLinkTargets(static fn (string $type): bool => $type !== 'wp_block'));
self::assertSame('page', $sanitizer->linkTargetType(12));
self::assertSame('post', $sanitizer->linkTargetType(20));
self::assertNull($sanitizer->linkTargetType(13), 'attached attachment');
self::assertNull($sanitizer->linkTargetType(14), 'trashed');
self::assertNull($sanitizer->linkTargetType(15), 'missing');
self::assertNull($sanitizer->linkTargetType(16), 'unreadable');
self::assertNull($sanitizer->linkTargetType(17), 'unattached attachment');
self::assertNull($sanitizer->linkTargetType(18), 'trashed attachment');
self::assertNull($sanitizer->linkTargetType(19), 'attachment with an own status');
}
}