The nonce actions check_admin_referer() was asked for. */
private array $nonces = [];
protected function setUp(): void
{
parent::setUp();
$this->redirect = null;
$this->nonces = [];
Functions\stubEscapeFunctions();
Functions\stubTranslationFunctions();
Functions\when('current_user_can')->justReturn(true);
Functions\when('check_admin_referer')->alias(function (string $action): int {
$this->nonces[] = $action;
return 1;
});
Functions\when('get_current_user_id')->justReturn(1);
Functions\when('wp_safe_redirect')->alias(function (string $url): bool {
$this->redirect = $url;
return true;
});
Functions\when('admin_url')->alias(static fn (string $path = ''): string => 'https://k.de/wp-admin/' . $path);
Functions\when('add_query_arg')->alias(
static fn (array $args, string $url): string => $url . '?' . http_build_query($args)
);
Functions\when('rest_url')->alias(static fn (string $path): string => 'https://k.de/wp-json/' . $path);
Functions\when('wp_create_nonce')->alias(static fn (string $action): string => 'nonce-' . $action);
Functions\when('wp_nonce_field')->alias(static function (string $action): void {
echo '';
});
Functions\when('submit_button')->alias(
static function (
string $text,
string $type = 'primary',
string $name = 'submit',
bool $wrap = true,
array $attributes = []
): void {
$extra = '';
foreach ($attributes as $key => $value) {
$extra .= ' ' . $key . '="' . esc_attr($value) . '"'; // WordPress escapes the values itself
}
echo '';
}
);
Functions\when('get_option')->alias(static fn (string $name): string => $name === 'date_format' ? 'Y-m-d' : '');
Functions\when('date_i18n')->alias(static fn (string $format, int $time): string => gmdate($format, $time));
Functions\when('get_userdata')->justReturn(false);
Functions\when('sanitize_key')->alias(
static fn (string $key): string => strtolower((string) preg_replace('/[^a-z0-9_\-]/i', '', $key))
);
Functions\when('wp_unslash')->returnArg();
}
protected function tearDown(): void
{
unset($_POST['profile'], $_GET['tab']);
parent::tearDown();
}
/** A profile of the invented plugin "fake"; $matrix only for ReleasePerVersion. */
private static function profile(
Tier $tier = Tier::LockOnFailure,
?CompatibilityMatrix $matrix = null,
bool $active = true,
?string $version = '1.2.0',
array $tested = [],
?Closure $stepLabel = null
): Profile {
return new Profile(
id: 'fake',
label: 'Fake ',
tier: $tier,
capabilities: self::CAPS,
active: static fn (): bool => $active,
version: static fn (): ?string => $version,
matrix: $tier === Tier::ReleasePerVersion
? static fn (): CompatibilityMatrix => $matrix ?? self::fail('no matrix expected')
: null,
testedVersions: $tested,
stateKey: static fn (string $v): string => 'key' . $v,
stepLabel: $stepLabel
);
}
private static function matrix(ReleaseState $state, array $problems = []): CompatibilityMatrix
{
return CompatibilityMatrix::uniform(self::CAPS, $state, $problems, null);
}
private static function report(bool $green = true, string $version = '1.2.0', string $key = 'key1.2.0'): CheckReport
{
$steps = [
new CheckStep(CheckStep::MANIFEST, $green ? [] : ['Funktion x fehlt'], false, 'Manifest Label'),
new CheckStep('old', [], false),
];
return new CheckReport('r1', '2026-10-01T08:00:00+00:00', 1, $version, Plugin::VERSION, $key, $steps);
}
private function tab(
Profile $profile,
?CheckReport $report = null,
?ReleaseStore $releases = null,
?FakeChangeLog $log = null
): CompatibilityTab {
$reports = Mockery::mock(CheckReportStore::class);
$reports->allows('latest')->andReturn($report);
$store = $releases ?? Mockery::mock(ReleaseStore::class);
if ($releases === null) {
$store->allows('entries')->andReturn([]);
$store->allows('find')->andReturn(null);
}
return new CompatibilityTab(
new Compatibility([$profile]),
$log ?? FakeChangeLog::forbidden(),
new AdminPostAction(static function (): void {
}),
static fn (): CheckReportStore => $reports,
static fn (): ReleaseStore => $store
);
}
private static function html(CompatibilityTab $tab): string
{
ob_start();
$tab->render();
return (string) ob_get_clean();
}
/** One source for the ability name of the release and withdraw entries (SuperCacheListener reads it too). */
public function testTheReleaseAbilityNameHasOneSource(): void
{
self::assertSame('lit/divi-release', CompatibilityTab::releaseAbility('divi'));
self::assertTrue(CompatibilityTab::isReleaseAbility(CompatibilityTab::releaseAbility('wpsc')));
foreach (['lit/content-update', 'lit/-release', 'lit/release', 'lit/divi-releases'] as $other) {
self::assertFalse(CompatibilityTab::isReleaseAbility($other), $other);
}
}
public function testItIsTheTabCompatibility(): void
{
$tab = $this->tab(self::profile());
self::assertInstanceOf(AdminTab::class, $tab);
self::assertSame('compatibility', $tab->slug());
self::assertSame('Compatibility', $tab->label());
}
public function testAnInactiveProfileIsOneLineAndNeverAsksForTheMatrix(): void
{
Actions\expectDone('lit_mcp_compat_profile_extra')->never();
// ReleasePerVersion with a matrix closure that fails the test when called.
$html = self::html($this->tab(self::profile(Tier::ReleasePerVersion, null, active: false)));
self::assertStringContainsString('nicht installiert oder nicht aktiv', $html);
self::assertStringContainsString('Fake <Plugin>', $html);
self::assertStringNotContainsString('