Initial Drupal 11 with DDEV setup
This commit is contained in:
@ -0,0 +1,5 @@
|
||||
name: 'Entity serialization test support'
|
||||
type: module
|
||||
description: 'Provides test support for entity serialization tests.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\entity_serialization_test\Hook;
|
||||
|
||||
use Drupal\Core\Access\AccessResult;
|
||||
use Drupal\Core\Hook\Attribute\Hook;
|
||||
|
||||
/**
|
||||
* Hook implementations for entity_serialization_test.
|
||||
*/
|
||||
class EntitySerializationTestHooks {
|
||||
|
||||
/**
|
||||
* Implements hook_entity_field_access_alter().
|
||||
*
|
||||
* Overrides some default access control to support testing.
|
||||
*
|
||||
* @see Drupal\serialization\Tests\EntitySerializationTest::testUserNormalize()
|
||||
*/
|
||||
#[Hook('entity_field_access_alter')]
|
||||
public function entityFieldAccessAlter(array &$grants, array $context): void {
|
||||
// Override default access control from UserAccessControlHandler to allow
|
||||
// access to 'pass' field for the test user.
|
||||
if ($context['field_definition']->getName() == 'pass' && $context['account']->getAccountName() == 'serialization_test_user') {
|
||||
$grants[':default'] = AccessResult::allowed()->inheritCacheability($grants[':default'])->addCacheableDependency($context['items']->getEntity());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'FieldItem normalization test support'
|
||||
type: module
|
||||
description: 'Provides test support for fieldItem normalization test support.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,6 @@
|
||||
services:
|
||||
serializer.normalizer.silly_field_item:
|
||||
class: Drupal\field_normalization_test\Normalization\TextItemSillyNormalizer
|
||||
tags:
|
||||
# The priority must be higher than serialization.normalizer.field_item.
|
||||
- { name: normalizer , priority: 9 }
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\field_normalization_test\Normalization;
|
||||
|
||||
use Drupal\serialization\Normalizer\FieldItemNormalizer;
|
||||
use Drupal\text\Plugin\Field\FieldType\TextItemBase;
|
||||
|
||||
/**
|
||||
* A test TextItem normalizer to test denormalization.
|
||||
*/
|
||||
class TextItemSillyNormalizer extends FieldItemNormalizer {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
$data = parent::normalize($object, $format, $context);
|
||||
$data['value'] .= '::silly_suffix';
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function constructValue($data, $context) {
|
||||
$value = parent::constructValue($data, $context);
|
||||
$value['value'] = str_replace('::silly_suffix', '', $value['value']);
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTypes(?string $format): array {
|
||||
return [TextItemBase::class => TRUE];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\test_datatype_boolean_emoji_normalizer\Normalizer;
|
||||
|
||||
use Drupal\Core\TypedData\Plugin\DataType\BooleanData;
|
||||
use Drupal\serialization\Normalizer\NormalizerBase;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
/**
|
||||
* Normalizes boolean data weirdly: renders them as 👍 (TRUE) or 👎 (FALSE).
|
||||
*/
|
||||
class BooleanNormalizer extends NormalizerBase implements DenormalizerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
return $object->getValue() ? '👍' : '👎';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function denormalize($data, $class, $format = NULL, array $context = []): mixed {
|
||||
if (!in_array($data, ['👍', '👎'], TRUE)) {
|
||||
throw new \UnexpectedValueException('Only 👍 and 👎 are acceptable values.');
|
||||
}
|
||||
return $data === '👍';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTypes(?string $format): array {
|
||||
return [BooleanData::class => TRUE];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Test @DataType normalizer'
|
||||
type: module
|
||||
description: 'Provides test support for @DataType-level normalization.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,6 @@
|
||||
services:
|
||||
serializer.normalizer.boolean.datatype.emoji:
|
||||
class: Drupal\test_datatype_boolean_emoji_normalizer\Normalizer\BooleanNormalizer
|
||||
tags:
|
||||
# The priority must be higher than serializer.normalizer.primitive_data.
|
||||
- { name: normalizer , priority: 1000 }
|
||||
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\test_fieldtype_boolean_emoji_normalizer\Normalizer;
|
||||
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\BooleanItem;
|
||||
use Drupal\serialization\Normalizer\FieldItemNormalizer;
|
||||
use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
|
||||
|
||||
/**
|
||||
* Normalizes boolean fields weirdly: renders them as 👍 (TRUE) or 👎 (FALSE).
|
||||
*/
|
||||
class BooleanItemNormalizer extends FieldItemNormalizer implements DenormalizerInterface {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
$data = parent::normalize($object, $format, $context);
|
||||
$data['value'] = $data['value'] ? '👍' : '👎';
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function constructValue($data, $context) {
|
||||
// Just like \Drupal\serialization\Normalizer\FieldItemNormalizer's logic
|
||||
// for denormalization, which uses TypedDataInterface::setValue(), allow the
|
||||
// keying by main property name ("value") to be implied.
|
||||
if (!is_array($data)) {
|
||||
$data = ['value' => $data];
|
||||
}
|
||||
|
||||
if (!in_array($data['value'], ['👍', '👎'], TRUE)) {
|
||||
throw new \UnexpectedValueException('Only 👍 and 👎 are acceptable values.');
|
||||
}
|
||||
$data['value'] = ($data['value'] === '👍');
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTypes(?string $format): array {
|
||||
return [BooleanItem::class => TRUE];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,5 @@
|
||||
name: 'Test @FieldType normalizer'
|
||||
type: module
|
||||
description: 'Provides test support for @FieldType-level normalization.'
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,6 @@
|
||||
services:
|
||||
serializer.normalizer.boolean.fieldtype.emoji:
|
||||
class: Drupal\test_fieldtype_boolean_emoji_normalizer\Normalizer\BooleanItemNormalizer
|
||||
tags:
|
||||
# The priority must be higher than serialization.normalizer.field_item.
|
||||
- { name: normalizer , priority: 1000 }
|
||||
@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\user_route_alter_test\Routing;
|
||||
|
||||
use Drupal\Core\Routing\RouteSubscriberBase;
|
||||
use Drupal\Core\Routing\RoutingEvents;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
* Alter the 'user.pass.http' route.
|
||||
*/
|
||||
class RouteSubscriber extends RouteSubscriberBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function alterRoutes(RouteCollection $collection): void {
|
||||
if ($route = $collection->get('user.pass.http')) {
|
||||
$route->setRequirements([]);
|
||||
$route->setRequirement('_access', 'FALSE');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function getSubscribedEvents(): array {
|
||||
$events = parent::getSubscribedEvents();
|
||||
// Ensure this event is triggered before
|
||||
// \Drupal\serialization\EventSubscriber\UserRouteAlterSubscriber.
|
||||
$events[RoutingEvents::ALTER] = ['onAlterRoutes', 1];
|
||||
return $events;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
name: 'User route alter test support'
|
||||
description: 'Provides test support for user route alter tests.'
|
||||
type: module
|
||||
version: VERSION
|
||||
@ -0,0 +1,4 @@
|
||||
services:
|
||||
_defaults:
|
||||
autoconfigure: true
|
||||
Drupal\user_route_alter_test\Routing\RouteSubscriber: ~
|
||||
@ -0,0 +1,5 @@
|
||||
name: Serialization test module
|
||||
type: module
|
||||
description: "Support module for serialization tests."
|
||||
package: Testing
|
||||
version: VERSION
|
||||
@ -0,0 +1,9 @@
|
||||
services:
|
||||
serializer.normalizer.serialization_test:
|
||||
class: Drupal\serialization_test\SerializationTestNormalizer
|
||||
tags:
|
||||
- { name: normalizer }
|
||||
serializer.encoder.serialization_test:
|
||||
class: Drupal\serialization_test\SerializationTestEncoder
|
||||
tags:
|
||||
- { name: encoder, format: serialization_test}
|
||||
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\serialization_test;
|
||||
|
||||
use Symfony\Component\Serializer\Encoder\EncoderInterface;
|
||||
|
||||
/**
|
||||
* Serialization encoder used for testing.
|
||||
*/
|
||||
class SerializationTestEncoder implements EncoderInterface {
|
||||
|
||||
/**
|
||||
* The format that this Encoder supports.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $format = 'serialization_test';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function encode($data, $format, array $context = []): string {
|
||||
// @see \Drupal\serialization_test\SerializationTestNormalizer::normalize().
|
||||
return 'Normalized by ' . $data['normalized_by'] . ', Encoded by SerializationTestEncoder';
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsEncoding(string $format, array $context = []): bool {
|
||||
return static::$format === $format;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\serialization_test;
|
||||
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* Serialization normalizer used for testing.
|
||||
*/
|
||||
class SerializationTestNormalizer implements NormalizerInterface {
|
||||
|
||||
/**
|
||||
* The format that this Normalizer supports.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected static $format = 'serialization_test';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
$normalized = (array) $object;
|
||||
// Add identifying value that can be used to verify that the expected
|
||||
// normalizer was invoked.
|
||||
$normalized['normalized_by'] = 'SerializationTestNormalizer';
|
||||
return $normalized;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function supportsNormalization($data, ?string $format = NULL, array $context = []): bool {
|
||||
return static::$format === $format;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTypes(?string $format): array {
|
||||
return [
|
||||
\stdClass::class => TRUE,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Functional;
|
||||
|
||||
use Drupal\Tests\system\Functional\Module\GenericModuleTestBase;
|
||||
|
||||
/**
|
||||
* Generic module test for serialization.
|
||||
*
|
||||
* @group serialization
|
||||
*/
|
||||
class GenericTest extends GenericModuleTestBase {}
|
||||
@ -0,0 +1,388 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Kernel;
|
||||
|
||||
use Drupal\Core\Cache\CacheableDependencyInterface;
|
||||
use Drupal\Core\Cache\CacheableMetadata;
|
||||
use Drupal\entity_test\Entity\EntityTestComputedField;
|
||||
use Drupal\Component\Serialization\Json;
|
||||
use Drupal\Component\Datetime\DateTimePlus;
|
||||
use Drupal\entity_test\Entity\EntitySerializedField;
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\filter\Entity\FilterFormat;
|
||||
use Drupal\serialization\Normalizer\CacheableNormalizerInterface;
|
||||
|
||||
/**
|
||||
* Tests that entities can be serialized to supported core formats.
|
||||
*
|
||||
* @group serialization
|
||||
*/
|
||||
class EntitySerializationTest extends NormalizerTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'serialization',
|
||||
'system',
|
||||
'field',
|
||||
'entity_test',
|
||||
'text',
|
||||
'filter',
|
||||
'user',
|
||||
'entity_serialization_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The test values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $values;
|
||||
|
||||
/**
|
||||
* The test entity.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityInterface
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The test user.
|
||||
*
|
||||
* @var \Drupal\user\Entity\User
|
||||
*/
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* The serializer service.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\Serializer
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* The class name of the test class.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityClass = 'Drupal\entity_test\Entity\EntityTest';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
FilterFormat::create([
|
||||
'format' => 'my_text_format',
|
||||
'name' => 'My Text Format',
|
||||
'filters' => [
|
||||
'filter_html' => [
|
||||
'module' => 'filter',
|
||||
'status' => TRUE,
|
||||
'weight' => 10,
|
||||
'settings' => [
|
||||
'allowed_html' => '<p>',
|
||||
],
|
||||
],
|
||||
'filter_autop' => [
|
||||
'module' => 'filter',
|
||||
'status' => TRUE,
|
||||
'weight' => 10,
|
||||
'settings' => [],
|
||||
],
|
||||
],
|
||||
])->save();
|
||||
|
||||
// Create a test user to use as the entity owner.
|
||||
$this->user = \Drupal::entityTypeManager()->getStorage('user')->create([
|
||||
'name' => 'serialization_test_user',
|
||||
'mail' => 'foo@example.com',
|
||||
'pass' => '123456',
|
||||
]);
|
||||
$this->user->save();
|
||||
|
||||
// Create a test entity to serialize.
|
||||
$test_text_value = $this->randomMachineName();
|
||||
$this->values = [
|
||||
'name' => $this->randomMachineName(),
|
||||
'user_id' => $this->user->id(),
|
||||
'field_test_text' => [
|
||||
'value' => $test_text_value,
|
||||
'format' => 'my_text_format',
|
||||
],
|
||||
];
|
||||
$this->entity = EntityTestMulRev::create($this->values);
|
||||
$this->entity->save();
|
||||
|
||||
$this->serializer = $this->container->get('serializer');
|
||||
|
||||
$this->installConfig(['field']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the normalize function.
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$expected = [
|
||||
'id' => [
|
||||
['value' => 1],
|
||||
],
|
||||
'uuid' => [
|
||||
['value' => $this->entity->uuid()],
|
||||
],
|
||||
'langcode' => [
|
||||
['value' => 'en'],
|
||||
],
|
||||
'name' => [
|
||||
['value' => $this->values['name']],
|
||||
],
|
||||
'type' => [
|
||||
['value' => 'entity_test_mulrev'],
|
||||
],
|
||||
'created' => [
|
||||
[
|
||||
'value' => (new \DateTime())->setTimestamp((int) $this->entity->get('created')->value)->setTimezone(new \DateTimeZone('UTC'))->format(\DateTime::RFC3339),
|
||||
'format' => \DateTime::RFC3339,
|
||||
],
|
||||
],
|
||||
'user_id' => [
|
||||
[
|
||||
// id() will return the string value as it comes from the database.
|
||||
'target_id' => (int) $this->user->id(),
|
||||
'target_type' => $this->user->getEntityTypeId(),
|
||||
'target_uuid' => $this->user->uuid(),
|
||||
'url' => $this->user->toUrl()->toString(),
|
||||
],
|
||||
],
|
||||
'revision_id' => [
|
||||
['value' => 1],
|
||||
],
|
||||
'default_langcode' => [
|
||||
['value' => TRUE],
|
||||
],
|
||||
'revision_translation_affected' => [
|
||||
['value' => TRUE],
|
||||
],
|
||||
'non_rev_field' => [],
|
||||
'non_mul_field' => [],
|
||||
'field_test_text' => [
|
||||
[
|
||||
'value' => $this->values['field_test_text']['value'],
|
||||
'format' => $this->values['field_test_text']['format'],
|
||||
'processed' => "<p>{$this->values['field_test_text']['value']}</p>",
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
$normalized = $this->serializer->normalize($this->entity);
|
||||
|
||||
foreach (array_keys($expected) as $fieldName) {
|
||||
$this->assertSame($expected[$fieldName], $normalized[$fieldName], "Normalization produces expected array for $fieldName.");
|
||||
}
|
||||
$this->assertEquals([], array_diff_key($normalized, $expected), 'No unexpected data is added to the normalized array.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests user normalization with some default access controls overridden.
|
||||
*
|
||||
* @see entity_serialization_test.module
|
||||
*/
|
||||
public function testUserNormalize(): void {
|
||||
// Test password isn't available.
|
||||
$normalized = $this->serializer->normalize($this->user);
|
||||
|
||||
$this->assertArrayNotHasKey('pass', $normalized);
|
||||
$this->assertArrayNotHasKey('mail', $normalized);
|
||||
|
||||
// Test again using our test user, so that our access control override will
|
||||
// allow password viewing.
|
||||
$normalized = $this->serializer->normalize($this->user, NULL, ['account' => $this->user]);
|
||||
|
||||
// The key 'pass' will now exist, but the password value should be
|
||||
// normalized to NULL.
|
||||
$this->assertSame([NULL], $normalized['pass'], '"pass" value is normalized to [NULL]');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests entity serialization for core's formats by a registered Serializer.
|
||||
*/
|
||||
public function testSerialize(): void {
|
||||
// Test that Serializer responds using the ComplexDataNormalizer and
|
||||
// JsonEncoder. The output of ComplexDataNormalizer::normalize() is tested
|
||||
// elsewhere, so we can just assume that it works properly here.
|
||||
$normalized = $this->serializer->normalize($this->entity, 'json');
|
||||
$expected = Json::encode($normalized);
|
||||
// Test 'json'.
|
||||
$actual = $this->serializer->serialize($this->entity, 'json');
|
||||
$this->assertSame($expected, $actual, 'Entity serializes to JSON when "json" is requested.');
|
||||
$actual = $this->serializer->serialize($normalized, 'json');
|
||||
$this->assertSame($expected, $actual, 'A normalized array serializes to JSON when "json" is requested');
|
||||
// Test 'ajax'.
|
||||
$actual = $this->serializer->serialize($this->entity, 'ajax');
|
||||
$this->assertSame($expected, $actual, 'Entity serializes to JSON when "ajax" is requested.');
|
||||
$actual = $this->serializer->serialize($normalized, 'ajax');
|
||||
$this->assertSame($expected, $actual, 'A normalized array serializes to JSON when "ajax" is requested');
|
||||
|
||||
// Generate the expected xml in a way that allows changes to entity property
|
||||
// order.
|
||||
$expected_created = [
|
||||
'value' => DateTimePlus::createFromTimestamp($this->entity->created->value, 'UTC')->format(\DateTime::RFC3339),
|
||||
'format' => \DateTime::RFC3339,
|
||||
];
|
||||
|
||||
$expected = [
|
||||
'id' => '<id><value>' . $this->entity->id() . '</value></id>',
|
||||
'uuid' => '<uuid><value>' . $this->entity->uuid() . '</value></uuid>',
|
||||
'langcode' => '<langcode><value>en</value></langcode>',
|
||||
'name' => '<name><value>' . $this->values['name'] . '</value></name>',
|
||||
'type' => '<type><value>entity_test_mulrev</value></type>',
|
||||
'created' => '<created><value>' . $expected_created['value'] . '</value><format>' . $expected_created['format'] . '</format></created>',
|
||||
'user_id' => '<user_id><target_id>' . $this->user->id() . '</target_id><target_type>' . $this->user->getEntityTypeId() . '</target_type><target_uuid>' . $this->user->uuid() . '</target_uuid><url>' . $this->user->toUrl()->toString() . '</url></user_id>',
|
||||
'revision_id' => '<revision_id><value>' . $this->entity->getRevisionId() . '</value></revision_id>',
|
||||
'default_langcode' => '<default_langcode><value>1</value></default_langcode>',
|
||||
'revision_translation_affected' => '<revision_translation_affected><value>1</value></revision_translation_affected>',
|
||||
'non_mul_field' => '<non_mul_field/>',
|
||||
'non_rev_field' => '<non_rev_field/>',
|
||||
'field_test_text' => '<field_test_text><value>' . $this->values['field_test_text']['value'] . '</value><format>' . $this->values['field_test_text']['format'] . '</format><processed><![CDATA[<p>' . $this->values['field_test_text']['value'] . '</p>]]></processed></field_test_text>',
|
||||
];
|
||||
// Sort it in the same order as normalized.
|
||||
$expected = array_merge($normalized, $expected);
|
||||
// Add header and footer.
|
||||
array_unshift($expected, '<?xml version="1.0"?>' . PHP_EOL . '<response>');
|
||||
$expected[] = '</response>' . PHP_EOL;
|
||||
// Reduced the array to a string.
|
||||
$expected = implode('', $expected);
|
||||
// Test 'xml'. The output should match that of Symfony's XmlEncoder.
|
||||
$actual = $this->serializer->serialize($this->entity, 'xml');
|
||||
$this->assertSame($expected, $actual);
|
||||
$actual = $this->serializer->serialize($normalized, 'xml');
|
||||
$this->assertSame($expected, $actual);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests denormalization of an entity.
|
||||
*/
|
||||
public function testDenormalize(): void {
|
||||
$normalized = $this->serializer->normalize($this->entity);
|
||||
|
||||
foreach (['json', 'xml'] as $type) {
|
||||
$denormalized = $this->serializer->denormalize($normalized, $this->entityClass, $type, ['entity_type' => 'entity_test_mulrev']);
|
||||
$this->assertInstanceOf($this->entityClass, $denormalized);
|
||||
$this->assertSame($this->entity->getEntityTypeId(), $denormalized->getEntityTypeId(), 'Expected entity type found.');
|
||||
$this->assertSame($this->entity->bundle(), $denormalized->bundle(), 'Expected entity bundle found.');
|
||||
$this->assertSame($this->entity->uuid(), $denormalized->uuid(), 'Expected entity UUID found.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests denormalizing serialized columns.
|
||||
*/
|
||||
public function testDenormalizeSerializedItem(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('The generic FieldItemNormalizer cannot denormalize string values for "value" properties of the "serialized" field (field item class: Drupal\entity_test\Plugin\Field\FieldType\SerializedItem).');
|
||||
$this->serializer->denormalize([
|
||||
'serialized' => [
|
||||
[
|
||||
'value' => 'boo',
|
||||
],
|
||||
],
|
||||
'type' => 'entity_test_serialized_field',
|
||||
], EntitySerializedField::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing/denormalizing custom serialized columns.
|
||||
*/
|
||||
public function testDenormalizeCustomSerializedItem(): void {
|
||||
$entity = EntitySerializedField::create(['serialized_text' => serialize(['Hello world!'])]);
|
||||
$normalized = $this->serializer->normalize($entity);
|
||||
$this->assertEquals(['Hello world!'], $normalized['serialized_text'][0]['value']);
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('The generic FieldItemNormalizer cannot denormalize string values for "value" properties of the "serialized_text" field (field item class: Drupal\entity_test\Plugin\Field\FieldType\SerializedPropertyItem).');
|
||||
$this->serializer->denormalize([
|
||||
'serialized_text' => [
|
||||
[
|
||||
'value' => 'boo',
|
||||
],
|
||||
],
|
||||
'type' => 'entity_test_serialized_field',
|
||||
], EntitySerializedField::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing/denormalizing invalid custom serialized fields.
|
||||
*/
|
||||
public function testDenormalizeInvalidCustomSerializedField(): void {
|
||||
$entity = EntitySerializedField::create(['serialized_long' => serialize(['Hello world!'])]);
|
||||
$normalized = $this->serializer->normalize($entity);
|
||||
$this->assertEquals(['Hello world!'], $normalized['serialized_long'][0]['value']);
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('The generic FieldItemNormalizer cannot denormalize string values for "value" properties of the "serialized_long" field (field item class: Drupal\Core\Field\Plugin\Field\FieldType\StringLongItem).');
|
||||
$this->serializer->denormalize([
|
||||
'serialized_long' => [
|
||||
[
|
||||
'value' => 'boo',
|
||||
],
|
||||
],
|
||||
'type' => 'entity_test_serialized_field',
|
||||
], EntitySerializedField::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing/denormalizing empty custom serialized fields.
|
||||
*/
|
||||
public function testDenormalizeEmptyCustomSerializedField(): void {
|
||||
$entity = EntitySerializedField::create(['serialized_long' => serialize([])]);
|
||||
$normalized = $this->serializer->normalize($entity);
|
||||
$this->assertEquals([], $normalized['serialized_long'][0]['value']);
|
||||
|
||||
$entity = $this->serializer->denormalize($normalized, EntitySerializedField::class);
|
||||
|
||||
$this->assertEquals(serialize([]), $entity->get('serialized_long')->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing/denormalizing valid custom serialized fields.
|
||||
*/
|
||||
public function testDenormalizeValidCustomSerializedField(): void {
|
||||
$entity = EntitySerializedField::create(['serialized_long' => serialize(['key' => 'value'])]);
|
||||
$normalized = $this->serializer->normalize($entity);
|
||||
$this->assertEquals(['key' => 'value'], $normalized['serialized_long'][0]['value']);
|
||||
|
||||
$entity = $this->serializer->denormalize($normalized, EntitySerializedField::class);
|
||||
|
||||
$this->assertEquals(serialize(['key' => 'value']), $entity->get('serialized_long')->value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing/denormalizing using string values.
|
||||
*/
|
||||
public function testDenormalizeStringValue(): void {
|
||||
$this->expectException(\LogicException::class);
|
||||
$this->expectExceptionMessage('The generic FieldItemNormalizer cannot denormalize string values for "value" properties of the "serialized_long" field (field item class: Drupal\Core\Field\Plugin\Field\FieldType\StringLongItem).');
|
||||
$this->serializer->denormalize([
|
||||
'serialized_long' => ['boo'],
|
||||
'type' => 'entity_test_serialized_field',
|
||||
], EntitySerializedField::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing cacheable computed field.
|
||||
*/
|
||||
public function testCacheableComputedField(): void {
|
||||
$context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY] = new CacheableMetadata();
|
||||
$entity = EntityTestComputedField::create();
|
||||
$normalized = $this->serializer->normalize($entity, NULL, $context);
|
||||
$this->assertEquals('computed test cacheable string field', $normalized['computed_test_cacheable_string_field'][0]['value']);
|
||||
$this->assertInstanceOf(CacheableDependencyInterface::class, $context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]);
|
||||
// See \Drupal\entity_test\Plugin\Field\ComputedTestCacheableStringItemList::computeValue().
|
||||
$this->assertEquals($context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]->getCacheContexts(), ['url.query_args:computed_test_cacheable_string_field']);
|
||||
$this->assertEquals($context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]->getCacheTags(), ['field:computed_test_cacheable_string_field']);
|
||||
$this->assertEquals($context[CacheableNormalizerInterface::SERIALIZATION_CONTEXT_CACHEABILITY]->getCacheMaxAge(), 800);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,243 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Kernel;
|
||||
|
||||
use Drupal\entity_test\Entity\EntityTestMulRev;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Test field level normalization process.
|
||||
*
|
||||
* @group serialization
|
||||
*/
|
||||
class FieldItemSerializationTest extends NormalizerTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'serialization',
|
||||
'system',
|
||||
'field',
|
||||
'entity_test',
|
||||
'text',
|
||||
'filter',
|
||||
'user',
|
||||
'field_normalization_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* The class name of the test class.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $entityClass = 'Drupal\entity_test\Entity\EntityTestMulRev';
|
||||
|
||||
/**
|
||||
* The test values.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $values;
|
||||
|
||||
/**
|
||||
* The test entity.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\ContentEntityBase
|
||||
*/
|
||||
protected $entity;
|
||||
|
||||
/**
|
||||
* The serializer service.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\Serializer
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Auto-create a field for testing default field values.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'field_name' => 'field_test_text_default',
|
||||
'type' => 'text',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'field_name' => 'field_test_text_default',
|
||||
'bundle' => 'entity_test_mulrev',
|
||||
'label' => 'Test text-field with default',
|
||||
'default_value' => [
|
||||
[
|
||||
'value' => 'This is the default',
|
||||
'format' => 'full_html',
|
||||
],
|
||||
],
|
||||
'widget' => [
|
||||
'type' => 'text_textfield',
|
||||
'weight' => 0,
|
||||
],
|
||||
])->save();
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'field_name' => 'field_test_boolean',
|
||||
'type' => 'boolean',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'field_name' => 'field_test_boolean',
|
||||
'bundle' => 'entity_test_mulrev',
|
||||
'label' => 'Test boolean',
|
||||
])->save();
|
||||
|
||||
// Create a test entity to serialize.
|
||||
$this->values = [
|
||||
'name' => $this->randomMachineName(),
|
||||
'field_test_text' => [
|
||||
'value' => $this->randomMachineName(),
|
||||
'format' => 'full_html',
|
||||
],
|
||||
'field_test_boolean' => [
|
||||
'value' => FALSE,
|
||||
],
|
||||
];
|
||||
$this->entity = EntityTestMulRev::create($this->values);
|
||||
$this->entity->save();
|
||||
|
||||
$this->serializer = $this->container->get('serializer');
|
||||
|
||||
$this->installConfig(['field']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing and denormalizing an entity with field item normalizer.
|
||||
*/
|
||||
public function testFieldNormalizeDenormalize(): void {
|
||||
$normalized = $this->serializer->normalize($this->entity, 'json');
|
||||
|
||||
$expected_field_value = $this->entity->field_test_text[0]->getValue()['value'] . '::silly_suffix';
|
||||
$this->assertEquals($expected_field_value, $normalized['field_test_text'][0]['value'], 'Text field item normalized');
|
||||
$denormalized = $this->serializer->denormalize($normalized, $this->entityClass, 'json');
|
||||
|
||||
$this->assertEquals($denormalized->field_test_text[0]->getValue(), $this->entity->field_test_text[0]->getValue(), 'Text field item denormalized.');
|
||||
$this->assertEquals($denormalized->field_test_text_default[0]->getValue(), $this->entity->field_test_text_default[0]->getValue(), 'Text field item with default denormalized.');
|
||||
|
||||
// Unset the values for text field that has a default value.
|
||||
unset($normalized['field_test_text_default']);
|
||||
$denormalized_without_all_fields = $this->serializer->denormalize($normalized, $this->entityClass, 'json');
|
||||
// Check that denormalized entity is still the same even if not all fields
|
||||
// are not provided.
|
||||
$this->assertEquals($denormalized_without_all_fields->field_test_text[0]->getValue(), $this->entity->field_test_text[0]->getValue(), 'Text field item denormalized.');
|
||||
// Even though field_test_text_default value was unset before
|
||||
// denormalization it should still have the default values for the field.
|
||||
$this->assertEquals($denormalized_without_all_fields->field_test_text_default[0]->getValue(), $this->entity->field_test_text_default[0]->getValue(), 'Text field item with default denormalized.');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests denormalizing using a scalar field value.
|
||||
*/
|
||||
public function testFieldDenormalizeWithScalarValue(): void {
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('Field values for "uuid" must use an array structure');
|
||||
|
||||
$normalized = $this->serializer->normalize($this->entity, 'json');
|
||||
|
||||
// Change the UUID value to use the UUID directly. No array structure.
|
||||
$normalized['uuid'] = $normalized['uuid'][0]['value'];
|
||||
|
||||
$this->serializer->denormalize($normalized, $this->entityClass, 'json');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests a format-agnostic normalizer.
|
||||
*
|
||||
* @param string[] $test_modules
|
||||
* The test modules to install.
|
||||
* @param string $format
|
||||
* The format to test. (NULL results in the format-agnostic normalization.)
|
||||
*
|
||||
* @dataProvider providerTestCustomBooleanNormalization
|
||||
*/
|
||||
public function testCustomBooleanNormalization(array $test_modules, $format): void {
|
||||
// Asserts the entity contains the value we set.
|
||||
$this->assertFalse($this->entity->field_test_boolean->value);
|
||||
|
||||
// Asserts normalizing the entity using core's 'serializer' service DOES
|
||||
// yield the value we set.
|
||||
$core_normalization = $this->container->get('serializer')->normalize($this->entity, $format);
|
||||
$this->assertFalse($core_normalization['field_test_boolean'][0]['value']);
|
||||
|
||||
$assert_denormalization = function (array $normalization) use ($format) {
|
||||
$denormalized_entity = $this->container->get('serializer')->denormalize($normalization, EntityTestMulRev::class, $format, []);
|
||||
$this->assertInstanceOf(EntityTestMulRev::class, $denormalized_entity);
|
||||
$this->assertTrue($denormalized_entity->field_test_boolean->value);
|
||||
};
|
||||
|
||||
// Asserts denormalizing the entity DOES yield the value we set:
|
||||
// - when using the detailed representation
|
||||
$core_normalization['field_test_boolean'][0]['value'] = TRUE;
|
||||
$assert_denormalization($core_normalization);
|
||||
// - and when using the shorthand representation
|
||||
$core_normalization['field_test_boolean'][0] = TRUE;
|
||||
$assert_denormalization($core_normalization);
|
||||
|
||||
// Install test module that contains a high-priority alternative normalizer.
|
||||
$this->enableModules($test_modules);
|
||||
|
||||
// Asserts normalizing the entity DOES NOT ANYMORE yield the value we set.
|
||||
$core_normalization = $this->container->get('serializer')->normalize($this->entity, $format);
|
||||
$this->assertSame('👎', $core_normalization['field_test_boolean'][0]['value']);
|
||||
|
||||
// Asserts denormalizing the entity DOES NOT ANYMORE yield the value we set:
|
||||
// - when using the detailed representation
|
||||
$core_normalization['field_test_boolean'][0]['value'] = '👍';
|
||||
$assert_denormalization($core_normalization);
|
||||
// - and when using the shorthand representation
|
||||
$core_normalization['field_test_boolean'][0] = '👍';
|
||||
$assert_denormalization($core_normalization);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider.
|
||||
*
|
||||
* @return array
|
||||
* Test cases.
|
||||
*/
|
||||
public static function providerTestCustomBooleanNormalization() {
|
||||
return [
|
||||
'Format-agnostic @FieldType-level normalizers SHOULD be able to affect the format-agnostic normalization' => [
|
||||
['test_fieldtype_boolean_emoji_normalizer'],
|
||||
NULL,
|
||||
],
|
||||
'Format-agnostic @DataType-level normalizers SHOULD be able to affect the format-agnostic normalization' => [
|
||||
['test_datatype_boolean_emoji_normalizer'],
|
||||
NULL,
|
||||
],
|
||||
'Format-agnostic @FieldType-level normalizers SHOULD be able to affect the JSON normalization' => [
|
||||
['test_fieldtype_boolean_emoji_normalizer'],
|
||||
'json',
|
||||
],
|
||||
'Format-agnostic @DataType-level normalizers SHOULD be able to affect the JSON normalization' => [
|
||||
['test_datatype_boolean_emoji_normalizer'],
|
||||
'json',
|
||||
],
|
||||
'Format-agnostic @FieldType-level normalizers SHOULD be able to affect the XML normalization' => [
|
||||
['test_fieldtype_boolean_emoji_normalizer'],
|
||||
'xml',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,138 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Kernel;
|
||||
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\TypedData\MapDataDefinition;
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* @group TypedData
|
||||
*/
|
||||
class MapDataNormalizerTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['system', 'serialization'];
|
||||
|
||||
/**
|
||||
* The serializer service.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\Serializer
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* The typed data manager.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataManagerInterface
|
||||
*/
|
||||
protected $typedDataManager;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->serializer = \Drupal::service('serializer');
|
||||
$this->typedDataManager = \Drupal::typedDataManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether map data can be normalized.
|
||||
*/
|
||||
public function testMapNormalize(): void {
|
||||
$typed_data = $this->buildExampleTypedData();
|
||||
$data = $this->serializer->normalize($typed_data, 'json');
|
||||
$expect_value = [
|
||||
'key1' => 'value1',
|
||||
'key2' => 'value2',
|
||||
'key3' => 3,
|
||||
'key4' => [
|
||||
0 => TRUE,
|
||||
1 => 'value6',
|
||||
'key7' => 'value7',
|
||||
],
|
||||
];
|
||||
$this->assertSame($expect_value, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests whether map data with properties can be normalized.
|
||||
*/
|
||||
public function testMapWithPropertiesNormalize(): void {
|
||||
$typed_data = $this->buildExampleTypedDataWithProperties();
|
||||
$data = $this->serializer->normalize($typed_data, 'json');
|
||||
$expect_value = [
|
||||
'key1' => 'value1',
|
||||
'key2' => 'value2',
|
||||
'key3' => 3,
|
||||
'key4' => [
|
||||
0 => TRUE,
|
||||
1 => 'value6',
|
||||
'key7' => 'value7',
|
||||
],
|
||||
];
|
||||
$this->assertSame($expect_value, $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds some example typed data object with no properties.
|
||||
*/
|
||||
protected function buildExampleTypedData() {
|
||||
$tree = [
|
||||
'key1' => 'value1',
|
||||
'key2' => 'value2',
|
||||
'key3' => 3,
|
||||
'key4' => [
|
||||
0 => TRUE,
|
||||
1 => 'value6',
|
||||
'key7' => 'value7',
|
||||
],
|
||||
];
|
||||
$map_data_definition = MapDataDefinition::create();
|
||||
$typed_data = $this->typedDataManager->create(
|
||||
$map_data_definition,
|
||||
$tree,
|
||||
'test name'
|
||||
);
|
||||
return $typed_data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds some example typed data object with properties.
|
||||
*/
|
||||
protected function buildExampleTypedDataWithProperties() {
|
||||
$tree = [
|
||||
'key1' => 'value1',
|
||||
'key2' => 'value2',
|
||||
'key3' => 3,
|
||||
'key4' => [
|
||||
0 => TRUE,
|
||||
1 => 'value6',
|
||||
'key7' => 'value7',
|
||||
],
|
||||
];
|
||||
$map_data_definition = MapDataDefinition::create()
|
||||
->setPropertyDefinition('key1', DataDefinition::create('string'))
|
||||
->setPropertyDefinition('key2', DataDefinition::create('string'))
|
||||
->setPropertyDefinition('key3', DataDefinition::create('integer'))
|
||||
->setPropertyDefinition('key4', MapDataDefinition::create()
|
||||
->setPropertyDefinition(0, DataDefinition::create('boolean'))
|
||||
->setPropertyDefinition(1, DataDefinition::create('string'))
|
||||
->setPropertyDefinition('key7', DataDefinition::create('string'))
|
||||
);
|
||||
|
||||
$typed_data = $this->typedDataManager->create(
|
||||
$map_data_definition,
|
||||
$tree,
|
||||
'test name'
|
||||
);
|
||||
|
||||
return $typed_data;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Drupal\field\Entity\FieldConfig;
|
||||
use Drupal\field\Entity\FieldStorageConfig;
|
||||
|
||||
/**
|
||||
* Helper base class to set up some test fields for serialization testing.
|
||||
*/
|
||||
abstract class NormalizerTestBase extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'serialization',
|
||||
'system',
|
||||
'field',
|
||||
'entity_test',
|
||||
'text',
|
||||
'filter',
|
||||
'user',
|
||||
];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->installEntitySchema('entity_test_mulrev');
|
||||
$this->installEntitySchema('user');
|
||||
$this->installConfig(['field']);
|
||||
\Drupal::moduleHandler()->invoke('rest', 'install');
|
||||
|
||||
// Auto-create a field for testing.
|
||||
FieldStorageConfig::create([
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'field_name' => 'field_test_text',
|
||||
'type' => 'text',
|
||||
'cardinality' => 1,
|
||||
'translatable' => FALSE,
|
||||
])->save();
|
||||
FieldConfig::create([
|
||||
'entity_type' => 'entity_test_mulrev',
|
||||
'field_name' => 'field_test_text',
|
||||
'bundle' => 'entity_test_mulrev',
|
||||
'label' => 'Test text-field',
|
||||
'widget' => [
|
||||
'type' => 'text_textfield',
|
||||
'weight' => 0,
|
||||
],
|
||||
])->save();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Functional tests for serialization system.
|
||||
*
|
||||
* @group serialization
|
||||
*/
|
||||
class SerializationTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = ['serialization', 'serialization_test'];
|
||||
|
||||
/**
|
||||
* The serializer service to test.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\SerializerInterface
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
$this->serializer = $this->container->get('serializer');
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that modules can register normalizers and encoders.
|
||||
*/
|
||||
public function testSerializerComponentRegistration(): void {
|
||||
$object = new \stdClass();
|
||||
$format = 'serialization_test';
|
||||
$expected = 'Normalized by SerializationTestNormalizer, Encoded by SerializationTestEncoder';
|
||||
|
||||
// Ensure the serialization invokes the expected normalizer and encoder.
|
||||
$this->assertSame($expected, $this->serializer->serialize($object, $format));
|
||||
|
||||
// Ensure the serialization fails for an unsupported format.
|
||||
try {
|
||||
$this->serializer->serialize($object, 'unsupported_format');
|
||||
$this->fail('The serializer was expected to throw an exception for an unsupported format, but did not.');
|
||||
}
|
||||
catch (UnexpectedValueException) {
|
||||
// Expected exception; just continue testing.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Kernel;
|
||||
|
||||
use Drupal\KernelTests\KernelTestBase;
|
||||
|
||||
/**
|
||||
* Tests that the user routes can be altered.
|
||||
*
|
||||
* @group serialization
|
||||
*/
|
||||
class UserRouteAlterTest extends KernelTestBase {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected static $modules = [
|
||||
'serialization',
|
||||
'user',
|
||||
'user_route_alter_test',
|
||||
];
|
||||
|
||||
/**
|
||||
* Tests the altered 'user.login.http' route.
|
||||
*/
|
||||
public function testUserAlteredRoute(): void {
|
||||
/** @var \Drupal\Core\Routing\RouteProviderInterface $route_provider */
|
||||
$route_provider = $this->container->get('router.route_provider');
|
||||
|
||||
// Ensure '_format' is set for the 'user.login.http' route.
|
||||
$requirements = $route_provider->getRouteByName('user.login.http')->getRequirements();
|
||||
$this->assertArrayHasKey('_format', $requirements, 'user.login.http route has "_format" requirement');
|
||||
$this->assertEquals('json|xml', $requirements['_format'], 'user.login.http route "_format" requirement is "json|xml"');
|
||||
|
||||
// Ensure the '_access' requirement is set to FALSE for the 'user.pass.http'
|
||||
// route.
|
||||
$requirements = $route_provider->getRouteByName('user.pass.http')->getRequirements();
|
||||
$this->assertArrayHasKey('_access', $requirements, 'user.pass.http route has "_access" requirement');
|
||||
$this->assertEquals('FALSE', $requirements['_access'], 'user.pass.http route "_access" requirement is "FALSE"');
|
||||
// Ensure '_format' is not set for the 'user.pass.http' route.
|
||||
$this->assertArrayNotHasKey('_format', $requirements, 'user.pass.http route does not have "_format" requirement');
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Traits;
|
||||
|
||||
use Drupal\serialization\Normalizer\PrimitiveDataNormalizer;
|
||||
use JsonSchema\Validator;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
|
||||
|
||||
/**
|
||||
* Trait for testing JSON Schema validity and fit to sample data.
|
||||
*
|
||||
* In most cases, tests need only implement the abstract method for providing
|
||||
* a full set of representative normalized values.
|
||||
*/
|
||||
trait JsonSchemaTestTrait {
|
||||
|
||||
/**
|
||||
* Format that should be used when performing test normalizations.
|
||||
*/
|
||||
protected function getJsonSchemaTestNormalizationFormat(): ?string {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for ::testNormalizedValuesAgainstJsonSchema.
|
||||
*
|
||||
* @return array
|
||||
* Array of possible normalized values to validate the JSON schema against.
|
||||
*/
|
||||
abstract public static function jsonSchemaDataProvider(): array;
|
||||
|
||||
/**
|
||||
* Method to make prophecy public for use in data provider closures.
|
||||
*
|
||||
* @return \Prophecy\Prophecy\ObjectProphecy<object>
|
||||
* A new prophecy object.
|
||||
*/
|
||||
public function doProphesize(?string $classOrInterface = NULL): ObjectProphecy {
|
||||
return $this->prophesize($classOrInterface);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that a valid schema is returned for the explicitly supported types.
|
||||
*
|
||||
* This is in many cases an interface, which would not be normalized directly,
|
||||
* however the schema should never return an invalid type. An empty array or
|
||||
* a type with only a '$comment' member is valid.
|
||||
*
|
||||
* @dataProvider supportedTypesDataProvider
|
||||
*/
|
||||
public function testSupportedTypesSchemaIsValid(string $type): void {
|
||||
$this->doTestJsonSchemaIsValid($type, TRUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check a schema is valid against the meta-schema.
|
||||
*
|
||||
* @param array $defined_schema
|
||||
* Defined schema.
|
||||
* @param bool $accept_no_schema_type
|
||||
* Whether to accept a schema with no meaningful type construct.
|
||||
*/
|
||||
protected function doCheckSchemaAgainstMetaSchema(array $defined_schema, bool $accept_no_schema_type = FALSE): void {
|
||||
$validator = $this->getValidator();
|
||||
// Ensure the schema contains a meaningful type construct.
|
||||
if (!$accept_no_schema_type) {
|
||||
$this->assertFalse(empty(array_filter(array_keys($defined_schema), fn($key) => in_array($key, ['type', 'allOf', 'oneOf', 'anyOf', 'not', '$ref']))));
|
||||
}
|
||||
// All associative arrays must be encoded as objects.
|
||||
$schema = json_decode(json_encode($defined_schema));
|
||||
$validator->validate(
|
||||
$schema,
|
||||
// Schemas must be compatible with draft 2020-12, however the validation
|
||||
// library, justinrainbow/json-schema, only supports up to draft-04.
|
||||
// Generally speaking this isn't an issue as there are few changes to the
|
||||
// spec that will affect core-provided normalization schemas, and we have
|
||||
// little other option in the PHP ecosystem for runtime validation.
|
||||
// @see https://www.drupal.org/project/drupal/issues/3350943
|
||||
(object) ['$ref' => 'file://' . __DIR__ . '/../../../src/json-schema-draft-04-meta-schema.json']
|
||||
);
|
||||
$this->assertTrue($validator->isValid());
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the normalizer's JSON schema.
|
||||
*
|
||||
* @param mixed $type
|
||||
* Object/type being normalized.
|
||||
* @param bool $accept_no_schema_type
|
||||
* Whether to accept a schema with no meaningful type.
|
||||
*
|
||||
* @return array
|
||||
* Schema, so later tests can avoid retrieving it again.
|
||||
*/
|
||||
public function doTestJsonSchemaIsValid(mixed $type, bool $accept_no_schema_type = FALSE): array {
|
||||
$defined_schema = $this->getNormalizer()->normalize($type, 'json_schema');
|
||||
$this->doCheckSchemaAgainstMetaSchema($defined_schema, $accept_no_schema_type);
|
||||
return $defined_schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
* Supported types for which to test schema generation.
|
||||
*/
|
||||
public static function supportedTypesDataProvider(): array {
|
||||
return array_map(fn ($type) => [$type], array_keys((new PrimitiveDataNormalizer())->getSupportedTypes(NULL)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test normalized values against the JSON schema.
|
||||
*
|
||||
* @dataProvider jsonSchemaDataProvider
|
||||
*/
|
||||
public function testNormalizedValuesAgainstJsonSchema(mixed $value): void {
|
||||
// Explicitly test the JSON Schema's validity here, because it will depend
|
||||
// on the type of the data being normalized, e.g. a class implementing the
|
||||
// interface defined in ::getSupportedTypes().
|
||||
if ($value instanceof \Closure) {
|
||||
$value = $value($this);
|
||||
}
|
||||
$schema = $this->doTestJsonSchemaIsValid($value);
|
||||
$validator = $this->getValidator();
|
||||
// Test the value validates to the schema.
|
||||
// All associative arrays must be encoded as objects.
|
||||
$normalized = json_decode(json_encode($this->getNormalizationForValue($value)));
|
||||
$validator->validate($normalized, $schema);
|
||||
$this->assertSame([], $validator->getErrors(), 'Validation errors on object ' . print_r($normalized, TRUE) . ' with schema ' . print_r($schema, TRUE));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to retrieve the normalizer.
|
||||
*
|
||||
* Override this method if the normalizer has a custom getter or is not
|
||||
* already present at $this->normalizer.
|
||||
*
|
||||
* @return \Symfony\Component\Serializer\Normalizer\NormalizerInterface
|
||||
* The normalizer under test.
|
||||
*/
|
||||
protected function getNormalizer(): NormalizerInterface {
|
||||
return $this->normalizer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the normalization for a value.
|
||||
*
|
||||
* Override this method if the normalization needs further processing, e.g.
|
||||
* in the case of JSON:API module's CacheableDependencyInterface.
|
||||
*
|
||||
* @param mixed $value
|
||||
* Value to be normalized.
|
||||
*
|
||||
* @return mixed
|
||||
* Final normalized value.
|
||||
*/
|
||||
protected function getNormalizationForValue(mixed $value): mixed {
|
||||
return $this->getNormalizer()->normalize($value, $this->getJsonSchemaTestNormalizationFormat());
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the JSON Schema Validator.
|
||||
*
|
||||
* Override this method to add additional schema translations to the loader.
|
||||
*
|
||||
* @return \JsonSchema\Validator
|
||||
* Schema validator.
|
||||
*/
|
||||
protected function getValidator(): Validator {
|
||||
return new Validator();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\CompilerPass;
|
||||
|
||||
use Drupal\Core\DependencyInjection\ContainerBuilder;
|
||||
use Drupal\serialization\RegisterSerializationClassesCompilerPass;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\DependencyInjection\Definition;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\RegisterSerializationClassesCompilerPass
|
||||
* @group serialization
|
||||
*/
|
||||
class RegisterSerializationClassesCompilerPassTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::process
|
||||
*/
|
||||
public function testEncoders(): void {
|
||||
$container = new ContainerBuilder();
|
||||
|
||||
$serializer_definition = new Definition(Serializer::class, [[], []]);
|
||||
$serializer_definition->setPublic(TRUE);
|
||||
$container->setDefinition('serializer', $serializer_definition);
|
||||
|
||||
$encoder_1_definition = new Definition('TestClass');
|
||||
$encoder_1_definition->addTag('encoder', ['format' => 'xml']);
|
||||
$encoder_1_definition->addTag('_provider', ['provider' => 'test_provider_a']);
|
||||
$encoder_1_definition->setPublic(TRUE);
|
||||
$container->setDefinition('encoder_1', $encoder_1_definition);
|
||||
|
||||
$encoder_2_definition = new Definition('TestClass');
|
||||
$encoder_2_definition->addTag('encoder', ['format' => 'json']);
|
||||
$encoder_2_definition->addTag('_provider', ['provider' => 'test_provider_a']);
|
||||
$encoder_2_definition->setPublic(TRUE);
|
||||
$container->setDefinition('encoder_2', $encoder_2_definition);
|
||||
|
||||
$normalizer_1_definition = new Definition('TestClass');
|
||||
$normalizer_1_definition->addTag('normalizer');
|
||||
$normalizer_1_definition->setPublic(TRUE);
|
||||
$container->setDefinition('normalizer_1', $normalizer_1_definition);
|
||||
|
||||
$compiler_pass = new RegisterSerializationClassesCompilerPass();
|
||||
$compiler_pass->process($container);
|
||||
|
||||
// Check registration of formats and providers.
|
||||
$this->assertEquals(['xml', 'json'], $container->getParameter('serializer.formats'));
|
||||
$this->assertEquals(['xml' => 'test_provider_a', 'json' => 'test_provider_a'], $container->getParameter('serializer.format_providers'));
|
||||
|
||||
// Check all encoder and normalizer service definitions are marked private.
|
||||
$this->assertFalse($encoder_1_definition->isPublic());
|
||||
$this->assertFalse($encoder_2_definition->isPublic());
|
||||
|
||||
$this->assertFalse($normalizer_1_definition->isPublic());
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Encoder;
|
||||
|
||||
use Drupal\serialization\Encoder\JsonEncoder;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Encoder\JsonEncoder
|
||||
* @group serialization
|
||||
*/
|
||||
class JsonEncoderTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests the supportsEncoding() method.
|
||||
*/
|
||||
public function testSupportsEncoding(): void {
|
||||
$encoder = new JsonEncoder();
|
||||
|
||||
$this->assertTrue($encoder->supportsEncoding('json'));
|
||||
$this->assertTrue($encoder->supportsEncoding('ajax'));
|
||||
$this->assertFalse($encoder->supportsEncoding('xml'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,120 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Encoder;
|
||||
|
||||
use Drupal\serialization\Encoder\XmlEncoder;
|
||||
use Symfony\Component\Serializer\Encoder\XmlEncoder as BaseXmlEncoder;
|
||||
use Symfony\Component\Serializer\Normalizer\GetSetMethodNormalizer;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Encoder\XmlEncoder
|
||||
* @group serialization
|
||||
*/
|
||||
class XmlEncoderTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The XmlEncoder instance.
|
||||
*
|
||||
* @var \Drupal\serialization\Encoder\XmlEncoder
|
||||
*/
|
||||
protected $encoder;
|
||||
|
||||
/**
|
||||
* The Symfony XML encoder.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\Encoder\XmlEncoder|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $baseEncoder;
|
||||
|
||||
/**
|
||||
* An array of test data.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $testArray = ['test' => 'test'];
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->baseEncoder = $this->createMock(BaseXmlEncoder::class);
|
||||
$this->encoder = new XmlEncoder();
|
||||
$this->encoder->setBaseEncoder($this->baseEncoder);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the supportsEncoding() method.
|
||||
*/
|
||||
public function testSupportsEncoding(): void {
|
||||
$this->assertTrue($this->encoder->supportsEncoding('xml'));
|
||||
$this->assertFalse($this->encoder->supportsEncoding('json'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the supportsDecoding() method.
|
||||
*/
|
||||
public function testSupportsDecoding(): void {
|
||||
$this->assertTrue($this->encoder->supportsDecoding('xml'));
|
||||
$this->assertFalse($this->encoder->supportsDecoding('json'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the encode() method.
|
||||
*/
|
||||
public function testEncode(): void {
|
||||
$this->baseEncoder->expects($this->once())
|
||||
->method('encode')
|
||||
->with($this->testArray, 'test', [])
|
||||
->willReturn('test');
|
||||
|
||||
$this->assertEquals('test', $this->encoder->encode($this->testArray, 'test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the decode() method.
|
||||
*/
|
||||
public function testDecode(): void {
|
||||
$this->baseEncoder->expects($this->once())
|
||||
->method('decode')
|
||||
->with('test', 'test', [])
|
||||
->willReturn($this->testArray);
|
||||
|
||||
$this->assertEquals($this->testArray, $this->encoder->decode('test', 'test'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::getBaseEncoder
|
||||
*/
|
||||
public function testDefaultEncoderHasSerializer(): void {
|
||||
// The serializer should be set on the Drupal encoder, which should then
|
||||
// set it on our default encoder.
|
||||
$encoder = new XmlEncoder();
|
||||
$serializer = new Serializer([new GetSetMethodNormalizer()]);
|
||||
$encoder->setSerializer($serializer);
|
||||
$base_encoder = $encoder->getBaseEncoder();
|
||||
$this->assertInstanceOf(BaseXmlEncoder::class, $base_encoder);
|
||||
// Test the encoder.
|
||||
$base_encoder->encode(['a' => new TestObject()], 'xml');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test class used for the encoding test.
|
||||
*/
|
||||
class TestObject {
|
||||
|
||||
/**
|
||||
* Return the characters "A".
|
||||
*/
|
||||
public function getA() {
|
||||
return 'A';
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\EntityResolver;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\serialization\EntityResolver\ChainEntityResolver;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\EntityResolver\ChainEntityResolver
|
||||
* @group serialization
|
||||
*/
|
||||
class ChainEntityResolverTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* A mocked normalizer.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\Normalizer\NormalizerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $testNormalizer;
|
||||
|
||||
/**
|
||||
* Test data passed to the resolve method.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
protected $testData;
|
||||
|
||||
/**
|
||||
* A test entity type.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $testEntityType = 'test_type';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->testNormalizer = $this->createMock('Symfony\Component\Serializer\Normalizer\NormalizerInterface');
|
||||
$this->testData = new \stdClass();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the resolve method with no matching resolvers.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::resolve
|
||||
*/
|
||||
public function testResolverWithNoneResolved(): void {
|
||||
$resolvers = [
|
||||
$this->createEntityResolverMock(),
|
||||
$this->createEntityResolverMock(),
|
||||
];
|
||||
|
||||
$resolver = new ChainEntityResolver($resolvers);
|
||||
|
||||
$this->assertNull($resolver->resolve($this->testNormalizer, $this->testData, $this->testEntityType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the resolve method with no matching resolvers, using addResolver.
|
||||
*
|
||||
* @covers ::addResolver
|
||||
* @covers ::resolve
|
||||
*/
|
||||
public function testResolverWithNoneResolvedUsingAddResolver(): void {
|
||||
$resolver = new ChainEntityResolver();
|
||||
$resolver->addResolver($this->createEntityResolverMock());
|
||||
$resolver->addResolver($this->createEntityResolverMock());
|
||||
|
||||
$this->assertNull($resolver->resolve($this->testNormalizer, $this->testData, $this->testEntityType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the resolve method with a matching resolver first.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::resolve
|
||||
*/
|
||||
public function testResolverWithFirstResolved(): void {
|
||||
$resolvers = [
|
||||
$this->createEntityResolverMock(10),
|
||||
$this->createEntityResolverMock(NULL, FALSE),
|
||||
];
|
||||
|
||||
$resolver = new ChainEntityResolver($resolvers);
|
||||
|
||||
$this->assertSame(10, $resolver->resolve($this->testNormalizer, $this->testData, $this->testEntityType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the resolve method with a matching resolver last.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::resolve
|
||||
*/
|
||||
public function testResolverWithLastResolved(): void {
|
||||
$resolvers = [
|
||||
$this->createEntityResolverMock(),
|
||||
$this->createEntityResolverMock(10),
|
||||
];
|
||||
|
||||
$resolver = new ChainEntityResolver($resolvers);
|
||||
|
||||
$this->assertSame(10, $resolver->resolve($this->testNormalizer, $this->testData, $this->testEntityType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the resolve method where one resolver returns 0.
|
||||
*
|
||||
* @covers ::__construct
|
||||
* @covers ::resolve
|
||||
*/
|
||||
public function testResolverWithResolvedToZero(): void {
|
||||
$resolvers = [
|
||||
$this->createEntityResolverMock(0),
|
||||
$this->createEntityResolverMock(NULL, FALSE),
|
||||
];
|
||||
|
||||
$resolver = new ChainEntityResolver($resolvers);
|
||||
|
||||
$this->assertSame(0, $resolver->resolve($this->testNormalizer, $this->testData, $this->testEntityType));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mock entity resolver.
|
||||
*
|
||||
* @param null|int $return
|
||||
* Whether the mocked resolve method should return TRUE or FALSE.
|
||||
* @param bool $called
|
||||
* Whether or not the resolve method is expected to be called.
|
||||
*
|
||||
* @return \Drupal\serialization\EntityResolver\EntityResolverInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
* The mocked entity resolver.
|
||||
*/
|
||||
protected function createEntityResolverMock($return = NULL, $called = TRUE) {
|
||||
$mock = $this->createMock('Drupal\serialization\EntityResolver\EntityResolverInterface');
|
||||
|
||||
if ($called) {
|
||||
$mock->expects($this->once())
|
||||
->method('resolve')
|
||||
->with($this->testNormalizer, $this->testData, $this->testEntityType)
|
||||
->willReturn($return);
|
||||
}
|
||||
else {
|
||||
$mock->expects($this->never())
|
||||
->method('resolve');
|
||||
}
|
||||
|
||||
return $mock;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\EntityResolver;
|
||||
|
||||
use Drupal\Core\Entity\EntityRepositoryInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\serialization\EntityResolver\UuidResolver;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\EntityResolver\UuidResolver
|
||||
* @group serialization
|
||||
*/
|
||||
class UuidResolverTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The UuidResolver instance.
|
||||
*
|
||||
* @var \Drupal\serialization\EntityResolver\UuidResolver
|
||||
*/
|
||||
protected $resolver;
|
||||
|
||||
/**
|
||||
* The mock entity repository service.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $entityRepository;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityRepository = $this->createMock(EntityRepositoryInterface::class);
|
||||
|
||||
$this->resolver = new UuidResolver($this->entityRepository);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests resolve() with a class using the incorrect interface.
|
||||
*/
|
||||
public function testResolveNotInInterface(): void {
|
||||
$this->entityRepository->expects($this->never())
|
||||
->method('loadEntityByUuid');
|
||||
|
||||
$normalizer = $this->createMock('Symfony\Component\Serializer\Normalizer\NormalizerInterface');
|
||||
$this->assertNull($this->resolver->resolve($normalizer, [], 'test_type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests resolve() with a class using the correct interface but no UUID.
|
||||
*/
|
||||
public function testResolveNoUuid(): void {
|
||||
$this->entityRepository->expects($this->never())
|
||||
->method('loadEntityByUuid');
|
||||
|
||||
$normalizer = $this->createMock('Drupal\serialization\EntityResolver\UuidReferenceInterface');
|
||||
$normalizer->expects($this->once())
|
||||
->method('getUuid')
|
||||
->with([])
|
||||
->willReturn(NULL);
|
||||
$this->assertNull($this->resolver->resolve($normalizer, [], 'test_type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests resolve() with correct interface but no matching entity for the UUID.
|
||||
*/
|
||||
public function testResolveNoEntity(): void {
|
||||
$uuid = '392eab92-35c2-4625-872d-a9dab4da008e';
|
||||
|
||||
$this->entityRepository->expects($this->once())
|
||||
->method('loadEntityByUuid')
|
||||
->with('test_type')
|
||||
->willReturn(NULL);
|
||||
|
||||
$normalizer = $this->createMock('Drupal\serialization\EntityResolver\UuidReferenceInterface');
|
||||
$normalizer->expects($this->once())
|
||||
->method('getUuid')
|
||||
->with([])
|
||||
->willReturn($uuid);
|
||||
|
||||
$this->assertNull($this->resolver->resolve($normalizer, [], 'test_type'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests resolve() when a UUID corresponds to an entity.
|
||||
*/
|
||||
public function testResolveWithEntity(): void {
|
||||
$uuid = '392eab92-35c2-4625-872d-a9dab4da008e';
|
||||
|
||||
$entity = $this->createMock('Drupal\Core\Entity\EntityInterface');
|
||||
$entity->expects($this->once())
|
||||
->method('id')
|
||||
->willReturn(1);
|
||||
|
||||
$this->entityRepository->expects($this->once())
|
||||
->method('loadEntityByUuid')
|
||||
->with('test_type', $uuid)
|
||||
->willReturn($entity);
|
||||
|
||||
$normalizer = $this->createMock('Drupal\serialization\EntityResolver\UuidReferenceInterface');
|
||||
$normalizer->expects($this->once())
|
||||
->method('getUuid')
|
||||
->with([])
|
||||
->willReturn($uuid);
|
||||
$this->assertSame(1, $this->resolver->resolve($normalizer, [], 'test_type'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\EventSubscriber;
|
||||
|
||||
use Drupal\serialization\Encoder\JsonEncoder;
|
||||
use Drupal\serialization\EventSubscriber\DefaultExceptionSubscriber;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\HttpFoundation\Request;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Symfony\Component\HttpKernel\Event\ExceptionEvent;
|
||||
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
|
||||
use Symfony\Component\HttpKernel\HttpKernelInterface;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\EventSubscriber\DefaultExceptionSubscriber
|
||||
* @group serialization
|
||||
*/
|
||||
class DefaultExceptionSubscriberTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* @covers ::on4xx
|
||||
*/
|
||||
public function testOn4xx(): void {
|
||||
$kernel = $this->prophesize(HttpKernelInterface::class);
|
||||
$request = Request::create('/test');
|
||||
$request->setRequestFormat('json');
|
||||
|
||||
$e = new MethodNotAllowedHttpException(['POST', 'PUT'], 'test message');
|
||||
$event = new ExceptionEvent($kernel->reveal(), $request, HttpKernelInterface::MAIN_REQUEST, $e);
|
||||
$subscriber = new DefaultExceptionSubscriber(new Serializer([], [new JsonEncoder()]), []);
|
||||
$subscriber->on4xx($event);
|
||||
$response = $event->getResponse();
|
||||
|
||||
$this->assertInstanceOf(Response::class, $response);
|
||||
$this->assertEquals('{"message":"test message"}', $response->getContent());
|
||||
$this->assertEquals(405, $response->getStatusCode());
|
||||
$this->assertEquals('POST, PUT', $response->headers->get('Allow'));
|
||||
$this->assertEquals('application/json', $response->headers->get('Content-Type'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\serialization\Normalizer\ComplexDataNormalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\ComplexDataNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class ComplexDataNormalizerTest extends UnitTestCase {
|
||||
|
||||
use InternalTypedDataTestTrait;
|
||||
|
||||
/**
|
||||
* Test format string.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
const TEST_FORMAT = 'test_format';
|
||||
|
||||
/**
|
||||
* The Complex data normalizer under test.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\ComplexDataNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->normalizer = new ComplexDataNormalizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$complex_data = $this->prophesize(ComplexDataInterface::class)->reveal();
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($complex_data));
|
||||
// Also test that an object not implementing ComplexDataInterface fails.
|
||||
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing complex data.
|
||||
*
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalizeComplexData(): void {
|
||||
$serializer_prophecy = $this->prophesize(Serializer::class);
|
||||
|
||||
$non_internal_property = $this->getTypedDataProperty(FALSE);
|
||||
|
||||
$serializer_prophecy->normalize($non_internal_property, static::TEST_FORMAT, [])
|
||||
->willReturn('A-normalized')
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->normalizer->setSerializer($serializer_prophecy->reveal());
|
||||
|
||||
$complex_data = $this->prophesize(ComplexDataInterface::class);
|
||||
$complex_data->getProperties(TRUE)
|
||||
->willReturn([
|
||||
'prop:a' => $non_internal_property,
|
||||
'prop:internal' => $this->getTypedDataProperty(TRUE),
|
||||
])
|
||||
->shouldBeCalled();
|
||||
|
||||
$normalized = $this->normalizer->normalize($complex_data->reveal(), static::TEST_FORMAT);
|
||||
$this->assertEquals(['prop:a' => 'A-normalized'], $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalize() where $object does not implement ComplexDataInterface.
|
||||
*
|
||||
* Normalizers extending ComplexDataNormalizer may have a different supported
|
||||
* class.
|
||||
*
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalizeNonComplex(): void {
|
||||
$normalizer = new TestExtendedNormalizer();
|
||||
$serialization_context = ['test' => 'test'];
|
||||
|
||||
$serializer_prophecy = $this->prophesize(Serializer::class);
|
||||
$serializer_prophecy->normalize('A', static::TEST_FORMAT, $serialization_context)
|
||||
->willReturn('A-normalized')
|
||||
->shouldBeCalled();
|
||||
$serializer_prophecy->normalize('B', static::TEST_FORMAT, $serialization_context)
|
||||
->willReturn('B-normalized')
|
||||
->shouldBeCalled();
|
||||
|
||||
$normalizer->setSerializer($serializer_prophecy->reveal());
|
||||
|
||||
$stdClass = new \stdClass();
|
||||
$stdClass->a = 'A';
|
||||
$stdClass->b = 'B';
|
||||
|
||||
$normalized = $normalizer->normalize($stdClass, static::TEST_FORMAT, $serialization_context);
|
||||
$this->assertEquals(['a' => 'A-normalized', 'b' => 'B-normalized'], $normalized);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test normalizer with a different supported class.
|
||||
*/
|
||||
class TestExtendedNormalizer extends ComplexDataNormalizer {
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTypes(?string $format): array {
|
||||
return [
|
||||
\stdClass::class => TRUE,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,109 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\Entity\ConfigEntityTypeInterface;
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityStorageInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeRepositoryInterface;
|
||||
use Drupal\serialization\Normalizer\ConfigEntityNormalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\ConfigEntityNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class ConfigEntityNormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests the normalize() method.
|
||||
*
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$test_export_properties = [
|
||||
'test' => 'test',
|
||||
'_core' => [
|
||||
'default_config_hash' => $this->randomMachineName(),
|
||||
$this->randomMachineName() => 'some random key',
|
||||
],
|
||||
];
|
||||
|
||||
$entity_field_manager = $this->createMock(EntityFieldManagerInterface::class);
|
||||
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
|
||||
$entity_type_repository = $this->createMock(EntityTypeRepositoryInterface::class);
|
||||
$normalizer = new ConfigEntityNormalizer(
|
||||
$entity_type_manager,
|
||||
$entity_type_repository,
|
||||
$entity_field_manager
|
||||
);
|
||||
|
||||
$config_entity = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityInterface');
|
||||
$config_entity->expects($this->once())
|
||||
->method('toArray')
|
||||
->willReturn($test_export_properties);
|
||||
|
||||
$this->assertSame(['test' => 'test'], $normalizer->normalize($config_entity));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalize(): void {
|
||||
$test_value = $this->randomMachineName();
|
||||
$data = [
|
||||
'test' => $test_value,
|
||||
'_core' => [
|
||||
'default_config_hash' => $this->randomMachineName(),
|
||||
$this->randomMachineName() => 'some random key',
|
||||
],
|
||||
];
|
||||
|
||||
$expected_storage_data = [
|
||||
'test' => $test_value,
|
||||
];
|
||||
|
||||
// Mock of the entity storage, to test our expectation that the '_core' key
|
||||
// never makes it to that point, thanks to the denormalizer omitting it.
|
||||
$entity_storage = $this->prophesize(EntityStorageInterface::class);
|
||||
$entity_storage->create($expected_storage_data)
|
||||
->shouldBeCalled()
|
||||
->will(function ($args) {
|
||||
$entity = new \stdClass();
|
||||
$entity->received_data = $args[0];
|
||||
return $entity;
|
||||
});
|
||||
|
||||
// Stubs for the denormalizer going from entity type manager to entity
|
||||
// storage.
|
||||
$entity_type_id = $this->randomMachineName();
|
||||
$entity_type_class = $this->randomMachineName();
|
||||
$entity_type_manager = $this->prophesize(EntityTypeManagerInterface::class);
|
||||
$entity_type_manager->getDefinition($entity_type_id, FALSE)
|
||||
->willReturn($this->prophesize(ConfigEntityTypeInterface::class)->reveal());
|
||||
$entity_type_manager->getStorage($entity_type_id)
|
||||
->willReturn($entity_storage->reveal());
|
||||
$entity_type_repository = $this->prophesize(EntityTypeRepositoryInterface::class);
|
||||
$entity_type_repository->getEntityTypeFromClass($entity_type_class)
|
||||
->willReturn($entity_type_id);
|
||||
$entity_field_manager = $this->prophesize(EntityFieldManagerInterface::class);
|
||||
$normalizer = new ConfigEntityNormalizer($entity_type_manager->reveal(), $entity_type_repository->reveal(), $entity_field_manager->reveal());
|
||||
|
||||
// Verify the denormalizer still works correctly: the mock above creates an
|
||||
// artificial entity object containing exactly the data it received. It also
|
||||
// should still set _restSubmittedFields correctly.
|
||||
$expected_denormalization = (object) [
|
||||
'_restSubmittedFields' => [
|
||||
'test',
|
||||
],
|
||||
'received_data' => [
|
||||
'test' => $test_value,
|
||||
],
|
||||
];
|
||||
$this->assertEquals($expected_denormalization, $normalizer->denormalize($data, $entity_type_class, 'json'));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeRepositoryInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Session\AccountInterface;
|
||||
use Drupal\Core\TypedData\ComplexDataInterface;
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\serialization\Normalizer\ContentEntityNormalizer;
|
||||
use Drupal\Tests\Core\Entity\ContentEntityBaseMockableClass;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\ContentEntityNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class ContentEntityNormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The mock serializer.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\Serializer|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* The normalizer under test.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\ContentEntityNormalizer
|
||||
*/
|
||||
protected $contentEntityNormalizer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$entity_field_manager = $this->createMock(EntityFieldManagerInterface::class);
|
||||
$entity_type_manager = $this->createMock(EntityTypeManagerInterface::class);
|
||||
$entity_type_repository = $this->createMock(EntityTypeRepositoryInterface::class);
|
||||
|
||||
$this->contentEntityNormalizer = new ContentEntityNormalizer($entity_type_manager, $entity_type_repository, $entity_field_manager);
|
||||
|
||||
$this->serializer = $this->prophesize(Serializer::class);
|
||||
$this->contentEntityNormalizer->setSerializer($this->serializer->reveal());
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$content_mock = $this->createMock('Drupal\Core\Entity\ContentEntityInterface');
|
||||
$config_mock = $this->createMock('Drupal\Core\Config\Entity\ConfigEntityInterface');
|
||||
$this->assertTrue($this->contentEntityNormalizer->supportsNormalization($content_mock));
|
||||
$this->assertFalse($this->contentEntityNormalizer->supportsNormalization($config_mock));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the normalize() method.
|
||||
*
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$this->serializer->normalize(Argument::type(FieldItemListInterface::class),
|
||||
'test_format', ['account' => NULL])->willReturn('test');
|
||||
|
||||
$definitions = [
|
||||
'field_accessible_external' => $this->createMockFieldListItem(TRUE, FALSE),
|
||||
'field_non-accessible_external' => $this->createMockFieldListItem(FALSE, FALSE),
|
||||
'field_accessible_internal' => $this->createMockFieldListItem(TRUE, TRUE),
|
||||
'field_non-accessible_internal' => $this->createMockFieldListItem(FALSE, TRUE),
|
||||
];
|
||||
$content_entity_mock = $this->createMockForContentEntity($definitions);
|
||||
|
||||
$normalized = $this->contentEntityNormalizer->normalize($content_entity_mock, 'test_format');
|
||||
|
||||
$this->assertArrayHasKey('field_accessible_external', $normalized);
|
||||
$this->assertEquals('test', $normalized['field_accessible_external']);
|
||||
$this->assertArrayNotHasKey('field_non-accessible_external', $normalized);
|
||||
$this->assertArrayNotHasKey('field_accessible_internal', $normalized);
|
||||
$this->assertArrayNotHasKey('field_non-accessible_internal', $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the normalize() method with account context passed.
|
||||
*
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalizeWithAccountContext(): void {
|
||||
$mock_account = $this->createMock('Drupal\Core\Session\AccountInterface');
|
||||
|
||||
$context = [
|
||||
'account' => $mock_account,
|
||||
];
|
||||
|
||||
$this->serializer->normalize(Argument::type(FieldItemListInterface::class),
|
||||
'test_format', $context)->willReturn('test');
|
||||
|
||||
// The mock account should get passed directly into the access() method on
|
||||
// field items from $context['account'].
|
||||
$definitions = [
|
||||
'field_1' => $this->createMockFieldListItem(TRUE, FALSE, $mock_account),
|
||||
'field_2' => $this->createMockFieldListItem(FALSE, FALSE, $mock_account),
|
||||
];
|
||||
$content_entity_mock = $this->createMockForContentEntity($definitions);
|
||||
|
||||
$normalized = $this->contentEntityNormalizer->normalize($content_entity_mock, 'test_format', $context);
|
||||
|
||||
$this->assertArrayHasKey('field_1', $normalized);
|
||||
$this->assertEquals('test', $normalized['field_1']);
|
||||
$this->assertArrayNotHasKey('field_2', $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mock content entity.
|
||||
*
|
||||
* @param array $definitions
|
||||
* The properties the will be returned.
|
||||
*
|
||||
* @return \PHPUnit\Framework\MockObject\MockObject
|
||||
* The mock content entity.
|
||||
*/
|
||||
public function createMockForContentEntity($definitions) {
|
||||
$content_entity_mock = $this->getMockBuilder(ContentEntityBaseMockableClass::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getTypedData'])
|
||||
->getMock();
|
||||
$typed_data = $this->prophesize(ComplexDataInterface::class);
|
||||
$typed_data->getProperties(TRUE)
|
||||
->willReturn($definitions)
|
||||
->shouldBeCalled();
|
||||
$content_entity_mock->expects($this->any())
|
||||
->method('getTypedData')
|
||||
->willReturn($typed_data->reveal());
|
||||
|
||||
return $content_entity_mock;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a mock field list item.
|
||||
*
|
||||
* @param bool $access
|
||||
* The value that access() will return.
|
||||
* @param bool $internal
|
||||
* The value that isInternal() will return.
|
||||
* @param \Drupal\Core\Session\AccountInterface $user_context
|
||||
* The user context used for the access check.
|
||||
*
|
||||
* @return \Drupal\Core\Field\FieldItemListInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
* The mock field list item.
|
||||
*/
|
||||
protected function createMockFieldListItem($access, $internal, ?AccountInterface $user_context = NULL) {
|
||||
$data_definition = $this->prophesize(DataDefinitionInterface::class);
|
||||
$mock = $this->createMock('Drupal\Core\Field\FieldItemListInterface');
|
||||
$mock->expects($this->once())
|
||||
->method('getDataDefinition')
|
||||
->willReturn($data_definition->reveal());
|
||||
$data_definition->isInternal()
|
||||
->willReturn($internal)
|
||||
->shouldBeCalled();
|
||||
if (!$internal) {
|
||||
$mock->expects($this->once())
|
||||
->method('access')
|
||||
->with('view', $user_context)
|
||||
->willReturn($access);
|
||||
}
|
||||
return $mock;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\ImmutableConfig;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\FieldItemBase;
|
||||
use Drupal\Core\Field\FieldStorageDefinitionInterface;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
|
||||
use Drupal\Core\TypedData\Type\DateTimeInterface;
|
||||
use Drupal\datetime\Plugin\Field\FieldType\DateTimeItem;
|
||||
use Drupal\serialization\Normalizer\DateTimeIso8601Normalizer;
|
||||
use Drupal\Tests\serialization\Traits\JsonSchemaTestTrait;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Unit test coverage for the "datetime_iso8601" @DataType.
|
||||
*
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\DateTimeIso8601Normalizer
|
||||
* @group serialization
|
||||
* @see \Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601
|
||||
* @see \Drupal\datetime\Plugin\Field\FieldType\DateTimeItem::DATETIME_TYPE_DATE
|
||||
*/
|
||||
class DateTimeIso8601NormalizerTest extends UnitTestCase {
|
||||
|
||||
use JsonSchemaTestTrait;
|
||||
|
||||
/**
|
||||
* The tested data type's normalizer.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\DateTimeIso8601Normalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The tested data type.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$system_date_config = $this->prophesize(ImmutableConfig::class);
|
||||
$system_date_config->get('timezone.default')
|
||||
->willReturn('Australia/Sydney');
|
||||
$config_factory = $this->prophesize(ConfigFactoryInterface::class);
|
||||
$config_factory->get('system.date')
|
||||
->willReturn($system_date_config->reveal());
|
||||
|
||||
$this->normalizer = new DateTimeIso8601Normalizer($config_factory->reveal());
|
||||
$this->data = $this->prophesize(DateTimeIso8601::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->data->reveal()));
|
||||
|
||||
$datetime = $this->prophesize(DateTimeInterface::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($datetime->reveal()));
|
||||
|
||||
$integer = $this->prophesize(IntegerData::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($integer->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsDenormalization
|
||||
*/
|
||||
public function testSupportsDenormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization($this->data->reveal(), DateTimeIso8601::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
* @dataProvider providerTestNormalize
|
||||
*/
|
||||
public function testNormalize($parent_field_item_class, $datetime_type, $expected_format): void {
|
||||
$formatted_string = $this->randomMachineName();
|
||||
|
||||
$field_item = $this->prophesize($parent_field_item_class);
|
||||
if ($parent_field_item_class === DateTimeItem::class) {
|
||||
$field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class);
|
||||
$field_storage_definition->getSetting('datetime_type')
|
||||
->willReturn($datetime_type);
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getFieldStorageDefinition()
|
||||
->willReturn($field_storage_definition);
|
||||
$field_item->getFieldDefinition()
|
||||
->willReturn($field_definition);
|
||||
}
|
||||
else {
|
||||
$field_item->getFieldDefinition(Argument::any())
|
||||
->shouldNotBeCalled();
|
||||
}
|
||||
$this->data->getParent()
|
||||
->willReturn($field_item);
|
||||
|
||||
$drupal_date_time = $this->prophesize(DateTimeIso8601NormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('Australia/Sydney'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format($expected_format)
|
||||
->willReturn($formatted_string);
|
||||
$this->data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->data->reveal());
|
||||
$this->assertSame($formatted_string, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
* @dataProvider providerTestNormalize
|
||||
*/
|
||||
public function testNormalizeWhenNull($parent_field_item_class, $datetime_type, $expected_format): void {
|
||||
$field_item = $this->prophesize($parent_field_item_class);
|
||||
if ($parent_field_item_class === DateTimeItem::class) {
|
||||
$field_storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class);
|
||||
$field_storage_definition->getSetting('datetime_type')
|
||||
->willReturn($datetime_type);
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getFieldStorageDefinition()
|
||||
->willReturn($field_storage_definition);
|
||||
$field_item->getFieldDefinition()
|
||||
->willReturn($field_definition);
|
||||
}
|
||||
else {
|
||||
$field_item->getFieldDefinition(Argument::any())
|
||||
->shouldNotBeCalled();
|
||||
}
|
||||
$this->data->getParent()
|
||||
->willReturn($field_item);
|
||||
|
||||
$this->data->getDateTime()
|
||||
->willReturn(NULL);
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->data->reveal());
|
||||
$this->assertNull($normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testNormalize.
|
||||
*
|
||||
* @return array
|
||||
* The data provider array.
|
||||
*/
|
||||
public static function providerTestNormalize() {
|
||||
return [
|
||||
// @see \Drupal\datetime\Plugin\Field\FieldType\DateTimeItem::DATETIME_TYPE_DATE
|
||||
'datetime field, configured to store only date: must be handled by DateTimeIso8601Normalizer' => [
|
||||
DateTimeItem::class,
|
||||
DateTimeItem::DATETIME_TYPE_DATE,
|
||||
// This expected format call proves that normalization is handled by \Drupal\serialization\Normalizer\DateTimeIso8601Normalizer::normalize().
|
||||
'Y-m-d',
|
||||
],
|
||||
// @see \Drupal\datetime\Plugin\Field\FieldType\DateTimeItem::DATETIME_TYPE_DATETIME
|
||||
'datetime field, configured to store date and time; must be handled by the parent normalizer' => [
|
||||
DateTimeItem::class,
|
||||
DateTimeItem::DATETIME_TYPE_DATETIME,
|
||||
\DateTime::RFC3339,
|
||||
],
|
||||
'non-datetime field; must be handled by the parent normalizer' => [
|
||||
FieldItemBase::class,
|
||||
NULL,
|
||||
\DateTime::RFC3339,
|
||||
],
|
||||
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with good data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
* @dataProvider providerTestDenormalizeValidFormats
|
||||
*/
|
||||
public function testDenormalizeValidFormats($type, $normalized, $expected): void {
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getSetting('datetime_type')->willReturn($type === 'date-only' ? DateTimeItem::DATETIME_TYPE_DATE : DateTimeItem::DATETIME_TYPE_DATETIME);
|
||||
$denormalized = $this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, [
|
||||
'field_definition' => $field_definition->reveal(),
|
||||
]);
|
||||
$this->assertSame($expected, $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeValidFormats.
|
||||
*
|
||||
* @return array
|
||||
* An array of test cases.
|
||||
*/
|
||||
public static function providerTestDenormalizeValidFormats() {
|
||||
$data = [];
|
||||
$data['just a date'] = ['date-only', '2016-11-06', '2016-11-06'];
|
||||
|
||||
$data['RFC3339'] = ['date+time', '2016-11-06T09:02:00+00:00', '2016-11-06T09:02:00'];
|
||||
$data['RFC3339 +0100'] = ['date+time', '2016-11-06T09:02:00+01:00', '2016-11-06T08:02:00'];
|
||||
$data['RFC3339 -0600'] = ['date+time', '2016-11-06T09:02:00-06:00', '2016-11-06T15:02:00'];
|
||||
|
||||
$data['ISO8601'] = ['date+time', '2016-11-06T09:02:00+0000', '2016-11-06T09:02:00'];
|
||||
$data['ISO8601 +0100'] = ['date+time', '2016-11-06T09:02:00+0100', '2016-11-06T08:02:00'];
|
||||
$data['ISO8601 -0600'] = ['date+time', '2016-11-06T09:02:00-0600', '2016-11-06T15:02:00'];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with bad data for the date-only case.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeDateOnlyException(): void {
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('The specified date "2016/11/06" is not in an accepted format: "Y-m-d" (date-only).');
|
||||
|
||||
$normalized = '2016/11/06';
|
||||
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getSetting('datetime_type')->willReturn(DateTimeItem::DATETIME_TYPE_DATE);
|
||||
$this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, ['field_definition' => $field_definition->reveal()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with bad data for the date+time case.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeDateAndTimeException(): void {
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('The specified date "on a rainy day" is not in an accepted format: "Y-m-d\TH:i:sP" (RFC 3339), "Y-m-d\TH:i:sO" (ISO 8601).');
|
||||
|
||||
$normalized = 'on a rainy day';
|
||||
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getSetting('datetime_type')->willReturn(DateTimeItem::DATETIME_TYPE_DATETIME);
|
||||
$this->normalizer->denormalize($normalized, DateTimeIso8601::class, NULL, ['field_definition' => $field_definition->reveal()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with incomplete serialization context.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeNoTargetInstanceOrFieldDefinitionException(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('$context[\'target_instance\'] or $context[\'field_definition\'] must be set to denormalize with the DateTimeIso8601Normalizer');
|
||||
$this->normalizer->denormalize('', DateTimeIso8601::class, NULL, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function jsonSchemaDataProvider(): array {
|
||||
$case = function (UnitTestCase $test) {
|
||||
assert(in_array(JsonSchemaTestTrait::class, class_uses($test)));
|
||||
$field_item = $test->doProphesize(DateTimeItem::class);
|
||||
$data = $test->doProphesize(DateTimeIso8601::class);
|
||||
|
||||
$field_storage_definition = $test->doProphesize(FieldStorageDefinitionInterface::class);
|
||||
$field_storage_definition->getSetting('datetime_type')
|
||||
->willReturn(DateTimeItem::DATETIME_TYPE_DATE);
|
||||
$field_definition = $test->doProphesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getFieldStorageDefinition()
|
||||
->willReturn($field_storage_definition);
|
||||
$field_item->getFieldDefinition()
|
||||
->willReturn($field_definition);
|
||||
$data->getParent()
|
||||
->willReturn($field_item);
|
||||
$drupal_date_time = $test->doProphesize(DateTimeIso8601NormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('Australia/Sydney'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format('Y-m-d')
|
||||
->willReturn('1991-09-19');
|
||||
$data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
return $data->reveal();
|
||||
};
|
||||
return [
|
||||
'ISO 8601 date-only' => [fn (UnitTestCase $test) => $case($test)],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a test class for testing DrupalDateTime.
|
||||
*
|
||||
* Note: Prophecy does not support magic methods. By subclassing and specifying
|
||||
* an explicit method, Prophecy works.
|
||||
*
|
||||
* @see https://github.com/phpspec/prophecy/issues/338
|
||||
* @see https://github.com/phpspec/prophecy/issues/34
|
||||
* @see https://github.com/phpspec/prophecy/issues/80
|
||||
*/
|
||||
class DateTimeIso8601NormalizerTestDrupalDateTime extends DrupalDateTime {
|
||||
|
||||
/**
|
||||
* Sets the timezone.
|
||||
*/
|
||||
public function setTimezone(\DateTimeZone $timezone) {
|
||||
parent::setTimezone($timezone);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,242 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Config\ImmutableConfig;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\DateTimeIso8601;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
|
||||
use Drupal\Core\TypedData\Type\DateTimeInterface;
|
||||
use Drupal\serialization\Normalizer\DateTimeNormalizer;
|
||||
use Drupal\Tests\serialization\Traits\JsonSchemaTestTrait;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Unit test coverage for @DataTypes implementing DateTimeInterface.
|
||||
*
|
||||
* @group serialization
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\DateTimeNormalizer
|
||||
* @see \Drupal\Core\TypedData\Type\DateTimeInterface
|
||||
*/
|
||||
class DateTimeNormalizerTest extends UnitTestCase {
|
||||
|
||||
use JsonSchemaTestTrait;
|
||||
|
||||
/**
|
||||
* The tested data type's normalizer.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\DateTimeNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The tested data type.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\Type\DateTimeInterface
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$system_date_config = $this->prophesize(ImmutableConfig::class);
|
||||
$system_date_config->get('timezone.default')
|
||||
->willReturn('Australia/Sydney');
|
||||
$config_factory = $this->prophesize(ConfigFactoryInterface::class);
|
||||
$config_factory->get('system.date')
|
||||
->willReturn($system_date_config->reveal());
|
||||
|
||||
$this->normalizer = new DateTimeNormalizer($config_factory->reveal());
|
||||
$this->data = $this->prophesize(DateTimeInterface::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->data->reveal()));
|
||||
|
||||
$datetimeiso8601 = $this->prophesize(DateTimeIso8601::class);
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($datetimeiso8601->reveal()));
|
||||
|
||||
$integer = $this->prophesize(IntegerData::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($integer->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsDenormalization
|
||||
*/
|
||||
public function testSupportsDenormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization($this->data->reveal(), DateTimeInterface::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$random_rfc_3339_string = $this->randomMachineName();
|
||||
|
||||
$drupal_date_time = $this->prophesize(DateTimeNormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('Australia/Sydney'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format(\DateTime::RFC3339)
|
||||
->willReturn($random_rfc_3339_string);
|
||||
|
||||
$this->data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->data->reveal());
|
||||
$this->assertSame($random_rfc_3339_string, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalizeWhenNull(): void {
|
||||
$this->data->getDateTime()
|
||||
->willReturn(NULL);
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->data->reveal());
|
||||
$this->assertNull($normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with good data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
* @dataProvider providerTestDenormalizeValidFormats
|
||||
*/
|
||||
public function testDenormalizeValidFormats($normalized, $expected): void {
|
||||
$denormalized = $this->normalizer->denormalize($normalized, DateTimeInterface::class, NULL, []);
|
||||
$this->assertSame(0, $denormalized->getTimestamp() - $expected->getTimestamp());
|
||||
$this->assertEquals($expected, $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeValidFormats.
|
||||
*
|
||||
* @return array
|
||||
* An array of test data.
|
||||
*/
|
||||
public static function providerTestDenormalizeValidFormats() {
|
||||
$data = [];
|
||||
|
||||
$data['RFC3339'] = ['2016-11-06T09:02:00+00:00', new \DateTimeImmutable('2016-11-06T09:02:00+00:00')];
|
||||
$data['RFC3339 +0100'] = ['2016-11-06T09:02:00+01:00', new \DateTimeImmutable('2016-11-06T09:02:00+01:00')];
|
||||
$data['RFC3339 -0600'] = ['2016-11-06T09:02:00-06:00', new \DateTimeImmutable('2016-11-06T09:02:00-06:00')];
|
||||
|
||||
$data['ISO8601'] = ['2016-11-06T09:02:00+0000', new \DateTimeImmutable('2016-11-06T09:02:00+00:00')];
|
||||
$data['ISO8601 +0100'] = ['2016-11-06T09:02:00+0100', new \DateTimeImmutable('2016-11-06T09:02:00+01:00')];
|
||||
$data['ISO8601 -0600'] = ['2016-11-06T09:02:00-0600', new \DateTimeImmutable('2016-11-06T09:02:00-06:00')];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with a user supplied format.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
* @dataProvider providerTestDenormalizeUserFormats
|
||||
*/
|
||||
public function testDenormalizeUserFormats($normalized, $format, $expected): void {
|
||||
$denormalized = $this->normalizer->denormalize($normalized, DateTimeInterface::class, NULL, ['datetime_allowed_formats' => [$format]]);
|
||||
$this->assertSame(0, $denormalized->getTimestamp() - $expected->getTimestamp());
|
||||
$this->assertEquals($expected, $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeUserFormats.
|
||||
*
|
||||
* @return array
|
||||
* An array of test data.
|
||||
*/
|
||||
public static function providerTestDenormalizeUserFormats() {
|
||||
$data = [];
|
||||
|
||||
$data['Y/m/d H:i:s P'] = [
|
||||
'2016/11/06 09:02:00 +00:00',
|
||||
'Y/m/d H:i:s P',
|
||||
new \DateTimeImmutable('2016-11-06T09:02:00+00:00'),
|
||||
];
|
||||
$data['H:i:s Y/m/d P'] = [
|
||||
'09:02:00 2016/11/06 +01:00',
|
||||
'H:i:s Y/m/d P',
|
||||
new \DateTimeImmutable('2016-11-06T09:02:00+01:00'),
|
||||
];
|
||||
$data['Y/m/d H:i:s'] = [
|
||||
'09:02:00 2016/11/06',
|
||||
'H:i:s Y/m/d',
|
||||
new \DateTimeImmutable('2016-11-06T09:02:00+11:00'),
|
||||
];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with bad data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeException(): void {
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('The specified date "2016/11/06 09:02am GMT" is not in an accepted format: "Y-m-d\TH:i:sP" (RFC 3339), "Y-m-d\TH:i:sO" (ISO 8601).');
|
||||
|
||||
$normalized = '2016/11/06 09:02am GMT';
|
||||
|
||||
$this->normalizer->denormalize($normalized, DateTimeInterface::class, NULL, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate test data for date data providers.
|
||||
*
|
||||
* @return array
|
||||
* Test data for time formats supported by DateTimeNormalizer.
|
||||
*/
|
||||
public static function jsonSchemaDataProvider(): array {
|
||||
$case = function (UnitTestCase $test) {
|
||||
$drupal_date_time = $test->prophesize(DateTimeNormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('Australia/Sydney'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format(\DateTime::RFC3339)
|
||||
->willReturn('1983-07-12T05:00:00-05:00');
|
||||
|
||||
$data = $test->prophesize(DateTimeInterface::class);
|
||||
$data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
return $data->reveal();
|
||||
};
|
||||
return [
|
||||
'RFC 3339' => [fn (UnitTestCase $test) => $case($test)],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Provides a test class for testing DrupalDateTime.
|
||||
*
|
||||
* Note: Prophecy does not support magic methods. By subclassing and specifying
|
||||
* an explicit method, Prophecy works.
|
||||
*
|
||||
* @see https://github.com/phpspec/prophecy/issues/338
|
||||
* @see https://github.com/phpspec/prophecy/issues/34
|
||||
* @see https://github.com/phpspec/prophecy/issues/80
|
||||
*/
|
||||
class DateTimeNormalizerTestDrupalDateTime extends DrupalDateTime {
|
||||
|
||||
/**
|
||||
* Sets the timezone.
|
||||
*/
|
||||
public function setTimezone(\DateTimeZone $timezone) {
|
||||
parent::setTimezone($timezone);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,425 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\EntityFieldManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeManagerInterface;
|
||||
use Drupal\Core\Entity\EntityTypeRepositoryInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\serialization\Normalizer\EntityNormalizer;
|
||||
use Drupal\Tests\Core\Entity\ContentEntityBaseMockableClass;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\EntityNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class EntityNormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The mock entity field manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityFieldManagerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $entityFieldManager;
|
||||
|
||||
/**
|
||||
* The mock entity type manager.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $entityTypeManager;
|
||||
|
||||
/**
|
||||
* The mock entity type repository.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityTypeRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $entityTypeRepository;
|
||||
|
||||
/**
|
||||
* The mock serializer.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\SerializerInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* The entity normalizer.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\EntityNormalizer
|
||||
*/
|
||||
protected $entityNormalizer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityFieldManager = $this->createMock(EntityFieldManagerInterface::class);
|
||||
$this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class);
|
||||
$this->entityTypeRepository = $this->createMock(EntityTypeRepositoryInterface::class);
|
||||
|
||||
$this->entityNormalizer = new EntityNormalizer(
|
||||
$this->entityTypeManager,
|
||||
$this->entityTypeRepository,
|
||||
$this->entityFieldManager
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the normalize() method.
|
||||
*
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$list_item_1 = $this->createMock('Drupal\Core\TypedData\TypedDataInterface');
|
||||
$list_item_2 = $this->createMock('Drupal\Core\TypedData\TypedDataInterface');
|
||||
|
||||
$definitions = [
|
||||
'field_1' => $list_item_1,
|
||||
'field_2' => $list_item_2,
|
||||
];
|
||||
|
||||
$content_entity = $this->getMockBuilder(ContentEntityBaseMockableClass::class)
|
||||
->disableOriginalConstructor()
|
||||
->onlyMethods(['getFields'])
|
||||
->getMock();
|
||||
$content_entity->expects($this->once())
|
||||
->method('getFields')
|
||||
->willReturn($definitions);
|
||||
|
||||
$serializer = $this->prophesize('Symfony\Component\Serializer\Serializer');
|
||||
$serializer->normalize($list_item_1, 'test_format', [])->shouldBeCalled();
|
||||
$serializer->normalize($list_item_2, 'test_format', [])->shouldBeCalled();
|
||||
|
||||
$this->entityNormalizer->setSerializer($serializer->reveal());
|
||||
|
||||
$this->entityNormalizer->normalize($content_entity, 'test_format');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize() method with no entity type provided in context.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithNoEntityType(): void {
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->entityNormalizer->denormalize([], ContentEntityBaseMockableClass::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize method with a bundle property.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithValidBundle(): void {
|
||||
$test_data = [
|
||||
'key_1' => 'value_1',
|
||||
'key_2' => 'value_2',
|
||||
'test_type' => [
|
||||
['name' => 'test_bundle'],
|
||||
],
|
||||
];
|
||||
|
||||
$entity_type = $this->createMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||
|
||||
$entity_type->expects($this->once())
|
||||
->method('id')
|
||||
->willReturn('test');
|
||||
$entity_type->expects($this->once())
|
||||
->method('hasKey')
|
||||
->with('bundle')
|
||||
->willReturn(TRUE);
|
||||
$entity_type->expects($this->once())
|
||||
->method('getKey')
|
||||
->with('bundle')
|
||||
->willReturn('test_type');
|
||||
$entity_type->expects($this->once())
|
||||
->method('entityClassImplements')
|
||||
->with(FieldableEntityInterface::class)
|
||||
->willReturn(TRUE);
|
||||
|
||||
$entity_type->expects($this->once())
|
||||
->method('getBundleEntityType')
|
||||
->willReturn('test_bundle');
|
||||
|
||||
$entity_type_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
|
||||
$entity_type_storage_definition->expects($this->once())
|
||||
->method('getMainPropertyName')
|
||||
->willReturn('name');
|
||||
|
||||
$entity_type_definition = $this->createMock('Drupal\Core\Field\FieldDefinitionInterface');
|
||||
$entity_type_definition->expects($this->once())
|
||||
->method('getFieldStorageDefinition')
|
||||
->willReturn($entity_type_storage_definition);
|
||||
|
||||
$base_definitions = [
|
||||
'test_type' => $entity_type_definition,
|
||||
];
|
||||
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getDefinition')
|
||||
->with('test')
|
||||
->willReturn($entity_type);
|
||||
$this->entityFieldManager->expects($this->once())
|
||||
->method('getBaseFieldDefinitions')
|
||||
->with('test')
|
||||
->willReturn($base_definitions);
|
||||
|
||||
$entity_query_mock = $this->createMock('Drupal\Core\Entity\Query\QueryInterface');
|
||||
$entity_query_mock->expects($this->once())
|
||||
->method('accessCheck')
|
||||
->with(TRUE)
|
||||
->willReturn($entity_query_mock);
|
||||
$entity_query_mock->expects($this->once())
|
||||
->method('execute')
|
||||
->willReturn(['test_bundle' => 'test_bundle']);
|
||||
|
||||
$entity_type_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
$entity_type_storage->expects($this->once())
|
||||
->method('getQuery')
|
||||
->willReturn($entity_query_mock);
|
||||
|
||||
$key_1 = $this->createMock(FieldItemListInterface::class);
|
||||
$key_2 = $this->createMock(FieldItemListInterface::class);
|
||||
|
||||
$entity = $this->createMock(ContentEntityBaseMockableClass::class);
|
||||
$entity->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->willReturnMap([
|
||||
['key_1', $key_1],
|
||||
['key_2', $key_2],
|
||||
]);
|
||||
|
||||
$storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
// Create should only be called with the bundle property at first.
|
||||
$expected_test_data = [
|
||||
'test_type' => 'test_bundle',
|
||||
];
|
||||
|
||||
$storage->expects($this->once())
|
||||
->method('create')
|
||||
->with($expected_test_data)
|
||||
->willReturn($entity);
|
||||
|
||||
$this->entityTypeManager->expects($this->exactly(2))
|
||||
->method('getStorage')
|
||||
->willReturnMap([
|
||||
['test_bundle', $entity_type_storage],
|
||||
['test', $storage],
|
||||
]);
|
||||
|
||||
// Setup expectations for the serializer. This will be called for each field
|
||||
// item.
|
||||
$serializer = $this->prophesize('Symfony\Component\Serializer\Serializer');
|
||||
$serializer->denormalize('value_1', get_class($key_1), NULL, ['target_instance' => $key_1, 'entity_type' => 'test'])
|
||||
->willReturn(NULL)
|
||||
->shouldBeCalled();
|
||||
$serializer->denormalize('value_2', get_class($key_2), NULL, ['target_instance' => $key_2, 'entity_type' => 'test'])
|
||||
->willReturn(NULL)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->entityNormalizer->setSerializer($serializer->reveal());
|
||||
|
||||
$this->assertNotNull($this->entityNormalizer->denormalize($test_data, ContentEntityBaseMockableClass::class, NULL, ['entity_type' => 'test']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize method with a bundle property.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithInvalidBundle(): void {
|
||||
$test_data = [
|
||||
'key_1' => 'value_1',
|
||||
'key_2' => 'value_2',
|
||||
'test_type' => [
|
||||
['name' => 'test_bundle'],
|
||||
],
|
||||
];
|
||||
|
||||
$entity_type = $this->createMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||
|
||||
$entity_type->expects($this->once())
|
||||
->method('id')
|
||||
->willReturn('test');
|
||||
$entity_type->expects($this->once())
|
||||
->method('hasKey')
|
||||
->with('bundle')
|
||||
->willReturn(TRUE);
|
||||
$entity_type->expects($this->once())
|
||||
->method('getKey')
|
||||
->with('bundle')
|
||||
->willReturn('test_type');
|
||||
$entity_type->expects($this->once())
|
||||
->method('entityClassImplements')
|
||||
->with(FieldableEntityInterface::class)
|
||||
->willReturn(TRUE);
|
||||
|
||||
$entity_type->expects($this->once())
|
||||
->method('getBundleEntityType')
|
||||
->willReturn('test_bundle');
|
||||
|
||||
$entity_type_storage_definition = $this->createMock('Drupal\Core\Field\FieldStorageDefinitionInterface');
|
||||
$entity_type_storage_definition->expects($this->once())
|
||||
->method('getMainPropertyName')
|
||||
->willReturn('name');
|
||||
|
||||
$entity_type_definition = $this->createMock('Drupal\Core\Field\FieldDefinitionInterface');
|
||||
$entity_type_definition->expects($this->once())
|
||||
->method('getFieldStorageDefinition')
|
||||
->willReturn($entity_type_storage_definition);
|
||||
|
||||
$base_definitions = [
|
||||
'test_type' => $entity_type_definition,
|
||||
];
|
||||
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getDefinition')
|
||||
->with('test')
|
||||
->willReturn($entity_type);
|
||||
$this->entityFieldManager->expects($this->once())
|
||||
->method('getBaseFieldDefinitions')
|
||||
->with('test')
|
||||
->willReturn($base_definitions);
|
||||
|
||||
$entity_query_mock = $this->createMock('Drupal\Core\Entity\Query\QueryInterface');
|
||||
$entity_query_mock->expects($this->once())
|
||||
->method('accessCheck')
|
||||
->with(TRUE)
|
||||
->willReturn($entity_query_mock);
|
||||
$entity_query_mock->expects($this->once())
|
||||
->method('execute')
|
||||
->willReturn(['test_bundle_other' => 'test_bundle_other']);
|
||||
|
||||
$entity_type_storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
$entity_type_storage->expects($this->once())
|
||||
->method('getQuery')
|
||||
->willReturn($entity_query_mock);
|
||||
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getStorage')
|
||||
->with('test_bundle')
|
||||
->willReturn($entity_type_storage);
|
||||
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->entityNormalizer->denormalize($test_data, ContentEntityBaseMockableClass::class, NULL, ['entity_type' => 'test']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize method with no bundle defined.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithNoBundle(): void {
|
||||
$test_data = [
|
||||
'key_1' => 'value_1',
|
||||
'key_2' => 'value_2',
|
||||
];
|
||||
|
||||
$entity_type = $this->createMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||
$entity_type->expects($this->once())
|
||||
->method('entityClassImplements')
|
||||
->with(FieldableEntityInterface::class)
|
||||
->willReturn(TRUE);
|
||||
$entity_type->expects($this->once())
|
||||
->method('hasKey')
|
||||
->with('bundle')
|
||||
->willReturn(FALSE);
|
||||
$entity_type->expects($this->never())
|
||||
->method('getKey');
|
||||
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getDefinition')
|
||||
->with('test')
|
||||
->willReturn($entity_type);
|
||||
|
||||
$key_1 = $this->createMock(FieldItemListInterface::class);
|
||||
$key_2 = $this->createMock(FieldItemListInterface::class);
|
||||
|
||||
$entity = $this->createMock(ContentEntityBaseMockableClass::class);
|
||||
$entity->expects($this->exactly(2))
|
||||
->method('get')
|
||||
->willReturnMap([
|
||||
['key_1', $key_1],
|
||||
['key_2', $key_2],
|
||||
]);
|
||||
|
||||
$storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
$storage->expects($this->once())
|
||||
->method('create')
|
||||
->with([])
|
||||
->willReturn($entity);
|
||||
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getStorage')
|
||||
->with('test')
|
||||
->willReturn($storage);
|
||||
|
||||
$this->entityFieldManager->expects($this->never())
|
||||
->method('getBaseFieldDefinitions');
|
||||
|
||||
// Setup expectations for the serializer. This will be called for each field
|
||||
// item.
|
||||
$serializer = $this->prophesize('Symfony\Component\Serializer\Serializer');
|
||||
$serializer->denormalize('value_1', get_class($key_1), NULL, ['target_instance' => $key_1, 'entity_type' => 'test'])
|
||||
->willReturn(NULL)
|
||||
->shouldBeCalled();
|
||||
$serializer->denormalize('value_2', get_class($key_2), NULL, ['target_instance' => $key_2, 'entity_type' => 'test'])
|
||||
->willReturn(NULL)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->entityNormalizer->setSerializer($serializer->reveal());
|
||||
|
||||
$this->assertNotNull($this->entityNormalizer->denormalize($test_data, ContentEntityBaseMockableClass::class, NULL, ['entity_type' => 'test']));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize method with no bundle defined.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithNoFieldableEntityType(): void {
|
||||
$test_data = [
|
||||
'key_1' => 'value_1',
|
||||
'key_2' => 'value_2',
|
||||
];
|
||||
|
||||
$entity_type = $this->createMock('Drupal\Core\Entity\EntityTypeInterface');
|
||||
$entity_type->expects($this->once())
|
||||
->method('entityClassImplements')
|
||||
->with(FieldableEntityInterface::class)
|
||||
->willReturn(FALSE);
|
||||
|
||||
$entity_type->expects($this->never())
|
||||
->method('getKey');
|
||||
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getDefinition')
|
||||
->with('test')
|
||||
->willReturn($entity_type);
|
||||
|
||||
$storage = $this->createMock('Drupal\Core\Entity\EntityStorageInterface');
|
||||
$storage->expects($this->once())
|
||||
->method('create')
|
||||
->with($test_data)
|
||||
->willReturn($this->createMock(ContentEntityBaseMockableClass::class));
|
||||
|
||||
$this->entityTypeManager->expects($this->once())
|
||||
->method('getStorage')
|
||||
->with('test')
|
||||
->willReturn($storage);
|
||||
|
||||
$this->entityFieldManager->expects($this->never())
|
||||
->method('getBaseFieldDefinitions');
|
||||
|
||||
$this->assertNotNull($this->entityNormalizer->denormalize($test_data, ContentEntityBaseMockableClass::class, NULL, ['entity_type' => 'test']));
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,515 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
|
||||
use Drupal\Core\GeneratedUrl;
|
||||
use Drupal\Core\TypedData\Type\IntegerInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\Core\Entity\EntityRepositoryInterface;
|
||||
use Drupal\Core\Entity\FieldableEntityInterface;
|
||||
use Drupal\Core\Field\FieldItemInterface;
|
||||
use Drupal\Core\Field\FieldItemListInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||
use Drupal\Core\Url;
|
||||
use Drupal\locale\StringInterface;
|
||||
use Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Argument;
|
||||
use Symfony\Component\Serializer\Exception\InvalidArgumentException;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class EntityReferenceFieldItemNormalizerTest extends UnitTestCase {
|
||||
|
||||
use InternalTypedDataTestTrait;
|
||||
|
||||
/**
|
||||
* The mock serializer.
|
||||
*
|
||||
* @var \Symfony\Component\Serializer\SerializerInterface|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $serializer;
|
||||
|
||||
/**
|
||||
* The normalizer under test.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\EntityReferenceFieldItemNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The mock field item.
|
||||
*
|
||||
* @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $fieldItem;
|
||||
|
||||
/**
|
||||
* The mock entity repository.
|
||||
*
|
||||
* @var \Drupal\Core\Entity\EntityRepositoryInterface|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $entityRepository;
|
||||
|
||||
/**
|
||||
* The mock field definition.
|
||||
*
|
||||
* @var \Drupal\Core\Field\FieldDefinitionInterface|\Prophecy\Prophecy\ObjectProphecy
|
||||
*/
|
||||
protected $fieldDefinition;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->entityRepository = $this->prophesize(EntityRepositoryInterface::class);
|
||||
$this->normalizer = new EntityReferenceFieldItemNormalizer($this->entityRepository->reveal());
|
||||
|
||||
$this->serializer = $this->prophesize(Serializer::class);
|
||||
// Set up the serializer to return an entity property.
|
||||
$this->serializer->normalize(Argument::cetera())
|
||||
->willReturn('test');
|
||||
|
||||
$this->normalizer->setSerializer($this->serializer->reveal());
|
||||
|
||||
$this->fieldItem = $this->prophesize(EntityReferenceItem::class);
|
||||
$this->fieldItem->getIterator()
|
||||
->willReturn(new \ArrayIterator(['target_id' => []]));
|
||||
|
||||
$this->fieldDefinition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$this->fieldDefinition->getItemDefinition()
|
||||
->willReturn($this->prophesize(FieldItemDataDefinition::class)->reveal());
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->fieldItem->reveal()));
|
||||
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsDenormalization
|
||||
*/
|
||||
public function testSupportsDenormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization([], EntityReferenceItem::class));
|
||||
$this->assertFalse($this->normalizer->supportsDenormalization([], FieldItemInterface::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$test_url = '/test/100';
|
||||
|
||||
$generated_url = (new GeneratedUrl())->setGeneratedUrl($test_url);
|
||||
|
||||
$url = $this->prophesize(Url::class);
|
||||
$url->toString(TRUE)
|
||||
->willReturn($generated_url);
|
||||
|
||||
$entity = $this->prophesize(EntityInterface::class);
|
||||
$entity->hasLinkTemplate('canonical')
|
||||
->willReturn(TRUE);
|
||||
$entity->isNew()
|
||||
->willReturn(FALSE)
|
||||
->shouldBeCalled();
|
||||
$entity->toUrl('canonical')
|
||||
->willReturn($url)
|
||||
->shouldBeCalled();
|
||||
$entity->uuid()
|
||||
->willReturn('080e3add-f9d5-41ac-9821-eea55b7b42fb')
|
||||
->shouldBeCalled();
|
||||
$entity->getEntityTypeId()
|
||||
->willReturn('test_type')
|
||||
->shouldBeCalled();
|
||||
|
||||
$entity_reference = $this->prophesize(TypedDataInterface::class);
|
||||
$entity_reference->getValue()
|
||||
->willReturn($entity->reveal())
|
||||
->shouldBeCalled();
|
||||
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getSetting('target_type')
|
||||
->willReturn('test_type');
|
||||
|
||||
$this->fieldItem->getFieldDefinition()
|
||||
->willReturn($field_definition->reveal());
|
||||
|
||||
$this->fieldItem->get('entity')
|
||||
->willReturn($entity_reference)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->fieldItem->getProperties(TRUE)
|
||||
->willReturn(['target_id' => $this->getTypedDataProperty(FALSE)])
|
||||
->shouldBeCalled();
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->fieldItem->reveal());
|
||||
|
||||
$expected = [
|
||||
'target_id' => 'test',
|
||||
'target_type' => 'test_type',
|
||||
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
|
||||
'url' => $test_url,
|
||||
];
|
||||
$this->assertSame($expected, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests normalizing with an entity reference field.
|
||||
*/
|
||||
public function testNormalizeWithNewEntityReference(): void {
|
||||
$test_url = '/test/100';
|
||||
|
||||
$generated_url = (new GeneratedUrl())->setGeneratedUrl($test_url);
|
||||
|
||||
$url = $this->prophesize(Url::class);
|
||||
$url->toString(TRUE)
|
||||
->willReturn($generated_url);
|
||||
|
||||
$entity = $this->prophesize(EntityInterface::class);
|
||||
$entity->hasLinkTemplate('canonical')
|
||||
->willReturn(TRUE);
|
||||
$entity->isNew()
|
||||
->willReturn(TRUE)
|
||||
->shouldBeCalled();
|
||||
$entity->uuid()
|
||||
->willReturn('080e3add-f9d5-41ac-9821-eea55b7b42fb')
|
||||
->shouldBeCalled();
|
||||
$entity->getEntityTypeId()
|
||||
->willReturn('test_type')
|
||||
->shouldBeCalled();
|
||||
$entity->toUrl('canonical')
|
||||
->willReturn($url)
|
||||
->shouldNotBeCalled();
|
||||
|
||||
$entity_reference = $this->prophesize(TypedDataInterface::class);
|
||||
$entity_reference->getValue()
|
||||
->willReturn($entity->reveal())
|
||||
->shouldBeCalled();
|
||||
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getSetting('target_type')
|
||||
->willReturn('test_type');
|
||||
|
||||
$this->fieldItem->getFieldDefinition()
|
||||
->willReturn($field_definition->reveal());
|
||||
|
||||
$this->fieldItem->get('entity')
|
||||
->willReturn($entity_reference)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->fieldItem->getProperties(TRUE)
|
||||
->willReturn(['target_id' => $this->getTypedDataProperty(FALSE)])
|
||||
->shouldBeCalled();
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->fieldItem->reveal());
|
||||
|
||||
$expected = [
|
||||
'target_id' => 'test',
|
||||
'target_type' => 'test_type',
|
||||
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
|
||||
];
|
||||
$this->assertSame($expected, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalizeWithEmptyTaxonomyTermReference(): void {
|
||||
// Override the serializer prophecy from setUp() to return a zero value.
|
||||
$this->serializer = $this->prophesize(Serializer::class);
|
||||
// Set up the serializer to return an entity property.
|
||||
$this->serializer->normalize(Argument::cetera())
|
||||
->willReturn(0);
|
||||
|
||||
$this->normalizer->setSerializer($this->serializer->reveal());
|
||||
|
||||
$entity_reference = $this->prophesize(TypedDataInterface::class);
|
||||
$entity_reference->getValue()
|
||||
->willReturn(NULL)
|
||||
->shouldBeCalled();
|
||||
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getSetting('target_type')
|
||||
->willReturn('taxonomy_term');
|
||||
|
||||
$this->fieldItem->getFieldDefinition()
|
||||
->willReturn($field_definition->reveal());
|
||||
|
||||
$this->fieldItem->get('entity')
|
||||
->willReturn($entity_reference)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->fieldItem->getProperties(TRUE)
|
||||
->willReturn(['target_id' => $this->getTypedDataProperty(FALSE)])
|
||||
->shouldBeCalled();
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->fieldItem->reveal());
|
||||
|
||||
$expected = [
|
||||
'target_id' => NULL,
|
||||
];
|
||||
$this->assertSame($expected, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalizeWithNoEntity(): void {
|
||||
$entity_reference = $this->prophesize(TypedDataInterface::class);
|
||||
$entity_reference->getValue()
|
||||
->willReturn(NULL)
|
||||
->shouldBeCalled();
|
||||
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$field_definition->getSetting('target_type')
|
||||
->willReturn('test_type');
|
||||
|
||||
$this->fieldItem->getFieldDefinition()
|
||||
->willReturn($field_definition->reveal());
|
||||
|
||||
$this->fieldItem->get('entity')
|
||||
->willReturn($entity_reference->reveal())
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->fieldItem->getProperties(TRUE)
|
||||
->willReturn(['target_id' => $this->getTypedDataProperty(FALSE)])
|
||||
->shouldBeCalled();
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->fieldItem->reveal());
|
||||
|
||||
$expected = [
|
||||
'target_id' => 'test',
|
||||
];
|
||||
$this->assertSame($expected, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithTypeAndUuid(): void {
|
||||
$data = [
|
||||
'target_id' => 'test',
|
||||
'target_type' => 'test_type',
|
||||
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
|
||||
];
|
||||
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$entity->id()
|
||||
->willReturn('test')
|
||||
->shouldBeCalled();
|
||||
$this->entityRepository
|
||||
->loadEntityByUuid($data['target_type'], $data['target_uuid'])
|
||||
->willReturn($entity)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->fieldItem->getProperties()->willReturn([
|
||||
'target_id' => $this->prophesize(IntegerInterface::class),
|
||||
]);
|
||||
$this->fieldItem->setValue(['target_id' => 'test'])->shouldBeCalled();
|
||||
|
||||
$this->assertDenormalize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithUuidWithoutType(): void {
|
||||
$data = [
|
||||
'target_id' => 'test',
|
||||
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
|
||||
];
|
||||
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$entity->id()
|
||||
->willReturn('test')
|
||||
->shouldBeCalled();
|
||||
$this->entityRepository
|
||||
->loadEntityByUuid('test_type', $data['target_uuid'])
|
||||
->willReturn($entity)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->fieldItem->getProperties()->willReturn([
|
||||
'target_id' => $this->prophesize(IntegerInterface::class),
|
||||
]);
|
||||
$this->fieldItem->setValue(['target_id' => 'test'])->shouldBeCalled();
|
||||
|
||||
$this->assertDenormalize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithUuidWithIncorrectType(): void {
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('The field "field_reference" property "target_type" must be set to "test_type" or omitted.');
|
||||
|
||||
$data = [
|
||||
'target_id' => 'test',
|
||||
'target_type' => 'wrong_type',
|
||||
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
|
||||
];
|
||||
|
||||
$this->fieldDefinition
|
||||
->getName()
|
||||
->willReturn('field_reference')
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->assertDenormalize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithTypeWithIncorrectUuid(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('No "test_type" entity found with UUID "unique-but-none-non-existent" for field "field_reference"');
|
||||
|
||||
$data = [
|
||||
'target_id' => 'test',
|
||||
'target_type' => 'test_type',
|
||||
'target_uuid' => 'unique-but-none-non-existent',
|
||||
];
|
||||
$this->entityRepository
|
||||
->loadEntityByUuid($data['target_type'], $data['target_uuid'])
|
||||
->willReturn(NULL)
|
||||
->shouldBeCalled();
|
||||
$this->fieldItem
|
||||
->getName()
|
||||
->willReturn('field_reference')
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->assertDenormalize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithEmptyUuid(): void {
|
||||
$this->expectException(InvalidArgumentException::class);
|
||||
$this->expectExceptionMessage('If provided "target_uuid" cannot be empty for field "field_reference".');
|
||||
|
||||
$data = [
|
||||
'target_id' => 'test',
|
||||
'target_type' => 'test_type',
|
||||
'target_uuid' => '',
|
||||
];
|
||||
$this->fieldItem
|
||||
->getName()
|
||||
->willReturn('field_reference')
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->assertDenormalize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeWithId(): void {
|
||||
$data = [
|
||||
'target_id' => 'test',
|
||||
];
|
||||
$this->fieldItem->setValue($data)->shouldBeCalled();
|
||||
|
||||
$this->assertDenormalize($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Asserts denormalization process is correct for give data.
|
||||
*
|
||||
* @param array $data
|
||||
* The data to denormalize.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
protected function assertDenormalize(array $data): void {
|
||||
$this->fieldItem->getParent()
|
||||
->willReturn($this->prophesize(FieldItemListInterface::class)->reveal());
|
||||
$this->fieldItem->getFieldDefinition()->willReturn($this->fieldDefinition->reveal());
|
||||
if (!empty($data['target_uuid'])) {
|
||||
$this->fieldDefinition
|
||||
->getSetting('target_type')
|
||||
->willReturn('test_type')
|
||||
->shouldBeCalled();
|
||||
}
|
||||
|
||||
// Avoid a static method call by returning dummy serialized property data.
|
||||
$this->fieldDefinition
|
||||
->getFieldStorageDefinition()
|
||||
->willReturn()
|
||||
->shouldBeCalled();
|
||||
$this->fieldDefinition
|
||||
->getName()
|
||||
->willReturn('field_reference')
|
||||
->shouldBeCalled();
|
||||
$entity = $this->prophesize(EntityInterface::class);
|
||||
$entity_type = $this->prophesize(EntityTypeInterface::class);
|
||||
$entity->getEntityType()
|
||||
->willReturn($entity_type->reveal())
|
||||
->shouldBeCalled();
|
||||
$this->fieldItem
|
||||
->getPluginDefinition()
|
||||
->willReturn([
|
||||
'serialized_property_names' => [
|
||||
'foo' => 'bar',
|
||||
],
|
||||
])
|
||||
->shouldBeCalled();
|
||||
$this->fieldItem
|
||||
->getEntity()
|
||||
->willReturn($entity->reveal())
|
||||
->shouldBeCalled();
|
||||
|
||||
$context = ['target_instance' => $this->fieldItem->reveal()];
|
||||
$denormalized = $this->normalizer->denormalize($data, EntityReferenceItem::class, 'json', $context);
|
||||
$this->assertSame($context['target_instance'], $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::constructValue
|
||||
*/
|
||||
public function testConstructValueProperties(): void {
|
||||
$data = [
|
||||
'target_id' => 'test',
|
||||
'target_type' => 'test_type',
|
||||
'target_uuid' => '080e3add-f9d5-41ac-9821-eea55b7b42fb',
|
||||
'extra_property' => 'extra_value',
|
||||
];
|
||||
|
||||
$entity = $this->prophesize(FieldableEntityInterface::class);
|
||||
$entity->id()
|
||||
->willReturn('test')
|
||||
->shouldBeCalled();
|
||||
$this->entityRepository
|
||||
->loadEntityByUuid($data['target_type'], $data['target_uuid'])
|
||||
->willReturn($entity)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->fieldItem->getProperties()->willReturn([
|
||||
'target_id' => $this->prophesize(IntegerInterface::class),
|
||||
'extra_property' => $this->prophesize(StringInterface::class),
|
||||
]);
|
||||
$this->fieldItem->setValue([
|
||||
'target_id' => 'test',
|
||||
'extra_property' => 'extra_value',
|
||||
])->shouldBeCalled();
|
||||
|
||||
$this->assertDenormalize($data);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
|
||||
/**
|
||||
* Trait that provides mocked typed data objects.
|
||||
*/
|
||||
trait InternalTypedDataTestTrait {
|
||||
|
||||
/**
|
||||
* Gets a typed data property.
|
||||
*
|
||||
* @param bool $internal
|
||||
* Whether the typed data property is internal.
|
||||
*
|
||||
* @return \Drupal\Core\TypedData\TypedDataInterface
|
||||
* The typed data property.
|
||||
*/
|
||||
protected function getTypedDataProperty($internal = TRUE) {
|
||||
$definition = $this->prophesize(DataDefinitionInterface::class);
|
||||
$definition->isInternal()
|
||||
->willReturn($internal)
|
||||
->shouldBeCalled();
|
||||
$definition = $definition->reveal();
|
||||
|
||||
$property = $this->prophesize(TypedDataInterface::class);
|
||||
$property->getDataDefinition()
|
||||
->willReturn($definition)
|
||||
->shouldBeCalled();
|
||||
return $property->reveal();
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\TypedData\TypedDataManagerInterface;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\serialization\Normalizer\ListNormalizer;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\ItemList;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\ListNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class ListNormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The ListNormalizer instance.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\ListNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The mock list instance.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\ListInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $list;
|
||||
|
||||
/**
|
||||
* The expected list values to use for testing.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $expectedListValues = ['test', 'test', 'test'];
|
||||
|
||||
/**
|
||||
* The mocked typed data.
|
||||
*
|
||||
* @var \PHPUnit\Framework\MockObject\MockObject|\Drupal\Core\TypedData\TypedDataInterface
|
||||
*/
|
||||
protected $typedData;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
// Mock the TypedDataManager to return a TypedDataInterface mock.
|
||||
$this->typedData = $this->createMock('Drupal\Core\TypedData\TypedDataInterface');
|
||||
$typed_data_manager = $this->createMock(TypedDataManagerInterface::class);
|
||||
$typed_data_manager->expects($this->any())
|
||||
->method('getPropertyInstance')
|
||||
->willReturn($this->typedData);
|
||||
|
||||
// Set up a mock container as ItemList() will call for the
|
||||
// 'typed_data_manager' service.
|
||||
$container = $this->getMockBuilder('Symfony\Component\DependencyInjection\ContainerBuilder')
|
||||
->onlyMethods(['get'])
|
||||
->getMock();
|
||||
$container->expects($this->any())
|
||||
->method('get')
|
||||
->with($this->equalTo('typed_data_manager'))
|
||||
->willReturn($typed_data_manager);
|
||||
|
||||
\Drupal::setContainer($container);
|
||||
|
||||
$this->normalizer = new ListNormalizer();
|
||||
|
||||
$this->list = new ItemList(new DataDefinition());
|
||||
$this->list->setValue($this->expectedListValues);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the supportsNormalization() method.
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->list));
|
||||
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the normalize() method.
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$serializer = $this->prophesize(Serializer::class);
|
||||
$serializer->normalize($this->typedData, 'json', ['mu' => 'nu'])
|
||||
->shouldBeCalledTimes(3)
|
||||
->willReturn('test');
|
||||
|
||||
$this->normalizer->setSerializer($serializer->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->list, 'json', ['mu' => 'nu']);
|
||||
|
||||
$this->assertEquals($this->expectedListValues, $normalized);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Render\Markup;
|
||||
use Drupal\Core\Template\Attribute;
|
||||
use Drupal\serialization\Normalizer\MarkupNormalizer;
|
||||
use Drupal\Tests\serialization\Traits\JsonSchemaTestTrait;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\MarkupNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
final class MarkupNormalizerTest extends UnitTestCase {
|
||||
|
||||
use JsonSchemaTestTrait;
|
||||
|
||||
/**
|
||||
* The TypedDataNormalizer instance.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\TypedDataNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->normalizer = new MarkupNormalizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the normalizer properly delegates schema discovery to its subject.
|
||||
*/
|
||||
public function testDelegatedSchemaDiscovery(): void {
|
||||
$schema = $this->normalizer->getNormalizationSchema(new Attribute(['data-test' => 'testing']));
|
||||
$this->assertEquals('Rendered HTML element attributes', $schema['description']);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function jsonSchemaDataProvider(): array {
|
||||
return [
|
||||
'markup' => [Markup::create('Generic Markup')],
|
||||
'attribute' => [new Attribute(['data-test' => 'testing'])],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\serialization\Normalizer\NormalizerBase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\NormalizerBase
|
||||
* @group serialization
|
||||
*/
|
||||
class NormalizerBaseTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* Tests the supportsNormalization method.
|
||||
*
|
||||
* @param bool $expected_return
|
||||
* The expected boolean return value from supportNormalization.
|
||||
* @param mixed $data
|
||||
* The data passed to supportsNormalization.
|
||||
* @param string $supported_types
|
||||
* (optional) The supported interface or class to set on the normalizer.
|
||||
*
|
||||
* @dataProvider providerTestSupportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization($expected_return, $data, $supported_types = NULL): void {
|
||||
$normalizer_base = new TestNormalizerBase();
|
||||
|
||||
if (isset($supported_types)) {
|
||||
$normalizer_base->setSupportedTypes($supported_types);
|
||||
}
|
||||
|
||||
$this->assertSame($expected_return, $normalizer_base->supportsNormalization($data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testSupportsNormalization.
|
||||
*
|
||||
* @return array
|
||||
* An array of provider data for testSupportsNormalization.
|
||||
*/
|
||||
public static function providerTestSupportsNormalization() {
|
||||
return [
|
||||
// Something that is not an object should return FALSE immediately.
|
||||
[FALSE, []],
|
||||
// An object with no class set should return FALSE.
|
||||
[FALSE, new \stdClass()],
|
||||
// Set a supported Class.
|
||||
[TRUE, new \stdClass(), 'stdClass'],
|
||||
// Set a supported interface.
|
||||
[TRUE, new \RecursiveArrayIterator(), 'RecursiveIterator'],
|
||||
// Set a different class.
|
||||
[FALSE, new \stdClass(), 'ArrayIterator'],
|
||||
// Set a different interface.
|
||||
[FALSE, new \stdClass(), 'RecursiveIterator'],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Testable class for NormalizerBase.
|
||||
*/
|
||||
class TestNormalizerBase extends NormalizerBase {
|
||||
|
||||
/**
|
||||
* The interface or class that this Normalizer supports.
|
||||
*
|
||||
* @var string[]
|
||||
*/
|
||||
protected array $supportedTypes = ['*' => FALSE];
|
||||
|
||||
/**
|
||||
* Sets the supported types.
|
||||
*
|
||||
* @param string $supported_types
|
||||
* The class name to set.
|
||||
*/
|
||||
public function setSupportedTypes($supported_types): void {
|
||||
$this->supportedTypes = [$supported_types => FALSE];
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function getSupportedTypes(?string $format): array {
|
||||
return $this->supportedTypes;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function normalize($object, $format = NULL, array $context = []): array|string|int|float|bool|\ArrayObject|NULL {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\TypedData\TypedDataInterface;
|
||||
use Drupal\serialization\Normalizer\NullNormalizer;
|
||||
use Drupal\Tests\serialization\Traits\JsonSchemaTestTrait;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\NullNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class NullNormalizerTest extends UnitTestCase {
|
||||
|
||||
use JsonSchemaTestTrait;
|
||||
|
||||
/**
|
||||
* The NullNormalizer instance.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\NullNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The interface to use in testing.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $interface = 'Drupal\Core\TypedData\TypedDataInterface';
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->normalizer = new NullNormalizer($this->interface);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::__construct
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$mock = $this->createMock('Drupal\Core\TypedData\TypedDataInterface');
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($mock));
|
||||
// Also test that an object not implementing TypedDataInterface fails.
|
||||
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$mock = $this->createMock('Drupal\Core\TypedData\TypedDataInterface');
|
||||
$this->assertNull($this->normalizer->normalize($mock));
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function jsonSchemaDataProvider(): array {
|
||||
return [
|
||||
'null' => [TypedDataInterface::class],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,139 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\TypedData\DataDefinition;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\BooleanData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\DecimalData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\DurationIso8601;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Email;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\FloatData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\StringData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Uri;
|
||||
use Drupal\Tests\serialization\Traits\JsonSchemaTestTrait;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\serialization\Normalizer\PrimitiveDataNormalizer;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\PrimitiveDataNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class PrimitiveDataNormalizerTest extends UnitTestCase {
|
||||
|
||||
use JsonSchemaTestTrait;
|
||||
|
||||
/**
|
||||
* The TypedDataNormalizer instance.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\TypedDataNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->normalizer = new PrimitiveDataNormalizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
* @dataProvider dataProviderPrimitiveData
|
||||
*/
|
||||
public function testSupportsNormalization($primitive_data, $expected): void {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($primitive_data));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalizationFail(): void {
|
||||
// Test that an object not implementing PrimitiveInterface fails.
|
||||
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
* @dataProvider dataProviderPrimitiveData
|
||||
*/
|
||||
public function testNormalize($primitive_data, $expected): void {
|
||||
$this->assertSame($expected, $this->normalizer->normalize($primitive_data));
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testNormalize().
|
||||
*/
|
||||
public static function dataProviderPrimitiveData() {
|
||||
$data = [];
|
||||
|
||||
$definition = DataDefinition::createFromDataType('string');
|
||||
$string = new StringData($definition, 'string');
|
||||
$string->setValue('test');
|
||||
|
||||
$data['string'] = [$string, 'test'];
|
||||
|
||||
$definition = DataDefinition::createFromDataType('string');
|
||||
$string = new StringData($definition, 'string');
|
||||
$string->setValue(NULL);
|
||||
|
||||
$data['string-null'] = [$string, NULL];
|
||||
|
||||
$definition = DataDefinition::createFromDataType('integer');
|
||||
$integer = new IntegerData($definition, 'integer');
|
||||
$integer->setValue(5);
|
||||
|
||||
$data['integer'] = [$integer, 5];
|
||||
|
||||
$definition = DataDefinition::createFromDataType('integer');
|
||||
$integer = new IntegerData($definition, 'integer');
|
||||
$integer->setValue(NULL);
|
||||
|
||||
$data['integer-null'] = [$integer, NULL];
|
||||
|
||||
$definition = DataDefinition::createFromDataType('boolean');
|
||||
$boolean = new BooleanData($definition, 'boolean');
|
||||
$boolean->setValue(TRUE);
|
||||
|
||||
$data['boolean'] = [$boolean, TRUE];
|
||||
|
||||
$definition = DataDefinition::createFromDataType('boolean');
|
||||
$boolean = new BooleanData($definition, 'boolean');
|
||||
$boolean->setValue(NULL);
|
||||
|
||||
$data['boolean-null'] = [$boolean, NULL];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function jsonSchemaDataProvider(): array {
|
||||
$email = new Email(DataDefinition::createFromDataType('email'));
|
||||
$email->setValue('test@example.com');
|
||||
$float = new FloatData(DataDefinition::createFromDataType('float'));
|
||||
$float->setValue(9.99);
|
||||
$uri = new Uri(DataDefinition::createFromDataType('uri'));
|
||||
$uri->setValue('https://example.com');
|
||||
$decimal = new DecimalData(DataDefinition::createFromDataType('decimal'));
|
||||
$decimal->setValue('9.99');
|
||||
// TimeSpan normalizes to an integer, however Iso8601 matches a format.
|
||||
$duration = new DurationIso8601(DataDefinition::createFromDataType('duration_iso8601'));
|
||||
$duration->setValue('P1D');
|
||||
|
||||
return [
|
||||
'email' => [$email],
|
||||
'float' => [$float],
|
||||
'uri' => [$uri],
|
||||
'decimal' => [$decimal],
|
||||
'duration' => [$duration],
|
||||
...array_map(fn ($value) => [$value[0]], static::dataProviderPrimitiveData()),
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Entity\EntityInterface;
|
||||
use Drupal\Core\Entity\EntityTypeInterface;
|
||||
use Drupal\Core\Field\FieldDefinitionInterface;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\CreatedItem;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem;
|
||||
use Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem;
|
||||
use Drupal\Core\TypedData\DataDefinitionInterface;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Timestamp;
|
||||
use Drupal\serialization\Normalizer\TimestampItemNormalizer;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Prophecy\Prophecy\ObjectProphecy;
|
||||
use Symfony\Component\Serializer\Serializer;
|
||||
|
||||
/**
|
||||
* Tests that TimestampItem (de)normalization uses Timestamp (de)normalization.
|
||||
*
|
||||
* @group serialization
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\TimestampItemNormalizer
|
||||
* @see \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
*/
|
||||
class TimestampItemNormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The time stamp normalizer.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\TimestampItemNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The test TimestampItem.
|
||||
*
|
||||
* @var \Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem
|
||||
*/
|
||||
protected $item;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->normalizer = new TimestampItemNormalizer();
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$timestamp_item = $this->createTimestampItemProphecy();
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($timestamp_item->reveal()));
|
||||
|
||||
$entity_ref_item = $this->prophesize(EntityReferenceItem::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($entity_ref_item->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsDenormalization
|
||||
*/
|
||||
public function testSupportsDenormalization(): void {
|
||||
$timestamp_item = $this->createTimestampItemProphecy();
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization($timestamp_item->reveal(), TimestampItem::class));
|
||||
|
||||
// CreatedItem extends regular TimestampItem.
|
||||
$timestamp_item = $this->prophesize(CreatedItem::class);
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization($timestamp_item->reveal(), TimestampItem::class));
|
||||
|
||||
$entity_ref_item = $this->prophesize(EntityReferenceItem::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($entity_ref_item->reveal(), TimestampItem::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
* @see \Drupal\Tests\serialization\Unit\Normalizer\TimestampNormalizerTest
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
// Mock TimestampItem @FieldType, which contains a Timestamp @DataType,
|
||||
// which has a DataDefinition.
|
||||
$data_definition = $this->prophesize(DataDefinitionInterface::class);
|
||||
$data_definition->isInternal()
|
||||
->willReturn(FALSE)
|
||||
->shouldBeCalled();
|
||||
$timestamp = $this->prophesize(Timestamp::class);
|
||||
$timestamp->getDataDefinition()
|
||||
->willReturn($data_definition->reveal())
|
||||
->shouldBeCalled();
|
||||
$timestamp = $timestamp->reveal();
|
||||
$timestamp_item = $this->createTimestampItemProphecy();
|
||||
$timestamp_item->getProperties(TRUE)
|
||||
->willReturn(['value' => $timestamp])
|
||||
->shouldBeCalled();
|
||||
|
||||
// Mock Serializer service, to assert that the Timestamp @DataType
|
||||
// normalizer would be called.
|
||||
$timestamp_datetype_normalization = $this->randomMachineName();
|
||||
$serializer_prophecy = $this->prophesize(Serializer::class);
|
||||
// This is where \Drupal\serialization\Normalizer\TimestampNormalizer would
|
||||
// be called.
|
||||
$serializer_prophecy->normalize($timestamp, NULL, [])
|
||||
->willReturn($timestamp_datetype_normalization)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->normalizer->setSerializer($serializer_prophecy->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($timestamp_item->reveal());
|
||||
$this->assertSame(['value' => $timestamp_datetype_normalization, 'format' => \DateTime::RFC3339], $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalize(): void {
|
||||
$timestamp_item_normalization = [
|
||||
'value' => $this->randomMachineName(),
|
||||
'format' => \DateTime::RFC3339,
|
||||
];
|
||||
$timestamp_data_denormalization = $this->randomMachineName();
|
||||
|
||||
$timestamp_item = $this->createTimestampItemProphecy();
|
||||
// The field item should get the Timestamp @DataType denormalization set as
|
||||
// a value, in FieldItemNormalizer::denormalize().
|
||||
$timestamp_item->setValue(['value' => $timestamp_data_denormalization])
|
||||
->shouldBeCalled();
|
||||
|
||||
// Avoid a static method call by returning dummy serialized property data.
|
||||
$field_definition = $this->prophesize(FieldDefinitionInterface::class);
|
||||
$timestamp_item
|
||||
->getFieldDefinition()
|
||||
->willReturn($field_definition->reveal())
|
||||
->shouldBeCalled();
|
||||
$timestamp_item->getPluginDefinition()
|
||||
->willReturn([
|
||||
'serialized_property_names' => [
|
||||
'foo' => 'bar',
|
||||
],
|
||||
])
|
||||
->shouldBeCalled();
|
||||
$entity = $this->prophesize(EntityInterface::class);
|
||||
$entity_type = $this->prophesize(EntityTypeInterface::class);
|
||||
$entity->getEntityType()
|
||||
->willReturn($entity_type->reveal())
|
||||
->shouldBeCalled();
|
||||
$timestamp_item
|
||||
->getEntity()
|
||||
->willReturn($entity->reveal())
|
||||
->shouldBeCalled();
|
||||
|
||||
$context = [
|
||||
'target_instance' => $timestamp_item->reveal(),
|
||||
'datetime_allowed_formats' => [\DateTime::RFC3339],
|
||||
];
|
||||
|
||||
// Mock Serializer service, to assert that the Timestamp @DataType
|
||||
// denormalizer would be called.
|
||||
$serializer_prophecy = $this->prophesize(Serializer::class);
|
||||
// This is where \Drupal\serialization\Normalizer\TimestampNormalizer would
|
||||
// be called.
|
||||
$serializer_prophecy->denormalize($timestamp_item_normalization['value'], Timestamp::class, NULL, $context)
|
||||
->willReturn($timestamp_data_denormalization)
|
||||
->shouldBeCalled();
|
||||
|
||||
$this->normalizer->setSerializer($serializer_prophecy->reveal());
|
||||
|
||||
$denormalized = $this->normalizer->denormalize($timestamp_item_normalization, TimestampItem::class, NULL, $context);
|
||||
$this->assertInstanceOf(TimestampItem::class, $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a TimestampItem prophecy.
|
||||
*
|
||||
* @return \Prophecy\Prophecy\ObjectProphecy<\Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem>
|
||||
* The TimestampItem prophecy.
|
||||
*/
|
||||
protected function createTimestampItemProphecy(): ObjectProphecy {
|
||||
$timestamp_item = $this->prophesize(TimestampItem::class);
|
||||
$timestamp_item->getParent()
|
||||
->willReturn(TRUE);
|
||||
|
||||
return $timestamp_item;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,182 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Core\Config\ConfigFactoryInterface;
|
||||
use Drupal\Core\Datetime\DrupalDateTime;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\IntegerData;
|
||||
use Drupal\Core\TypedData\Plugin\DataType\Timestamp;
|
||||
use Drupal\Core\TypedData\Type\DateTimeInterface;
|
||||
use Drupal\serialization\Normalizer\TimestampNormalizer;
|
||||
use Drupal\Tests\serialization\Traits\JsonSchemaTestTrait;
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Symfony\Component\Serializer\Exception\UnexpectedValueException;
|
||||
|
||||
/**
|
||||
* Unit test coverage for the "Timestamp" @DataType.
|
||||
*
|
||||
* @group serialization
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
* @see \Drupal\Core\TypedData\Plugin\DataType\Timestamp
|
||||
*/
|
||||
class TimestampNormalizerTest extends UnitTestCase {
|
||||
|
||||
use JsonSchemaTestTrait;
|
||||
|
||||
/**
|
||||
* The tested data type's normalizer.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\TimestampNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The tested data type.
|
||||
*
|
||||
* @var \Drupal\Core\Field\Plugin\Field\FieldType\TimestampItem
|
||||
*/
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->normalizer = new TimestampNormalizer($this->prophesize(ConfigFactoryInterface::class)->reveal());
|
||||
$this->data = $this->prophesize(Timestamp::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsNormalization
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->data->reveal()));
|
||||
|
||||
$integer = $this->prophesize(IntegerData::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($integer->reveal()));
|
||||
|
||||
$datetime = $this->prophesize(DateTimeInterface::class);
|
||||
$this->assertFalse($this->normalizer->supportsNormalization($datetime->reveal()));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::supportsDenormalization
|
||||
*/
|
||||
public function testSupportsDenormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsDenormalization($this->data->reveal(), Timestamp::class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @covers ::normalize
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$random_rfc_3339_string = $this->randomMachineName();
|
||||
|
||||
$drupal_date_time = $this->prophesize(TimestampNormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('UTC'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format(\DateTime::RFC3339)
|
||||
->willReturn($random_rfc_3339_string);
|
||||
|
||||
$this->data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
|
||||
$normalized = $this->normalizer->normalize($this->data->reveal());
|
||||
$this->assertSame($random_rfc_3339_string, $normalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with good data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
* @dataProvider providerTestDenormalizeValidFormats
|
||||
*/
|
||||
public function testDenormalizeValidFormats($normalized, $expected): void {
|
||||
$denormalized = $this->normalizer->denormalize($normalized, Timestamp::class, NULL, []);
|
||||
$this->assertSame($expected, $denormalized);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for testDenormalizeValidFormats.
|
||||
*
|
||||
* @return array
|
||||
* An array of test data.
|
||||
*/
|
||||
public static function providerTestDenormalizeValidFormats() {
|
||||
$expected_stamp = 1478422920;
|
||||
|
||||
$data = [];
|
||||
|
||||
$data['U'] = [$expected_stamp, $expected_stamp];
|
||||
$data['RFC3339'] = ['2016-11-06T09:02:00+00:00', $expected_stamp];
|
||||
$data['RFC3339 +0100'] = ['2016-11-06T09:02:00+01:00', $expected_stamp - 1 * 3600];
|
||||
$data['RFC3339 -0600'] = ['2016-11-06T09:02:00-06:00', $expected_stamp + 6 * 3600];
|
||||
|
||||
$data['ISO8601'] = ['2016-11-06T09:02:00+0000', $expected_stamp];
|
||||
$data['ISO8601 +0100'] = ['2016-11-06T09:02:00+0100', $expected_stamp - 1 * 3600];
|
||||
$data['ISO8601 -0600'] = ['2016-11-06T09:02:00-0600', $expected_stamp + 6 * 3600];
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the denormalize function with bad data.
|
||||
*
|
||||
* @covers ::denormalize
|
||||
*/
|
||||
public function testDenormalizeException(): void {
|
||||
$this->expectException(UnexpectedValueException::class);
|
||||
$this->expectExceptionMessage('The specified date "2016/11/06 09:02am GMT" is not in an accepted format: "U" (UNIX timestamp), "Y-m-d\TH:i:sO" (ISO 8601), "Y-m-d\TH:i:sP" (RFC 3339).');
|
||||
|
||||
$normalized = '2016/11/06 09:02am GMT';
|
||||
|
||||
$this->normalizer->denormalize($normalized, Timestamp::class, NULL, []);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public static function jsonSchemaDataProvider(): array {
|
||||
$case = function (UnitTestCase $test) {
|
||||
assert(in_array(JsonSchemaTestTrait::class, class_uses($test)));
|
||||
$drupal_date_time = $test->doProphesize(TimestampNormalizerTestDrupalDateTime::class);
|
||||
$drupal_date_time->setTimezone(new \DateTimeZone('UTC'))
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
$drupal_date_time->format(\DateTime::RFC3339)
|
||||
->willReturn('1983-07-12T09:05:00-05:00');
|
||||
|
||||
$data = $test->doProphesize(Timestamp::class);
|
||||
$data->getDateTime()
|
||||
->willReturn($drupal_date_time->reveal());
|
||||
return $data->reveal();
|
||||
};
|
||||
return [
|
||||
'RFC 3339' => [fn (UnitTestCase $test) => $case($test)],
|
||||
];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides a test class for testing DrupalDateTime.
|
||||
*
|
||||
* Note: Prophecy does not support magic methods. By subclassing and specifying
|
||||
* an explicit method, Prophecy works.
|
||||
*
|
||||
* @see https://github.com/phpspec/prophecy/issues/338
|
||||
* @see https://github.com/phpspec/prophecy/issues/34
|
||||
* @see https://github.com/phpspec/prophecy/issues/80
|
||||
*/
|
||||
class TimestampNormalizerTestDrupalDateTime extends DrupalDateTime {
|
||||
|
||||
/**
|
||||
* Sets the timezone.
|
||||
*/
|
||||
public function setTimezone(\DateTimeZone $timezone) {
|
||||
parent::setTimezone($timezone);
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Drupal\Tests\serialization\Unit\Normalizer;
|
||||
|
||||
use Drupal\Tests\UnitTestCase;
|
||||
use Drupal\serialization\Normalizer\TypedDataNormalizer;
|
||||
|
||||
/**
|
||||
* @coversDefaultClass \Drupal\serialization\Normalizer\TypedDataNormalizer
|
||||
* @group serialization
|
||||
*/
|
||||
class TypedDataNormalizerTest extends UnitTestCase {
|
||||
|
||||
/**
|
||||
* The TypedDataNormalizer instance.
|
||||
*
|
||||
* @var \Drupal\serialization\Normalizer\TypedDataNormalizer
|
||||
*/
|
||||
protected $normalizer;
|
||||
|
||||
/**
|
||||
* The mock typed data instance.
|
||||
*
|
||||
* @var \Drupal\Core\TypedData\TypedDataInterface|\PHPUnit\Framework\MockObject\MockObject
|
||||
*/
|
||||
protected $typedData;
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
protected function setUp(): void {
|
||||
parent::setUp();
|
||||
|
||||
$this->normalizer = new TypedDataNormalizer();
|
||||
$this->typedData = $this->createMock('Drupal\Core\TypedData\TypedDataInterface');
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the supportsNormalization() method.
|
||||
*/
|
||||
public function testSupportsNormalization(): void {
|
||||
$this->assertTrue($this->normalizer->supportsNormalization($this->typedData));
|
||||
// Also test that an object not implementing TypedDataInterface fails.
|
||||
$this->assertFalse($this->normalizer->supportsNormalization(new \stdClass()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests the normalize() method.
|
||||
*/
|
||||
public function testNormalize(): void {
|
||||
$this->typedData->expects($this->once())
|
||||
->method('getValue')
|
||||
->willReturn('test');
|
||||
|
||||
$this->assertEquals('test', $this->normalizer->normalize($this->typedData));
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user