feat: lit-site-status, Admin-Tab und Admin-Hinweis zeigen Divi-Fähigkeiten mit Zustand, Quelle, Problemen und betroffenen Werkzeugen

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Jan-Niclas Loosen
2026-10-18 22:29:38 +02:00
co-authored by Claude Sonnet 5.5
parent b92545a8fe
commit 2aef6e1154
13 changed files with 831 additions and 31 deletions
@@ -8,6 +8,13 @@ use Brain\Monkey\Functions;
use LIT\MCP\Integrations\Core\EnvironmentComponent;
use LIT\MCP\Integrations\Divi\DiviCompatibility;
use LIT\MCP\Integrations\Divi\DiviEnvironmentComponent;
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
use LIT\MCP\Integrations\Divi\Release\FeatureOverview;
use LIT\MCP\Integrations\Divi\Release\ReleaseGate;
use LIT\MCP\Integrations\Divi\Release\ReleaseState;
use LIT\MCP\Integrations\Divi\Release\ReleaseStatus;
use LIT\MCP\Tests\Support\FeatureFakeTool;
use LIT\MCP\Tests\Support\MatrixFixtures;
use LIT\MCP\Tests\Unit\TestCase;
use Mockery;
@@ -61,8 +68,78 @@ final class DiviEnvironmentComponentTest extends TestCase
))->describe();
self::assertSame(
['id' => 'divi', 'active' => false, 'version' => null, 'problems' => [], 'release' => null],
['id' => 'divi', 'active' => false, 'version' => null, 'problems' => [], 'release' => null,
'features' => []],
$described
);
}
public function testListsEveryFeatureWithStateSourceProblemsAndTools(): void
{
$this->theme('Divi', '5.3.0');
$matrix = MatrixFixtures::with(
DiviFeature::PageWrite,
ReleaseState::Incompatible,
'auto',
'Seiten: Signatur von et_pb_x() geändert'
);
ReleaseGate::useResolver(static fn (): ReleaseStatus => new ReleaseStatus(
ReleaseState::Incompatible,
'5.3.0',
null,
[],
null,
matrix: $matrix
));
$overview = new FeatureOverview(static fn (): array => [
new FeatureFakeTool('lit/divi-outline', true, [DiviFeature::PageRead]),
new FeatureFakeTool('lit/divi-insert', true, [DiviFeature::PageRead, DiviFeature::PageWrite]),
]);
$component = (new DiviEnvironmentComponent(new DiviCompatibility([]), $overview))->describe();
$features = array_column($component['features'], null, 'id');
self::assertSame('divi', $component['id']);
self::assertCount(count(DiviFeature::cases()), $features);
self::assertSame('incompatible', $features['page-write']['state']);
self::assertNull($features['page-write']['source']);
self::assertSame(['Seiten: Signatur von et_pb_x() geändert'], $features['page-write']['problems']);
self::assertSame(['lit-divi-insert'], $features['page-write']['tools']);
self::assertSame('Seiten schreiben', $features['page-write']['label']);
self::assertSame('released', $features['page-read']['state']);
self::assertSame('auto', $features['page-read']['source']);
self::assertSame(['lit-divi-outline', 'lit-divi-insert'], $features['page-read']['tools']);
self::assertSame('incompatible', $component['release']['state']);
}
public function testTheMatrixComesFromTheGateAndIsNotComputedAgain(): void
{
$this->theme('Divi', '5.3.0');
$calls = 0;
ReleaseGate::useResolver(static function () use (&$calls): ReleaseStatus {
++$calls;
return ReleaseStatus::released('5.3.0');
});
$component = new DiviEnvironmentComponent(new DiviCompatibility([]));
$component->describe();
$component->describe();
self::assertSame(1, $calls);
}
public function testWithoutDiviThereAreNoFeaturesAndNoGateCall(): void
{
$this->theme('twentytwentyfive', '1.0');
ReleaseGate::useResolver(static function (): ReleaseStatus {
self::fail('the gate must not be asked without Divi');
});
$component = (new DiviEnvironmentComponent(new DiviCompatibility([])))->describe();
self::assertFalse($component['active']);
self::assertSame([], $component['features']);
self::assertNull($component['release']);
}
}
@@ -20,6 +20,7 @@ use LIT\MCP\Integrations\Divi\DiviIntegration;
use LIT\MCP\Integrations\Divi\DiviLogoUsage;
use LIT\MCP\Integrations\Divi\Pages\DiviContentComposer;
use LIT\MCP\Integrations\Divi\Release\DiviCheckRoute;
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
use LIT\MCP\Integrations\Divi\Release\ReleaseResolver;
use LIT\MCP\Integrations\Integration;
use LIT\MCP\Integrations\IntegrationRegistry;
@@ -27,6 +28,7 @@ use LIT\MCP\Integrations\Media\MediaUsageSource;
use LIT\MCP\Integrations\WordPress\BuilderDetector;
use LIT\MCP\Integrations\WordPress\Schema\ContentSchema;
use LIT\MCP\Tests\Support\FakeChangeLog;
use LIT\MCP\Tests\Support\FeatureFakeTool;
use LIT\MCP\Tests\Support\LibraryFixtures;
use LIT\MCP\Tests\Unit\TestCase;
use Mockery;
@@ -327,4 +329,28 @@ final class DiviIntegrationTest extends TestCase
self::assertSame($base, $all[0]);
self::assertInstanceOf(DiviTab::class, $all[1]);
}
public function testTheFeatureOverviewFindsTheToolsOfAllIntegrations(): void
{
$integration = new DiviIntegration(
Mockery::mock(ContentSchema::class),
FakeChangeLog::forbidden(),
allAbilities: static fn (): array => [
new FeatureFakeTool('lit/divi-insert', true, [DiviFeature::PageWrite]),
new FeatureFakeTool('lit/library-insert', true, [DiviFeature::PageWrite, DiviFeature::LibraryRead]),
]
);
self::assertSame(
['page-write' => ['lit-divi-insert', 'lit-library-insert'], 'library-read' => ['lit-library-insert']],
$integration->featureOverview()->toolsByFeature()
);
}
public function testWithoutAnAbilityListTheOverviewKnowsNoTools(): void
{
$integration = new DiviIntegration(Mockery::mock(ContentSchema::class), FakeChangeLog::forbidden());
self::assertSame([], $integration->featureOverview()->toolsByFeature());
}
}
@@ -0,0 +1,76 @@
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Release;
use LIT\MCP\Integrations\Divi\Release\DiviFeature;
use LIT\MCP\Integrations\Divi\Release\FeatureOverview;
use LIT\MCP\Integrations\Divi\Release\ReleaseState;
use LIT\MCP\Tests\Support\FeatureFakeTool;
use LIT\MCP\Tests\Support\MatrixFixtures;
use PHPUnit\Framework\TestCase;
final class FeatureOverviewTest extends TestCase
{
private static function overview(): FeatureOverview
{
return new FeatureOverview(static fn (): array => [
new FeatureFakeTool('lit/divi-outline', true, [DiviFeature::PageRead]),
new FeatureFakeTool('lit/divi-insert', true, [DiviFeature::PageRead, DiviFeature::PageWrite]),
new FeatureFakeTool('lit/divi-presets-assign', false, [DiviFeature::PresetWrite]),
new FeatureFakeTool('lit/content-create', true, []),
]);
}
public function testToolsAreNamedAsTheClientSeesThem(): void
{
$tools = self::overview()->toolsByFeature();
self::assertSame(['lit-divi-outline', 'lit-divi-insert'], $tools['page-read']);
self::assertSame(['lit-divi-insert'], $tools['page-write']);
self::assertSame(['lit/divi-presets-assign'], $tools['preset-write']);
}
public function testAToolWithoutFeaturesIsLeftOut(): void
{
$overview = new FeatureOverview(static fn (): array => [new FeatureFakeTool('lit/content-create', true, [])]);
self::assertSame([], $overview->toolsByFeature());
}
public function testRowsCarryTheMatrixAndTheToolsOfEachFeature(): void
{
$matrix = MatrixFixtures::with(
DiviFeature::PageWrite,
ReleaseState::Incompatible,
'code',
'Seiten: et_pb_x() fehlt'
);
$rows = array_column(self::overview()->rows($matrix), null, 'id');
self::assertCount(count(DiviFeature::cases()), $rows);
self::assertSame(
[
'id' => 'page-write',
'label' => 'Seiten schreiben',
'state' => 'incompatible',
'problems' => ['Seiten: et_pb_x() fehlt'],
'source' => null,
'tools' => ['lit-divi-insert'],
],
$rows['page-write']
);
self::assertSame('released', $rows['page-read']['state']);
self::assertSame('code', $rows['page-read']['source']);
self::assertSame([], $rows['catalog']['tools']);
}
public function testWithoutToolsEveryRowHasAnEmptyToolList(): void
{
foreach (FeatureOverview::withoutTools()->rows(MatrixFixtures::released()) as $row) {
self::assertSame([], $row['tools']);
}
}
}