compose([ ['type' => 'section', 'label' => 'Test', 'modules' => [ ['type' => 'heading', 'text' => 'Titel', 'level' => 'h1'], ['type' => 'heading', 'text' => 'Zu tief', 'level' => 'h4'], ['type' => 'image', 'src' => 'https://kunde.test/a.jpg'], ['type' => 'text', 'content' => ''], ['type' => 'code', 'content' => '
x
'], ['type' => 'accordion'], ['type' => 'button', 'text' => 'Los', 'attrs' => [ 'button.decoration.background' => ['desktop' => ['color' => '#2ea3f3']], 'button.decoration.font.font' => ['desktop' => ['family' => 'Open Sans', 'color' => '#abcdef']], ]], ]], ], null, 'nodes'); self::assertSame([], $composed['errors']); $document = new Document(); foreach ($composed['blocks'] as $block) { $document->append($block); } $warnings = (new Warnings(DiviFixtures::designSystem()))->collect(NodeIndex::build($document), $composed['blocks']); self::assertSame( [ 's0.r0.c0.m2: Bild ohne Alt-Text.', 's0.r0.c0.m3: text ist leer (content fehlt).', 's0.r0.c0.m4: Code-Modul verwendet – im Divi-Builder nicht visuell bearbeitbar; nur nutzen, wenn kein ' . 'natives Modul passt.', 's0.r0.c0.m5: accordion ohne Einträge.', 's0.r0.c0.m6: feste Farbe #2ea3f3 – globale Farbe "Primärfarbe" ({{color:primary}}) passt.', 's0.r0.c0.m6: feste Schrift Open Sans – {{font:heading}} verwenden.', 's0.r0.c0.m1: Sprung in der Überschriftenhierarchie (h4 nach h1).', ], $warnings ); self::assertSame([], (new Warnings(DiviFixtures::designSystem()))->collect(NodeIndex::build($document), [])); } /** @return array Button attrs with a preset and a local font color. */ private static function button(string $color, string $preset = 'p-2'): array { return [ 'modulePreset' => [$preset], 'button' => ['decoration' => ['font' => ['font' => ['desktop' => ['value' => ['color' => $color]]]]]], ]; } /** Preset p-2 "CTA primär" sets the font color #ffffff; g-1 is a group preset without values. */ private static function book(): PresetBook { return PresetBook::fromDivi([ 'module' => ['divi/button' => ['default' => '', 'items' => [ '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'], ]]], ]); } /** @param int $loads Counts how often the preset book is read. */ private static function presets(int &$loads): PresetAssignment { return ComposeFixture::presetAssignment(null, null, null, null, static function () use (&$loads): PresetBook { ++$loads; return self::book(); }); } /** * @param list $blocks top-level blocks of the page * @param list $touched * @return array{0: list, 1: NodeIndex} */ private static function collect(array $blocks, array $touched, PresetAssignment $presets): array { $document = new Document(); foreach ($blocks as $block) { $document->append($block); } $index = NodeIndex::build($document); $warnings = (new Warnings(DiviFixtures::designSystem(), $presets))->collect($index, $touched); return [ array_values(array_filter($warnings, static fn (string $w): bool => str_contains($w, 'überdecken'))), $index, ]; } public function testWarnsWhenLocalValuesCoverAnAssignedPreset(): void { $loads = 0; $button = Block::create('divi/button', self::button('#000000')); [$covering, $index] = self::collect([$button], [$button], self::presets($loads)); self::assertCount(1, $covering); self::assertStringStartsWith( $index->idOf($button) . ': lokale Werte überdecken „CTA primär“: ', $covering[0] ); self::assertStringContainsString('button.decoration.font.font', $covering[0]); self::assertStringEndsWith('clear_overrides: true entfernt sie.', $covering[0]); } /** A group preset only triggered loading before: its values are overridden by local ones like a module preset's. */ public function testWarnsWhenLocalValuesCoverAGroupPreset(): void { $path = 'button.decoration.font.font'; $book = PresetBook::fromDivi(['group' => ['divi/font' => ['default' => '', 'items' => [ 'g-1' => [ 'type' => 'group', 'id' => 'g-1', 'name' => 'Fließtext groß', 'groupName' => 'divi/font', 'groupId' => $path, 'moduleName' => 'divi/button', 'attrs' => ['button' => ['decoration' => ['font' => ['font' => [ 'desktop' => ['value' => ['color' => '#ffffff']], ]]]]], ], ]]]]); $presets = ComposeFixture::presetAssignment(null, null, null, null, static fn (): PresetBook => $book); $button = Block::create('divi/button', [ 'groupPreset' => [$path => ['presetId' => ['g-1'], 'groupName' => 'divi/font']], 'button' => ['decoration' => ['font' => ['font' => ['desktop' => ['value' => ['color' => '#000000']]]]]], ]); [$covering, $index] = self::collect([$button], [$button], $presets); self::assertCount(1, $covering); self::assertStringStartsWith( $index->idOf($button) . ': lokale Werte überdecken „Fließtext groß“: ', $covering[0] ); self::assertStringContainsString($path, $covering[0]); } public function testEqualValuesAndUntouchedBlocksDoNotWarn(): void { $loads = 0; $same = Block::create('divi/button', self::button('#ffffff')); $other = Block::create('divi/button', self::button('#000000')); [$covering] = self::collect([$same, $other], [$same], self::presets($loads)); self::assertSame([], $covering, 'the same value covers nothing; $other was not touched'); } public function testChildrenOfATouchedBlockAreChecked(): void { $loads = 0; $button = Block::create('divi/button', self::button('#000000')); $section = Block::create('divi/section', [], [ Block::create('divi/row', [], [Block::create('divi/column', [], [$button])]), ]); [$covering, $index] = self::collect([$section], [$section], self::presets($loads)); self::assertCount(1, $covering); self::assertStringStartsWith($index->idOf($button) . ': lokale Werte', $covering[0]); } public function testThePresetBookIsOnlyReadForBlocksThatHavePresets(): void { $loads = 0; $plain = Block::create('divi/button', [ 'button' => ['decoration' => ['font' => ['font' => ['desktop' => ['value' => ['color' => '#000000']]]]]], ]); $presets = self::presets($loads); self::collect([$plain], [$plain], $presets); self::assertSame(0, $loads, 'no touched block names a preset'); $grouped = Block::create('divi/button', [ 'groupPreset' => ['button.decoration.font.font' => ['groupName' => 'divi/font', 'presetId' => ['g-1']]], ]); $stacked = Block::create('divi/button', self::button('#000000')); [$covering] = self::collect([$plain, $grouped, $stacked], [$plain, $grouped, $stacked], $presets); self::assertSame(1, $loads, 'read once, for the first block that has a preset'); self::assertCount(1, $covering); } public function testOpaqueAndForeignBlocksAreSkipped(): void { $loads = 0; $attrs = self::button('#000000'); $opaque = Block::parsed( 'divi/button', $attrs + ['globalModule' => '9'], '', true, true ); $foreign = Block::create('core/paragraph', $attrs); [$covering] = self::collect([$opaque, $foreign], [$opaque, $foreign], self::presets($loads)); self::assertSame([], $covering); self::assertSame(0, $loads); } }