fix: Theme Builder access needs edit_theme_options (ruling Q15)

Divi's Theme Builder page is gated by edit_theme_options; without it every
TB ID answers like a missing one. Docblocks (Q16): the wp_die guard cannot
restore WP_Hook nesting after a third-party wp_die inside a hook, and
canEditTemplate expects IDs from Divi's list.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-09-27 15:24:32 +02:00
co-authored by Claude Opus 5.5
parent d04a508751
commit 4ff1c1e2a5
3 changed files with 34 additions and 2 deletions
@@ -5,6 +5,7 @@ declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\ThemeBuilder;
use Brain\Monkey\Functions;
use LIT\MCP\Integrations\WordPress\PostGuard;
use LIT\MCP\Tests\Support\LibraryFixtures;
use LIT\MCP\Tests\Support\ThemeBuilderFixtures;
use LIT\MCP\Tests\Unit\TestCase;
@@ -31,6 +32,25 @@ final class ThemeBuilderAccessTest extends TestCase
self::assertToolError(ThemeBuilderFixtures::access()->canUse(), 'forbidden');
}
/** Ruling Q15: Divi's Theme Builder page also needs edit_theme_options (theme-builder.php:2059-2063). */
public function testWithoutEditThemeOptionsTheThemeBuilderIsHidden(): void
{
LibraryFixtures::given([ThemeBuilderFixtures::layout(1062), ThemeBuilderFixtures::templatePost()], [
'edit_theme_options',
]);
$access = ThemeBuilderFixtures::access();
self::assertToolError($access->canUse(), 'forbidden');
self::assertStringContainsString('edit_theme_options', self::clientError($access->canUse())['message']);
self::assertFalse($access->visible());
self::assertFalse($access->canEditTemplate(ThemeBuilderFixtures::TEMPLATE));
self::assertToolError($access->canCreate(), 'forbidden');
$hidden = $access->layout(1062, false);
self::assertSame(self::clientError($hidden), self::clientError(PostGuard::notFound(1062)));
$template = self::assertToolError($access->template(ThemeBuilderFixtures::TEMPLATE, false), 'not_found');
self::assertSame('Theme-Builder-Vorlage 1066 gibt es nicht.', $template['message'], 'like a missing ID');
}
/** P12 pattern: without access a layout ID answers exactly like a missing one. */
public function testLayoutsAreHiddenLikeMissingIdsWithoutAccess(): void
{