refactor(divi): DiviTab geht im Reiter „Compatibility“ auf
Der eigene Divi-Reiter entfällt. DiviAdminExtras behält Admin-Hinweis, Orphan-Bereinigung vor der Ausgabe (lit_mcp_compat_profile_load), Hinweis zum nächsten Schritt und zur privaten Testseite (lit_mcp_compat_profile_intro), Zustandstexte mit Quelle (lit_mcp_compat_explanation), Automatik-Notiz und Visual-Builder-Hinweis (lit_mcp_compat_profile_extra) sowie die Spalten Quelle, Probleme und Betroffene Tools über die neue, gegen fehlerhafte Callbacks abgesicherte Erweiterung lit_mcp_compat_capability_columns. Texte, Doku, CSS (lit-mcp-compat-*) und Smoke auf den neuen Reiter umgestellt, tote divi_*-Hinweise entfernt. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
85f4138b36
commit
b40b81f585
@@ -5,6 +5,7 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Tests\Unit\Admin;
|
||||
|
||||
use Brain\Monkey\Actions;
|
||||
use Brain\Monkey\Filters;
|
||||
use Brain\Monkey\Functions;
|
||||
use Closure;
|
||||
use LIT\MCP\Admin\AdminPostAction;
|
||||
@@ -312,6 +313,45 @@ final class CompatibilityTabTest extends TestCase
|
||||
);
|
||||
}
|
||||
|
||||
public function testAProfileCanAddColumnsToTheCapabilityTableAndTheTabEscapesThem(): void
|
||||
{
|
||||
Filters\expectApplied(CompatibilityTab::COLUMNS_FILTER)->once()->with([], 'fake')->andReturn([
|
||||
[
|
||||
'heading' => 'Quelle <b>',
|
||||
'cell' => static fn (FakeCapability $c, CompatibilityMatrix $m): array => $c === FakeCapability::Read
|
||||
? ['<i>a</i>']
|
||||
: [],
|
||||
],
|
||||
[
|
||||
'heading' => 'Tools',
|
||||
'cell' => static fn (FakeCapability $c, CompatibilityMatrix $m): array => ['tool-' . $c->id()],
|
||||
'code' => true,
|
||||
],
|
||||
]);
|
||||
|
||||
$html = self::html($this->tab(self::profile()));
|
||||
|
||||
self::assertStringContainsString('<th scope="col">Quelle <b></th>', $html);
|
||||
self::assertStringContainsString('<td data-label="Quelle <b>"><ul><li><i>a</i></li></ul></td>', $html);
|
||||
self::assertStringContainsString('<td data-label="Quelle <b>">–</td>', $html);
|
||||
self::assertStringContainsString('<li><code>tool-' . FakeCapability::Read->id() . '</code></li>', $html);
|
||||
self::assertStringNotContainsString('<i>a</i>', $html);
|
||||
}
|
||||
|
||||
public function testTheCapabilityTableIsOpenUnlessReleasedAndFolded(): void
|
||||
{
|
||||
$released = self::html($this->tab(
|
||||
self::profile(Tier::ReleasePerVersion, self::matrix(ReleaseState::Released))
|
||||
));
|
||||
$untested = self::html($this->tab(
|
||||
self::profile(Tier::ReleasePerVersion, self::matrix(ReleaseState::Untested))
|
||||
));
|
||||
|
||||
self::assertStringContainsString('<details class="lit-mcp-compat-details">', $released);
|
||||
self::assertStringContainsString('<details class="lit-mcp-compat-details" open>', $untested);
|
||||
self::assertStringContainsString('lit-mcp-compat-features', $released);
|
||||
}
|
||||
|
||||
public function testEveryButtonNamesItsProfileForAssistiveTechnology(): void
|
||||
{
|
||||
$profile = self::profile(Tier::ReleasePerVersion, self::matrix(ReleaseState::Untested));
|
||||
@@ -430,6 +470,120 @@ final class CompatibilityTabTest extends TestCase
|
||||
self::html($this->tab(self::profile(), self::report()));
|
||||
}
|
||||
|
||||
public function testTheProfileIntroRunsBeforeTheCapabilityTableAndTheExtraAfterIt(): void
|
||||
{
|
||||
Actions\expectDone(CompatibilityTab::INTRO_ACTION)->once()->with('fake')->whenHappen(static function (): void {
|
||||
echo '[INTRO]';
|
||||
});
|
||||
Actions\expectDone(CompatibilityTab::EXTRA_ACTION)->once()->with('fake')->whenHappen(static function (): void {
|
||||
echo '[EXTRA]';
|
||||
});
|
||||
|
||||
$html = self::html($this->tab(self::profile(), self::report()));
|
||||
|
||||
self::assertLessThan(strpos($html, 'lit-mcp-compat-details'), strpos($html, '[INTRO]'));
|
||||
self::assertGreaterThan(strpos($html, '</table></details>'), strpos($html, '[EXTRA]'));
|
||||
}
|
||||
|
||||
public function testAnInactiveProfileFiresNeitherIntroNorExtra(): void
|
||||
{
|
||||
Actions\expectDone(CompatibilityTab::INTRO_ACTION)->never();
|
||||
Actions\expectDone(CompatibilityTab::EXTRA_ACTION)->never();
|
||||
|
||||
self::html($this->tab(self::profile(active: false)));
|
||||
}
|
||||
|
||||
public function testAProfileCanReplaceTheGenericExplanationAndTheTabEscapesIt(): void
|
||||
{
|
||||
Filters\expectApplied(CompatibilityTab::EXPLANATION_FILTER)
|
||||
->once()
|
||||
->with('Alle Fähigkeiten sind nutzbar.', 'fake', ReleaseState::Released)
|
||||
->andReturn('Eigener <b>Text</b>');
|
||||
|
||||
$html = self::html($this->tab(self::profile()));
|
||||
|
||||
self::assertStringContainsString('Eigener <b>Text</b>', $html);
|
||||
self::assertStringNotContainsString('Alle Fähigkeiten sind nutzbar.', $html);
|
||||
}
|
||||
|
||||
public function testANonStringExplanationFromTheFilterKeepsTheGenericText(): void
|
||||
{
|
||||
Filters\expectApplied(CompatibilityTab::EXPLANATION_FILTER)->once()->andReturn(['x']);
|
||||
|
||||
self::assertStringContainsString('Alle Fähigkeiten sind nutzbar.', self::html($this->tab(self::profile())));
|
||||
}
|
||||
|
||||
/** @return array{heading: string, cell: Closure} */
|
||||
private static function column(string $heading, Closure $cell): array
|
||||
{
|
||||
return ['heading' => $heading, 'cell' => $cell];
|
||||
}
|
||||
|
||||
public function testInvalidColumnsAreDroppedAndTheRestIsReindexed(): void
|
||||
{
|
||||
Filters\expectApplied(CompatibilityTab::COLUMNS_FILTER)->once()->andReturn([
|
||||
3 => self::column('Gut', static fn (): array => ['ok']),
|
||||
5 => ['heading' => 42, 'cell' => static fn (): array => ['x']],
|
||||
6 => ['heading' => 'Ohne Closure', 'cell' => 'strlen'],
|
||||
7 => 'kaputt',
|
||||
9 => ['heading' => 'Ohne Zelle'],
|
||||
]);
|
||||
|
||||
$html = self::html($this->tab(self::profile()));
|
||||
|
||||
self::assertStringContainsString('<th scope="col">Gut</th>', $html);
|
||||
self::assertSame(3, substr_count($html, '<th scope="col">'));
|
||||
self::assertStringContainsString('<td data-label="Gut"><ul><li>ok</li></ul></td>', $html);
|
||||
}
|
||||
|
||||
public function testAThrowingCellShowsADashLogsTheFailureAndKeepsTheTableClosed(): void
|
||||
{
|
||||
Filters\expectApplied(CompatibilityTab::COLUMNS_FILTER)->once()->andReturn([
|
||||
self::column('Kaputt', static function (): array {
|
||||
throw new \RuntimeException('Zelle <kaputt>');
|
||||
}),
|
||||
self::column('Heil', static fn (): array => ['ok']),
|
||||
]);
|
||||
$log = (string) tempnam(sys_get_temp_dir(), 'lit-mcp-log');
|
||||
$previous = ini_set('error_log', $log);
|
||||
|
||||
try {
|
||||
$html = self::html($this->tab(self::profile()));
|
||||
} finally {
|
||||
ini_set('error_log', (string) $previous);
|
||||
}
|
||||
$logged = (string) file_get_contents($log);
|
||||
unlink($log);
|
||||
|
||||
self::assertStringContainsString('<td data-label="Kaputt">–</td>', $html);
|
||||
self::assertStringContainsString('<td data-label="Heil"><ul><li>ok</li></ul></td>', $html);
|
||||
self::assertStringContainsString('</tbody></table></details>', $html);
|
||||
self::assertStringContainsString('fake capability column "Kaputt" failed', $logged);
|
||||
self::assertStringContainsString('Zelle <kaputt>', $logged);
|
||||
}
|
||||
|
||||
public function testACellThatReturnsNoListShowsADash(): void
|
||||
{
|
||||
Filters\expectApplied(CompatibilityTab::COLUMNS_FILTER)->once()->andReturn([
|
||||
self::column('Text', static fn (): string => 'kein Array'),
|
||||
self::column('Karte', static fn (): array => ['a' => 'x']),
|
||||
self::column('Zahlen', static fn (): array => [1, 2]),
|
||||
]);
|
||||
|
||||
$html = self::html($this->tab(self::profile()));
|
||||
|
||||
self::assertStringContainsString('<td data-label="Text">–</td>', $html);
|
||||
self::assertStringContainsString('<td data-label="Karte">–</td>', $html);
|
||||
self::assertStringContainsString('<td data-label="Zahlen">–</td>', $html);
|
||||
}
|
||||
|
||||
public function testANonArrayColumnsFilterResultMeansNoExtraColumns(): void
|
||||
{
|
||||
Filters\expectApplied(CompatibilityTab::COLUMNS_FILTER)->once()->andReturn('kaputt');
|
||||
|
||||
self::assertSame(2, substr_count(self::html($this->tab(self::profile())), '<th scope="col">'));
|
||||
}
|
||||
|
||||
public function testEverythingFromThirdPartiesIsEscaped(): void
|
||||
{
|
||||
$profile = self::profile(
|
||||
|
||||
@@ -177,31 +177,6 @@ final class SettingsPageTest extends TestCase
|
||||
$page->register();
|
||||
}
|
||||
|
||||
/** @dataProvider diviNotices */
|
||||
public function testDiviNoticesAreShown(string $key, string $class, string $text): void
|
||||
{
|
||||
Functions\when('current_user_can')->justReturn(true);
|
||||
$_GET['lit_mcp_notice'] = $key;
|
||||
|
||||
ob_start();
|
||||
(new SettingsPage([$this->tab('verbindung', 'Verbindung')]))->render();
|
||||
$html = (string) ob_get_clean();
|
||||
|
||||
self::assertStringContainsString('notice-' . $class, $html);
|
||||
self::assertStringContainsString($text, $html);
|
||||
}
|
||||
|
||||
/** @return list<list<string>> */
|
||||
public static function diviNotices(): array
|
||||
{
|
||||
return [
|
||||
['divi_released', 'success', 'für LIT MCP freigegeben'],
|
||||
['divi_release_refused', 'error', 'Prüfung erneut ausführen'],
|
||||
['divi_withdrawn', 'success', 'Freigabe zurückgezogen'],
|
||||
['divi_withdraw_failed', 'error', 'keine Freigabe'],
|
||||
];
|
||||
}
|
||||
|
||||
/** @dataProvider compatNotices */
|
||||
public function testCompatNoticesNameTheKnownProfile(string $key, string $class, string $text): void
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user