3 namespace Drupal\Tests\link\Functional;
5 use Drupal\Component\Utility\Html;
6 use Drupal\Component\Utility\Unicode;
8 use Drupal\entity_test\Entity\EntityTest;
9 use Drupal\field\Entity\FieldConfig;
10 use Drupal\link\LinkItemInterface;
11 use Drupal\node\NodeInterface;
12 use Drupal\Tests\BrowserTestBase;
13 use Drupal\field\Entity\FieldStorageConfig;
16 * Tests link field widgets and formatters.
20 class LinkFieldTest extends BrowserTestBase {
27 public static $modules = [
31 'link_test_base_field',
35 * A field to use in this test class.
37 * @var \Drupal\field\Entity\FieldStorageConfig
39 protected $fieldStorage;
42 * The instance used in this test class.
44 * @var \Drupal\field\Entity\FieldConfig
48 protected function setUp() {
51 $this->drupalLogin($this->drupalCreateUser([
53 'administer entity_test content',
59 * Tests link field URL validation.
61 public function testURLValidation() {
62 $field_name = mb_strtolower($this->randomMachineName());
63 // Create a field with settings to validate.
64 $this->fieldStorage = FieldStorageConfig::create([
65 'field_name' => $field_name,
66 'entity_type' => 'entity_test',
69 $this->fieldStorage->save();
70 $this->field = FieldConfig::create([
71 'field_storage' => $this->fieldStorage,
72 'bundle' => 'entity_test',
74 'title' => DRUPAL_DISABLED,
75 'link_type' => LinkItemInterface::LINK_GENERIC,
79 entity_get_form_display('entity_test', 'entity_test', 'default')
80 ->setComponent($field_name, [
81 'type' => 'link_default',
83 'placeholder_url' => 'http://example.com',
87 entity_get_display('entity_test', 'entity_test', 'full')
88 ->setComponent($field_name, [
93 // Display creation form.
94 $this->drupalGet('entity_test/add');
95 $this->assertFieldByName("{$field_name}[0][uri]", '', 'Link URL field is displayed');
96 $this->assertRaw('placeholder="http://example.com"');
98 // Create a path alias.
99 \Drupal::service('path.alias_storage')->save('/admin', '/a/path/alias');
101 // Create a node to test the link widget.
102 $node = $this->drupalCreateNode();
104 $restricted_node = $this->drupalCreateNode(['status' => NodeInterface::NOT_PUBLISHED]);
106 // Define some valid URLs (keys are the entered values, values are the
107 // strings displayed to the user).
108 $valid_external_entries = [
109 'http://www.example.com/' => 'http://www.example.com/',
110 // Strings within parenthesis without leading space char.
111 'http://www.example.com/strings_(string_within_parenthesis)' => 'http://www.example.com/strings_(string_within_parenthesis)',
112 // Numbers within parenthesis without leading space char.
113 'http://www.example.com/numbers_(9999)' => 'http://www.example.com/numbers_(9999)',
115 $valid_internal_entries = [
116 '/entity_test/add' => '/entity_test/add',
117 '/a/path/alias' => '/a/path/alias',
119 // Front page, with query string and fragment.
120 '/' => '<front>',
121 '/?example=llama' => '<front>?example=llama',
122 '/#example' => '<front>#example',
124 // Trailing spaces should be ignored.
125 '/ ' => '<front>',
126 '/path with spaces ' => '/path with spaces',
128 // @todo '<front>' is valid input for BC reasons, may be removed by
129 // https://www.drupal.org/node/2421941
130 '<front>' => '<front>',
131 '<front>#example' => '<front>#example',
132 '<front>?example=llama' => '<front>?example=llama',
134 // Query string and fragment.
135 '?example=llama' => '?example=llama',
136 '#example' => '#example',
138 // Entity reference autocomplete value.
139 $node->label() . ' (1)' => $node->label() . ' (1)',
140 // Entity URI displayed as ER autocomplete value when displayed in a form.
141 'entity:node/1' => $node->label() . ' (1)',
142 // URI for an entity that exists, but is not accessible by the user.
143 'entity:node/' . $restricted_node->id() => '- Restricted access - (' . $restricted_node->id() . ')',
144 // URI for an entity that doesn't exist, but with a valid ID.
145 'entity:user/999999' => 'entity:user/999999',
148 // Define some invalid URLs.
149 $validation_error_1 = "The path '@link_path' is invalid.";
150 $validation_error_2 = 'Manually entered paths should start with /, ? or #.';
151 $validation_error_3 = "The path '@link_path' is inaccessible.";
152 $invalid_external_entries = [
154 'invalid://not-a-valid-protocol' => $validation_error_1,
156 'http://' => $validation_error_1,
158 $invalid_internal_entries = [
159 'no-leading-slash' => $validation_error_2,
160 'entity:non_existing_entity_type/yar' => $validation_error_1,
161 // URI for an entity that doesn't exist, with an invalid ID.
162 'entity:user/invalid-parameter' => $validation_error_1,
165 // Test external and internal URLs for 'link_type' = LinkItemInterface::LINK_GENERIC.
166 $this->assertValidEntries($field_name, $valid_external_entries + $valid_internal_entries);
167 $this->assertInvalidEntries($field_name, $invalid_external_entries + $invalid_internal_entries);
169 // Test external URLs for 'link_type' = LinkItemInterface::LINK_EXTERNAL.
170 $this->field->setSetting('link_type', LinkItemInterface::LINK_EXTERNAL);
171 $this->field->save();
172 $this->assertValidEntries($field_name, $valid_external_entries);
173 $this->assertInvalidEntries($field_name, $valid_internal_entries + $invalid_external_entries);
175 // Test external URLs for 'link_type' = LinkItemInterface::LINK_INTERNAL.
176 $this->field->setSetting('link_type', LinkItemInterface::LINK_INTERNAL);
177 $this->field->save();
178 $this->assertValidEntries($field_name, $valid_internal_entries);
179 $this->assertInvalidEntries($field_name, $valid_external_entries + $invalid_internal_entries);
181 // Ensure that users with 'link to any page', don't apply access checking.
182 $this->drupalLogin($this->drupalCreateUser([
184 'administer entity_test content',
186 $this->assertValidEntries($field_name, ['/entity_test/add' => '/entity_test/add']);
187 $this->assertInValidEntries($field_name, ['/admin' => $validation_error_3]);
191 * Asserts that valid URLs can be submitted.
193 * @param string $field_name
195 * @param array $valid_entries
196 * An array of valid URL entries.
198 protected function assertValidEntries($field_name, array $valid_entries) {
199 foreach ($valid_entries as $uri => $string) {
201 "{$field_name}[0][uri]" => $uri,
203 $this->drupalPostForm('entity_test/add', $edit, t('Save'));
204 preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
206 $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
207 $this->assertRaw('"' . $string . '"');
212 * Asserts that invalid URLs cannot be submitted.
214 * @param string $field_name
216 * @param array $invalid_entries
217 * An array of invalid URL entries.
219 protected function assertInvalidEntries($field_name, array $invalid_entries) {
220 foreach ($invalid_entries as $invalid_value => $error_message) {
222 "{$field_name}[0][uri]" => $invalid_value,
224 $this->drupalPostForm('entity_test/add', $edit, t('Save'));
225 $this->assertText(t($error_message, ['@link_path' => $invalid_value]));
230 * Tests the link title settings of a link field.
232 public function testLinkTitle() {
233 $field_name = mb_strtolower($this->randomMachineName());
234 // Create a field with settings to validate.
235 $this->fieldStorage = FieldStorageConfig::create([
236 'field_name' => $field_name,
237 'entity_type' => 'entity_test',
240 $this->fieldStorage->save();
241 $this->field = FieldConfig::create([
242 'field_storage' => $this->fieldStorage,
243 'bundle' => 'entity_test',
244 'label' => 'Read more about this entity',
246 'title' => DRUPAL_OPTIONAL,
247 'link_type' => LinkItemInterface::LINK_GENERIC,
250 $this->field->save();
251 entity_get_form_display('entity_test', 'entity_test', 'default')
252 ->setComponent($field_name, [
253 'type' => 'link_default',
255 'placeholder_url' => 'http://example.com',
256 'placeholder_title' => 'Enter the text for this link',
260 entity_get_display('entity_test', 'entity_test', 'full')
261 ->setComponent($field_name, [
267 // Verify that the link text field works according to the field setting.
268 foreach ([DRUPAL_DISABLED, DRUPAL_REQUIRED, DRUPAL_OPTIONAL] as $title_setting) {
269 // Update the link title field setting.
270 $this->field->setSetting('title', $title_setting);
271 $this->field->save();
273 // Display creation form.
274 $this->drupalGet('entity_test/add');
275 // Assert label is shown.
276 $this->assertText('Read more about this entity');
277 $this->assertFieldByName("{$field_name}[0][uri]", '', 'URL field found.');
278 $this->assertRaw('placeholder="http://example.com"');
280 if ($title_setting === DRUPAL_DISABLED) {
281 $this->assertNoFieldByName("{$field_name}[0][title]", '', 'Link text field not found.');
282 $this->assertNoRaw('placeholder="Enter the text for this link"');
285 $this->assertRaw('placeholder="Enter the text for this link"');
287 $this->assertFieldByName("{$field_name}[0][title]", '', 'Link text field found.');
288 if ($title_setting === DRUPAL_OPTIONAL) {
289 // Verify that the URL is required, if the link text is non-empty.
291 "{$field_name}[0][title]" => 'Example',
293 $this->drupalPostForm(NULL, $edit, t('Save'));
294 $this->assertText(t('The URL field is required when the @title field is specified.', ['@title' => t('Link text')]));
296 if ($title_setting === DRUPAL_REQUIRED) {
297 // Verify that the link text is required, if the URL is non-empty.
299 "{$field_name}[0][uri]" => 'http://www.example.com',
301 $this->drupalPostForm(NULL, $edit, t('Save'));
302 $this->assertText(t('@title field is required if there is @uri input.', ['@title' => t('Link text'), '@uri' => t('URL')]));
304 // Verify that the link text is not required, if the URL is empty.
306 "{$field_name}[0][uri]" => '',
308 $this->drupalPostForm(NULL, $edit, t('Save'));
309 $this->assertNoText(t('@name field is required.', ['@name' => t('Link text')]));
311 // Verify that a URL and link text meets requirements.
312 $this->drupalGet('entity_test/add');
314 "{$field_name}[0][uri]" => 'http://www.example.com',
315 "{$field_name}[0][title]" => 'Example',
317 $this->drupalPostForm(NULL, $edit, t('Save'));
318 $this->assertNoText(t('@name field is required.', ['@name' => t('Link text')]));
323 // Verify that a link without link text is rendered using the URL as text.
324 $value = 'http://www.example.com/';
326 "{$field_name}[0][uri]" => $value,
327 "{$field_name}[0][title]" => '',
329 $this->drupalPostForm(NULL, $edit, t('Save'));
330 preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
332 $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
334 $output = $this->renderTestEntity($id);
335 $expected_link = (string) \Drupal::l($value, Url::fromUri($value));
336 $this->assertContains($expected_link, $output);
338 // Verify that a link with text is rendered using the link text.
339 $title = $this->randomMachineName();
341 "{$field_name}[0][title]" => $title,
343 $this->drupalPostForm("entity_test/manage/$id/edit", $edit, t('Save'));
344 $this->assertText(t('entity_test @id has been updated.', ['@id' => $id]));
346 $output = $this->renderTestEntity($id);
347 $expected_link = (string) \Drupal::l($title, Url::fromUri($value));
348 $this->assertContains($expected_link, $output);
352 * Tests the default 'link' formatter.
354 public function testLinkFormatter() {
355 $field_name = mb_strtolower($this->randomMachineName());
356 // Create a field with settings to validate.
357 $this->fieldStorage = FieldStorageConfig::create([
358 'field_name' => $field_name,
359 'entity_type' => 'entity_test',
363 $this->fieldStorage->save();
364 FieldConfig::create([
365 'field_storage' => $this->fieldStorage,
366 'label' => 'Read more about this entity',
367 'bundle' => 'entity_test',
369 'title' => DRUPAL_OPTIONAL,
370 'link_type' => LinkItemInterface::LINK_GENERIC,
373 entity_get_form_display('entity_test', 'entity_test', 'default')
374 ->setComponent($field_name, [
375 'type' => 'link_default',
382 entity_get_display('entity_test', 'entity_test', 'full')
383 ->setComponent($field_name, $display_options)
386 // Create an entity with three link field values:
387 // - The first field item uses a URL only.
388 // - The second field item uses a URL and link text.
389 // - The third field item uses a fragment-only URL with text.
390 // For consistency in assertion code below, the URL is assigned to the title
391 // variable for the first field.
392 $this->drupalGet('entity_test/add');
393 $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com';
394 $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org';
397 // Intentionally contains an ampersand that needs sanitization on output.
398 $title2 = 'A very long & strange example title that could break the nice layout of the site';
399 $title3 = 'Fragment only';
401 "{$field_name}[0][uri]" => $url1,
402 // Note that $title1 is not submitted.
403 "{$field_name}[0][title]" => '',
404 "{$field_name}[1][uri]" => $url2,
405 "{$field_name}[1][title]" => $title2,
406 "{$field_name}[2][uri]" => $url3,
407 "{$field_name}[2][title]" => $title3,
409 // Assert label is shown.
410 $this->assertText('Read more about this entity');
411 $this->drupalPostForm(NULL, $edit, t('Save'));
412 preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
414 $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
416 // Verify that the link is output according to the formatter settings.
417 // Not using generatePermutations(), since that leads to 32 cases, which
418 // would not test actual link field formatter functionality but rather
419 // the link generator and options/attributes. Only 'url_plain' has a
420 // dependency on 'url_only', so we have a total of ~10 cases.
422 'trim_length' => [NULL, 6],
423 'rel' => [NULL, 'nofollow'],
424 'target' => [NULL, '_blank'],
426 ['url_only' => FALSE],
427 ['url_only' => FALSE, 'url_plain' => TRUE],
428 ['url_only' => TRUE],
429 ['url_only' => TRUE, 'url_plain' => TRUE],
432 foreach ($options as $setting => $values) {
433 foreach ($values as $new_value) {
434 // Update the field formatter settings.
435 if (!is_array($new_value)) {
436 $display_options['settings'] = [$setting => $new_value];
439 $display_options['settings'] = $new_value;
441 entity_get_display('entity_test', 'entity_test', 'full')
442 ->setComponent($field_name, $display_options)
445 $output = $this->renderTestEntity($id);
449 $title = isset($new_value) ? Unicode::truncate($title1, $new_value, FALSE, TRUE) : $title1;
450 $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
453 $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2;
454 $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
457 $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
458 $this->assertContains('<a href="' . Html::escape($url) . '">' . Html::escape($title) . '</a>', $output);
462 $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
463 $this->assertContains('<a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($title1) . '</a>', $output);
464 $this->assertContains('<a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($title2) . '</a>', $output);
465 $this->assertContains('<a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($title3) . '</a>', $output);
469 $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
470 $this->assertContains('<a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($title1) . '</a>', $output);
471 $this->assertContains('<a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($title2) . '</a>', $output);
472 $this->assertContains('<a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($title3) . '</a>', $output);
476 // In this case, $new_value is an array.
477 if (!$new_value['url_only']) {
478 $this->assertContains('<a href="' . Html::escape($url1) . '">' . Html::escape($title1) . '</a>', $output);
479 $this->assertContains('<a href="' . Html::escape($url2) . '">' . Html::escape($title2) . '</a>', $output);
480 $this->assertContains('<a href="' . Html::escape($url3) . '">' . Html::escape($title3) . '</a>', $output);
483 if (empty($new_value['url_plain'])) {
484 $this->assertContains('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>', $output);
485 $this->assertContains('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>', $output);
486 $this->assertContains('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>', $output);
489 $this->assertNotContains('<a href="' . Html::escape($url1) . '">' . Html::escape($url1) . '</a>', $output);
490 $this->assertNotContains('<a href="' . Html::escape($url2) . '">' . Html::escape($url2) . '</a>', $output);
491 $this->assertNotContains('<a href="' . Html::escape($url3) . '">' . Html::escape($url3) . '</a>', $output);
492 $this->assertContains(Html::escape($url1), $output);
493 $this->assertContains(Html::escape($url2), $output);
494 $this->assertContains(Html::escape($url3), $output);
504 * Tests the 'link_separate' formatter.
506 * This test is mostly the same as testLinkFormatter(), but they cannot be
507 * merged, since they involve different configuration and output.
509 public function testLinkSeparateFormatter() {
510 $field_name = mb_strtolower($this->randomMachineName());
511 // Create a field with settings to validate.
512 $this->fieldStorage = FieldStorageConfig::create([
513 'field_name' => $field_name,
514 'entity_type' => 'entity_test',
518 $this->fieldStorage->save();
519 FieldConfig::create([
520 'field_storage' => $this->fieldStorage,
521 'bundle' => 'entity_test',
523 'title' => DRUPAL_OPTIONAL,
524 'link_type' => LinkItemInterface::LINK_GENERIC,
528 'type' => 'link_separate',
531 entity_get_form_display('entity_test', 'entity_test', 'default')
532 ->setComponent($field_name, [
533 'type' => 'link_default',
536 entity_get_display('entity_test', 'entity_test', 'full')
537 ->setComponent($field_name, $display_options)
540 // Create an entity with three link field values:
541 // - The first field item uses a URL only.
542 // - The second field item uses a URL and link text.
543 // - The third field item uses a fragment-only URL with text.
544 // For consistency in assertion code below, the URL is assigned to the title
545 // variable for the first field.
546 $this->drupalGet('entity_test/add');
547 $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com';
548 $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org';
550 // Intentionally contains an ampersand that needs sanitization on output.
551 $title2 = 'A very long & strange example title that could break the nice layout of the site';
552 $title3 = 'Fragment only';
554 "{$field_name}[0][uri]" => $url1,
555 "{$field_name}[1][uri]" => $url2,
556 "{$field_name}[1][title]" => $title2,
557 "{$field_name}[2][uri]" => $url3,
558 "{$field_name}[2][title]" => $title3,
560 $this->drupalPostForm(NULL, $edit, t('Save'));
561 preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match);
563 $this->assertText(t('entity_test @id has been created.', ['@id' => $id]));
565 // Verify that the link is output according to the formatter settings.
567 'trim_length' => [NULL, 6],
568 'rel' => [NULL, 'nofollow'],
569 'target' => [NULL, '_blank'],
571 foreach ($options as $setting => $values) {
572 foreach ($values as $new_value) {
573 // Update the field formatter settings.
574 $display_options['settings'] = [$setting => $new_value];
575 entity_get_display('entity_test', 'entity_test', 'full')
576 ->setComponent($field_name, $display_options)
579 $output = $this->renderTestEntity($id);
583 $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
584 $expected = '<div class="link-item">';
585 $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
586 $expected .= '</div>';
587 $this->assertContains($expected, $output);
590 $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
591 $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2;
592 $expected = '<div class="link-item">';
593 $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
594 $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
595 $expected .= '</div>';
596 $this->assertContains($expected, $output);
599 $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url;
600 $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3;
601 $expected = '<div class="link-item">';
602 $expected .= '<div class="link-title">' . Html::escape($title) . '</div>';
603 $expected .= '<div class="link-url"><a href="' . Html::escape($url) . '">' . Html::escape($url_title) . '</a></div>';
604 $expected .= '</div>';
605 $this->assertContains($expected, $output);
609 $rel = isset($new_value) ? ' rel="' . $new_value . '"' : '';
610 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $rel . '>' . Html::escape($url1) . '</a></div>', $output);
611 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $rel . '>' . Html::escape($url2) . '</a></div>', $output);
612 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $rel . '>' . Html::escape($url3) . '</a></div>', $output);
616 $target = isset($new_value) ? ' target="' . $new_value . '"' : '';
617 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url1) . '"' . $target . '>' . Html::escape($url1) . '</a></div>', $output);
618 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url2) . '"' . $target . '>' . Html::escape($url2) . '</a></div>', $output);
619 $this->assertContains('<div class="link-url"><a href="' . Html::escape($url3) . '"' . $target . '>' . Html::escape($url3) . '</a></div>', $output);
627 * Test '#link_type' property exists on 'link_default' widget.
629 * Make sure the 'link_default' widget exposes a '#link_type' property on
630 * its element. Modules can use it to understand if a text form element is
631 * a link and also which LinkItemInterface::LINK_* is (EXTERNAL, GENERIC,
634 public function testLinkTypeOnLinkWidget() {
636 $link_type = LinkItemInterface::LINK_EXTERNAL;
637 $field_name = mb_strtolower($this->randomMachineName());
639 // Create a field with settings to validate.
640 $this->fieldStorage = FieldStorageConfig::create([
641 'field_name' => $field_name,
642 'entity_type' => 'entity_test',
646 $this->fieldStorage->save();
647 FieldConfig::create([
648 'field_storage' => $this->fieldStorage,
649 'label' => 'Read more about this entity',
650 'bundle' => 'entity_test',
652 'title' => DRUPAL_OPTIONAL,
653 'link_type' => $link_type,
657 $this->container->get('entity.manager')
658 ->getStorage('entity_form_display')
659 ->load('entity_test.entity_test.default')
660 ->setComponent($field_name, [
661 'type' => 'link_default',
665 $form = \Drupal::service('entity.form_builder')->getForm(EntityTest::create());
666 $this->assertEqual($form[$field_name]['widget'][0]['uri']['#link_type'], $link_type);
670 * Tests editing a link to a non-node entity.
672 public function testEditNonNodeEntityLink() {
674 $entity_type_manager = \Drupal::entityTypeManager();
675 $entity_test_storage = $entity_type_manager->getStorage('entity_test');
677 // Create a field with settings to validate.
678 $this->fieldStorage = FieldStorageConfig::create([
679 'field_name' => 'field_link',
680 'entity_type' => 'entity_test',
684 $this->fieldStorage->save();
685 FieldConfig::create([
686 'field_storage' => $this->fieldStorage,
687 'label' => 'Read more about this entity',
688 'bundle' => 'entity_test',
690 'title' => DRUPAL_OPTIONAL,
695 ->getStorage('entity_form_display')
696 ->load('entity_test.entity_test.default')
697 ->setComponent('field_link', [
698 'type' => 'link_default',
702 // Create a node and a test entity to have a possibly valid reference for
703 // both. Create another test entity that references the first test entity.
704 $entity_test_link = $entity_test_storage->create(['name' => 'correct link target']);
705 $entity_test_link->save();
707 $node = $this->drupalCreateNode(['wrong link target']);
709 $correct_link = 'entity:entity_test/' . $entity_test_link->id();
710 $entity_test = $entity_test_storage->create([
711 'name' => 'correct link target',
712 'field_link' => $correct_link,
714 $entity_test->save();
716 // Edit the entity and save it, verify the correct link is kept and not
717 // changed to point to a node. Currently, widget does not support non-node
718 // autocomplete and therefore must show the link unaltered.
719 $this->drupalGet($entity_test->toUrl('edit-form'));
720 $this->assertSession()->fieldValueEquals('field_link[0][uri]', $correct_link);
721 $this->drupalPostForm(NULL, [], 'Save');
723 $entity_test_storage->resetCache();
724 $entity_test = $entity_test_storage->load($entity_test->id());
726 $this->assertEquals($correct_link, $entity_test->get('field_link')->uri);
730 * Renders a test_entity and returns the output.
733 * The test_entity ID to render.
734 * @param string $view_mode
735 * (optional) The view mode to use for rendering.
737 * (optional) Whether to reset the entity_test storage cache. Defaults to
738 * TRUE to simplify testing.
741 * The rendered HTML output.
743 protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) {
745 $this->container->get('entity.manager')->getStorage('entity_test')->resetCache([$id]);
747 $entity = EntityTest::load($id);
748 $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), $view_mode);
749 $content = $display->build($entity);
750 $output = \Drupal::service('renderer')->renderRoot($content);
751 $output = (string) $output;
752 $this->verbose($output);