(array) ($sample['defaults'][$name] ?? []) ); } public function testKnowsModulesTitlesCategoriesAndChildren(): void { $catalog = self::sampleCatalog(); self::assertTrue($catalog->has('divi/button')); self::assertFalse($catalog->has('button')); self::assertSame('divi/button', ModuleCatalog::normalize(' Button ')); self::assertSame('divi/accordion-item', ModuleCatalog::normalize('divi/accordion-item')); self::assertSame('structure', $catalog->category('divi/section')); self::assertSame(['divi/accordion-item'], $catalog->childrenOf('divi/accordion')); self::assertSame(['divi/column'], $catalog->childrenOf('divi/row')); self::assertContains( ['name' => 'divi/heading', 'title' => 'Heading', 'category' => 'module', 'children' => []], $catalog->summary() ); } public function testDerivesAttributePathsFromSettingsAndDefaults(): void { $catalog = self::sampleCatalog(); foreach ( [ 'button.innerContent', 'button.decoration.button', 'button.decoration.background', 'module.advanced.alignment', 'module.decoration.spacing', 'module.meta.adminLabel', 'module.advanced.text.text', ] as $path ) { self::assertTrue($catalog->isKnownPath('divi/button', $path), $path); } self::assertTrue($catalog->isKnownPath('divi/heading', 'title.decoration.font.font')); self::assertFalse($catalog->isKnownPath('divi/heading', 'title.decoration.font')); self::assertTrue($catalog->isKnownPath('divi/text', 'content.decoration.bodyFont.body.font')); self::assertTrue($catalog->isKnownPath('divi/accordion-item', 'module.advanced.open')); self::assertTrue($catalog->isKnownPath('divi/column', 'module.decoration.sizing')); self::assertFalse($catalog->isKnownPath('divi/button', 'button.innerContent.text')); self::assertSame('title.decoration.font.font', $catalog->suggestions('divi/heading', 'title.decoration.fnt.font')[0]); } public function testPathIndexFromHandwrittenMetadata(): void { $paths = AttrPathIndex::paths( [ 'title' => ['settings' => [ 'innerContent' => ['groupType' => 'group-item', 'item' => ['attrName' => 'title.innerContent']], 'decoration' => ['font' => [], 'groupType' => 'x'], ]], 'module' => ['settings' => [ 'meta' => ['meta' => []], 'advanced' => ['text' => ['item' => ['attrName' => 'module.advanced.alignment']]], ]], ], ['module' => ['advanced' => ['open' => ['desktop' => ['value' => 'off']]]], 'builderVersion' => '5'] ); self::assertSame( [ 'module.advanced.alignment', 'module.advanced.open', 'module.advanced.text.text', 'module.advanced.text.textShadow', 'module.meta.adminLabel', 'title.decoration.font.font', 'title.decoration.font.textShadow', 'title.innerContent', ], $paths ); } public function testEveryShortformPathIsKnownForTheSampledModules(): void { $catalog = self::sampleCatalog(); foreach (['text', 'heading', 'button', 'image', 'blurb', 'code', 'accordion-item'] as $type) { foreach (ShortformMap::fields($type) as $field => $spec) { self::assertTrue( $catalog->isKnownPath('divi/' . $type, $spec['path']), sprintf('%s.%s → %s', $type, $field, $spec['path']) ); } } } public function testShortformTableIsConsistent(): void { $kinds = ['text', 'html', 'code', 'url', 'media', 'enum', 'onoff', 'level', 'number', 'ids', 'color']; foreach (ShortformMap::types() as $type) { $fields = ShortformMap::fields($type); $primary = ShortformMap::primary($type); self::assertTrue($primary === null || isset($fields[$primary]), $type . ' primary'); $child = ShortformMap::childType($type); self::assertTrue($child === null || ShortformMap::has($child), $type . ' child'); foreach ($fields as $field => $spec) { self::assertContains($spec['kind'], $kinds, $type . '.' . $field); self::assertSame(1, preg_match('/^[a-zA-Z]+(\.[a-zA-Z]+)+$/', $spec['path']), $type . '.' . $field); self::assertSame($spec['kind'] === 'media', isset($spec['keys']), $type . '.' . $field); self::assertSame($spec['kind'] === 'enum', isset($spec['values']), $type . '.' . $field); } } self::assertSame( ['text' => 'Termin', 'url' => '/kontakt'], ShortformMap::values('button', ['button' => ['innerContent' => ['desktop' => ['value' => [ 'text' => 'Termin', 'linkUrl' => '/kontakt', ]]]]]) ); } public function testDerivesAttributePathKindsFromComponentMetadataWherePossible(): void { $catalog = self::sampleCatalog(); self::assertSame('richtext', $catalog->pathKind('divi/text', 'content.innerContent')); self::assertSame('text', $catalog->pathKind('divi/heading', 'title.innerContent')); self::assertSame('code', $catalog->pathKind('divi/code', 'content.innerContent')); self::assertSame('url', $catalog->pathKind('divi/button', 'button.innerContent')); self::assertSame('text', $catalog->pathKind('divi/button', 'module.meta.adminLabel')); // Structural/style groups (fonts, backgrounds, …) carry no single scalar component in // Divi's metadata; 2b falls back to key-name routing for them (spec §5.6, ruling R3). self::assertSame('unknown', $catalog->pathKind('divi/button', 'button.decoration.background')); self::assertSame('unknown', $catalog->pathKind('divi/button', 'no.such.path')); // Every kind in the module's kinds() map is a string, keyed by a known attribute path. $paths = $catalog->paths('divi/heading'); foreach ($catalog->kinds('divi/heading') as $path => $kind) { self::assertIsString($kind); self::assertContains($path, $paths); } } }