Stored format like GlobalPreset::get_data(). */ public static function stored(): array { return [ 'module' => [ 'divi/button' => ['default' => '', 'items' => [ 'aaaaaaaaaa' => ['type' => 'module', 'id' => 'aaaaaaaaaa', 'name' => 'CTA primär', 'moduleName' => 'divi/button', 'attrs' => ['button' => ['x' => 1]]], 'bbbbbbbbbb' => ['type' => 'module', 'id' => 'bbbbbbbbbb', 'name' => 'Doppelt', 'moduleName' => 'divi/button', 'attrs' => []], 'cccccccccc' => ['type' => 'module', 'id' => 'cccccccccc', 'name' => 'doppelt', 'moduleName' => 'divi/button', 'attrs' => []], ]], 'divi/text' => ['default' => 'dddddddddd', 'items' => [ 'dddddddddd' => ['type' => 'module', 'id' => 'dddddddddd', 'name' => 'Fließtext', 'moduleName' => 'divi/text', 'attrs' => [], 'groupPresets' => ['content.decoration.bodyFont' => [ 'presetId' => ['gggggggggg'], 'groupName' => 'divi/font-body']]], ]], ], 'group' => [ 'divi/font-body' => ['default' => '', 'items' => [ 'gggggggggg' => ['type' => 'group', 'id' => 'gggggggggg', 'name' => 'Lesetext', 'groupName' => 'divi/font-body', 'groupId' => 'content.decoration.bodyFont', 'moduleName' => 'divi/text', 'attrs' => []], ]], ], ]; } /** @return array stored() with HTML in one preset and CSS with ">" in another. */ public static function storedWithMarkup(): array { $stored = self::stored(); $stored['module']['divi/button']['items']['bbbbbbbbbb']['attrs'] = [ 'module' => ['advanced' => ['html' => ['desktop' => ['value' => ['htmlBefore' => 'x']]]]], ]; $stored['group']['divi/font-body']['items']['gggggggggg']['styleAttrs'] = [ 'css' => ['desktop' => ['value' => ['freeForm' => 'selector > p { color: red; }']]], ]; return $stored; } /** * stored() with references: CTA primär uses color gcid-holz000001, Doppelt variable gvid-slogan0001 (whole with * before/after, and embedded in a longer value), Lesetext variable gvid-radius0001 in its styleAttrs. * * @return array */ public static function storedWithReferences(): array { $stored = self::stored(); $stored['module']['divi/button']['items']['aaaaaaaaaa']['attrs'] = [ 'button' => ['decoration' => ['background' => ['desktop' => ['value' => [ 'color' => VariableRef::serialize('color', 'gcid-holz000001', []), ]]]]], ]; $stored['module']['divi/button']['items']['bbbbbbbbbb']['attrs'] = [ 'button' => ['innerContent' => ['desktop' => ['value' => [ 'text' => VariableRef::serialize('content', 'gvid-slogan0001', ['before' => 'Ab ', 'after' => '!']), ]]]], 'module' => ['meta' => ['adminLabel' => ['desktop' => [ 'value' => 'Vorher ' . VariableRef::serialize('content', 'gvid-slogan0001', []), ]]]], ]; $stored['group']['divi/font-body']['items']['gggggggggg']['styleAttrs'] = [ 'font' => ['size' => VariableRef::serialize('content', 'gvid-radius0001', [])], ]; return $stored; } public function testReferencingListsThePresetsThatNameAColorOrVariable(): void { $book = PresetBook::fromDivi(self::storedWithReferences()); self::assertSame( [['id' => 'aaaaaaaaaa', 'name' => 'CTA primär', 'scope' => 'divi/button']], $book->referencing('gcid-holz000001') ); self::assertSame( [['id' => 'bbbbbbbbbb', 'name' => 'Doppelt', 'scope' => 'divi/button']], $book->referencing('gvid-slogan0001') ); self::assertSame( [['id' => 'gggggggggg', 'name' => 'Lesetext', 'scope' => 'divi/font-body']], $book->referencing('gvid-radius0001') ); self::assertSame([], $book->referencing('gvid-fehlt00001')); } public function testWithVariableResolvedReplacesWholeReferencesOnly(): void { $stored = self::storedWithReferences(); $button = new PresetScope('module', 'divi/button'); $book = PresetBook::fromDivi($stored)->withVariableResolved('gvid-slogan0001', 'Holz mit Herz'); $attrs = $book->items($button)['bbbbbbbbbb']['attrs']; self::assertSame('Holz mit Herz', $attrs['button']['innerContent']['desktop']['value']['text']); self::assertSame( $stored['module']['divi/button']['items']['bbbbbbbbbb']['attrs']['module'], $attrs['module'], 'an embedded reference stays' ); self::assertSame($stored['module']['divi/button']['items']['aaaaaaaaaa'], $book->items($button)['aaaaaaaaaa']); } /** Ruling V9: a strings variable keeps the before/after of the reference. */ public function testWithVariableResolvedFramesTheValueWithBeforeAndAfter(): void { $book = PresetBook::fromDivi(self::storedWithReferences()) ->withVariableResolved('gvid-slogan0001', 'Holz mit Herz', true); self::assertSame( 'Ab Holz mit Herz!', $book->items(new PresetScope('module', 'divi/button'))['bbbbbbbbbb']['attrs']['button']['innerContent'] ['desktop']['value']['text'] ); } public function testWithVariableResolvedReachesStyleAttrsAndLeavesOtherPresetsAlone(): void { $stored = self::storedWithReferences(); $book = PresetBook::fromDivi($stored)->withVariableResolved('gvid-radius0001', '8px'); self::assertSame( ['font' => ['size' => '8px']], $book->items(new PresetScope('group', 'divi/font-body'))['gggggggggg']['styleAttrs'] ); self::assertSame( PresetBook::fromDivi($stored)->items(new PresetScope('module', 'divi/button')), $book->items(new PresetScope('module', 'divi/button')) ); self::assertSame( PresetBook::fromDivi($stored)->data(), PresetBook::fromDivi($stored)->withVariableResolved('gvid-fehlt00001', 'x')->data() ); } /** Final review I-2: presets whose HTML/CSS Divi's prepare_data() would strip for editors. */ public function testMarkupOutsideListsUntouchedPresetsWithHtmlOrCss(): void { $book = PresetBook::fromDivi(self::storedWithMarkup()); self::assertSame([], PresetBook::fromDivi(self::stored())->markupOutside([])); self::assertSame(['bbbbbbbbbb', 'gggggggggg'], $book->markupOutside([])); self::assertSame(['gggggggggg'], $book->markupOutside(['bbbbbbbbbb', 'aaaaaaaaaa'])); self::assertSame([], $book->markupOutside(['bbbbbbbbbb', 'gggggggggg'])); } public function testFindsByIdOrUniqueName(): void { $book = PresetBook::fromDivi(self::stored()); $button = new PresetScope('module', 'divi/button'); self::assertSame('aaaaaaaaaa', $book->find($button, 'aaaaaaaaaa')['id']); self::assertSame('aaaaaaaaaa', $book->find($button, 'cta PRIMÄR')['id']); self::assertNull($book->find($button, 'Fehlt')); self::assertNull($book->find(new PresetScope('module', 'divi/text'), 'aaaaaaaaaa')); self::assertSame(['bbbbbbbbbb', 'cccccccccc'], $book->idsNamed($button, 'DOPPELT')); } public function testAmbiguousNameListsTheIds(): void { $this->expectException(InvalidArgumentException::class); $this->expectExceptionMessage( 'Preset-Name "Doppelt" ist mehrdeutig; bitte die ID angeben: bbbbbbbbbb, cccccccccc.' ); PresetBook::fromDivi(self::stored())->find(new PresetScope('module', 'divi/button'), 'Doppelt'); } public function testStdClassGroupsBecomeArraysAndCountCoversBothKinds(): void { $raw = self::stored(); $raw['group'] = (object) ['divi/font-body' => (object) $raw['group']['divi/font-body']]; $book = PresetBook::fromDivi($raw); self::assertSame(5, $book->count()); self::assertIsArray($book->data()['group']['divi/font-body']['items']); self::assertSame('group', $book->scopeOf('gggggggggg')?->kind); self::assertNull($book->scopeOf('zzzzzzzzzz')); self::assertSame(0, PresetBook::fromDivi(new stdClass())->count()); } public function testWithWithoutAndDefaultAreImmutable(): void { $book = PresetBook::fromDivi(self::stored()); $button = new PresetScope('module', 'divi/button'); $new = [ 'type' => 'module', 'id' => 'eeeeeeeeee', 'name' => 'Neu', 'moduleName' => 'divi/button', 'attrs' => [], ]; $added = $book->with($button, $new)->withDefault($button, 'eeeeeeeeee'); $removed = $added->without($button, 'eeeeeeeeee'); self::assertSame(5, $book->count()); self::assertSame(6, $added->count()); self::assertSame('eeeeeeeeee', $added->defaultId($button)); self::assertSame('', $removed->defaultId($button), 'removing the default clears it'); self::assertNotSame($book->rev(), $added->rev()); self::assertSame($book->rev(), PresetBook::fromDivi(self::stored())->rev()); self::assertSame('', $book->defaultId(new PresetScope('group', 'divi/spacing'))); } public function testNestedUsersOfAGroupPreset(): void { $users = PresetBook::fromDivi(self::stored())->nestedUsers('gggggggggg'); self::assertCount(1, $users); self::assertSame(['dddddddddd', 'Fließtext'], [$users[0]['id'], $users[0]['name']]); self::assertSame('divi/text', $users[0]['scope']->name); } public function testRestFormatListsItemsPerName(): void { $rest = PresetBook::fromDivi(self::stored())->toRest(); self::assertSame('dddddddddd', $rest['module'][1]['default']); self::assertSame(['aaaaaaaaaa', 'bbbbbbbbbb', 'cccccccccc'], array_column($rest['module'][0]['items'], 'id')); self::assertSame('gggggggggg', $rest['group'][0]['items'][0]['id']); self::assertTrue(array_is_list($rest['module'])); } public function testNewIdsLookLikeTheBuildersIds(): void { self::assertMatchesRegularExpression('/^[0-9a-z]{10}$/', PresetBook::newId()); self::assertNotSame(PresetBook::newId(), PresetBook::newId()); } public function testWithoutNestedRefRemovesTheIdFromEveryStack(): void { $stored = self::stored(); $stored['module']['divi/button']['items']['aaaaaaaaaa']['groupPresets'] = [ 'button.decoration.font' => ['presetId' => ['hhhhhhhhhh', 'gggggggggg'], 'groupName' => 'divi/font'], ]; $book = PresetBook::fromDivi($stored); $after = $book->withoutNestedRef('gggggggggg'); self::assertSame([], $after->nestedUsers('gggggggggg')); self::assertSame([], $after->items(new PresetScope('module', 'divi/text'))['dddddddddd']['groupPresets']); self::assertSame( ['button.decoration.font' => ['presetId' => ['hhhhhhhhhh'], 'groupName' => 'divi/font']], $after->items(new PresetScope('module', 'divi/button'))['aaaaaaaaaa']['groupPresets'] ); self::assertCount(2, $book->nestedUsers('gggggggggg'), 'immutable'); self::assertSame( [['dddddddddd', 'changed', ['groupPresets']], ['aaaaaaaaaa', 'changed', ['groupPresets']]], array_map( static fn (array $row): array => [$row['id'], $row['action'], $row['fields']], array_reverse(PresetDiff::between($book, $after)) ) ); } public function testWithoutNestedRefTakesAStringIdAndLeavesOthersAlone(): void { $book = PresetBook::fromDivi(self::stored()); self::assertSame($book->rev(), $book->withoutNestedRef('zzzzzzzzzz')->rev()); $stored = self::stored(); $text = &$stored['module']['divi/text']['items']['dddddddddd']; $text['groupPresets']['content.decoration.bodyFont']['presetId'] = 'gggggggggg'; unset($text); self::assertSame([], PresetBook::fromDivi($stored)->withoutNestedRef('gggggggggg')->nestedUsers('gggggggggg')); } }