EntityLinksTest.php
Namespace
Drupal\Tests\filter\KernelFile
-
core/
modules/ filter/ tests/ src/ Kernel/ EntityLinksTest.php
View source
<?php
declare (strict_types=1);
namespace Drupal\Tests\filter\Kernel;
use ColinODell\PsrTestLogger\TestLogger;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\DependencyInjection\ContainerBuilder;
use Drupal\Core\Language\LanguageInterface;
use Drupal\entity_test\Entity\EntityTestMul;
use Drupal\file\Entity\File;
use Drupal\file\FileInterface;
use Drupal\filter\FilterPluginCollection;
use Drupal\filter\FilterProcessResult;
use Drupal\filter\Plugin\Filter\EntityLinks;
use Drupal\KernelTests\KernelTestBase;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\media\Entity\Media;
use Drupal\media\Entity\MediaType;
use Drupal\menu_link_content\Entity\MenuLinkContent;
use Drupal\shortcut\Entity\Shortcut;
use Drupal\Tests\Traits\Core\PathAliasTestTrait;
use PHPUnit\Framework\Attributes\CoversClass;
use PHPUnit\Framework\Attributes\DataProvider;
use PHPUnit\Framework\Attributes\Group;
use PHPUnit\Framework\Attributes\RunTestsInSeparateProcesses;
/**
* Tests the behavior of generating entity URLs when using entity links in CKEditor.
*/
class EntityLinksTest extends KernelTestBase {
use PathAliasTestTrait;
/**
* The entity_links filter.
*
* @var \Drupal\filter\Plugin\Filter\EntityLinks
*/
protected EntityLinks $filter;
/**
* The test logger.
*
* @var \ColinODell\PsrTestLogger\TestLogger
*/
protected TestLogger $logger;
/**
* {@inheritdoc}
*/
protected static $modules = [
'filter',
'entity_test',
'path',
'path_alias',
'language',
'file',
'user',
// @see ::testMediaEntity
'system',
'field',
'image',
'media',
'media_test_source',
// @see ::testMenuLinkContentEntity
'link',
'menu_link_content',
// @see ::testShortcutEntity
'shortcut',
];
/**
* {@inheritdoc}
*/
protected function setUp() : void {
parent::setUp();
// @see ::test
$this->installEntitySchema('entity_test');
$this->installEntitySchema('entity_test_mul');
$this->installEntitySchema('path_alias');
// @see ::testFileEntity
// @see ::testMediaEntity
$this->installEntitySchema('file');
// @see ::testMediaEntity
$this->installEntitySchema('media');
$this->installEntitySchema('media_type');
$this->installEntitySchema('field_storage_config');
$this->installEntitySchema('field_config');
$this->installEntitySchema('user');
$this->installSchema('file', [
'file_usage',
]);
$this->installConfig([
'media',
]);
// @see ::testMenuLinkContentEntity
$this->installEntitySchema('menu_link_content');
// @see ::testShortcutEntity
$this->installEntitySchema('shortcut');
$this->installConfig([
'shortcut',
]);
// Add Swedish, Danish and Finnish.
ConfigurableLanguage::createFromLangcode('sv')->save();
ConfigurableLanguage::createFromLangcode('da')->save();
ConfigurableLanguage::createFromLangcode('fi')->save();
/** @var \Drupal\Component\Plugin\PluginManagerInterface $manager */
$manager = $this->container
->get('plugin.manager.filter');
$bag = new FilterPluginCollection($manager, []);
$this->filter = $bag->get('entity_links');
// Add test logger to the 'filter' channel to assert no exceptions occurred.
$this->logger = new TestLogger();
$this->container
->get('logger.factory')
->get('filter')
->addLogger($this->logger);
}
/**
* {@inheritdoc}
*/
public function register(ContainerBuilder $container) : void {
parent::register($container);
// Undo what the parent did, to allow testing path aliases in kernel tests.
$container->getDefinition('path_alias.path_processor')
->addTag('path_processor_inbound')
->addTag('path_processor_outbound');
}
/**
* @legacy-covers ::process
*/
public function test() : void {
$expected_aliases = [
'da' => '/foo-da',
'en' => '/foo-en',
'fi' => '/foo-fi',
'sv' => '/foo-sv',
];
$expected_hrefs = $expected_aliases + [
LanguageInterface::LANGCODE_DEFAULT => '/foo-en',
LanguageInterface::LANGCODE_NOT_APPLICABLE => '/foo-en',
LanguageInterface::LANGCODE_NOT_SPECIFIED => '/foo-en',
LanguageInterface::LANGCODE_SITE_DEFAULT => '/foo-en',
];
// Create an entity and add translations to that.
/** @var \Drupal\entity_test\Entity\EntityTestMul $entity */
$entity = EntityTestMul::create([
'name' => $this->randomMachineName(),
]);
$entity->addTranslation('sv', [
'name' => $this->randomMachineName(),
'langcode' => 'sv',
]);
$entity->addTranslation('da', [
'name' => $this->randomMachineName(),
'langcode' => 'da',
]);
$entity->addTranslation('fi', [
'name' => $this->randomMachineName(),
'langcode' => 'fi',
]);
$entity->save();
// Assert the entity has a translation for every expected language.
$this->assertSame(array_keys($expected_aliases), array_keys($entity->getTranslationLanguages()));
// Create per-translation URL aliases.
$canonical_url = $entity->toUrl()
->toString(TRUE)
->getGeneratedUrl();
foreach ($expected_aliases as $langcode => $alias) {
$this->createPathAlias($canonical_url, $alias, $langcode);
}
foreach ($expected_hrefs as $langcode => $expected_alias) {
$expected_result = (new FilterProcessResult())->setProcessedText(sprintf('<a href="%s">Link text</a>', $expected_alias))
->setCacheTags([
'entity_test_mul:1',
])
->setCacheContexts([])
->setCacheMaxAge(Cache::PERMANENT);
// The expected href is generated.
$this->assertFilterProcessResult(sprintf('<a data-entity-type="entity_test_mul" data-entity-uuid="%s">Link text</a>', $entity->uuid()), $langcode, $expected_result);
// The existing href is overwritten with the expected value.
$this->assertFilterProcessResult(sprintf('<a data-entity-type="entity_test_mul" data-entity-uuid="%s" href="something">Link text</a>', $entity->uuid()), $langcode, $expected_result);
// The existing href is overwritten, but its customized query string and
// fragment remain unchanged.
$this->assertFilterProcessResult(sprintf('<a data-entity-type="entity_test_mul" data-entity-uuid="%s" href="something?query=string#fragment">Link text</a>', $entity->uuid()), $langcode, (new FilterProcessResult())->setProcessedText(sprintf('<a href="%s?query=string#fragment">Link text</a>', $expected_alias))
->setCacheTags([
'entity_test_mul:1',
])
->setCacheContexts([])
->setCacheMaxAge(Cache::PERMANENT));
}
}
/**
* @legacy-covers ::getUrl
* @legacy-covers \Drupal\file\Entity\FileLinkTarget
*/
public function testFileEntity() : void {
$file = File::create([
'uid' => 1,
'filename' => 'druplicon.txt',
'uri' => 'public://druplicon.txt',
'filemime' => 'text/plain',
'status' => FileInterface::STATUS_PERMANENT,
]);
$file->save();
$this->assertFilterProcessResult(sprintf('<a data-entity-type="file" data-entity-uuid="%s" href="something?query=string#fragment">Link text</a>', $file->uuid()), 'en', (new FilterProcessResult())->setProcessedText(sprintf('<a href="%s?query=string#fragment">Link text</a>', $file->createFileUrl(TRUE)))
->setCacheTags([
'file:1',
])
->setCacheContexts([])
->setCacheMaxAge(Cache::PERMANENT));
}
/**
* @legacy-covers ::getUrl
* @legacy-covers \Drupal\media\Entity\MediaLinkTarget
*
* @param bool $standalone_url_setting
* Whether the standalone_url setting is off (Drupal's default) or on.
* @param string $media_source
* Which media source to use.
* @param array $media_entity_values
* Which values to assign to the media entity.
* @param string $expected_url
* The expected URL.
* @param string[] $expected_cache_tags
* The expected cache tags.
*/
public function testMediaEntity(bool $standalone_url_setting, string $media_source, array $media_entity_values, string $expected_url, array $expected_cache_tags) : void {
\Drupal::configFactory()->getEditable('media.settings')
->set('standalone_url', $standalone_url_setting)
->save();
// Create media type using the given source plugin.
$media_type = MediaType::create([
'label' => 'test',
'id' => 'test',
'description' => 'Test type.',
'source' => $media_source,
]);
$media_type->save();
$source_field = $media_type->getSource()
->createSourceField($media_type);
$source_field->getFieldStorageDefinition()
->save();
$source_field->save();
$media_type->set('source_configuration', [
'source_field' => $source_field->getName(),
])
->save();
// @see \Drupal\media\Plugin\media\Source\File
if ($media_source === 'file') {
$file = File::create([
'uid' => 1,
'filename' => 'druplicon.txt',
'uri' => 'public://druplicon.txt',
'filemime' => 'text/plain',
'status' => FileInterface::STATUS_PERMANENT,
]);
$file->save();
}
$media = Media::create([
'bundle' => 'test',
$source_field->getName() => $media_entity_values,
]);
$media->save();
$expected_url = str_replace('<SITE_DIRECTORY>', $this->siteDirectory, $expected_url);
$this->assertFilterProcessResult(sprintf('<a data-entity-type="media" data-entity-uuid="%s" href="something?query=string#fragment">Link text</a>', $media->uuid()), 'en', (new FilterProcessResult())->setProcessedText(sprintf('<a href="%s?query=string#fragment">Link text</a>', $expected_url))
->setCacheTags($expected_cache_tags)
->setCacheContexts([])
->setCacheMaxAge(Cache::PERMANENT));
}
/**
* Data provider for testMediaEntity.
*/
public static function providerTestMediaEntity() : array {
return [
[
TRUE,
'file',
[
'target_id' => 1,
],
'/media/1',
[
'media:1',
],
],
[
FALSE,
'file',
[
'target_id' => 1,
],
'/<SITE_DIRECTORY>/files/druplicon.txt',
[
'file:1',
'media:1',
],
],
[
TRUE,
'oembed:video',
[
'value' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
],
'/media/1',
[
'media:1',
],
],
[
FALSE,
'oembed:video',
[
'value' => 'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
],
'https://www.youtube.com/watch?v=dQw4w9WgXcQ',
[
'media:1',
],
],
[
TRUE,
'test',
[
'value' => 'foobar',
],
'/media/1',
[
'media:1',
],
],
[
FALSE,
'test',
[
'value' => 'foobar',
],
'',
[
'media:1',
],
],
];
}
/**
* @legacy-covers ::getUrl
* @legacy-covers \Drupal\menu_link_content\Entity\MenuLinkContentLinkTarget
*/
public function testMenuLinkContentEntity() : void {
$link = 'https://nl.wikipedia.org/wiki/Llama';
$menu_link_content = MenuLinkContent::create([
'id' => 'llama',
'title' => 'Llama Gabilondo',
'description' => 'Llama Gabilondo',
'link' => $link,
'weight' => 0,
'menu_name' => 'main',
]);
$menu_link_content->save();
$this->assertFilterProcessResult(sprintf('<a data-entity-type="menu_link_content" data-entity-uuid="%s" href="something?query=string#fragment">Link text</a>', $menu_link_content->uuid()), 'en', (new FilterProcessResult())->setProcessedText(sprintf('<a href="%s?query=string#fragment">Link text</a>', $link))
->setCacheTags([
'menu_link_content:1',
])
->setCacheContexts([])
->setCacheMaxAge(Cache::PERMANENT));
}
/**
* @legacy-covers ::getUrl
* @legacy-covers \Drupal\shortcut\Entity\ShortcutLinkTarget
*/
public function testShortcutEntity() : void {
// cspell:disable-next-line
$path = '/user/logout?token=fzL0Ox4jS6qafdt6gzGzjWGb_hsR6kJ8L8E0D4hC5Mo';
$shortcut = Shortcut::create([
'shortcut_set' => 'default',
'title' => 'Comments',
'weight' => -20,
'link' => [
'uri' => "internal:{$path}",
],
]);
$shortcut->save();
$this->assertFilterProcessResult(sprintf('<a data-entity-type="shortcut" data-entity-uuid="%s" href="something?query=string#fragment">Link text</a>', $shortcut->uuid()), 'en', (new FilterProcessResult())->setProcessedText(sprintf('<a href="%s?query=string#fragment">Link text</a>', $path))
->setCacheTags([
'config:shortcut.set.default',
])
->setCacheContexts([])
->setCacheMaxAge(Cache::PERMANENT));
}
/**
* Asserts an input string + langcode yield the expected FilterProcessResult.
*
* @param string $input
* The text string to be filtered.
* @param string $langcode
* The language code of the text to be filtered.
* @param \Drupal\filter\FilterProcessResult $expected_result
* The expected filtered result.
*/
private function assertFilterProcessResult(string $input, string $langcode, FilterProcessResult $expected_result) : void {
$result = $this->filter
->process($input, $langcode);
// No exceptions should have occurred.
$this->assertSame([], $this->logger->records);
// Assert both the processed text and the associated cacheability.
$this->assertSame($expected_result->getProcessedText(), $result->getProcessedText());
$this->assertEquals(CacheableMetadata::createFromObject($expected_result), CacheableMetadata::createFromObject($result));
}
}
Classes
| Title | Deprecated | Summary |
|---|---|---|
| EntityLinksTest | Tests the behavior of generating entity URLs when using entity links in CKEditor. |
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.