From 5b397d02c27427fdc3471303404c5d5804da96b6 Mon Sep 17 00:00:00 2001 From: Wim Leers <wim.leers@acquia.com> Date: Wed, 7 May 2025 16:43:13 +0200 Subject: [PATCH 01/22] Add missing test coverage. --- .../Kernel/Config/ComponentValidationTest.php | 29 ++++++++++ tests/src/Kernel/PropSourceTest.php | 58 +++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/tests/src/Kernel/Config/ComponentValidationTest.php b/tests/src/Kernel/Config/ComponentValidationTest.php index e131cf494b..2476b40601 100644 --- a/tests/src/Kernel/Config/ComponentValidationTest.php +++ b/tests/src/Kernel/Config/ComponentValidationTest.php @@ -12,6 +12,7 @@ use Drupal\experience_builder\Plugin\ComponentPluginManager; use Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\BlockComponent; use Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\JsComponent; use Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\SingleDirectoryComponent; +use Drupal\Tests\experience_builder\Traits\BetterConfigDependencyManagerTrait; use Drupal\Tests\experience_builder\Traits\ContribStrictConfigSchemaTestTrait; use Drupal\Tests\experience_builder\Traits\GenerateComponentConfigTrait; use Symfony\Component\Yaml\Yaml; @@ -24,6 +25,7 @@ use Symfony\Component\Yaml\Yaml; */ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { + use BetterConfigDependencyManagerTrait; use ContribStrictConfigSchemaTestTrait; use GenerateComponentConfigTrait; @@ -116,6 +118,33 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { $this->componentPluginManager = $this->container->get(ComponentPluginManager::class); } + /** + * {@inheritdoc} + */ + public function testEntityIsValid(): void { + parent::testEntityIsValid(); + + // @todo Change the tested component here to one that includes an `image` prop shape, to allow testing with entity reference field type + media module dependency + media library dependency ONLY because of the widget. + + // Beyond validity, validate config dependencies are computed correctly. + $this->assertSame( + [ + 'module' => [ + 'options', + 'sdc_test', + ], + ], + $this->entity->getDependencies() + ); + $this->assertSame([ + 'module' => [ + 'options', + 'sdc_test', + 'experience_builder', + ], + ], $this->getAllDependencies($this->entity)); + } + /** * @covers `type: experience_builder.component_source_settings.*` * @covers `type: experience_builder.generated_field_explicit_input_ux` diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index b883d370f2..161e4df9fc 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -25,6 +25,7 @@ use Drupal\experience_builder\PropSource\DynamicPropSource; use Drupal\experience_builder\PropSource\PropSource; use Drupal\experience_builder\PropSource\StaticPropSource; use Drupal\KernelTests\KernelTestBase; +use Drupal\media\Entity\MediaType; use Drupal\node\Entity\NodeType; use Drupal\Tests\experience_builder\Traits\ContribStrictConfigSchemaTestTrait; use Drupal\Tests\node\Traits\NodeCreationTrait; @@ -49,10 +50,25 @@ class PropSourceTest extends KernelTestBase { 'user', 'datetime', 'datetime_range', + 'media', + 'media_library', 'system', 'media', + 'views', ]; + /** + * {@inheritdoc} + */ + protected function setUp(): void { + parent::setUp(); + MediaType::create([ + 'id' => 'image', + 'label' => 'Image', + 'source' => 'image', + ])->save(); + } + /** * @coversClass \Drupal\experience_builder\PropSource\StaticPropSource */ @@ -160,6 +176,48 @@ class PropSourceTest extends KernelTestBase { ], ], $complex_example->calculateDependencies()); + // A complex *empty* example. + $complex_empty_example = StaticPropSource::parse([ + 'sourceType' => 'static:field_item:entity_reference', + 'value' => NULL, + 'expression' => 'ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}', + 'sourceTypeSettings' => [ + 'storage' => ['target_type' => 'media'], + 'instance' => [ + 'handler' => 'default:media', + 'handler_settings' => [ + 'target_bundles' => ['image' => 'image'], + ], + ], + ], + ]); + // First, get the string representation and parse it back, to prove + // serialization and deserialization works. + $json_representation = (string) $complex_empty_example; + $this->assertSame('{"sourceType":"static:field_item:entity_reference","value":null,"expression":"ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}","sourceTypeSettings":{"storage":{"target_type":"media"},"instance":{"handler":"default:media","handler_settings":{"target_bundles":{"image":"image"}}}}}', $json_representation); + $decoded_representation = json_decode($json_representation, TRUE); + $complex_empty_example = PropSource::parse($decoded_representation); + $this->assertInstanceOf(StaticPropSource::class, $complex_empty_example); + // The contained information read back out. + $this->assertSame('static:field_item:entity_reference', $complex_empty_example->getSourceType()); + $this->assertInstanceOf(FieldTypeObjectPropsExpression::class, StructuredDataPropExpression::fromString($complex_empty_example->asChoice())); + $this->assertNull($complex_empty_example->getValue()); + self::assertSame([ + 'config' => [ + 'media.type.image', + ], + 'content' => [], + 'module' => [ + 'media', + ], + 'plugin' => [ + 'field_type:entity_reference', + 'field_type:entity_reference', + 'field_type:entity_reference', + 'field_type:entity_reference', + ], + ], $complex_empty_example->calculateDependencies()); + // A simple (expression targeting a simple prop) array example (with // cardinality specified, rather than the default of `cardinality=1`). $simple_array_example = StaticPropSource::parse([ -- GitLab From 9bef31a7e566cf4fdb71231ed20a0bdc3382ea6a Mon Sep 17 00:00:00 2001 From: Wim Leers <wim.leers@acquia.com> Date: Wed, 7 May 2025 16:43:35 +0200 Subject: [PATCH 02/22] Fix `StaticPropSource::calculateDependencies()`. --- src/PropSource/StaticPropSource.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/PropSource/StaticPropSource.php b/src/PropSource/StaticPropSource.php index bd6460d5d3..7b2b7a9526 100644 --- a/src/PropSource/StaticPropSource.php +++ b/src/PropSource/StaticPropSource.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\experience_builder\PropSource; +use Drupal\Component\Utility\NestedArray; use Drupal\Core\Datetime\DrupalDateTime; use Drupal\Core\Entity\FieldableEntityInterface; use Drupal\Core\Entity\Plugin\DataType\EntityAdapter; @@ -481,7 +482,21 @@ final class StaticPropSource extends PropSourceBase { // calculated dependencies will be limited to the entity types, bundle (if // any) and fields (if any) that this expression depends on. // @see \Drupal\Tests\experience_builder\Kernel\PropExpressionDependenciesTest - return $this->expression->calculateDependencies($this->fieldItemList); + $expression_deps = $this->expression->calculateDependencies($this->fieldItemList); + + // Let the field type plugin specify its own dependencies, based on storage + // settings and instance settings. + $field_item_class = $this->fieldItemList->getItemDefinition()->getClass(); + $instance_deps = $field_item_class::calculateDependencies($this->fieldItemList->getFieldDefinition()); + $storage_deps = $field_item_class::calculateStorageDependencies($this->fieldItemList->getFieldDefinition()->getFieldStorageDefinition()); + + $dependencies = NestedArray::mergeDeep( + $expression_deps, + $instance_deps, + $storage_deps, + ); + ksort($dependencies); + return $dependencies; } } -- GitLab From f0a32f345caa0d96d31e7d8acba64f93b4eb9dfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Thu, 15 May 2025 09:02:45 +0200 Subject: [PATCH 03/22] Issue #3460230: Return unique and sorted dependencies. --- src/PropSource/StaticPropSource.php | 5 +++++ tests/src/Kernel/PropSourceTest.php | 5 ----- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/PropSource/StaticPropSource.php b/src/PropSource/StaticPropSource.php index 7b2b7a9526..54175e8dc6 100644 --- a/src/PropSource/StaticPropSource.php +++ b/src/PropSource/StaticPropSource.php @@ -496,6 +496,11 @@ final class StaticPropSource extends PropSourceBase { $storage_deps, ); ksort($dependencies); + $dependencies = array_map(function ($values) { + $values = array_unique($values); + ksort($values); + return $values; + }, $dependencies); return $dependencies; } diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index 161e4df9fc..463d979a4c 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -168,11 +168,9 @@ class PropSourceTest extends KernelTestBase { self::assertSame([ 'module' => [ 'datetime_range', - 'datetime_range', ], 'plugin' => [ 'field_type:daterange', - 'field_type:daterange', ], ], $complex_example->calculateDependencies()); @@ -212,9 +210,6 @@ class PropSourceTest extends KernelTestBase { ], 'plugin' => [ 'field_type:entity_reference', - 'field_type:entity_reference', - 'field_type:entity_reference', - 'field_type:entity_reference', ], ], $complex_empty_example->calculateDependencies()); -- GitLab From 7ef84f4951ebb5ba7b69a60fbbe798acd91de67f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Thu, 15 May 2025 09:16:33 +0200 Subject: [PATCH 04/22] Issue #3460230: Inline variable and reindex the dependencies array. --- src/PropSource/StaticPropSource.php | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/PropSource/StaticPropSource.php b/src/PropSource/StaticPropSource.php index 54175e8dc6..c7f9a3133e 100644 --- a/src/PropSource/StaticPropSource.php +++ b/src/PropSource/StaticPropSource.php @@ -496,12 +496,11 @@ final class StaticPropSource extends PropSourceBase { $storage_deps, ); ksort($dependencies); - $dependencies = array_map(function ($values) { + return array_map(static function ($values) { $values = array_unique($values); - ksort($values); + sort($values); return $values; }, $dependencies); - return $dependencies; } } -- GitLab From 5debfa446c13018dff4232fff5c9218516c7831c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Thu, 15 May 2025 09:24:18 +0200 Subject: [PATCH 05/22] Issue #3460230: return unique dependencies for component sources. --- .../GeneratedFieldExplicitInputUxComponentSourceBase.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/Plugin/ExperienceBuilder/ComponentSource/GeneratedFieldExplicitInputUxComponentSourceBase.php b/src/Plugin/ExperienceBuilder/ComponentSource/GeneratedFieldExplicitInputUxComponentSourceBase.php index 8393dc4ea5..f94837d7ef 100644 --- a/src/Plugin/ExperienceBuilder/ComponentSource/GeneratedFieldExplicitInputUxComponentSourceBase.php +++ b/src/Plugin/ExperienceBuilder/ComponentSource/GeneratedFieldExplicitInputUxComponentSourceBase.php @@ -123,7 +123,12 @@ abstract class GeneratedFieldExplicitInputUxComponentSourceBase extends Componen $dependencies['module'][] = $field_widget_definition['provider']; } - return $dependencies; + ksort($dependencies); + return array_map(static function ($values) { + $values = array_unique($values); + sort($values); + return $values; + }, $dependencies); } /** -- GitLab From 9362d1af00af583d3b42fbdecf63a17551fb4094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Thu, 15 May 2025 09:39:39 +0200 Subject: [PATCH 06/22] Issue #3460320: Fix dependencies tests. --- .../SingleDirectoryComponentTest.php | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/tests/src/Kernel/Plugin/ExperienceBuilder/ComponentSource/SingleDirectoryComponentTest.php b/tests/src/Kernel/Plugin/ExperienceBuilder/ComponentSource/SingleDirectoryComponentTest.php index 5ef2e3744a..c7fa13dc6a 100644 --- a/tests/src/Kernel/Plugin/ExperienceBuilder/ComponentSource/SingleDirectoryComponentTest.php +++ b/tests/src/Kernel/Plugin/ExperienceBuilder/ComponentSource/SingleDirectoryComponentTest.php @@ -707,44 +707,38 @@ HTML, self::assertSame([ 'sdc.xb_test_sdc.crash' => [ 'module' => [ - 'core', 'core', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.deprecated' => [ 'module' => [ - 'core', 'core', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.experimental' => [ 'module' => [ - 'core', 'core', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.grid-container' => [ 'module' => [ - 'options', 'core', + 'options', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.image-gallery' => [ 'module' => [ - 'core', 'core', 'image', - 'image', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.image-optional-with-example' => [ 'module' => [ - 'image', 'image', 'xb_test_sdc', ], @@ -752,43 +746,36 @@ HTML, 'sdc.xb_test_sdc.image-optional-with-example-and-additional-prop' => [ 'module' => [ 'core', - 'core', - 'image', 'image', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.image-optional-without-example' => [ 'module' => [ - 'image', 'image', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.image-required-with-example' => [ 'module' => [ - 'image', 'image', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.props-no-slots' => [ 'module' => [ - 'core', 'core', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.props-slots' => [ 'module' => [ - 'core', 'core', 'xb_test_sdc', ], ], 'sdc.xb_test_sdc.sparkline' => [ 'module' => [ - 'core', 'core', 'xb_test_sdc', ], -- GitLab From 910da164d72ff24339e6646e2d52af50e629c0cf Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Mon, 19 May 2025 12:41:47 -0400 Subject: [PATCH 07/22] assert field item class --- src/PropSource/StaticPropSource.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/PropSource/StaticPropSource.php b/src/PropSource/StaticPropSource.php index c7f9a3133e..ea92626580 100644 --- a/src/PropSource/StaticPropSource.php +++ b/src/PropSource/StaticPropSource.php @@ -487,6 +487,7 @@ final class StaticPropSource extends PropSourceBase { // Let the field type plugin specify its own dependencies, based on storage // settings and instance settings. $field_item_class = $this->fieldItemList->getItemDefinition()->getClass(); + assert(is_subclass_of($field_item_class, FieldItemInterface::class)); $instance_deps = $field_item_class::calculateDependencies($this->fieldItemList->getFieldDefinition()); $storage_deps = $field_item_class::calculateStorageDependencies($this->fieldItemList->getFieldDefinition()->getFieldStorageDefinition()); -- GitLab From 6a0be485414dec5b471b6616454decfa67670d02 Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Mon, 19 May 2025 15:33:48 -0400 Subject: [PATCH 08/22] set up source field for media in PropSourceTest --- src/Hook/ShapeMatchingHooks.php | 1 - tests/src/Kernel/PropSourceTest.php | 23 +++++++++++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/src/Hook/ShapeMatchingHooks.php b/src/Hook/ShapeMatchingHooks.php index d0d0f0a81d..c7df951677 100644 --- a/src/Hook/ShapeMatchingHooks.php +++ b/src/Hook/ShapeMatchingHooks.php @@ -244,7 +244,6 @@ class ShapeMatchingHooks { } $source_field_definition = $image_media_type->getSource() ->getSourceFieldDefinition($image_media_type); - \assert($source_field_definition !== \NULL); $source_field_name = $source_field_definition->getName(); $storable_prop_shape->fieldTypeProp = new FieldTypeObjectPropsExpression('entity_reference', [ 'src' => new ReferenceFieldTypePropExpression(new FieldTypePropExpression('entity_reference', 'entity'), new ReferenceFieldPropExpression(new FieldPropExpression(EntityDataDefinition::create('media', 'image'), $source_field_name, \NULL, 'entity'), new FieldPropExpression(EntityDataDefinition::create('file'), 'uri', \NULL, 'url'))), diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index 463d979a4c..bd37303f0e 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -46,6 +46,8 @@ class PropSourceTest extends KernelTestBase { */ protected static $modules = [ 'experience_builder', + 'field', + 'file', 'node', 'user', 'datetime', @@ -62,11 +64,24 @@ class PropSourceTest extends KernelTestBase { */ protected function setUp(): void { parent::setUp(); - MediaType::create([ + $this->installEntitySchema('field_storage_config'); + $this->installEntitySchema('field_config'); + $media_type = MediaType::create([ 'id' => 'image', 'label' => 'Image', - 'source' => 'image', - ])->save(); + 'source' => 'file', + ]); + $media_type->save(); + // Create the source field. + $source_field = $media_type->getSource()->createSourceField($media_type); + $source_field->getFieldStorageDefinition()->save(); + + $source_field->save(); + $media_type + ->set('source_configuration', [ + 'source_field' => $source_field->getName(), + ]) + ->save(); } /** @@ -651,7 +666,7 @@ class PropSourceTest extends KernelTestBase { * @coversClass \Drupal\experience_builder\PropSource\DefaultRelativeUrlPropSource */ public function testDefaultRelativeUrlPropSource(): void { - $this->enableModules(['xb_test_sdc', 'link', 'image', 'file', 'options']); + $this->enableModules(['xb_test_sdc', 'link', 'image', 'options']); // Force rebuilding of the definitions which will create the required // component. $plugin_manager = $this->container->get(ComponentPluginManager::class); -- GitLab From 373166d315c3ae055cf616f942630a42bdc46007 Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Mon, 19 May 2025 15:40:03 -0400 Subject: [PATCH 09/22] dependencies are now ensured not to have the same value more than once --- .../ExperienceBuilder/ComponentSource/JsComponentTest.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/src/Kernel/Plugin/ExperienceBuilder/ComponentSource/JsComponentTest.php b/tests/src/Kernel/Plugin/ExperienceBuilder/ComponentSource/JsComponentTest.php index c49e9b253d..50bc468ed1 100644 --- a/tests/src/Kernel/Plugin/ExperienceBuilder/ComponentSource/JsComponentTest.php +++ b/tests/src/Kernel/Plugin/ExperienceBuilder/ComponentSource/JsComponentTest.php @@ -483,7 +483,6 @@ final class JsComponentTest extends ComponentSourceTestBase { 'js.xb_test_code_components_vanilla_image' => [ 'module' => [ 'image', - 'image', ], 'config' => [ 'experience_builder.js_component.xb_test_code_components_vanilla_image', @@ -497,9 +496,6 @@ final class JsComponentTest extends ComponentSourceTestBase { 'js.xb_test_code_components_with_props' => [ 'module' => [ 'core', - 'core', - 'core', - 'core', ], 'config' => [ 'experience_builder.js_component.xb_test_code_components_with_props', -- GitLab From 98bd34ce675e3595d240f4ea613f8b3929af91c9 Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Mon, 19 May 2025 15:48:56 -0400 Subject: [PATCH 10/22] accidental removal --- src/Hook/ShapeMatchingHooks.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/Hook/ShapeMatchingHooks.php b/src/Hook/ShapeMatchingHooks.php index c7df951677..d0d0f0a81d 100644 --- a/src/Hook/ShapeMatchingHooks.php +++ b/src/Hook/ShapeMatchingHooks.php @@ -244,6 +244,7 @@ class ShapeMatchingHooks { } $source_field_definition = $image_media_type->getSource() ->getSourceFieldDefinition($image_media_type); + \assert($source_field_definition !== \NULL); $source_field_name = $source_field_definition->getName(); $storable_prop_shape->fieldTypeProp = new FieldTypeObjectPropsExpression('entity_reference', [ 'src' => new ReferenceFieldTypePropExpression(new FieldTypePropExpression('entity_reference', 'entity'), new ReferenceFieldPropExpression(new FieldPropExpression(EntityDataDefinition::create('media', 'image'), $source_field_name, \NULL, 'entity'), new FieldPropExpression(EntityDataDefinition::create('file'), 'uri', \NULL, 'url'))), -- GitLab From ff5b64128873f5d469c7e05009f51098a200e21a Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Mon, 19 May 2025 15:49:33 -0400 Subject: [PATCH 11/22] phpstan --- tests/src/Kernel/PropSourceTest.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index bd37303f0e..ffe305c851 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -4,6 +4,7 @@ declare(strict_types=1); namespace Drupal\Tests\experience_builder\Kernel; +use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\Plugin\Field\FieldWidget\NumberWidget; use Drupal\Core\Extension\ExtensionPathResolver; @@ -74,7 +75,9 @@ class PropSourceTest extends KernelTestBase { $media_type->save(); // Create the source field. $source_field = $media_type->getSource()->createSourceField($media_type); - $source_field->getFieldStorageDefinition()->save(); + $field_storage_definition = $source_field->getFieldStorageDefinition(); + assert($field_storage_definition instanceof EntityInterface); + $field_storage_definition->save(); $source_field->save(); $media_type -- GitLab From d88237a833ba7efd2c2157b06a5676033fa72737 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Tue, 20 May 2025 14:48:23 +0200 Subject: [PATCH 12/22] Issue #3460230: Add static:field_item:boolean and simplify simple static tests with a provider. --- tests/src/Kernel/PropSourceTest.php | 194 +++++++++++++++++----------- 1 file changed, 120 insertions(+), 74 deletions(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index ffe305c851..de4899a27d 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -5,10 +5,12 @@ declare(strict_types=1); namespace Drupal\Tests\experience_builder\Kernel; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Extension\ExtensionPathResolver; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\Field\Plugin\Field\FieldWidget\BooleanCheckboxWidget; use Drupal\Core\Field\Plugin\Field\FieldWidget\NumberWidget; -use Drupal\Core\Extension\ExtensionPathResolver; use Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextfieldWidget; +use Drupal\Core\Field\Plugin\Field\FieldWidget\UriWidget; use Drupal\Core\Url; use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem; use Drupal\datetime_range\Plugin\Field\FieldWidget\DateRangeDatelistWidget; @@ -89,18 +91,32 @@ class PropSourceTest extends KernelTestBase { /** * @coversClass \Drupal\experience_builder\PropSource\StaticPropSource + * @dataProvider providerSimpleStaticPropSource */ - public function testStaticPropSource(): void { + public function testSimpleStaticPropSource( + string $source_type, + array|null $source_type_settings, + mixed $value, + string $expression, + string $expected_json_representation, + array $field_widgets, + mixed $expected_user_value, + string $expected_prop_expection_class, + array $expected_dependencies, + ): void { + // A simple example. + // @phpstan-ignore-next-line $simple_example = StaticPropSource::parse([ - 'sourceType' => 'static:field_item:string', - 'value' => 'Hello, world!', - 'expression' => 'ℹ︎string␟value', + 'sourceType' => $source_type, + 'value' => $value, + 'expression' => $expression, + 'sourceTypeSettings' => $source_type_settings, ]); // First, get the string representation and parse it back, to prove // serialization and deserialization works. $json_representation = (string) $simple_example; - $this->assertSame('{"sourceType":"static:field_item:string","value":"Hello, world!","expression":"ℹ︎string␟value"}', $json_representation); + $this->assertSame($expected_json_representation, $json_representation); $decoded_representation = json_decode($json_representation, TRUE); try { StaticPropSource::isMinimalRepresentation($decoded_representation); @@ -111,86 +127,116 @@ class PropSourceTest extends KernelTestBase { $simple_example = PropSource::parse($decoded_representation); $this->assertInstanceOf(StaticPropSource::class, $simple_example); // The contained information read back out. - $this->assertSame('static:field_item:string', $simple_example->getSourceType()); - $this->assertInstanceOf(FieldTypePropExpression::class, StructuredDataPropExpression::fromString($simple_example->asChoice())); - $this->assertSame('Hello, world!', $simple_example->getValue()); + $this->assertSame($source_type, $simple_example->getSourceType()); + /** @var class-string $expected_prop_expection_class */ + $this->assertInstanceOf($expected_prop_expection_class, StructuredDataPropExpression::fromString($simple_example->asChoice())); + $this->assertSame($value, $simple_example->getValue()); // Test the functionality of a StaticPropSource: // - evaluate it to populate an SDC prop - $this->assertSame('Hello, world!', $simple_example->evaluate(User::create([]))); + $this->assertSame($expected_user_value, $simple_example->evaluate(User::create([]))); // - the field type's item's raw value is minimized if it is single-property - $this->assertSame('Hello, world!', $simple_example->getValue()); + $this->assertSame($value, $simple_example->getValue()); // - generate a widget to edit the stored value — using the default widget // or a specified widget. // @see \Drupal\experience_builder\Entity\Component::$defaults - $this->assertInstanceOf(StringTextfieldWidget::class, $simple_example->getWidget('irrelevant-for-test', $this->randomString(), NULL)); - $this->assertInstanceOf(StringTextfieldWidget::class, $simple_example->getWidget('irrelevant-for-test', $this->randomString(), 'string_textfield')); - // The widget plugin manager ignores any request for another widget type and - // falls back to the default widget if - // @see \Drupal\Core\Field\WidgetPluginManager::getInstance() - $this->assertInstanceOf(StringTextfieldWidget::class, $simple_example->getWidget('irrelevant-for-test', $this->randomString(), 'string_textarea')); - self::assertSame([ - 'plugin' => [ - 'field_type:string', - ], - ], $simple_example->calculateDependencies()); + foreach ($field_widgets as $widget_type => $expected_widget_class) { + $this->assertInstanceOf($expected_widget_class, $simple_example->getWidget('irrelevant-for-test', $this->randomString(), $widget_type)); + } + self::assertSame($expected_dependencies, $simple_example->calculateDependencies()); + } - // A complex example. - $complex_example = StaticPropSource::parse([ - 'sourceType' => 'static:field_item:daterange', - 'value' => [ - 'value' => '2020-04-16T00:00', - 'end_value' => '2024-07-10T10:24', + public static function providerSimpleStaticPropSource(): array { + return [ + [ + 'sourceType' => 'static:field_item:string', + 'sourceTypeSettings' => NULL, + 'value' => 'Hello, world!', + 'expression' => 'ℹ︎string␟value', + 'expected_json_representation' => '{"sourceType":"static:field_item:string","value":"Hello, world!","expression":"ℹ︎string␟value"}', + 'field_widgets' => [ + NULL => StringTextfieldWidget::class, + 'string_textfield' => StringTextfieldWidget::class, + 'string_textarea' => StringTextfieldWidget::class, + ], + 'expected_user_value' => 'Hello, world!', + 'expected_prop_expression' => FieldTypePropExpression::class, + 'expected_dependencies' => [ + 'plugin' => [ + 'field_type:string', + ], + ], ], - 'expression' => 'ℹ︎daterange␟{start↠value,stop↠end_value}', - ]); - // First, get the string representation and parse it back, to prove - // serialization and deserialization works. - $json_representation = (string) $complex_example; - $this->assertSame('{"sourceType":"static:field_item:daterange","value":{"value":"2020-04-16T00:00","end_value":"2024-07-10T10:24"},"expression":"ℹ︎daterange␟{start↠value,stop↠end_value}"}', $json_representation); - $decoded_representation = json_decode($json_representation, TRUE); - try { - StaticPropSource::isMinimalRepresentation($decoded_representation); - } - catch (\LogicException) { - $this->fail("Not a minimal representation: $json_representation."); - } - $complex_example = PropSource::parse($decoded_representation); - $this->assertInstanceOf(StaticPropSource::class, $complex_example); - // The contained information read back out. - $this->assertSame('static:field_item:daterange', $complex_example->getSourceType()); - $this->assertInstanceOf(FieldTypeObjectPropsExpression::class, StructuredDataPropExpression::fromString($complex_example->asChoice())); - $this->assertSame([ - 'value' => '2020-04-16T00:00', - 'end_value' => '2024-07-10T10:24', - ], $complex_example->getValue()); - // Test the functionality of a StaticPropSource: - // - evaluate it to populate an SDC prop - $this->assertSame([ - 'start' => '2020-04-16T00:00', - 'stop' => '2024-07-10T10:24', - ], $complex_example->evaluate(User::create([]))); - // - the field type's item's raw value is minimized if it is single-property - $this->assertSame( [ - 'value' => '2020-04-16T00:00', - 'end_value' => '2024-07-10T10:24', + 'sourceType' => 'static:field_item:uri', + 'sourceTypeSettings' => NULL, + 'value' => 'https://drupal.org', + 'expression' => 'ℹ︎uri␟value', + 'expected_json_representation' => '{"sourceType":"static:field_item:uri","value":"https:\/\/drupal.org","expression":"ℹ︎uri␟value"}', + 'field_widgets' => [ + NULL => UriWidget::class, + 'uri' => UriWidget::class, + ], + 'expected_user_value' => 'https://drupal.org', + 'expected_prop_expression' => FieldTypePropExpression::class, + 'expected_dependencies' => [ + 'plugin' => [ + 'field_type:uri', + ], + ], ], - $complex_example->getValue() - ); - // - generate a widget to edit the stored value — using the default widget - // or a specified widget. - // @see \Drupal\experience_builder\Entity\Component::$defaults - $this->assertInstanceOf(DateRangeDefaultWidget::class, $complex_example->getWidget('irrelevant-for-test', $this->randomString(), NULL)); - $this->assertInstanceOf(DateRangeDefaultWidget::class, $complex_example->getWidget('irrelevant-for-test', $this->randomString(), 'daterange_default')); - $this->assertInstanceOf(DateRangeDatelistWidget::class, $complex_example->getWidget('irrelevant-for-test', $this->randomString(), 'daterange_datelist')); - self::assertSame([ - 'module' => [ - 'datetime_range', + [ + 'sourceType' => 'static:field_item:boolean', + 'sourceTypeSettings' => NULL, + 'value' => TRUE, + 'expression' => 'ℹ︎boolean␟value', + 'expected_json_representation' => '{"sourceType":"static:field_item:boolean","value":true,"expression":"ℹ︎boolean␟value"}', + 'field_widgets' => [ + NULL => BooleanCheckboxWidget::class, + 'boolean_checkbox' => BooleanCheckboxWidget::class, + ], + 'expected_user_value' => TRUE, + 'expected_prop_expression' => FieldTypePropExpression::class, + 'expected_dependencies' => [ + 'plugin' => [ + 'field_type:boolean', + ], + ], ], - 'plugin' => [ - 'field_type:daterange', + [ + 'sourceType' => 'static:field_item:daterange', + 'sourceTypeSettings' => NULL, + 'value' => [ + 'value' => '2020-04-16T00:00', + 'end_value' => '2024-07-10T10:24', + ], + 'expression' => 'ℹ︎daterange␟{start↠value,stop↠end_value}', + 'expected_json_representation' => '{"sourceType":"static:field_item:daterange","value":{"value":"2020-04-16T00:00","end_value":"2024-07-10T10:24"},"expression":"ℹ︎daterange␟{start↠value,stop↠end_value}"}', + 'field_widgets' => [ + NULL => DateRangeDefaultWidget::class, + 'daterange_default' => DateRangeDefaultWidget::class, + 'daterange_datelist' => DateRangeDatelistWidget::class, + ], + 'expected_user_value' => [ + 'start' => '2020-04-16T00:00', + 'stop' => '2024-07-10T10:24', + ], + 'expected_prop_expression' => FieldTypeObjectPropsExpression::class, + 'expected_dependencies' => [ + 'module' => [ + 'datetime_range', + ], + 'plugin' => [ + 'field_type:daterange', + ], + ], ], - ], $complex_example->calculateDependencies()); + ]; + } + + /** + * @coversClass \Drupal\experience_builder\PropSource\StaticPropSource + */ + public function testStaticPropSource(): void { // A complex *empty* example. $complex_empty_example = StaticPropSource::parse([ -- GitLab From a9adf71988a9f3a75723ed40339d44f65f259630 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Tue, 20 May 2025 15:51:26 +0200 Subject: [PATCH 13/22] Issue #3460230: Convert provider to Generator for Dx --- tests/src/Kernel/PropSourceTest.php | 150 ++++++++++++++-------------- 1 file changed, 74 insertions(+), 76 deletions(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index de4899a27d..0dd7857c89 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -145,89 +145,87 @@ class PropSourceTest extends KernelTestBase { self::assertSame($expected_dependencies, $simple_example->calculateDependencies()); } - public static function providerSimpleStaticPropSource(): array { - return [ - [ - 'sourceType' => 'static:field_item:string', - 'sourceTypeSettings' => NULL, - 'value' => 'Hello, world!', - 'expression' => 'ℹ︎string␟value', - 'expected_json_representation' => '{"sourceType":"static:field_item:string","value":"Hello, world!","expression":"ℹ︎string␟value"}', - 'field_widgets' => [ - NULL => StringTextfieldWidget::class, - 'string_textfield' => StringTextfieldWidget::class, - 'string_textarea' => StringTextfieldWidget::class, - ], - 'expected_user_value' => 'Hello, world!', - 'expected_prop_expression' => FieldTypePropExpression::class, - 'expected_dependencies' => [ - 'plugin' => [ - 'field_type:string', - ], + public static function providerSimpleStaticPropSource(): \Generator { + yield "simple string" => [ + 'sourceType' => 'static:field_item:string', + 'sourceTypeSettings' => NULL, + 'value' => 'Hello, world!', + 'expression' => 'ℹ︎string␟value', + 'expected_json_representation' => '{"sourceType":"static:field_item:string","value":"Hello, world!","expression":"ℹ︎string␟value"}', + 'field_widgets' => [ + NULL => StringTextfieldWidget::class, + 'string_textfield' => StringTextfieldWidget::class, + 'string_textarea' => StringTextfieldWidget::class, + ], + 'expected_user_value' => 'Hello, world!', + 'expected_prop_expression' => FieldTypePropExpression::class, + 'expected_dependencies' => [ + 'plugin' => [ + 'field_type:string', ], ], - [ - 'sourceType' => 'static:field_item:uri', - 'sourceTypeSettings' => NULL, - 'value' => 'https://drupal.org', - 'expression' => 'ℹ︎uri␟value', - 'expected_json_representation' => '{"sourceType":"static:field_item:uri","value":"https:\/\/drupal.org","expression":"ℹ︎uri␟value"}', - 'field_widgets' => [ - NULL => UriWidget::class, - 'uri' => UriWidget::class, - ], - 'expected_user_value' => 'https://drupal.org', - 'expected_prop_expression' => FieldTypePropExpression::class, - 'expected_dependencies' => [ - 'plugin' => [ - 'field_type:uri', - ], + ]; + yield "simple uri" => [ + 'sourceType' => 'static:field_item:uri', + 'sourceTypeSettings' => NULL, + 'value' => 'https://drupal.org', + 'expression' => 'ℹ︎uri␟value', + 'expected_json_representation' => '{"sourceType":"static:field_item:uri","value":"https:\/\/drupal.org","expression":"ℹ︎uri␟value"}', + 'field_widgets' => [ + NULL => UriWidget::class, + 'uri' => UriWidget::class, + ], + 'expected_user_value' => 'https://drupal.org', + 'expected_prop_expression' => FieldTypePropExpression::class, + 'expected_dependencies' => [ + 'plugin' => [ + 'field_type:uri', ], ], - [ - 'sourceType' => 'static:field_item:boolean', - 'sourceTypeSettings' => NULL, - 'value' => TRUE, - 'expression' => 'ℹ︎boolean␟value', - 'expected_json_representation' => '{"sourceType":"static:field_item:boolean","value":true,"expression":"ℹ︎boolean␟value"}', - 'field_widgets' => [ - NULL => BooleanCheckboxWidget::class, - 'boolean_checkbox' => BooleanCheckboxWidget::class, - ], - 'expected_user_value' => TRUE, - 'expected_prop_expression' => FieldTypePropExpression::class, - 'expected_dependencies' => [ - 'plugin' => [ - 'field_type:boolean', - ], + ]; + yield "simple boolean" => [ + 'sourceType' => 'static:field_item:boolean', + 'sourceTypeSettings' => NULL, + 'value' => TRUE, + 'expression' => 'ℹ︎boolean␟value', + 'expected_json_representation' => '{"sourceType":"static:field_item:boolean","value":true,"expression":"ℹ︎boolean␟value"}', + 'field_widgets' => [ + NULL => BooleanCheckboxWidget::class, + 'boolean_checkbox' => BooleanCheckboxWidget::class, + ], + 'expected_user_value' => TRUE, + 'expected_prop_expression' => FieldTypePropExpression::class, + 'expected_dependencies' => [ + 'plugin' => [ + 'field_type:boolean', ], ], - [ - 'sourceType' => 'static:field_item:daterange', - 'sourceTypeSettings' => NULL, - 'value' => [ - 'value' => '2020-04-16T00:00', - 'end_value' => '2024-07-10T10:24', - ], - 'expression' => 'ℹ︎daterange␟{start↠value,stop↠end_value}', - 'expected_json_representation' => '{"sourceType":"static:field_item:daterange","value":{"value":"2020-04-16T00:00","end_value":"2024-07-10T10:24"},"expression":"ℹ︎daterange␟{start↠value,stop↠end_value}"}', - 'field_widgets' => [ - NULL => DateRangeDefaultWidget::class, - 'daterange_default' => DateRangeDefaultWidget::class, - 'daterange_datelist' => DateRangeDatelistWidget::class, - ], - 'expected_user_value' => [ - 'start' => '2020-04-16T00:00', - 'stop' => '2024-07-10T10:24', + ]; + yield "simple daterange" => [ + 'sourceType' => 'static:field_item:daterange', + 'sourceTypeSettings' => NULL, + 'value' => [ + 'value' => '2020-04-16T00:00', + 'end_value' => '2024-07-10T10:24', + ], + 'expression' => 'ℹ︎daterange␟{start↠value,stop↠end_value}', + 'expected_json_representation' => '{"sourceType":"static:field_item:daterange","value":{"value":"2020-04-16T00:00","end_value":"2024-07-10T10:24"},"expression":"ℹ︎daterange␟{start↠value,stop↠end_value}"}', + 'field_widgets' => [ + NULL => DateRangeDefaultWidget::class, + 'daterange_default' => DateRangeDefaultWidget::class, + 'daterange_datelist' => DateRangeDatelistWidget::class, + ], + 'expected_user_value' => [ + 'start' => '2020-04-16T00:00', + 'stop' => '2024-07-10T10:24', + ], + 'expected_prop_expression' => FieldTypeObjectPropsExpression::class, + 'expected_dependencies' => [ + 'module' => [ + 'datetime_range', ], - 'expected_prop_expression' => FieldTypeObjectPropsExpression::class, - 'expected_dependencies' => [ - 'module' => [ - 'datetime_range', - ], - 'plugin' => [ - 'field_type:daterange', - ], + 'plugin' => [ + 'field_type:daterange', ], ], ]; -- GitLab From 6a67fa3034b87e7d28a5f0f78b65e5c0f3d8edcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Tue, 20 May 2025 16:15:15 +0200 Subject: [PATCH 14/22] Issue #3460230: Refactor testStaticPropSource to a provider with \Generator. --- tests/src/Kernel/PropSourceTest.php | 271 +++++++++++----------------- 1 file changed, 105 insertions(+), 166 deletions(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index 0dd7857c89..65f8cd6c20 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -91,15 +91,15 @@ class PropSourceTest extends KernelTestBase { /** * @coversClass \Drupal\experience_builder\PropSource\StaticPropSource - * @dataProvider providerSimpleStaticPropSource + * @dataProvider providerStaticPropSource */ - public function testSimpleStaticPropSource( + public function testStaticPropSource( string $source_type, array|null $source_type_settings, mixed $value, string $expression, string $expected_json_representation, - array $field_widgets, + array|null $field_widgets, mixed $expected_user_value, string $expected_prop_expection_class, array $expected_dependencies, @@ -107,7 +107,7 @@ class PropSourceTest extends KernelTestBase { // A simple example. // @phpstan-ignore-next-line - $simple_example = StaticPropSource::parse([ + $prop_source_example = StaticPropSource::parse([ 'sourceType' => $source_type, 'value' => $value, 'expression' => $expression, @@ -115,37 +115,43 @@ class PropSourceTest extends KernelTestBase { ]); // First, get the string representation and parse it back, to prove // serialization and deserialization works. - $json_representation = (string) $simple_example; + $json_representation = (string) $prop_source_example; $this->assertSame($expected_json_representation, $json_representation); $decoded_representation = json_decode($json_representation, TRUE); + $prop_source_example = PropSource::parse($decoded_representation); + $this->assertInstanceOf(StaticPropSource::class, $prop_source_example); + // The contained information read back out. + $this->assertSame($source_type, $prop_source_example->getSourceType()); + /** @var class-string $expected_prop_expection_class */ + $this->assertInstanceOf($expected_prop_expection_class, StructuredDataPropExpression::fromString($prop_source_example->asChoice())); + if (NULL === $value) { + // Do not continue testing if there is no values. + return; + } + try { StaticPropSource::isMinimalRepresentation($decoded_representation); } catch (\LogicException) { $this->fail("Not a minimal representation: $json_representation."); } - $simple_example = PropSource::parse($decoded_representation); - $this->assertInstanceOf(StaticPropSource::class, $simple_example); - // The contained information read back out. - $this->assertSame($source_type, $simple_example->getSourceType()); - /** @var class-string $expected_prop_expection_class */ - $this->assertInstanceOf($expected_prop_expection_class, StructuredDataPropExpression::fromString($simple_example->asChoice())); - $this->assertSame($value, $simple_example->getValue()); + $this->assertSame($value, $prop_source_example->getValue()); // Test the functionality of a StaticPropSource: // - evaluate it to populate an SDC prop - $this->assertSame($expected_user_value, $simple_example->evaluate(User::create([]))); + $this->assertSame($expected_user_value, $prop_source_example->evaluate(User::create([]))); // - the field type's item's raw value is minimized if it is single-property - $this->assertSame($value, $simple_example->getValue()); + $this->assertSame($value, $prop_source_example->getValue()); // - generate a widget to edit the stored value — using the default widget // or a specified widget. // @see \Drupal\experience_builder\Entity\Component::$defaults + \assert(is_array($field_widgets)); foreach ($field_widgets as $widget_type => $expected_widget_class) { - $this->assertInstanceOf($expected_widget_class, $simple_example->getWidget('irrelevant-for-test', $this->randomString(), $widget_type)); + $this->assertInstanceOf($expected_widget_class, $prop_source_example->getWidget('irrelevant-for-test', $this->randomString(), $widget_type)); } - self::assertSame($expected_dependencies, $simple_example->calculateDependencies()); + self::assertSame($expected_dependencies, $prop_source_example->calculateDependencies()); } - public static function providerSimpleStaticPropSource(): \Generator { + public static function providerStaticPropSource(): \Generator { yield "simple string" => [ 'sourceType' => 'static:field_item:string', 'sourceTypeSettings' => NULL, @@ -201,6 +207,38 @@ class PropSourceTest extends KernelTestBase { ], ], ]; + yield "simple integer" => [ + 'sourceType' => 'static:field_item:integer', + 'sourceTypeSettings' => [ + 'cardinality' => 5, + ], + 'value' => [ + 20, + 06, + 1, + 88, + 92, + ], + 'expression' => 'ℹ︎integer␟value', + 'expected_json_representation' => '{"sourceType":"static:field_item:integer","value":[20,6,1,88,92],"expression":"ℹ︎integer␟value","sourceTypeSettings":{"cardinality":5}}', + 'field_widgets' => [ + NULL => NumberWidget::class, + 'number' => NumberWidget::class, + ], + 'expected_user_value' => [ + 20, + 06, + 1, + 88, + 92, + ], + 'expected_prop_expression' => FieldTypePropExpression::class, + 'expected_dependencies' => [ + 'plugin' => [ + 'field_type:integer', + ], + ], + ]; yield "simple daterange" => [ 'sourceType' => 'static:field_item:daterange', 'sourceTypeSettings' => NULL, @@ -229,103 +267,7 @@ class PropSourceTest extends KernelTestBase { ], ], ]; - } - - /** - * @coversClass \Drupal\experience_builder\PropSource\StaticPropSource - */ - public function testStaticPropSource(): void { - - // A complex *empty* example. - $complex_empty_example = StaticPropSource::parse([ - 'sourceType' => 'static:field_item:entity_reference', - 'value' => NULL, - 'expression' => 'ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}', - 'sourceTypeSettings' => [ - 'storage' => ['target_type' => 'media'], - 'instance' => [ - 'handler' => 'default:media', - 'handler_settings' => [ - 'target_bundles' => ['image' => 'image'], - ], - ], - ], - ]); - // First, get the string representation and parse it back, to prove - // serialization and deserialization works. - $json_representation = (string) $complex_empty_example; - $this->assertSame('{"sourceType":"static:field_item:entity_reference","value":null,"expression":"ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}","sourceTypeSettings":{"storage":{"target_type":"media"},"instance":{"handler":"default:media","handler_settings":{"target_bundles":{"image":"image"}}}}}', $json_representation); - $decoded_representation = json_decode($json_representation, TRUE); - $complex_empty_example = PropSource::parse($decoded_representation); - $this->assertInstanceOf(StaticPropSource::class, $complex_empty_example); - // The contained information read back out. - $this->assertSame('static:field_item:entity_reference', $complex_empty_example->getSourceType()); - $this->assertInstanceOf(FieldTypeObjectPropsExpression::class, StructuredDataPropExpression::fromString($complex_empty_example->asChoice())); - $this->assertNull($complex_empty_example->getValue()); - self::assertSame([ - 'config' => [ - 'media.type.image', - ], - 'content' => [], - 'module' => [ - 'media', - ], - 'plugin' => [ - 'field_type:entity_reference', - ], - ], $complex_empty_example->calculateDependencies()); - - // A simple (expression targeting a simple prop) array example (with - // cardinality specified, rather than the default of `cardinality=1`). - $simple_array_example = StaticPropSource::parse([ - 'sourceType' => 'static:field_item:integer', - 'sourceTypeSettings' => [ - 'cardinality' => 5, - ], - 'value' => [ - 20, - 06, - 1, - 88, - 92, - ], - 'expression' => 'ℹ︎integer␟value', - ]); - // First, get the string representation and parse it back, to prove - // serialization and deserialization works. - $json_representation = (string) $simple_array_example; - $this->assertSame('{"sourceType":"static:field_item:integer","value":[20,6,1,88,92],"expression":"ℹ︎integer␟value","sourceTypeSettings":{"cardinality":5}}', $json_representation); - $decoded_representation = json_decode($json_representation, TRUE); - try { - StaticPropSource::isMinimalRepresentation($decoded_representation); - } - catch (\LogicException) { - $this->fail("Not a minimal representation: $json_representation."); - } - $simple_array_example = PropSource::parse($decoded_representation); - $this->assertInstanceOf(StaticPropSource::class, $simple_array_example); - // The contained information read back out. - $this->assertSame('static:field_item:integer', $simple_array_example->getSourceType()); - $this->assertInstanceOf(FieldTypePropExpression::class, StructuredDataPropExpression::fromString($simple_array_example->asChoice())); - $this->assertSame([20, 06, 1, 88, 92], $simple_array_example->getValue()); - // Test the functionality of a StaticPropSource: - // - evaluate it to populate an SDC prop - $this->assertSame([20, 06, 1, 88, 92], $simple_array_example->evaluate(User::create([]))); - // - the field type's item's raw value is minimized if it is single-property - $this->assertSame([20, 06, 1, 88, 92], $simple_array_example->getValue()); - // - generate a widget to edit the stored value — using the default widget - // or a specified widget. - // @see \Drupal\experience_builder\Entity\Component::$defaults - $this->assertInstanceOf(NumberWidget::class, $simple_array_example->getWidget('irrelevant-for-test', $this->randomString(), NULL)); - $this->assertInstanceOf(NumberWidget::class, $simple_array_example->getWidget('irrelevant-for-test', $this->randomString(), 'number')); - // The widget plugin manager ignores any request for another widget type and - // falls back to the default widget if - // @see \Drupal\Core\Field\WidgetPluginManager::getInstance() - $this->assertInstanceOf(NumberWidget::class, $simple_array_example->getWidget('irrelevant-for-test', $this->randomString(), 'number')); - - // A complex (expression targeting multiple props) array example (with - // cardinality specified, rather than the default of `cardinality=1`). - $complex_array_example = StaticPropSource::parse([ + yield "complex daterange with cardinality" => [ 'sourceType' => 'static:field_item:daterange', 'sourceTypeSettings' => [ 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, @@ -341,65 +283,62 @@ class PropSourceTest extends KernelTestBase { ], ], 'expression' => 'ℹ︎daterange␟{start↠value,stop↠end_value}', - ]); - // First, get the string representation and parse it back, to prove - // serialization and deserialization works. - $json_representation = (string) $complex_array_example; - $this->assertSame('{"sourceType":"static:field_item:daterange","value":[{"value":"2020-04-16T00:00","end_value":"2024-07-10T10:24"},{"value":"2020-04-16T00:00","end_value":"2024-09-26T11:31"}],"expression":"ℹ︎daterange␟{start↠value,stop↠end_value}","sourceTypeSettings":{"cardinality":-1}}', $json_representation); - $decoded_representation = json_decode($json_representation, TRUE); - try { - StaticPropSource::isMinimalRepresentation($decoded_representation); - } - catch (\LogicException) { - $this->fail("Not a minimal representation: $json_representation."); - } - $complex_array_example = PropSource::parse($decoded_representation); - $this->assertInstanceOf(StaticPropSource::class, $complex_array_example); - // The contained information read back out. - $this->assertSame('static:field_item:daterange', $complex_array_example->getSourceType()); - $this->assertInstanceOf(FieldTypeObjectPropsExpression::class, StructuredDataPropExpression::fromString($complex_array_example->asChoice())); - $this->assertSame([ - [ - 'value' => '2020-04-16T00:00', - 'end_value' => '2024-07-10T10:24', + 'expected_json_representation' => '{"sourceType":"static:field_item:daterange","value":[{"value":"2020-04-16T00:00","end_value":"2024-07-10T10:24"},{"value":"2020-04-16T00:00","end_value":"2024-09-26T11:31"}],"expression":"ℹ︎daterange␟{start↠value,stop↠end_value}","sourceTypeSettings":{"cardinality":-1}}', + 'field_widgets' => [ + NULL => DateRangeDefaultWidget::class, + 'daterange_default' => DateRangeDefaultWidget::class, + 'daterange_datelist' => DateRangeDatelistWidget::class, ], - [ - 'value' => '2020-04-16T00:00', - 'end_value' => '2024-09-26T11:31', + 'expected_user_value' => [ + [ + 'start' => '2020-04-16T00:00', + 'stop' => '2024-07-10T10:24', + ], + [ + 'start' => '2020-04-16T00:00', + 'stop' => '2024-09-26T11:31', + ], ], - ], $complex_array_example->getValue()); - // Test the functionality of a StaticPropSource: - // - evaluate it to populate an SDC prop - $this->assertSame([ - [ - 'start' => '2020-04-16T00:00', - 'stop' => '2024-07-10T10:24', + 'expected_prop_expression' => FieldTypeObjectPropsExpression::class, + 'expected_dependencies' => [ + 'module' => [ + 'datetime_range', + ], + 'plugin' => [ + 'field_type:daterange', + ], ], - [ - 'start' => '2020-04-16T00:00', - 'stop' => '2024-09-26T11:31', + ]; + yield "complex empty example with entity_reference" => [ + 'sourceType' => 'static:field_item:entity_reference', + 'sourceTypeSettings' => [ + 'storage' => ['target_type' => 'media'], + 'instance' => [ + 'handler' => 'default:media', + 'handler_settings' => [ + 'target_bundles' => ['image' => 'image'], + ], + ], ], - ], $complex_array_example->evaluate(User::create([]))); - // - the field type's item's raw value is minimized if it is single-property - $this->assertSame( - [ - [ - 'value' => '2020-04-16T00:00', - 'end_value' => '2024-07-10T10:24', + 'value' => NULL, + 'expression' => 'ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}', + 'expected_json_representation' => '{"sourceType":"static:field_item:entity_reference","value":null,"expression":"ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}","sourceTypeSettings":{"storage":{"target_type":"media"},"instance":{"handler":"default:media","handler_settings":{"target_bundles":{"image":"image"}}}}}', + 'field_widgets' => NULL, + 'expected_user_value' => NULL, + 'expected_prop_expression' => FieldTypeObjectPropsExpression::class, + 'expected_dependencies' => [ + 'config' => [ + 'media.type.image', ], - [ - 'value' => '2020-04-16T00:00', - 'end_value' => '2024-09-26T11:31', + 'content' => [], + 'module' => [ + 'media', + ], + 'plugin' => [ + 'field_type:entity_reference', ], ], - $complex_array_example->getValue() - ); - // - generate a widget to edit the stored value — using the default widget - // or a specified widget. - // @see \Drupal\experience_builder\Entity\Component::$defaults - $this->assertInstanceOf(DateRangeDefaultWidget::class, $complex_array_example->getWidget('irrelevant-for-test', $this->randomString(), NULL)); - $this->assertInstanceOf(DateRangeDefaultWidget::class, $complex_array_example->getWidget('irrelevant-for-test', $this->randomString(), 'daterange_default')); - $this->assertInstanceOf(DateRangeDatelistWidget::class, $complex_array_example->getWidget('irrelevant-for-test', $this->randomString(), 'daterange_datelist')); + ]; } /** -- GitLab From 6b63910aec63e6b953fd968c79433e0d51dd336e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Tue, 20 May 2025 16:28:29 +0200 Subject: [PATCH 15/22] Issue #3460230: Fix mispelling. --- tests/src/Kernel/PropSourceTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index 65f8cd6c20..e9522bbf73 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -101,7 +101,7 @@ class PropSourceTest extends KernelTestBase { string $expected_json_representation, array|null $field_widgets, mixed $expected_user_value, - string $expected_prop_expection_class, + string $expected_prop_expression_class, array $expected_dependencies, ): void { @@ -122,8 +122,8 @@ class PropSourceTest extends KernelTestBase { $this->assertInstanceOf(StaticPropSource::class, $prop_source_example); // The contained information read back out. $this->assertSame($source_type, $prop_source_example->getSourceType()); - /** @var class-string $expected_prop_expection_class */ - $this->assertInstanceOf($expected_prop_expection_class, StructuredDataPropExpression::fromString($prop_source_example->asChoice())); + /** @var class-string $expected_prop_expression_class */ + $this->assertInstanceOf($expected_prop_expression_class, StructuredDataPropExpression::fromString($prop_source_example->asChoice())); if (NULL === $value) { // Do not continue testing if there is no values. return; -- GitLab From 8deb320422377d02fbfb86f1aaaa4ba229f60c69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ignacio=20S=C3=A1nchez?= <nacho@isholgueras.com> Date: Tue, 20 May 2025 18:41:50 +0200 Subject: [PATCH 16/22] Issue #3460230: Add a component with media_library to cover the dependencies. --- .../Kernel/Config/ComponentValidationTest.php | 36 +++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/tests/src/Kernel/Config/ComponentValidationTest.php b/tests/src/Kernel/Config/ComponentValidationTest.php index 2476b40601..e371b922f9 100644 --- a/tests/src/Kernel/Config/ComponentValidationTest.php +++ b/tests/src/Kernel/Config/ComponentValidationTest.php @@ -46,6 +46,12 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { 'options', 'path', 'link', + 'field', + 'media', + 'media_library', + 'views', + 'user', + 'filter', ]; /** @@ -72,6 +78,12 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { */ protected function setUp(): void { parent::setUp(); + $this->installEntitySchema('media'); + $this->installEntitySchema('user'); + $this->setInstallProfile('standard'); + $this->installConfig(['media']); + $this->installSchema('file', ['file_usage']); + $this->installEntitySchema('filter_format'); $this->entity = Component::create([ 'id' => 'sdc.sdc_test.my-cta', @@ -110,6 +122,23 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { 'default_value' => NULL, 'expression' => 'ℹ︎list_string␟value', ], + 'image' => [ + 'field_type' => 'image', + 'field_storage_settings' => [ + 'target_type' => 'media', + ], + 'field_instance_settings' => [ + 'handler' => 'default:media', + 'handler_settings' => [ + 'target_bundles' => [ + 'image' => 'image', + ], + ], + ], + 'field_widget' => 'media_library_widget', + 'default_value' => [], + 'expression' => 'ℹ︎image␟{src↝entity␜␜entity:file␝uri␞␟url,alt↠alt,width↠width,height↠height}', + ], ], ], 'label' => 'Test', @@ -124,12 +153,12 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { public function testEntityIsValid(): void { parent::testEntityIsValid(); - // @todo Change the tested component here to one that includes an `image` prop shape, to allow testing with entity reference field type + media module dependency + media library dependency ONLY because of the widget. - // Beyond validity, validate config dependencies are computed correctly. $this->assertSame( [ 'module' => [ + 'image', + 'media_library', 'options', 'sdc_test', ], @@ -138,6 +167,8 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { ); $this->assertSame([ 'module' => [ + 'image', + 'media_library', 'options', 'sdc_test', 'experience_builder', @@ -207,6 +238,7 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { ]); $this->assertValidationErrors([ 'settings.prop_field_definitions' => "'target' is a required key.", + 'settings.prop_field_definitions.image' => "'image' is not a supported key.", ]); // @see \Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\BlockComponent -- GitLab From 4af04039dc2b2fb3b45cbe26de14097bae85db72 Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Tue, 20 May 2025 16:12:29 -0400 Subject: [PATCH 17/22] image property is not in the test component --- tests/src/Kernel/Config/ComponentValidationTest.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/src/Kernel/Config/ComponentValidationTest.php b/tests/src/Kernel/Config/ComponentValidationTest.php index e371b922f9..5b86b59552 100644 --- a/tests/src/Kernel/Config/ComponentValidationTest.php +++ b/tests/src/Kernel/Config/ComponentValidationTest.php @@ -231,14 +231,16 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { 'local_source_id' => 'my-cta', 'prop_field_definitions' => array_diff_key( $this->entity->getSettings()['prop_field_definitions'], - array_flip(['target']), + // Remove the 'target' key to trigger a validation error. + // Remove the 'image' because the property is not in the JS component + // created above. + array_flip(['target', 'image']), ), ], 'label' => 'Test', ]); $this->assertValidationErrors([ 'settings.prop_field_definitions' => "'target' is a required key.", - 'settings.prop_field_definitions.image' => "'image' is not a supported key.", ]); // @see \Drupal\experience_builder\Plugin\ExperienceBuilder\ComponentSource\BlockComponent -- GitLab From c62d5eb8fc90ca8d28479d6d6bac213c77c1e623 Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Tue, 20 May 2025 17:05:26 -0400 Subject: [PATCH 18/22] test EntityReferenceAutocompleteWidget widget for static prop source --- tests/src/Kernel/PropSourceTest.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index e9522bbf73..00dc7eace8 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -8,6 +8,7 @@ use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Extension\ExtensionPathResolver; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\Plugin\Field\FieldWidget\BooleanCheckboxWidget; +use Drupal\Core\Field\Plugin\Field\FieldWidget\EntityReferenceAutocompleteWidget; use Drupal\Core\Field\Plugin\Field\FieldWidget\NumberWidget; use Drupal\Core\Field\Plugin\Field\FieldWidget\StringTextfieldWidget; use Drupal\Core\Field\Plugin\Field\FieldWidget\UriWidget; @@ -323,7 +324,11 @@ class PropSourceTest extends KernelTestBase { 'value' => NULL, 'expression' => 'ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}', 'expected_json_representation' => '{"sourceType":"static:field_item:entity_reference","value":null,"expression":"ℹ︎entity_reference␟{src↝entity␜␜entity:media:image␝field_media_image␞␟entity␜␜entity:file␝uri␞␟url,alt↝entity␜␜entity:media:image␝field_media_image␞␟alt,width↝entity␜␜entity:media:image␝field_media_image␞␟width,height↝entity␜␜entity:media:image␝field_media_image␞␟height}","sourceTypeSettings":{"storage":{"target_type":"media"},"instance":{"handler":"default:media","handler_settings":{"target_bundles":{"image":"image"}}}}}', - 'field_widgets' => NULL, + 'field_widgets' => [ + NULL => EntityReferenceAutocompleteWidget::class, + 'entity_reference_autocomplete' => EntityReferenceAutocompleteWidget::class, + 'daterange_datelist' => EntityReferenceAutocompleteWidget::class, + ], 'expected_user_value' => NULL, 'expected_prop_expression' => FieldTypeObjectPropsExpression::class, 'expected_dependencies' => [ -- GitLab From 03cb8ac0a6e0dfa1e05e4a61f5a18333592178dd Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Tue, 20 May 2025 17:06:16 -0400 Subject: [PATCH 19/22] remove 'simple example' not comment no longer needed --- tests/src/Kernel/PropSourceTest.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index 00dc7eace8..8856f321fc 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -105,8 +105,6 @@ class PropSourceTest extends KernelTestBase { string $expected_prop_expression_class, array $expected_dependencies, ): void { - - // A simple example. // @phpstan-ignore-next-line $prop_source_example = StaticPropSource::parse([ 'sourceType' => $source_type, -- GitLab From 8de7c8df460224617f16ff2e58912a6d175d245d Mon Sep 17 00:00:00 2001 From: Ted Bowman <ted+git@tedbow.com> Date: Tue, 20 May 2025 17:07:29 -0400 Subject: [PATCH 20/22] we can test depedencies and widget even with no value --- tests/src/Kernel/PropSourceTest.php | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index 8856f321fc..234eb1c294 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -123,7 +123,20 @@ class PropSourceTest extends KernelTestBase { $this->assertSame($source_type, $prop_source_example->getSourceType()); /** @var class-string $expected_prop_expression_class */ $this->assertInstanceOf($expected_prop_expression_class, StructuredDataPropExpression::fromString($prop_source_example->asChoice())); + self::assertSame($expected_dependencies, $prop_source_example->calculateDependencies()); + // - generate a widget to edit the stored value — using the default widget + // or a specified widget. + // @see \Drupal\experience_builder\Entity\Component::$defaults + \assert(is_array($field_widgets)); + // Ensure we always test the default widget. + \assert(isset($field_widgets[NULL])); + // Ensure an unknown widget type is handled gracefully. + $field_widgets['not_real'] = $field_widgets[NULL]; + foreach ($field_widgets as $widget_type => $expected_widget_class) { + $this->assertInstanceOf($expected_widget_class, $prop_source_example->getWidget('irrelevant-for-test', $this->randomString(), $widget_type)); + } if (NULL === $value) { + $this->assertNull($expected_user_value); // Do not continue testing if there is no values. return; } @@ -140,14 +153,6 @@ class PropSourceTest extends KernelTestBase { $this->assertSame($expected_user_value, $prop_source_example->evaluate(User::create([]))); // - the field type's item's raw value is minimized if it is single-property $this->assertSame($value, $prop_source_example->getValue()); - // - generate a widget to edit the stored value — using the default widget - // or a specified widget. - // @see \Drupal\experience_builder\Entity\Component::$defaults - \assert(is_array($field_widgets)); - foreach ($field_widgets as $widget_type => $expected_widget_class) { - $this->assertInstanceOf($expected_widget_class, $prop_source_example->getWidget('irrelevant-for-test', $this->randomString(), $widget_type)); - } - self::assertSame($expected_dependencies, $prop_source_example->calculateDependencies()); } public static function providerStaticPropSource(): \Generator { -- GitLab From 2f69d01ae5783e67c732b860d5e7463fe78113c9 Mon Sep 17 00:00:00 2001 From: Wim Leers <44946-wimleers@users.noreply.drupalcode.org> Date: Wed, 21 May 2025 10:37:34 +0000 Subject: [PATCH 21/22] Nits: restore lost comments + clarify case labels. --- tests/src/Kernel/PropSourceTest.php | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/tests/src/Kernel/PropSourceTest.php b/tests/src/Kernel/PropSourceTest.php index 234eb1c294..a179c90763 100644 --- a/tests/src/Kernel/PropSourceTest.php +++ b/tests/src/Kernel/PropSourceTest.php @@ -156,7 +156,7 @@ class PropSourceTest extends KernelTestBase { } public static function providerStaticPropSource(): \Generator { - yield "simple string" => [ + yield "scalar shape, field type=string, cardinality=1" => [ 'sourceType' => 'static:field_item:string', 'sourceTypeSettings' => NULL, 'value' => 'Hello, world!', @@ -175,7 +175,7 @@ class PropSourceTest extends KernelTestBase { ], ], ]; - yield "simple uri" => [ + yield "scalar shape, field type=uri, cardinality=1" => [ 'sourceType' => 'static:field_item:uri', 'sourceTypeSettings' => NULL, 'value' => 'https://drupal.org', @@ -193,7 +193,7 @@ class PropSourceTest extends KernelTestBase { ], ], ]; - yield "simple boolean" => [ + yield "scalar shape, field type=boolean, cardinality=1" => [ 'sourceType' => 'static:field_item:boolean', 'sourceTypeSettings' => NULL, 'value' => TRUE, @@ -211,7 +211,9 @@ class PropSourceTest extends KernelTestBase { ], ], ]; - yield "simple integer" => [ + // A simple (expression targeting a simple prop) array example (with + // cardinality specified, rather than the default of `cardinality=1`). + yield "scalar shape, field type=integer, cardinality=5" => [ 'sourceType' => 'static:field_item:integer', 'sourceTypeSettings' => [ 'cardinality' => 5, @@ -243,7 +245,7 @@ class PropSourceTest extends KernelTestBase { ], ], ]; - yield "simple daterange" => [ + yield "object shape, daterange field, cardinality=1" => [ 'sourceType' => 'static:field_item:daterange', 'sourceTypeSettings' => NULL, 'value' => [ @@ -271,7 +273,9 @@ class PropSourceTest extends KernelTestBase { ], ], ]; - yield "complex daterange with cardinality" => [ + // A complex (expression targeting multiple props) array example (with + // cardinality specified, rather than the default of `cardinality=1`). + yield "object shape, daterange field, cardinality=UNLIMITED" => [ 'sourceType' => 'static:field_item:daterange', 'sourceTypeSettings' => [ 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, -- GitLab From 159e46e91b77cdbd595637ad10c4f9842c2463a7 Mon Sep 17 00:00:00 2001 From: Wim Leers <44946-wimleers@users.noreply.drupalcode.org> Date: Wed, 21 May 2025 10:44:35 +0000 Subject: [PATCH 22/22] Two `@todo`s pointing to https://www.drupal.org/project/experience_builder/issues/3525759 --- tests/src/Kernel/Config/ComponentValidationTest.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/src/Kernel/Config/ComponentValidationTest.php b/tests/src/Kernel/Config/ComponentValidationTest.php index 5b86b59552..cf4868b288 100644 --- a/tests/src/Kernel/Config/ComponentValidationTest.php +++ b/tests/src/Kernel/Config/ComponentValidationTest.php @@ -122,6 +122,7 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { 'default_value' => NULL, 'expression' => 'ℹ︎list_string␟value', ], + // @todo This will start failing validation in https://www.drupal.org/i/3525759. 'image' => [ 'field_type' => 'image', 'field_storage_settings' => [ @@ -234,6 +235,7 @@ class ComponentValidationTest extends BetterConfigEntityValidationTestBase { // Remove the 'target' key to trigger a validation error. // Remove the 'image' because the property is not in the JS component // created above. + // @todo Remove "image" from this in https://www.drupal.org/i/3525759. array_flip(['target', 'image']), ), ], -- GitLab