From e45fc718683125264c472578941286558e83aa5b Mon Sep 17 00:00:00 2001 From: Lee Rowlands <lee.rowlands@previousnext.com.au> Date: Fri, 31 Mar 2023 12:53:36 +1000 Subject: [PATCH] Issue #2745179 by juancasantito, heddn, dpi, acbramley, Xano, dawehner, alexpott, catch: Uncaught exception in link formatter if a link field has malformed data (cherry picked from commit 98f29f420c5e9f0e93bb487420a69c012924791b) --- core/modules/link/src/LinkItemInterface.php | 3 + .../Field/FieldFormatter/LinkFormatter.php | 8 +- .../link/tests/src/Unit/LinkFormatterTest.php | 154 ++++++++++++++++++ 3 files changed, 164 insertions(+), 1 deletion(-) create mode 100644 core/modules/link/tests/src/Unit/LinkFormatterTest.php diff --git a/core/modules/link/src/LinkItemInterface.php b/core/modules/link/src/LinkItemInterface.php index 530617044832..ed726473afb7 100644 --- a/core/modules/link/src/LinkItemInterface.php +++ b/core/modules/link/src/LinkItemInterface.php @@ -37,6 +37,9 @@ public function isExternal(); * * @return \Drupal\Core\Url * Returns a Url object. + * + * @throws \InvalidArgumentException + * Thrown when there is a problem with field data. */ public function getUrl(); diff --git a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php index 75a46fa4aa34..c8709c568999 100644 --- a/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php +++ b/core/modules/link/src/Plugin/Field/FieldFormatter/LinkFormatter.php @@ -235,7 +235,13 @@ public function viewElements(FieldItemListInterface $items, $langcode) { * A Url object. */ protected function buildUrl(LinkItemInterface $item) { - $url = $item->getUrl() ?: Url::fromRoute('<none>'); + try { + $url = $item->getUrl(); + } + catch (\InvalidArgumentException $e) { + // @todo Add logging here in https://www.drupal.org/project/drupal/issues/3348020 + $url = Url::fromRoute('<none>'); + } $settings = $this->getSettings(); $options = $item->options; diff --git a/core/modules/link/tests/src/Unit/LinkFormatterTest.php b/core/modules/link/tests/src/Unit/LinkFormatterTest.php new file mode 100644 index 000000000000..3670652847a7 --- /dev/null +++ b/core/modules/link/tests/src/Unit/LinkFormatterTest.php @@ -0,0 +1,154 @@ +<?php + +namespace Drupal\Tests\link\Unit; + +use Drupal\Core\DependencyInjection\ContainerBuilder; +use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Field\FieldDefinitionInterface; +use Drupal\Core\Field\FieldItemList; +use Drupal\Core\Field\FieldTypePluginManagerInterface; +use Drupal\Core\Path\PathValidatorInterface; +use Drupal\Core\Routing\UrlGenerator; +use Drupal\Core\Url; +use Drupal\link\LinkItemInterface; +use Drupal\link\Plugin\Field\FieldFormatter\LinkFormatter; +use Drupal\Tests\UnitTestCase; + +/** + * Tests the Field Formatter for the link field type. + * + * @group link + */ +class LinkFormatterTest extends UnitTestCase { + + /** + * Tests when LinkItem::getUrl with malformed URL renders empty link. + * + * LinkItem::getUrl will throw \InvalidArgumentException. + */ + public function testFormatterLinkItemUrlMalformed() { + $entity = $this->createMock(EntityInterface::class); + + $linkItem = $this->createMock(LinkItemInterface::class); + $exception = new \InvalidArgumentException(); + $linkItem->expects($this->any()) + ->method('getParent') + ->willReturn($entity); + $linkItem->expects($this->once()) + ->method('getUrl') + ->willThrowException($exception); + $linkItem->expects($this->any()) + ->method('__get') + ->with('options') + ->willReturn([]); + $fieldDefinition = $this->createMock(FieldDefinitionInterface::class); + $fieldList = new FieldItemList($fieldDefinition, '', $linkItem); + + $fieldTypePluginManager = $this->createMock(FieldTypePluginManagerInterface::class); + $fieldTypePluginManager->expects($this->once()) + ->method('createFieldItem') + ->will($this->returnValue($linkItem)); + $urlGenerator = $this->createMock(UrlGenerator::class); + $urlGenerator->expects($this->once()) + ->method('generateFromRoute') + ->with('<none>', [], [], FALSE) + ->willReturn('http://example.com'); + $container = new ContainerBuilder(); + $container->set('plugin.manager.field.field_type', $fieldTypePluginManager); + $container->set('url_generator', $urlGenerator); + \Drupal::setContainer($container); + $fieldList->setValue([$linkItem]); + + $pathValidator = $this->createMock(PathValidatorInterface::class); + $linkFormatter = new LinkFormatter('', [], $fieldDefinition, [], '', '', [], $pathValidator); + $elements = $linkFormatter->viewElements($fieldList, 'es'); + $this->assertEquals('link', $elements[0]['#type']); + } + + /** + * Tests when LinkItem::getUrl throws an unexpected exception. + */ + public function testFormatterLinkItemUrlUnexpectedException() { + $exception = new \Exception('Unexpected!!!'); + + $linkItem = $this->createMock(LinkItemInterface::class); + $entity = $this->createMock(EntityInterface::class); + $linkItem->expects($this->any()) + ->method('getParent') + ->willReturn($entity); + $linkItem->expects($this->once()) + ->method('getUrl') + ->willThrowException($exception); + $linkItem->expects($this->any()) + ->method('__get') + ->with('options') + ->willReturn([]); + $fieldDefinition = $this->createMock(FieldDefinitionInterface::class); + $fieldList = new FieldItemList($fieldDefinition, '', $linkItem); + + $fieldTypePluginManager = $this->createMock(FieldTypePluginManagerInterface::class); + $fieldTypePluginManager->expects($this->once()) + ->method('createFieldItem') + ->will($this->returnValue($linkItem)); + $container = new ContainerBuilder(); + $container->set('plugin.manager.field.field_type', $fieldTypePluginManager); + \Drupal::setContainer($container); + $fieldList->setValue([$linkItem]); + + $pathValidator = $this->createMock(PathValidatorInterface::class); + $linkFormatter = new LinkFormatter('', [], $fieldDefinition, [], '', '', [], $pathValidator); + $this->expectException(\Exception::class); + $this->expectExceptionMessage('Unexpected!!!'); + $linkFormatter->viewElements($fieldList, 'fr'); + } + + /** + * Tests when LinkItem::getUrl returns a functional URL. + */ + public function testFormatterLinkItem() { + $expectedUrl = Url::fromUri('route:<front>'); + + $linkItem = $this->createMock(LinkItemInterface::class); + $entity = $this->createMock(EntityInterface::class); + $linkItem->expects($this->any()) + ->method('getParent') + ->willReturn($entity); + $linkItem->expects($this->once()) + ->method('getUrl') + ->willReturn($expectedUrl); + $linkItem->expects($this->any()) + ->method('__get') + ->with('options') + ->willReturn([]); + $fieldDefinition = $this->createMock(FieldDefinitionInterface::class); + $fieldList = new FieldItemList($fieldDefinition, '', $linkItem); + + $fieldTypePluginManager = $this->createMock(FieldTypePluginManagerInterface::class); + $fieldTypePluginManager->expects($this->once()) + ->method('createFieldItem') + ->will($this->returnValue($linkItem)); + $urlGenerator = $this->createMock(UrlGenerator::class); + $urlGenerator->expects($this->once()) + ->method('generateFromRoute') + ->with('<front>', [], [], FALSE) + ->willReturn('http://example.com'); + $container = new ContainerBuilder(); + $container->set('plugin.manager.field.field_type', $fieldTypePluginManager); + $container->set('url_generator', $urlGenerator); + \Drupal::setContainer($container); + $fieldList->setValue([$linkItem]); + + $pathValidator = $this->createMock(PathValidatorInterface::class); + $linkFormatter = new LinkFormatter('', [], $fieldDefinition, [], '', '', [], $pathValidator); + $elements = $linkFormatter->viewElements($fieldList, 'zh'); + $this->assertEquals([ + [ + '#type' => 'link', + '#title' => 'http://example.com', + '#options' => [], + '#url' => $expectedUrl, + ], + ], $elements); + } + +} -- GitLab