feat: Presets als Stapel, Gruppen-Presets und clear_overrides im Composer
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
fafa140546
commit
be6ca5bee0
@@ -4,6 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace LIT\MCP\Tests\Support;
|
||||
|
||||
use Closure;
|
||||
use LIT\MCP\Integrations\Divi\Compose\AttrWriter;
|
||||
use LIT\MCP\Integrations\Divi\Compose\Composer;
|
||||
use LIT\MCP\Integrations\Divi\Compose\LinkTargets;
|
||||
@@ -15,6 +16,8 @@ use LIT\MCP\Integrations\Divi\Compose\ValueCleaner;
|
||||
use LIT\MCP\Integrations\Divi\Compose\ValueSanitizer;
|
||||
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystem;
|
||||
use LIT\MCP\Integrations\Divi\Design\PresetAssignment;
|
||||
use LIT\MCP\Integrations\Divi\Design\PresetBook;
|
||||
|
||||
/**
|
||||
* Compose classes wired with the real Divi 5.2.1 sample catalog, the sample design system and
|
||||
@@ -193,10 +196,17 @@ final class ComposeFixture
|
||||
/**
|
||||
* Builder version 5.2.1. $design replaces the sample design system (one Placeholders, one
|
||||
* VariableRef and one ValueCleaner shared by AttrWriter, ShortformWriter and Composer, like the
|
||||
* live wiring – ruling S31).
|
||||
* live wiring – ruling S31). The presets come from $book (default: the presets of $design, see
|
||||
* bookOf()); $groupsOf names the option groups per module (default: none).
|
||||
*
|
||||
* @param (Closure(string): array<string, string>)|null $groupsOf
|
||||
*/
|
||||
public static function composer(bool $unfiltered = false, ?DesignSystem $design = null): Composer
|
||||
{
|
||||
public static function composer(
|
||||
bool $unfiltered = false,
|
||||
?DesignSystem $design = null,
|
||||
?PresetBook $book = null,
|
||||
?Closure $groupsOf = null
|
||||
): Composer {
|
||||
$design ??= DiviFixtures::designSystem();
|
||||
$placeholders = new Placeholders($design);
|
||||
$cleaner = new ValueCleaner(DiviFixtures::catalog(), new VariableRef($design), self::sanitizer($unfiltered));
|
||||
@@ -208,8 +218,52 @@ final class ComposeFixture
|
||||
new ShortformWriter($placeholders, self::media(), $cleaner),
|
||||
$attrWriter,
|
||||
$cleaner,
|
||||
$design,
|
||||
self::presetAssignment($book, $groupsOf, $design, $cleaner),
|
||||
'5.2.1'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* The presets of $design in Divi's stored format (ids, names, defaults; no attrs) as a PresetBook – so the tests
|
||||
* that hand a DesignSystem to composer() keep their presets.
|
||||
*/
|
||||
public static function bookOf(DesignSystem $design): PresetBook
|
||||
{
|
||||
$raw = ['module' => [], 'group' => []];
|
||||
foreach ($design->presets() as $preset) {
|
||||
$raw[$preset['kind']][$preset['module']] ??= ['default' => '', 'items' => []];
|
||||
$raw[$preset['kind']][$preset['module']]['items'][$preset['id']] = [
|
||||
'id' => $preset['id'],
|
||||
'name' => $preset['name'],
|
||||
$preset['kind'] === 'module' ? 'moduleName' : 'groupName' => $preset['module'],
|
||||
];
|
||||
if ($preset['default']) {
|
||||
$raw[$preset['kind']][$preset['module']]['default'] = $preset['id'];
|
||||
}
|
||||
}
|
||||
|
||||
return PresetBook::fromDivi($raw);
|
||||
}
|
||||
|
||||
/**
|
||||
* PresetAssignment as the live wiring builds it: the book is $book (default: bookOf($design), else of the sample
|
||||
* design system), the id check is the cleaner's keepsId().
|
||||
*
|
||||
* @param (Closure(string): array<string, string>)|null $groupsOf
|
||||
*/
|
||||
public static function presetAssignment(
|
||||
?PresetBook $book = null,
|
||||
?Closure $groupsOf = null,
|
||||
?DesignSystem $design = null,
|
||||
?ValueCleaner $cleaner = null
|
||||
): PresetAssignment {
|
||||
$book ??= self::bookOf($design ?? DiviFixtures::designSystem());
|
||||
$cleaner ??= self::cleaner();
|
||||
|
||||
return new PresetAssignment(
|
||||
static fn (): PresetBook => $book,
|
||||
$groupsOf ?? static fn (string $module): array => [],
|
||||
$cleaner->keepsId(...)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,14 @@ declare(strict_types=1);
|
||||
namespace LIT\MCP\Tests\Unit\Integrations\Divi\Compose;
|
||||
|
||||
use InvalidArgumentException;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\AttrMap;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\Block;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\Parser;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\Serializer;
|
||||
use LIT\MCP\Integrations\Divi\Compose\Composer;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystem;
|
||||
use LIT\MCP\Integrations\Divi\Design\PresetBook;
|
||||
use LIT\MCP\Tests\Support\ComposeFixture;
|
||||
use LIT\MCP\Tests\Support\DiviFixtures;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
@@ -424,6 +426,165 @@ final class ComposerTest extends TestCase
|
||||
self::assertArrayNotHasKey('modulePreset', $result['attrs']);
|
||||
}
|
||||
|
||||
/** Button presets p-1/p-2 as in the sample design system; p-2 carries a font color, g-1 is a font group preset. */
|
||||
private static function coveringBook(): PresetBook
|
||||
{
|
||||
return PresetBook::fromDivi([
|
||||
'module' => ['divi/button' => ['default' => 'p-1', 'items' => [
|
||||
'p-1' => ['id' => 'p-1', 'name' => 'Standard', 'moduleName' => 'divi/button'],
|
||||
'p-2' => [
|
||||
'id' => 'p-2',
|
||||
'name' => 'CTA primär',
|
||||
'moduleName' => 'divi/button',
|
||||
'attrs' => ['button' => ['decoration' => ['font' => ['font' => [
|
||||
'desktop' => ['value' => ['color' => '#ffffff']],
|
||||
]]]]],
|
||||
],
|
||||
]]],
|
||||
'group' => ['divi/font' => ['default' => '', 'items' => [
|
||||
'g-1' => ['id' => 'g-1', 'name' => 'Fließtext groß', 'groupName' => 'divi/font'],
|
||||
]]],
|
||||
]);
|
||||
}
|
||||
|
||||
/** @return array<string, string> The option groups the tests give every button. */
|
||||
private static function buttonGroups(string $module): array
|
||||
{
|
||||
return $module === 'divi/button' ? ['button.decoration.font.font' => 'divi/font'] : [];
|
||||
}
|
||||
|
||||
public function testPresetStackIsStoredInTheGivenOrder(): void
|
||||
{
|
||||
$composer = ComposeFixture::composer();
|
||||
|
||||
$result = $composer->compose(
|
||||
[['type' => 'button', 'text' => 'Los', 'preset' => ['CTA primär', 'p-1']]],
|
||||
Block::create('divi/column'),
|
||||
'nodes'
|
||||
);
|
||||
self::assertSame([], $result['errors']);
|
||||
self::assertSame(['p-2', 'p-1'], $result['blocks'][0]->attrs()['modulePreset']);
|
||||
|
||||
$update = $composer->update(Block::create('divi/button'), ['preset' => ['Standard', 'CTA primär']], 'changes[0]');
|
||||
self::assertSame([], $update['errors']);
|
||||
self::assertSame(['p-1', 'p-2'], $update['attrs']['modulePreset']);
|
||||
}
|
||||
|
||||
public function testPresetStackErrorsKeepTheirInputPaths(): void
|
||||
{
|
||||
$composer = ComposeFixture::composer();
|
||||
$button = Block::create('divi/button');
|
||||
|
||||
$unknown = $composer->update($button, ['preset' => ['Standard', 'Gibt es nicht']], 'changes[0]');
|
||||
self::assertSame(
|
||||
'unbekanntes Preset; vorhanden für divi/button: Standard, CTA primär',
|
||||
$unknown['errors']['changes[0].preset.1']
|
||||
);
|
||||
self::assertArrayNotHasKey('modulePreset', $unknown['attrs']);
|
||||
|
||||
foreach ([[], ['Standard', 'p-1']] as $invalid) {
|
||||
$result = $composer->update($button, ['preset' => $invalid], 'changes[0]');
|
||||
self::assertNotSame([], preg_grep('/^changes\[0\]\.preset/', array_keys($result['errors'])));
|
||||
self::assertArrayNotHasKey('modulePreset', $result['attrs']);
|
||||
}
|
||||
}
|
||||
|
||||
public function testGroupPresetsAreAssignedByShortNameAndRemovedWithNull(): void
|
||||
{
|
||||
$composer = ComposeFixture::composer(false, null, null, self::buttonGroups(...));
|
||||
$groups = ['button.decoration.font.font' => ['groupName' => 'divi/font', 'presetIds' => ['g-1']]];
|
||||
|
||||
$update = $composer->update(Block::create('divi/button'), ['group_presets' => ['font' => 'Fließtext groß']], 'changes[0]');
|
||||
self::assertSame([], $update['errors']);
|
||||
self::assertSame($groups, NodeSummary::groupPresets(Block::create('divi/button', $update['attrs'])));
|
||||
|
||||
$created = $composer->compose(
|
||||
[['type' => 'button', 'text' => 'Los', 'group_presets' => ['divi/font' => 'g-1']]],
|
||||
Block::create('divi/column'),
|
||||
'nodes'
|
||||
);
|
||||
self::assertSame([], $created['errors']);
|
||||
self::assertSame($groups, NodeSummary::groupPresets($created['blocks'][0]));
|
||||
|
||||
$removed = $composer->update(Block::create('divi/button', $update['attrs']), ['group_presets' => ['font' => null]], 'changes[0]');
|
||||
self::assertSame([], $removed['errors']);
|
||||
self::assertSame([], NodeSummary::groupPresets(Block::create('divi/button', $removed['attrs'])));
|
||||
self::assertArrayNotHasKey('groupPreset', $removed['attrs']);
|
||||
|
||||
$unknown = $composer->update(Block::create('divi/button'), ['group_presets' => ['farbe' => 'x']], 'changes[0]');
|
||||
self::assertArrayHasKey('changes[0].group_presets.farbe', $unknown['errors']);
|
||||
}
|
||||
|
||||
/** Ruling R7: clear_overrides runs after set/attrs of the same node and removes only leaves a preset also sets. */
|
||||
public function testClearOverridesRunsAfterSetAndAttrsOnANewNode(): void
|
||||
{
|
||||
$composer = ComposeFixture::composer(false, null, self::coveringBook());
|
||||
$font = ['desktop' => ['color' => '#abcdef', 'family' => 'Open Sans']];
|
||||
|
||||
$kept = $composer->compose(
|
||||
[['type' => 'button', 'text' => 'Los', 'preset' => 'CTA primär', 'attrs' => ['button.decoration.font.font' => $font]]],
|
||||
Block::create('divi/column'),
|
||||
'nodes'
|
||||
);
|
||||
self::assertSame([], $kept['errors']);
|
||||
self::assertSame(
|
||||
['color' => '#abcdef', 'family' => 'Open Sans'],
|
||||
AttrMap::desktop($kept['blocks'][0]->attrs(), 'button.decoration.font.font')
|
||||
);
|
||||
|
||||
$cleared = $composer->compose(
|
||||
[[
|
||||
'type' => 'button',
|
||||
'text' => 'Los',
|
||||
'preset' => 'CTA primär',
|
||||
'clear_overrides' => true,
|
||||
'attrs' => ['button.decoration.font.font' => $font],
|
||||
]],
|
||||
Block::create('divi/column'),
|
||||
'nodes'
|
||||
);
|
||||
self::assertSame([], $cleared['errors']);
|
||||
$attrs = $cleared['blocks'][0]->attrs();
|
||||
self::assertSame(['p-2'], $attrs['modulePreset']);
|
||||
self::assertSame(['family' => 'Open Sans'], AttrMap::desktop($attrs, 'button.decoration.font.font'));
|
||||
self::assertSame('Los', AttrMap::desktop($attrs, 'button.innerContent')['text']);
|
||||
}
|
||||
|
||||
public function testClearOverridesOnAnUpdateKeepsEverythingNoPresetCovers(): void
|
||||
{
|
||||
$composer = ComposeFixture::composer(false, null, self::coveringBook());
|
||||
$attrs = ['modulePreset' => ['p-2'], 'button' => ['decoration' => ['font' => ['font' => [
|
||||
'desktop' => ['value' => ['color' => '#000000']],
|
||||
]]]]];
|
||||
|
||||
$result = $composer->update(Block::create('divi/button', $attrs), ['clear_overrides' => true], 'changes[0]');
|
||||
self::assertSame([], $result['errors']);
|
||||
self::assertSame(['modulePreset' => ['p-2']], $result['attrs']);
|
||||
|
||||
$plain = Block::create('divi/button', ['button' => ['innerContent' => ['desktop' => ['value' => ['text' => 'Alt']]]]]);
|
||||
$unchanged = $composer->update($plain, ['clear_overrides' => true], 'changes[0]');
|
||||
self::assertSame([], $unchanged['errors']);
|
||||
self::assertSame($plain->attrs(), $unchanged['attrs']);
|
||||
|
||||
$off = $composer->update(Block::create('divi/button', $attrs), ['clear_overrides' => false], 'changes[0]');
|
||||
self::assertSame($attrs, $off['attrs']);
|
||||
}
|
||||
|
||||
public function testClearOverridesMustBeABoolean(): void
|
||||
{
|
||||
$composer = ComposeFixture::composer();
|
||||
|
||||
$update = $composer->update(Block::create('divi/button'), ['clear_overrides' => 'ja'], 'changes[0]');
|
||||
self::assertSame('true oder false erwartet', $update['errors']['changes[0].clear_overrides']);
|
||||
|
||||
$compose = $composer->compose(
|
||||
[['type' => 'section', 'clear_overrides' => null, 'modules' => []]],
|
||||
null,
|
||||
'nodes'
|
||||
);
|
||||
self::assertSame('true oder false erwartet', $compose['errors']['nodes[0].clear_overrides']);
|
||||
}
|
||||
|
||||
public function testBuilderVersionMustBeAVersion(): void
|
||||
{
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
@@ -434,7 +595,7 @@ final class ComposerTest extends TestCase
|
||||
ComposeFixture::shortform(),
|
||||
ComposeFixture::attrWriter(),
|
||||
ComposeFixture::cleaner(),
|
||||
DiviFixtures::designSystem(),
|
||||
ComposeFixture::presetAssignment(),
|
||||
'5.2.1"},"x":{"'
|
||||
);
|
||||
}
|
||||
|
||||
@@ -253,4 +253,17 @@ final class ValueCleanerTest extends TestCase
|
||||
self::assertSame('x', ComposeFixture::cleaner()->clean('divi/lottie', 'lottie.advanced.width', 'x'));
|
||||
self::assertSame('x', ComposeFixture::cleaner()->clean('divi/lottie', 'lottie.innerContentish', 'x'));
|
||||
}
|
||||
|
||||
/** Design-system ids (preset ids) are no client text, but they pass the choke point unchanged (ruling S13). */
|
||||
public function testKeepsIdAcceptsPlainIdsAndRefusesEverythingTheCleanerChanges(): void
|
||||
{
|
||||
$cleaner = ComposeFixture::cleaner();
|
||||
|
||||
self::assertTrue($cleaner->keepsId('3gbmxlurey'));
|
||||
self::assertTrue($cleaner->keepsId('a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d'));
|
||||
self::assertFalse($cleaner->keepsId('<b>p</b>'), 'tags would be stripped');
|
||||
self::assertFalse($cleaner->keepsId('p%41x'), 'octets would be stripped');
|
||||
self::assertFalse($cleaner->keepsId("p\x01"), 'control characters would be stripped');
|
||||
self::assertFalse($cleaner->keepsId(ComposeFixture::REFERENCE_ALT), 'an id is never a $variable reference');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,10 +8,12 @@ use LIT\MCP\Integrations\Divi\BlockTree\AttrMap;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\Block;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\Document;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\NodeIndex;
|
||||
use LIT\MCP\Integrations\Divi\BlockTree\NodeSummary;
|
||||
use LIT\MCP\Integrations\Divi\Compose\StoredNodeGuard;
|
||||
use LIT\MCP\Integrations\Divi\Compose\TreeEditor;
|
||||
use LIT\MCP\Integrations\Divi\Compose\VariableRef;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemReader;
|
||||
use LIT\MCP\Integrations\Divi\Design\DesignSystemWriter;
|
||||
use LIT\MCP\Integrations\Divi\Pages\DiviComposition;
|
||||
use LIT\MCP\Integrations\Divi\Pages\DynamicContentMap;
|
||||
use LIT\MCP\Integrations\Divi\Schema\DiviModules;
|
||||
@@ -26,7 +28,7 @@ final class DiviCompositionTest extends TestCase
|
||||
/** @var list<int> */
|
||||
private array $registryCalls = [];
|
||||
|
||||
private function composition(): DiviComposition
|
||||
private function composition(?DesignSystemWriter $presets = null): DiviComposition
|
||||
{
|
||||
$modules = Mockery::mock(DiviModules::class);
|
||||
$modules->allows('catalog')->andReturn(DiviFixtures::catalog());
|
||||
@@ -45,10 +47,33 @@ final class DiviCompositionTest extends TestCase
|
||||
ComposeFixture::sanitizer(),
|
||||
ComposeFixture::media(),
|
||||
$registry,
|
||||
ComposeFixture::links()
|
||||
ComposeFixture::links(),
|
||||
presetSource: $presets
|
||||
);
|
||||
}
|
||||
|
||||
/** The preset source of the live wiring: Divi's stored presets (raw format) and the option groups per module. */
|
||||
private function presetSource(int $reads = 1): DesignSystemWriter
|
||||
{
|
||||
$writer = Mockery::mock(DesignSystemWriter::class);
|
||||
$writer->expects('presets')->times($reads)->andReturn([
|
||||
'module' => ['divi/button' => ['default' => 'p-1', 'items' => [
|
||||
'p-1' => ['id' => 'p-1', 'name' => 'Standard', 'moduleName' => 'divi/button'],
|
||||
'p-2' => ['id' => 'p-2', 'name' => 'CTA primär', 'moduleName' => 'divi/button'],
|
||||
]]],
|
||||
'group' => ['divi/font' => ['default' => '', 'items' => [
|
||||
'g-1' => ['id' => 'g-1', 'name' => 'Fließtext groß', 'groupName' => 'divi/font'],
|
||||
]]],
|
||||
]);
|
||||
$writer->allows('groupsOf')->andReturnUsing(
|
||||
static fn (string $module): array => $module === 'divi/button'
|
||||
? ['button.decoration.font.font' => 'divi/font']
|
||||
: []
|
||||
);
|
||||
|
||||
return $writer;
|
||||
}
|
||||
|
||||
private static function siteTitle(): string
|
||||
{
|
||||
return VariableRef::serialize('content', 'site_title', ['before' => '', 'after' => '']);
|
||||
@@ -156,4 +181,61 @@ final class DiviCompositionTest extends TestCase
|
||||
DiviWriterFixture::composition(DiviWriterFixture::designReader(), true)->storedGuard()->problems($code)
|
||||
);
|
||||
}
|
||||
|
||||
public function testPresetStackAndGroupsComeFromTheWritersBookAndAreReadOnce(): void
|
||||
{
|
||||
$composer = $this->composition($this->presetSource())->forPost(7)->composer();
|
||||
|
||||
$result = $composer->compose([
|
||||
['type' => 'button', 'text' => 'Eins', 'preset' => ['CTA primär', 'Standard'], 'group_presets' => ['font' => 'g-1']],
|
||||
['type' => 'button', 'text' => 'Zwei', 'preset' => 'p-2'],
|
||||
], Block::create('divi/column'), 'nodes');
|
||||
|
||||
self::assertSame([], $result['errors']);
|
||||
self::assertSame(['p-2', 'p-1'], $result['blocks'][0]->attrs()['modulePreset']);
|
||||
self::assertSame(['p-2'], $result['blocks'][1]->attrs()['modulePreset']);
|
||||
self::assertSame(
|
||||
['button.decoration.font.font' => ['groupName' => 'divi/font', 'presetIds' => ['g-1']]],
|
||||
NodeSummary::groupPresets($result['blocks'][0])
|
||||
);
|
||||
}
|
||||
|
||||
public function testThePresetBookIsNotReadWithoutAPresetInTheInput(): void
|
||||
{
|
||||
$writer = Mockery::mock(DesignSystemWriter::class);
|
||||
$writer->shouldNotReceive('presets');
|
||||
$writer->shouldNotReceive('groupsOf');
|
||||
|
||||
$result = $this->composition($writer)->forPost(7)->composer()->compose(
|
||||
[['type' => 'button', 'text' => 'Los']],
|
||||
Block::create('divi/column'),
|
||||
'nodes'
|
||||
);
|
||||
|
||||
self::assertSame([], $result['errors']);
|
||||
}
|
||||
|
||||
public function testWithoutAPresetSourceNoPresetExists(): void
|
||||
{
|
||||
$result = $this->composition()->forPost(7)->composer()->compose(
|
||||
[['type' => 'button', 'text' => 'Los', 'preset' => 'Standard']],
|
||||
Block::create('divi/column'),
|
||||
'nodes'
|
||||
);
|
||||
|
||||
self::assertSame(
|
||||
'unbekanntes Preset; vorhanden für divi/button: (keine)',
|
||||
$result['errors']['nodes[0].preset']
|
||||
);
|
||||
}
|
||||
|
||||
public function testOnePresetAssignmentPerBoundComposition(): void
|
||||
{
|
||||
$base = $this->composition($this->presetSource(0));
|
||||
$seven = $base->forPost(7);
|
||||
|
||||
self::assertSame($seven->presets(), $seven->presets());
|
||||
self::assertNotSame($seven->presets(), $base->forPost(8)->presets());
|
||||
self::assertNotSame($base->presets(), $seven->presets());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,5 +261,51 @@ final class DiviUpdateAbilityTest extends DiviWriteAbilityTestCase
|
||||
self::assertStringNotContainsString('1058', (string) json_encode($hidden));
|
||||
self::assertStringContainsString('[global 1059]', (string) $shown['outline']);
|
||||
}
|
||||
}
|
||||
|
||||
public function testUpdateOffersPresetStacksGroupPresetsAndClearOverrides(): void
|
||||
{
|
||||
$change = $this->update()->inputSchema()['properties']['changes']['items']['properties'];
|
||||
|
||||
self::assertSame(['string', 'array', 'null'], $change['preset']['type']);
|
||||
self::assertSame(['type' => 'string'], $change['preset']['items']);
|
||||
self::assertSame([1, 10], [$change['preset']['minItems'], $change['preset']['maxItems']]);
|
||||
self::assertSame('object', $change['group_presets']['type']);
|
||||
self::assertSame(['string', 'array', 'null'], $change['group_presets']['additionalProperties']['type']);
|
||||
self::assertSame([1, 10], [
|
||||
$change['group_presets']['additionalProperties']['minItems'],
|
||||
$change['group_presets']['additionalProperties']['maxItems'],
|
||||
]);
|
||||
self::assertSame('boolean', $change['clear_overrides']['type']);
|
||||
|
||||
$description = $this->update()->description();
|
||||
foreach (['group_presets', 'clear_overrides', 'das letzte Preset'] as $text) {
|
||||
self::assertStringContainsString($text, $description);
|
||||
}
|
||||
self::assertStringContainsString('group_presets', $this->insert()->description());
|
||||
}
|
||||
|
||||
public function testClearOverridesMustBeABoolean(): void
|
||||
{
|
||||
$result = $this->update()->execute(['post_id' => 7, 'tree_rev' => $this->treeRev(), 'changes' => [
|
||||
['id' => 's0.r0.c1.m2', 'clear_overrides' => 'ja'],
|
||||
]]);
|
||||
|
||||
self::assertToolError($result, 'validation_failed');
|
||||
self::assertSame(
|
||||
'true oder false erwartet',
|
||||
self::clientError($result)['fields']['changes[0].clear_overrides']
|
||||
);
|
||||
self::assertSame([], $this->saved);
|
||||
}
|
||||
|
||||
/** A pure clear_overrides that finds no covering value changes nothing and saves nothing. */
|
||||
public function testClearOverridesWithoutAHitIsNoChange(): void
|
||||
{
|
||||
$result = $this->update()->execute(['post_id' => 7, 'tree_rev' => $this->treeRev(), 'changes' => [
|
||||
['id' => 's0.r0.c1.m2', 'clear_overrides' => true],
|
||||
]]);
|
||||
|
||||
self::assertStringStartsWith('Keine Änderung', $result['summary']);
|
||||
self::assertSame([], $this->saved);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -263,4 +263,13 @@ final class ContentCreateAbilityTest extends TestCase
|
||||
Functions\when('current_user_can')->justReturn(false);
|
||||
self::assertToolError($ability->permission(['post_type' => 'page']), 'forbidden');
|
||||
}
|
||||
|
||||
public function testDescriptionNamesPresetStacksAndGroupPresets(): void
|
||||
{
|
||||
$ability = new ContentCreateAbility($this->schema(), Mockery::mock(PostWriter::class), FakeChangeLog::forbidden());
|
||||
$description = $ability->description();
|
||||
|
||||
self::assertStringContainsString('group_presets', $description);
|
||||
self::assertStringContainsString('clear_overrides', $description);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user