Files
WP-LIT-MCP/tests/Unit/Integrations/Divi/Design/DiviPresetsDeleteAbilityTest.php
T

193 lines
7.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Design;
use Brain\Monkey\Functions;
use LIT\MCP\Integrations\Divi\Design\DesignChange;
use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter;
use LIT\MCP\Integrations\Divi\Design\DiviPresetsDeleteAbility;
use LIT\MCP\Integrations\Divi\Design\PresetUsageQuery;
use LIT\MCP\Integrations\Divi\Design\SnapshotStore;
use LIT\MCP\Integrations\Divi\Library\GlobalUsages;
use LIT\MCP\Integrations\Divi\Library\UsageReport;
use LIT\MCP\Tests\Support\FakeChangeLog;
use LIT\MCP\Tests\Support\LockTable;
use LIT\MCP\Tests\Unit\TestCase;
use Mockery;
use ReflectionProperty;
final class DiviPresetsDeleteAbilityTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
// DesignLock (final review I-5): the lock is always free here.
LockTable::install();
}
private function ability(
UsageReport $report,
?DesignSystemWriter $writer = null,
?SnapshotStore $snapshots = null,
bool $unfiltered = true,
array $stored = []
): DiviPresetsDeleteAbility {
Functions\when('current_user_can')->justReturn(true);
if ($writer === null) {
$writer = Mockery::mock(DesignSystemWriter::class);
$writer->allows('refresh');
$writer->allows('presets')->andReturn($stored === [] ? PresetBookTest::stored() : $stored);
$writer->allows('canUseVisualBuilder')->andReturn(true);
}
$usages = Mockery::mock(GlobalUsages::class);
$usages->allows('reportFor')->andReturn($report);
return new DiviPresetsDeleteAbility(
$writer,
new DesignChange($snapshots ?? Mockery::mock(SnapshotStore::class), new FakeChangeLog(3)),
$usages,
static fn (): bool => $unfiltered
);
}
private static function unused(): UsageReport
{
return new UsageReport(0, [], 0, 0, [], 0, false);
}
public function testUnusedPresetIsDeletedAfterConfirm(): void
{
$writer = Mockery::mock(DesignSystemWriter::class);
$writer->allows('refresh');
$writer->allows('presets')->andReturn(PresetBookTest::stored());
$writer->allows('canUseVisualBuilder')->andReturn(true);
$writer->expects('savePresets')
->with(Mockery::on(static function (array $rest): bool {
$ids = array_column($rest['module'][0]['items'], 'id');
return $ids === ['aaaaaaaaaa', 'cccccccccc'];
}), true)
->andReturnUsing(static function (): array {
$stored = PresetBookTest::stored();
unset($stored['module']['divi/button']['items']['bbbbbbbbbb']);
return $stored;
});
$snapshots = Mockery::mock(SnapshotStore::class);
$snapshots->expects('save')->andReturn('presets-9');
$snapshots->expects('attachLog');
$ability = $this->ability(self::unused(), $writer, $snapshots);
$input = ['items' => [['module' => 'button', 'preset' => 'bbbbbbbbbb']]];
$dry = self::assertToolError($ability->execute($input), 'confirm_required');
self::assertSame('removed', $dry['diff'][0]['action']);
$result = $ability->execute($input + ['confirm' => true, 'design_rev' => $dry['design_rev']]);
self::assertSame('presets-9', $result['snapshot_id']);
}
public function testPresetInUseIsRefused(): void
{
$used = new UsageReport(2, [['post_id' => 3, 'title' => 'Start', 'status' => 'publish', 'type' => 'page']],
1, 1, [], 0, false);
$error = self::assertToolError(
$this->ability($used)->execute(['items' => [['module' => 'button', 'preset' => 'CTA primär']]]),
'in_use'
);
self::assertSame(3, $error['usages'][0]['post_id']);
self::assertStringContainsString('lit/divi-design-detach', $error['hint']);
self::assertStringNotContainsString('4b', $error['hint'], 'the tool exists now');
}
public function testNestedUseInAnotherPresetCounts(): void
{
$error = self::assertToolError(
$this->ability(self::unused())->execute(['items' => [['group' => 'font-body', 'preset' => 'Lesetext']]]),
'in_use'
);
self::assertSame('preset', $error['usages'][0]['type']);
self::assertSame('dddddddddd', $error['usages'][0]['preset_id']);
}
public function testDeletingTheDefaultClearsIt(): void
{
$dry = self::assertToolError(
$this->ability(self::unused())->execute(['items' => [['module' => 'text', 'preset' => 'Fließtext']]]),
'confirm_required'
);
self::assertSame(['removed', 'default_cleared'], array_column($dry['diff'], 'action'));
}
/** Final review I-2: deleting saves the whole inventory – other presets' HTML/CSS would be stripped. */
public function testEditorIsRefusedWhenOtherPresetsCarryMarkup(): void
{
$input = ['items' => [['module' => 'button', 'preset' => 'cccccccccc']]];
$markup = PresetBookTest::storedWithMarkup();
$editor = self::assertToolError(
$this->ability(self::unused(), null, null, false, $markup)->execute($input),
'forbidden'
);
self::assertStringContainsString('unfiltered_html', $editor['message']);
self::assertToolError($this->ability(self::unused(), null, null, true, $markup)->execute($input), 'confirm_required');
self::assertToolError($this->ability(self::unused(), null, null, false)->execute($input), 'confirm_required');
}
/** @return list<bool> The default flags of a PresetUsageQuery's targets. */
private static function defaultFlags(PresetUsageQuery $query): array
{
$targets = (new ReflectionProperty(PresetUsageQuery::class, 'targets'))->getValue($query);
return array_column($targets, 'default');
}
/** Final review I-6 (spec §6.4): implicit use of a default preset does not block; the dry run lists it. */
public function testImplicitUseOfADefaultPresetIsListedNotBlocking(): void
{
$used = new UsageReport(1, [['post_id' => 3, 'title' => 'Start', 'status' => 'publish', 'type' => 'page']],
0, 1, [], 0, false);
Functions\when('current_user_can')->justReturn(true);
$writer = Mockery::mock(DesignSystemWriter::class);
$writer->allows('refresh');
$writer->allows('presets')->andReturn(PresetBookTest::stored());
$writer->allows('canUseVisualBuilder')->andReturn(true);
$usages = Mockery::mock(GlobalUsages::class);
$usages->expects('reportFor')
->with(Mockery::on(static fn (PresetUsageQuery $q): bool => self::defaultFlags($q) === [false]), false)
->andReturn(self::unused());
$usages->expects('reportFor')
->with(Mockery::on(static fn (PresetUsageQuery $q): bool => self::defaultFlags($q) === [true]))
->andReturn($used);
$ability = new DiviPresetsDeleteAbility(
$writer,
new DesignChange(Mockery::mock(SnapshotStore::class), FakeChangeLog::forbidden()),
$usages,
static fn (): bool => true
);
$dry = self::assertToolError(
$ability->execute(['items' => [['module' => 'text', 'preset' => 'Fließtext']]]),
'confirm_required'
);
self::assertSame(3, $dry['affected'][0]['post_id']);
self::assertSame(1, $dry['affected_count']);
self::assertContains('Module ohne eigenes Preset verwenden danach das Divi-Standardaussehen.', $dry['warnings']);
}
public function testUnknownPreset(): void
{
self::assertToolError(
$this->ability(self::unused())->execute(['items' => [['module' => 'button', 'preset' => 'Fehlt']]]),
'validation_failed'
);
}
}