Files

268 lines
12 KiB
PHP
Raw Permalink 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 LIT\MCP\Integrations\Divi\Design\PresetOverrides;
use PHPUnit\Framework\TestCase;
use stdClass;
final class PresetOverridesTest extends TestCase
{
private const SPACING = 'module.decoration.spacing';
private const BODY_FONT = 'content.decoration.bodyFont';
/** A breakpoint map with one desktop value, the shape Divi stores at an attribute path. */
private static function desktop(mixed $value): array
{
return ['desktop' => ['value' => $value]];
}
/** Presets in Divi's stored format (formats §3): a module preset nesting a group preset, a group preset. */
private static function overrides(): PresetOverrides
{
$items = [
'mmmmmmmmmm' => [
'type' => 'module', 'id' => 'mmmmmmmmmm', 'name' => 'Blurb Leistung', 'moduleName' => 'divi/blurb',
'attrs' => [
'module' => ['decoration' => ['spacing' => self::desktop([
'padding' => ['top' => '20px', 'bottom' => '10px'],
])]],
'title' => ['decoration' => ['font' => ['font' => self::desktop(['weight' => '700'])]]],
],
'groupPresets' => [
'title.decoration.bodyFont' => ['presetId' => ['ffffffffff'], 'groupName' => 'divi/font-body'],
],
],
'ssssssssss' => [
'type' => 'group', 'id' => 'ssssssssss', 'name' => 'Abstand Karte', 'groupName' => 'divi/spacing',
'groupId' => self::SPACING, 'moduleName' => 'divi/blurb',
'attrs' => ['module' => ['decoration' => ['spacing' => self::desktop([
'margin' => ['bottom' => '24px'],
])]]],
],
'ffffffffff' => [
'type' => 'group', 'id' => 'ffffffffff', 'name' => 'Fließtext', 'groupName' => 'divi/font-body',
'groupId' => self::BODY_FONT, 'moduleName' => 'divi/text',
'attrs' => ['content' => ['decoration' => ['bodyFont' => ['desktop' => [
'value' => ['size' => '18px'],
'hover' => ['size' => '20px'],
], 'phone' => ['value' => ['size' => '16px']]]]]],
],
];
return new PresetOverrides(static fn (string $id): ?array => $items[$id] ?? null);
}
public function testLeavesFlattenBreakpointsStatesAndSubpaths(): void
{
$leaves = PresetOverrides::leaves([
'builderVersion' => '5.2.1',
'modulePreset' => ['mmmmmmmmmm'],
'groupPreset' => [self::SPACING => ['presetId' => ['ssssssssss'], 'groupName' => 'divi/spacing']],
'module' => ['decoration' => ['spacing' => [
'desktop' => ['value' => ['padding' => ['top' => '20px'], 'syncVertical' => 'on'], 'hover' => [
'margin' => ['bottom' => '4px'],
]],
'phone' => ['value' => ['padding' => ['top' => '8px']]],
]]],
'title' => ['innerContent' => self::desktop('Hallo'), 'decoration' => ['font' => ['font' => self::desktop(
new stdClass()
)]]],
'content' => ['decoration' => ['bodyFont' => self::desktop(['families' => ['Lato', 'Arial']])]],
]);
self::assertSame(
[
'content.decoration.bodyFont|desktop|value|families' => ['Lato', 'Arial'],
'module.decoration.spacing|desktop|value|padding.top' => '20px',
'module.decoration.spacing|desktop|value|syncVertical' => 'on',
'module.decoration.spacing|desktop|hover|margin.bottom' => '4px',
'module.decoration.spacing|phone|value|padding.top' => '8px',
'title.innerContent|desktop|value|' => 'Hallo',
],
$leaves
);
}
public function testFindReportsOnlyDifferingValuesOfTheModulePreset(): void
{
$attrs = [
'modulePreset' => ['mmmmmmmmmm'],
'module' => ['decoration' => ['spacing' => self::desktop(['padding' => ['top' => '40px', 'bottom' => '10px']])]],
// 700 as a number is the same value as "700" (the builder writes strings, an API client may not).
'title' => ['decoration' => ['font' => ['font' => self::desktop(['weight' => 700])]]],
];
self::assertSame(
['mmmmmmmmmm' => ['module.decoration.spacing|desktop|value|padding.top']],
self::overrides()->find($attrs)
);
}
public function testNoOverlapNoReport(): void
{
// A local padding.left does not cover the preset's padding.top/bottom (array_replace_recursive).
$attrs = [
'modulePreset' => ['mmmmmmmmmm'],
'module' => ['decoration' => ['spacing' => self::desktop(['padding' => ['left' => '5px']])]],
'title' => ['innerContent' => self::desktop('Text')],
];
self::assertSame([], self::overrides()->find($attrs));
self::assertSame([], self::overrides()->find([]));
}
public function testGroupPresetLeavesMoveFromTheStoredGroupIdToTheBlocksKey(): void
{
// The preset was made for content.decoration.bodyFont; this block uses it on text.decoration.bodyFont.
$attrs = [
'groupPreset' => ['text.decoration.bodyFont' => ['presetId' => ['ffffffffff'], 'groupName' => 'divi/font-body']],
'text' => ['decoration' => ['bodyFont' => [
'desktop' => ['value' => ['size' => '22px'], 'hover' => ['size' => '20px']],
'phone' => ['value' => ['size' => '12px']],
]]],
// Same path as the preset's stored groupId, but not the block's key: no overlap.
'content' => ['decoration' => ['bodyFont' => self::desktop(['size' => '99px'])]],
];
self::assertSame(
['ffffffffff' => [
'text.decoration.bodyFont|desktop|value|size',
'text.decoration.bodyFont|phone|value|size',
]],
self::overrides()->find($attrs)
);
}
public function testNestedGroupPresetsOfAnAssignedModulePresetCount(): void
{
$attrs = [
'modulePreset' => ['mmmmmmmmmm'],
'title' => ['decoration' => ['bodyFont' => self::desktop(['size' => '30px'])]],
];
// The nested entry sits on title.decoration.bodyFont of the module preset; its leaves move there.
self::assertSame(
['ffffffffff' => ['title.decoration.bodyFont|desktop|value|size']],
self::overrides()->find($attrs)
);
}
public function testPresetsNobodyAssignedAreNotCounted(): void
{
// Default presets ("", "default", "_initial") and unknown IDs are no explicit assignment (ruling R4).
$attrs = [
'modulePreset' => ['', 'default', '_initial', 'unbekannt'],
'module' => ['decoration' => ['spacing' => self::desktop(['padding' => ['top' => '40px']])]],
];
self::assertSame([], self::overrides()->find($attrs));
self::assertSame($attrs, self::overrides()->clear($attrs));
}
public function testStackAndSingleStringAreRead(): void
{
$attrs = [
'modulePreset' => 'mmmmmmmmmm',
'groupPreset' => [self::SPACING => ['presetId' => 'ssssssssss', 'groupName' => 'divi/spacing']],
'module' => ['decoration' => ['spacing' => self::desktop([
'padding' => ['bottom' => '11px'], 'margin' => ['bottom' => '1px'],
])]],
];
self::assertSame(
[
'mmmmmmmmmm' => ['module.decoration.spacing|desktop|value|padding.bottom'],
'ssssssssss' => ['module.decoration.spacing|desktop|value|margin.bottom'],
],
self::overrides()->find($attrs)
);
}
public function testClearRemovesLocalLeavesOnPresetLeavesAndEmptyParents(): void
{
$attrs = [
'builderVersion' => '5.2.1',
'modulePreset' => ['mmmmmmmmmm'],
'module' => ['decoration' => ['spacing' => self::desktop([
// padding.top is a preset leaf (different value), padding.bottom too (same value), left is not.
'padding' => ['top' => '40px', 'bottom' => '10px', 'left' => '5px'],
])]],
'title' => [
'innerContent' => self::desktop('Hallo'),
'decoration' => ['font' => ['font' => self::desktop(['weight' => '400'])]],
],
];
self::assertSame(
[
'builderVersion' => '5.2.1',
'modulePreset' => ['mmmmmmmmmm'],
'module' => ['decoration' => ['spacing' => self::desktop(['padding' => ['left' => '5px']])]],
'title' => ['innerContent' => self::desktop('Hallo')],
],
self::overrides()->clear($attrs)
);
}
public function testClearDropsAWholeBranchWhenNothingElseIsLeft(): void
{
$attrs = [
'modulePreset' => ['mmmmmmmmmm'],
'title' => ['decoration' => ['font' => ['font' => self::desktop(['weight' => '400'])]]],
];
self::assertSame(['modulePreset' => ['mmmmmmmmmm']], self::overrides()->clear($attrs));
}
public function testDescribeFollowsRulingR5(): void
{
$attrs = [
'modulePreset' => ['mmmmmmmmmm'],
'groupPreset' => ['text.decoration.bodyFont' => ['presetId' => ['ffffffffff'], 'groupName' => 'divi/font-body']],
'module' => ['decoration' => ['spacing' => self::desktop(['padding' => ['top' => '40px']])]],
'text' => ['decoration' => ['bodyFont' => [
'desktop' => ['hover' => ['size' => '1px'], 'value' => ['size' => '2px']],
'phone' => ['value' => ['size' => '3px']],
]]],
];
$names = ['mmmmmmmmmm' => 'Blurb Leistung', 'ffffffffff' => 'Fließtext'];
self::assertSame(
[
's0.r0.c0.m2: lokale Werte überdecken „Blurb Leistung“: module.decoration.spacing '
. '– clear_overrides: true entfernt sie.',
's0.r0.c0.m2: lokale Werte überdecken „Fließtext“: text.decoration.bodyFont, '
. 'text.decoration.bodyFont (desktop, hover), text.decoration.bodyFont (phone) '
. '– clear_overrides: true entfernt sie.',
],
self::overrides()->describe('s0.r0.c0.m2', $attrs, $names)
);
}
public function testDescribeShowsAtMostFivePathsAndCountsTheRest(): void
{
$preset = ['id' => 'pppppppppp', 'name' => 'Viele', 'attrs' => []];
$local = ['modulePreset' => ['pppppppppp']];
foreach (range(1, 7) as $number) {
$preset['attrs']['module']['decoration']['p' . $number] = self::desktop('a');
$local['module']['decoration']['p' . $number] = self::desktop('b');
}
$overrides = new PresetOverrides(static fn (string $id): ?array => $id === 'pppppppppp' ? $preset : null);
self::assertSame(
['x1: lokale Werte überdecken „pppppppppp“: module.decoration.p1, module.decoration.p2, '
. 'module.decoration.p3, module.decoration.p4, module.decoration.p5 und 2 weitere '
. '– clear_overrides: true entfernt sie.'],
$overrides->describe('x1', $local, [])
);
}
public function testDescribeIsEmptyWithoutOverrides(): void
{
self::assertSame([], self::overrides()->describe('s0', ['modulePreset' => ['mmmmmmmmmm']], []));
}
}