123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663 |
- <?php
- namespace Drupal\Tests\Core\Entity;
- use Drupal\Core\DependencyInjection\ContainerBuilder;
- use Drupal\Core\Entity\EntityBase;
- use Drupal\Core\Entity\EntityMalformedException;
- use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
- use Drupal\Core\Entity\EntityTypeInterface;
- use Drupal\Core\Entity\Exception\UndefinedLinkTemplateException;
- use Drupal\Core\Entity\RevisionableInterface;
- use Drupal\Core\GeneratedUrl;
- use Drupal\Core\Link;
- use Drupal\Core\Routing\UrlGeneratorInterface;
- use Drupal\Core\Url;
- use Drupal\Tests\UnitTestCase;
- use Symfony\Component\Routing\Exception\MissingMandatoryParametersException;
- /**
- * Tests URL handling of the \Drupal\Core\Entity\Entity class.
- *
- * @coversDefaultClass \Drupal\Core\Entity\Entity
- *
- * @group Entity
- */
- class EntityUrlTest extends UnitTestCase {
- /**
- * The entity type bundle info service mock used in this test.
- *
- * @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityTypeBundleInfoInterface
- */
- protected $entityTypeBundleInfo;
- /**
- * The ID of the entity type used in this test.
- *
- * @var string
- */
- protected $entityTypeId = 'test_entity';
- /**
- * The entity type mock used in this test.
- *
- * @var \Prophecy\Prophecy\ProphecyInterface|\Drupal\Core\Entity\EntityTypeInterface
- */
- protected $entityType;
- /**
- * The ID of the entity used in this test.
- *
- * @var int
- */
- protected $entityId = 1;
- /**
- * The revision ID of the entity used in this test.
- *
- * @var int
- */
- protected $revisionId = 2;
- /**
- * The language code of the entity used in this test.
- *
- * @var string
- */
- protected $langcode = 'en';
- /**
- * Indicator for default revisions.
- *
- * @var true
- */
- const DEFAULT_REVISION = TRUE;
- /**
- * Indicator for non-default revisions.
- *
- * @var false
- */
- const NON_DEFAULT_REVISION = FALSE;
- /**
- * Indicator that the test entity type has no bundle key.
- *
- * @var false
- */
- const HAS_NO_BUNDLE_KEY = FALSE;
- /**
- * Indicator that the test entity type has a bundle key.
- *
- * @var true
- */
- const HAS_BUNDLE_KEY = TRUE;
- /**
- * Tests the toUrl() method without an entity ID.
- *
- * @covers ::toUrl
- */
- public function testToUrlNoId() {
- $entity = $this->getEntity(EntityBase::class, []);
- $this->expectException(EntityMalformedException::class);
- $this->expectExceptionMessage('The "' . $this->entityTypeId . '" entity cannot have a URI as it does not have an ID');
- $entity->toUrl();
- }
- /**
- * Tests the toUrl() method with simple link templates.
- *
- * @param string $link_template
- * The link template to test.
- * @param string $expected_route_name
- * The expected route name of the generated URL.
- *
- * @dataProvider providerTestToUrlLinkTemplates
- *
- * @covers ::toUrl
- * @covers ::linkTemplates
- * @covers ::urlRouteParameters
- */
- public function testToUrlLinkTemplates($link_template, $expected_route_name) {
- $values = ['id' => $this->entityId, 'langcode' => $this->langcode];
- $entity = $this->getEntity(EntityBase::class, $values);
- $this->registerLinkTemplate($link_template);
- /** @var \Drupal\Core\Url $url */
- $url = $entity->toUrl($link_template);
- // The entity ID is the sole route parameter for the link templates tested
- // here.
- $this->assertUrl($expected_route_name, ['test_entity' => $this->entityId], $entity, TRUE, $url);
- }
- /**
- * Provides data for testToUrlLinkTemplates().
- *
- * @return array
- * An array of test cases for testToUrlLinkTemplates().
- */
- public function providerTestToUrlLinkTemplates() {
- $test_cases = [];
- $test_cases['canonical'] = ['canonical', 'entity.test_entity.canonical'];
- $test_cases['version-history'] = ['version-history', 'entity.test_entity.version_history'];
- $test_cases['edit-form'] = ['edit-form', 'entity.test_entity.edit_form'];
- $test_cases['delete-form'] = ['delete-form', 'entity.test_entity.delete_form'];
- $test_cases['revision'] = ['revision', 'entity.test_entity.revision'];
- return $test_cases;
- }
- /**
- * Tests the toUrl() method with the 'revision' link template.
- *
- * @param bool $is_default_revision
- * Whether or not the mock entity should be the default revision.
- * @param string $link_template
- * The link template to test.
- * @param string $expected_route_name
- * The expected route name of the generated URL.
- * @param array $expected_route_parameters
- * The expected route parameters of the generated URL.
- *
- * @dataProvider providerTestToUrlLinkTemplateRevision
- *
- * @covers ::toUrl
- * @covers ::linkTemplates
- * @covers ::urlRouteParameters
- */
- public function testToUrlLinkTemplateRevision($is_default_revision, $link_template, $expected_route_name, array $expected_route_parameters) {
- $values = ['id' => $this->entityId, 'langcode' => $this->langcode];
- $entity = $this->getEntity(RevisionableEntity::class, $values);
- $entity->method('getRevisionId')->willReturn($this->revisionId);
- $entity->method('isDefaultRevision')->willReturn($is_default_revision);
- $this->registerLinkTemplate($link_template);
- // Even though this is tested with both the 'canonical' and the 'revision'
- // template registered with the entity, we always ask for the 'revision'
- // link template, to test that it falls back to the 'canonical' link
- // template in case of the default revision.
- /** @var \Drupal\Core\Url $url */
- $url = $entity->toUrl('revision');
- $this->assertUrl($expected_route_name, $expected_route_parameters, $entity, TRUE, $url);
- }
- /**
- * Provides data for testToUrlLinkTemplateRevision().
- *
- * @return array
- * An array of test cases for testToUrlLinkTemplateRevision().
- */
- public function providerTestToUrlLinkTemplateRevision() {
- $test_cases = [];
- $route_parameters = ['test_entity' => $this->entityId];
- $test_cases['default_revision'] = [static::DEFAULT_REVISION, 'canonical', 'entity.test_entity.canonical', $route_parameters];
- // Add the revision ID to the expected route parameters.
- $route_parameters['test_entity_revision'] = $this->revisionId;
- $test_cases['non_default_revision'] = [static::NON_DEFAULT_REVISION, 'revision', 'entity.test_entity.revision', $route_parameters];
- return $test_cases;
- }
- /**
- * Tests the toUrl() method with link templates without an entity ID.
- *
- * @param string $link_template
- * The link template to test.
- * @param string $expected_route_name
- * The expected route name of the generated URL.
- *
- * @dataProvider providerTestToUrlLinkTemplateNoId
- *
- * @covers ::toUrl
- * @covers ::linkTemplates
- * @covers ::urlRouteParameters
- */
- public function testToUrlLinkTemplateNoId($link_template, $expected_route_name) {
- $entity = $this->getEntity(EntityBase::class, ['id' => $this->entityId]);
- $this->registerLinkTemplate($link_template);
- /** @var \Drupal\Core\Url $url */
- $url = $entity->toUrl($link_template);
- $this->assertUrl($expected_route_name, [], $entity, FALSE, $url);
- }
- /**
- * Provides data for testToUrlLinkTemplateNoId().
- *
- * @return array
- * An array of test cases for testToUrlLinkTemplateNoId().
- */
- public function providerTestToUrlLinkTemplateNoId() {
- $test_cases = [];
- $test_cases['collection'] = ['collection', 'entity.test_entity.collection'];
- $test_cases['add-page'] = ['add-page', 'entity.test_entity.add_page'];
- return $test_cases;
- }
- /**
- * Tests the toUrl() method with the 'revision' link template.
- *
- * @param bool $has_bundle_key
- * Whether or not the mock entity type should have a bundle key.
- * @param string|null $bundle_entity_type
- * The ID of the bundle entity type of the mock entity type, or NULL if the
- * mock entity type should not have a bundle entity type.
- * @param string $bundle_key
- * The bundle key of the mock entity type or FALSE if the entity type should
- * not have a bundle key.
- * @param array $expected_route_parameters
- * The expected route parameters of the generated URL.
- *
- * @dataProvider providerTestToUrlLinkTemplateAddForm
- *
- * @covers ::toUrl
- * @covers ::linkTemplates
- * @covers ::urlRouteParameters
- */
- public function testToUrlLinkTemplateAddForm($has_bundle_key, $bundle_entity_type, $bundle_key, $expected_route_parameters) {
- $values = ['id' => $this->entityId, 'langcode' => $this->langcode];
- $entity = $this->getEntity(EntityBase::class, $values);
- $this->entityType->hasKey('bundle')->willReturn($has_bundle_key);
- $this->entityType->getBundleEntityType()->willReturn($bundle_entity_type);
- $this->entityType->getKey('bundle')->willReturn($bundle_key);
- $link_template = 'add-form';
- $this->registerLinkTemplate($link_template);
- /** @var \Drupal\Core\Url $url */
- $url = $entity->toUrl($link_template);
- $this->assertUrl('entity.test_entity.add_form', $expected_route_parameters, $entity, FALSE, $url);
- }
- /**
- * Provides data for testToUrlLinkTemplateAddForm().
- *
- * @return array
- * An array of test cases for testToUrlLinkTemplateAddForm().
- */
- public function providerTestToUrlLinkTemplateAddForm() {
- $test_cases = [];
- $route_parameters = [];
- $test_cases['no_bundle_key'] = [static::HAS_NO_BUNDLE_KEY, NULL, FALSE, $route_parameters];
- $route_parameters = ['type' => $this->entityTypeId];
- $test_cases['bundle_entity_type'] = [static::HAS_BUNDLE_KEY, 'type', FALSE, $route_parameters];
- $test_cases['bundle_key'] = [static::HAS_BUNDLE_KEY, NULL, 'type', $route_parameters];
- return $test_cases;
- }
- /**
- * Tests the toUrl() method with neither link templates nor a URI callback.
- *
- * @param array $bundle_info
- * An array of bundle info to register.
- * @param string $uri_callback
- * The entity type URI callback to register.
- *
- * @dataProvider providerTestToUrlUriCallbackUndefined
- *
- * @covers ::toUrl
- * @covers ::linkTemplates
- */
- public function testToUrlUriCallbackUndefined(array $bundle_info, $uri_callback) {
- $entity = $this->getEntity(EntityBase::class, ['id' => $this->entityId]);
- $this->registerBundleInfo($bundle_info);
- $this->entityType->getUriCallback()->willReturn($uri_callback);
- $link_template = 'canonical';
- $this->expectException(UndefinedLinkTemplateException::class);
- $this->expectExceptionMessage("No link template '$link_template' found for the '$this->entityTypeId' entity type");
- $entity->toUrl($link_template);
- }
- /**
- * Provides data for testToUrlUriCallbackUndefined().
- *
- * @return array
- * An array of test cases for testToUrlUriCallbackUndefined().
- */
- public function providerTestToUrlUriCallbackUndefined() {
- $test_cases = [];
- $test_cases['no_callback'] = [[], NULL];
- $test_cases['uri_callback'] = [[], 'not_a_callable'];
- $test_cases['bundle_uri_callback'] = [['uri_callback' => 'not_a_callable'], NULL];
- return $test_cases;
- }
- /**
- * Tests the toUrl() method with a URI callback.
- *
- * @param array $bundle_info
- * An array of bundle info to register.
- * @param string $uri_callback
- * The entity type URI callback to register.
- *
- * @covers ::toUrl
- * @covers ::linkTemplates
- *
- * @dataProvider providerTestToUrlUriCallback
- */
- public function testToUrlUriCallback(array $bundle_info, $uri_callback) {
- $entity = $this->getEntity(EntityBase::class, ['id' => $this->entityId, 'langcode' => $this->langcode]);
- $this->registerBundleInfo($bundle_info);
- $this->entityType->getUriCallback()->willReturn($uri_callback);
- /** @var \Drupal\Core\Url $url */
- $url = $entity->toUrl('canonical');
- $this->assertUrl('<none>', [], $entity, TRUE, $url);
- }
- /**
- * Provides data for testToUrlUriCallback().
- *
- * @return array
- * An array of test cases for testToUrlUriCallback().
- */
- public function providerTestToUrlUriCallback() {
- $test_cases = [];
- $uri_callback = function () {
- return Url::fromRoute('<none>');
- };
- $test_cases['uri_callback'] = [[], $uri_callback];
- $test_cases['bundle_uri_callback'] = [['uri_callback' => $uri_callback], NULL];
- return $test_cases;
- }
- /**
- * Tests the urlInfo() method.
- *
- * @param string $rel
- * The link relation to test.
- * @param array $options
- * An array of URL options to test with.
- *
- * @covers ::urlInfo
- *
- * @dataProvider providerTestUrlInfo
- *
- * @group legacy
- * @expectedDeprecation EntityInterface::urlInfo() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toUrl() instead. See https://www.drupal.org/node/2614344
- */
- public function testUrlInfo($rel, $options) {
- $entity = $this->getEntity(EntityBase::class, [], ['toUrl']);
- $entity->expects($this->once())
- ->method('toUrl')
- ->with($rel, $options);
- $entity->urlInfo($rel, $options);
- }
- /**
- * Tests the link() method.
- *
- * @covers ::urlInfo
- *
- * @group legacy
- * @expectedDeprecation EntityInterface::link() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. Use EntityInterface::toLink()->toString() instead. Note, the default relationship for configuration entities changes from 'edit-form' to 'canonical'. See https://www.drupal.org/node/2614344
- */
- public function testLink() {
- $link = $this->createMock(Link::class);
- $link->expects($this->once())
- ->method('toString')
- ->willReturn('<a href="/foo">The link</a>');
- $entity = $this->getEntity(EntityBase::class, [], ['toLink']);
- $entity->expects($this->once())
- ->method('toLink')
- ->with(NULL, 'canonical')
- ->willReturn($link);
- $this->assertEquals('<a href="/foo">The link</a>', $entity->link());
- }
- /**
- * Provides data for testUrlInfo().
- *
- * @return array
- * An array of test cases for testUrlInfo().
- */
- public function providerTestUrlInfo() {
- $test_cases = [];
- $test_cases['default'] = ['canonical', []];
- $test_cases['with_option'] = ['canonical', ['absolute' => TRUE]];
- $test_cases['revision'] = ['revision', []];
- return $test_cases;
- }
- /**
- * Tests the url() method without an entity ID.
- *
- * @param string $rel
- * The link relation to test.
- *
- * @covers ::url
- * @covers ::hasLinkTemplate
- * @covers ::linkTemplates
- *
- * @dataProvider providerTestUrl
- *
- * @group legacy
- * @expectedDeprecation EntityInterface::url() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toUrl() instead. Note, a \Drupal\Core\Url object is returned. See https://www.drupal.org/node/2614344
- */
- public function testUrlEmpty($rel) {
- $entity = $this->getEntity(EntityBase::class, []);
- $this->assertEquals('', $entity->url($rel));
- }
- /**
- * Provides data for testUrlEmpty().
- *
- * @return array
- * An array of test cases for testUrlEmpty().
- */
- public function providerTestUrlEmpty() {
- $test_cases = [];
- $test_cases['default'] = ['canonical', []];
- $test_cases['revision'] = ['revision', []];
- return $test_cases;
- }
- /**
- * Tests the url() method.
- *
- * @param string $rel
- * The link relation to test.
- * @param array $options
- * An array of URL options to call url() with.
- * @param array $default_options
- * An array of URL options that toUrl() should generate.
- * @param array $expected_options
- * An array of combined URL options that should be set on the final URL.
- *
- * @covers ::url
- * @covers ::hasLinkTemplate
- * @covers ::linkTemplates
- *
- * @dataProvider providerTestUrl
- *
- * @group legacy
- * @expectedDeprecation EntityInterface::url() is deprecated in Drupal 8.0.0 and will be removed in Drupal 9.0.0. EntityInterface::toUrl() instead. Note, a \Drupal\Core\Url object is returned. See https://www.drupal.org/node/2614344
- */
- public function testUrl($rel, $options, $default_options, $expected_options) {
- $entity = $this->getEntity(EntityBase::class, ['id' => $this->entityId], ['toUrl']);
- $this->registerLinkTemplate($rel);
- $uri = $this->prophesize(Url::class);
- $uri->getOptions()->willReturn($default_options);
- $uri->setOptions($expected_options)->shouldBeCalled();
- $url_string = "/test-entity/{$this->entityId}/$rel";
- $uri->toString()->willReturn($url_string);
- $entity->expects($this->once())
- ->method('toUrl')
- ->with($rel)
- ->willReturn($uri->reveal());
- $this->assertEquals($url_string, $entity->url($rel, $options));
- }
- /**
- * Provides data for testUrl().
- *
- * @return array
- * An array of test cases for testUrl().
- */
- public function providerTestUrl() {
- $test_cases = [];
- $test_cases['default'] = ['canonical', [], [], []];
- $test_cases['revision'] = ['revision', [], [], []];
- $test_cases['option'] = ['canonical', ['absolute' => TRUE], [], ['absolute' => TRUE]];
- $test_cases['default_option'] = ['canonical', [], ['absolute' => TRUE], ['absolute' => TRUE]];
- $test_cases['option_merge'] = ['canonical', ['absolute' => TRUE], ['entity_type' => $this->entityTypeId], ['absolute' => TRUE, 'entity_type' => $this->entityTypeId]];
- $test_cases['option_override'] = ['canonical', ['absolute' => TRUE], ['absolute' => FALSE], ['absolute' => TRUE]];
- return $test_cases;
- }
- /**
- * Tests the uriRelationships() method.
- *
- * @covers ::uriRelationships
- */
- public function testUriRelationships() {
- $entity = $this->getEntity(EntityBase::class, ['id' => $this->entityId]);
- $container_builder = new ContainerBuilder();
- $url_generator = $this->createMock(UrlGeneratorInterface::class);
- $container_builder->set('url_generator', $url_generator);
- \Drupal::setContainer($container_builder);
- // Test route with no mandatory parameters.
- $this->registerLinkTemplate('canonical');
- $route_name_0 = 'entity.' . $this->entityTypeId . '.canonical';
- $url_generator->expects($this->at(0))
- ->method('generateFromRoute')
- ->with($route_name_0)
- ->willReturn((new GeneratedUrl())->setGeneratedUrl('/entity_test'));
- $this->assertEquals(['canonical'], $entity->uriRelationships());
- // Test route with non-default mandatory parameters.
- $this->registerLinkTemplate('{non_default_parameter}');
- $route_name_1 = 'entity.' . $this->entityTypeId . '.{non_default_parameter}';
- $url_generator->expects($this->at(0))
- ->method('generateFromRoute')
- ->with($route_name_1)
- ->willThrowException(new MissingMandatoryParametersException());
- $this->assertEquals([], $entity->uriRelationships());
- }
- /**
- * Returns a mock entity for testing.
- *
- * @param string $class
- * The class name to mock. Should be \Drupal\Core\Entity\Entity or a
- * subclass.
- * @param array $values
- * An array of entity values to construct the mock entity with.
- * @param array $methods
- * (optional) An array of additional methods to mock on the entity object.
- * The getEntityType() and entityTypeBundleInfo() methods are always mocked.
- *
- * @return \Drupal\Core\Entity\Entity|\PHPUnit\Framework\MockObject\MockObject
- */
- protected function getEntity($class, array $values, array $methods = []) {
- $methods = array_merge($methods, ['getEntityType', 'entityTypeBundleInfo']);
- // Prophecy does not allow prophesizing abstract classes while actually
- // calling their code. We use Prophecy below because that allows us to
- // add method prophecies later while still revealing the prophecy now.
- $entity = $this->getMockBuilder($class)
- ->setConstructorArgs([$values, $this->entityTypeId])
- ->setMethods($methods)
- ->getMockForAbstractClass();
- $this->entityType = $this->prophesize(EntityTypeInterface::class);
- $this->entityType->getLinkTemplates()->willReturn([]);
- $this->entityType->getKey('langcode')->willReturn(FALSE);
- $entity->method('getEntityType')->willReturn($this->entityType->reveal());
- $this->entityTypeBundleInfo = $this->prophesize(EntityTypeBundleInfoInterface::class);
- $entity->method('entityTypeBundleInfo')->willReturn($this->entityTypeBundleInfo->reveal());
- return $entity;
- }
- /**
- * Asserts that a given URL object matches the expectations.
- *
- * @param string $expected_route_name
- * The expected route name of the generated URL.
- * @param array $expected_route_parameters
- * The expected route parameters of the generated URL.
- * @param \Drupal\Core\Entity\Entity|\PHPUnit\Framework\MockObject\MockObject $entity
- * The entity that is expected to be set as a URL option.
- * @param bool $has_language
- * Whether or not the URL is expected to have a language option.
- * @param \Drupal\Core\Url $url
- * The URL option to make the assertions on.
- */
- protected function assertUrl($expected_route_name, array $expected_route_parameters, $entity, $has_language, Url $url) {
- $this->assertEquals($expected_route_name, $url->getRouteName());
- $this->assertEquals($expected_route_parameters, $url->getRouteParameters());
- $this->assertEquals($this->entityTypeId, $url->getOption('entity_type'));
- $this->assertEquals($entity, $url->getOption('entity'));
- if ($has_language) {
- $this->assertEquals($this->langcode, $url->getOption('language')->getId());
- }
- else {
- $this->assertNull($url->getOption('language'));
- }
- }
- /**
- * Registers a link template for the mock entity.
- *
- * @param string $link_template
- * The link template to register.
- */
- protected function registerLinkTemplate($link_template) {
- $link_templates = [
- // The path is actually never used because we never invoke the URL
- // generator but perform assertions on the URL object directly.
- $link_template => "/test-entity/{test_entity}/$link_template",
- ];
- $this->entityType->getLinkTemplates()->willReturn($link_templates);
- }
- /**
- * Registers bundle information for the mock entity type.
- *
- * @param array $bundle_info
- * The bundle information to register.
- */
- protected function registerBundleInfo($bundle_info) {
- $this->entityTypeBundleInfo
- ->getBundleInfo($this->entityTypeId)
- ->willReturn([$this->entityTypeId => $bundle_info]);
- }
- }
- abstract class RevisionableEntity extends EntityBase implements RevisionableInterface {}
|