Chris@0: drupalLogin($this->drupalCreateUser([ Chris@0: 'view test entity', Chris@0: 'administer entity_test content', Chris@0: 'link to any page', Chris@0: ])); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests link field URL validation. Chris@0: */ Chris@0: public function testURLValidation() { Chris@17: $field_name = mb_strtolower($this->randomMachineName()); Chris@0: // Create a field with settings to validate. Chris@0: $this->fieldStorage = FieldStorageConfig::create([ Chris@0: 'field_name' => $field_name, Chris@0: 'entity_type' => 'entity_test', Chris@0: 'type' => 'link', Chris@0: ]); Chris@0: $this->fieldStorage->save(); Chris@0: $this->field = FieldConfig::create([ Chris@0: 'field_storage' => $this->fieldStorage, Chris@0: 'bundle' => 'entity_test', Chris@0: 'settings' => [ Chris@0: 'title' => DRUPAL_DISABLED, Chris@0: 'link_type' => LinkItemInterface::LINK_GENERIC, Chris@0: ], Chris@0: ]); Chris@0: $this->field->save(); Chris@0: entity_get_form_display('entity_test', 'entity_test', 'default') Chris@0: ->setComponent($field_name, [ Chris@0: 'type' => 'link_default', Chris@0: 'settings' => [ Chris@0: 'placeholder_url' => 'http://example.com', Chris@0: ], Chris@0: ]) Chris@0: ->save(); Chris@0: entity_get_display('entity_test', 'entity_test', 'full') Chris@0: ->setComponent($field_name, [ Chris@0: 'type' => 'link', Chris@0: ]) Chris@0: ->save(); Chris@0: Chris@0: // Display creation form. Chris@0: $this->drupalGet('entity_test/add'); Chris@0: $this->assertFieldByName("{$field_name}[0][uri]", '', 'Link URL field is displayed'); Chris@0: $this->assertRaw('placeholder="http://example.com"'); Chris@0: Chris@0: // Create a path alias. Chris@0: \Drupal::service('path.alias_storage')->save('/admin', '/a/path/alias'); Chris@0: Chris@0: // Create a node to test the link widget. Chris@0: $node = $this->drupalCreateNode(); Chris@0: Chris@0: $restricted_node = $this->drupalCreateNode(['status' => NodeInterface::NOT_PUBLISHED]); Chris@0: Chris@0: // Define some valid URLs (keys are the entered values, values are the Chris@0: // strings displayed to the user). Chris@0: $valid_external_entries = [ Chris@0: 'http://www.example.com/' => 'http://www.example.com/', Chris@0: // Strings within parenthesis without leading space char. Chris@0: 'http://www.example.com/strings_(string_within_parenthesis)' => 'http://www.example.com/strings_(string_within_parenthesis)', Chris@0: // Numbers within parenthesis without leading space char. Chris@0: 'http://www.example.com/numbers_(9999)' => 'http://www.example.com/numbers_(9999)', Chris@0: ]; Chris@0: $valid_internal_entries = [ Chris@0: '/entity_test/add' => '/entity_test/add', Chris@0: '/a/path/alias' => '/a/path/alias', Chris@0: Chris@0: // Front page, with query string and fragment. Chris@0: '/' => '<front>', Chris@0: '/?example=llama' => '<front>?example=llama', Chris@0: '/#example' => '<front>#example', Chris@0: Chris@16: // Trailing spaces should be ignored. Chris@16: '/ ' => '<front>', Chris@16: '/path with spaces ' => '/path with spaces', Chris@16: Chris@0: // @todo '' is valid input for BC reasons, may be removed by Chris@0: // https://www.drupal.org/node/2421941 Chris@0: '' => '<front>', Chris@0: '#example' => '<front>#example', Chris@0: '?example=llama' => '<front>?example=llama', Chris@0: Chris@0: // Query string and fragment. Chris@0: '?example=llama' => '?example=llama', Chris@0: '#example' => '#example', Chris@0: Chris@0: // Entity reference autocomplete value. Chris@0: $node->label() . ' (1)' => $node->label() . ' (1)', Chris@0: // Entity URI displayed as ER autocomplete value when displayed in a form. Chris@0: 'entity:node/1' => $node->label() . ' (1)', Chris@0: // URI for an entity that exists, but is not accessible by the user. Chris@0: 'entity:node/' . $restricted_node->id() => '- Restricted access - (' . $restricted_node->id() . ')', Chris@0: // URI for an entity that doesn't exist, but with a valid ID. Chris@0: 'entity:user/999999' => 'entity:user/999999', Chris@0: ]; Chris@0: Chris@0: // Define some invalid URLs. Chris@0: $validation_error_1 = "The path '@link_path' is invalid."; Chris@18: $validation_error_2 = 'Manually entered paths should start with one of the following characters: / ? #'; Chris@0: $validation_error_3 = "The path '@link_path' is inaccessible."; Chris@0: $invalid_external_entries = [ Chris@0: // Invalid protocol Chris@0: 'invalid://not-a-valid-protocol' => $validation_error_1, Chris@0: // Missing host name Chris@0: 'http://' => $validation_error_1, Chris@0: ]; Chris@0: $invalid_internal_entries = [ Chris@0: 'no-leading-slash' => $validation_error_2, Chris@0: 'entity:non_existing_entity_type/yar' => $validation_error_1, Chris@0: // URI for an entity that doesn't exist, with an invalid ID. Chris@0: 'entity:user/invalid-parameter' => $validation_error_1, Chris@0: ]; Chris@0: Chris@0: // Test external and internal URLs for 'link_type' = LinkItemInterface::LINK_GENERIC. Chris@0: $this->assertValidEntries($field_name, $valid_external_entries + $valid_internal_entries); Chris@0: $this->assertInvalidEntries($field_name, $invalid_external_entries + $invalid_internal_entries); Chris@0: Chris@0: // Test external URLs for 'link_type' = LinkItemInterface::LINK_EXTERNAL. Chris@0: $this->field->setSetting('link_type', LinkItemInterface::LINK_EXTERNAL); Chris@0: $this->field->save(); Chris@0: $this->assertValidEntries($field_name, $valid_external_entries); Chris@0: $this->assertInvalidEntries($field_name, $valid_internal_entries + $invalid_external_entries); Chris@0: Chris@0: // Test external URLs for 'link_type' = LinkItemInterface::LINK_INTERNAL. Chris@0: $this->field->setSetting('link_type', LinkItemInterface::LINK_INTERNAL); Chris@0: $this->field->save(); Chris@0: $this->assertValidEntries($field_name, $valid_internal_entries); Chris@0: $this->assertInvalidEntries($field_name, $valid_external_entries + $invalid_internal_entries); Chris@0: Chris@0: // Ensure that users with 'link to any page', don't apply access checking. Chris@0: $this->drupalLogin($this->drupalCreateUser([ Chris@0: 'view test entity', Chris@0: 'administer entity_test content', Chris@0: ])); Chris@0: $this->assertValidEntries($field_name, ['/entity_test/add' => '/entity_test/add']); Chris@0: $this->assertInValidEntries($field_name, ['/admin' => $validation_error_3]); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Asserts that valid URLs can be submitted. Chris@0: * Chris@0: * @param string $field_name Chris@0: * The field name. Chris@0: * @param array $valid_entries Chris@0: * An array of valid URL entries. Chris@0: */ Chris@0: protected function assertValidEntries($field_name, array $valid_entries) { Chris@0: foreach ($valid_entries as $uri => $string) { Chris@0: $edit = [ Chris@0: "{$field_name}[0][uri]" => $uri, Chris@0: ]; Chris@0: $this->drupalPostForm('entity_test/add', $edit, t('Save')); Chris@0: preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match); Chris@0: $id = $match[1]; Chris@0: $this->assertText(t('entity_test @id has been created.', ['@id' => $id])); Chris@16: $this->assertRaw('"' . $string . '"'); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Asserts that invalid URLs cannot be submitted. Chris@0: * Chris@0: * @param string $field_name Chris@0: * The field name. Chris@0: * @param array $invalid_entries Chris@0: * An array of invalid URL entries. Chris@0: */ Chris@0: protected function assertInvalidEntries($field_name, array $invalid_entries) { Chris@0: foreach ($invalid_entries as $invalid_value => $error_message) { Chris@0: $edit = [ Chris@0: "{$field_name}[0][uri]" => $invalid_value, Chris@0: ]; Chris@0: $this->drupalPostForm('entity_test/add', $edit, t('Save')); Chris@0: $this->assertText(t($error_message, ['@link_path' => $invalid_value])); Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests the link title settings of a link field. Chris@0: */ Chris@0: public function testLinkTitle() { Chris@17: $field_name = mb_strtolower($this->randomMachineName()); Chris@0: // Create a field with settings to validate. Chris@0: $this->fieldStorage = FieldStorageConfig::create([ Chris@0: 'field_name' => $field_name, Chris@0: 'entity_type' => 'entity_test', Chris@0: 'type' => 'link', Chris@0: ]); Chris@0: $this->fieldStorage->save(); Chris@0: $this->field = FieldConfig::create([ Chris@0: 'field_storage' => $this->fieldStorage, Chris@0: 'bundle' => 'entity_test', Chris@0: 'label' => 'Read more about this entity', Chris@0: 'settings' => [ Chris@0: 'title' => DRUPAL_OPTIONAL, Chris@0: 'link_type' => LinkItemInterface::LINK_GENERIC, Chris@0: ], Chris@0: ]); Chris@0: $this->field->save(); Chris@0: entity_get_form_display('entity_test', 'entity_test', 'default') Chris@0: ->setComponent($field_name, [ Chris@0: 'type' => 'link_default', Chris@0: 'settings' => [ Chris@0: 'placeholder_url' => 'http://example.com', Chris@0: 'placeholder_title' => 'Enter the text for this link', Chris@0: ], Chris@0: ]) Chris@0: ->save(); Chris@0: entity_get_display('entity_test', 'entity_test', 'full') Chris@0: ->setComponent($field_name, [ Chris@0: 'type' => 'link', Chris@0: 'label' => 'hidden', Chris@0: ]) Chris@0: ->save(); Chris@0: Chris@0: // Verify that the link text field works according to the field setting. Chris@0: foreach ([DRUPAL_DISABLED, DRUPAL_REQUIRED, DRUPAL_OPTIONAL] as $title_setting) { Chris@0: // Update the link title field setting. Chris@0: $this->field->setSetting('title', $title_setting); Chris@0: $this->field->save(); Chris@0: Chris@0: // Display creation form. Chris@0: $this->drupalGet('entity_test/add'); Chris@0: // Assert label is shown. Chris@0: $this->assertText('Read more about this entity'); Chris@0: $this->assertFieldByName("{$field_name}[0][uri]", '', 'URL field found.'); Chris@0: $this->assertRaw('placeholder="http://example.com"'); Chris@0: Chris@0: if ($title_setting === DRUPAL_DISABLED) { Chris@0: $this->assertNoFieldByName("{$field_name}[0][title]", '', 'Link text field not found.'); Chris@0: $this->assertNoRaw('placeholder="Enter the text for this link"'); Chris@0: } Chris@0: else { Chris@0: $this->assertRaw('placeholder="Enter the text for this link"'); Chris@0: Chris@0: $this->assertFieldByName("{$field_name}[0][title]", '', 'Link text field found.'); Chris@16: if ($title_setting === DRUPAL_OPTIONAL) { Chris@16: // Verify that the URL is required, if the link text is non-empty. Chris@16: $edit = [ Chris@16: "{$field_name}[0][title]" => 'Example', Chris@16: ]; Chris@16: $this->drupalPostForm(NULL, $edit, t('Save')); Chris@16: $this->assertText(t('The URL field is required when the @title field is specified.', ['@title' => t('Link text')])); Chris@16: } Chris@0: if ($title_setting === DRUPAL_REQUIRED) { Chris@0: // Verify that the link text is required, if the URL is non-empty. Chris@0: $edit = [ Chris@0: "{$field_name}[0][uri]" => 'http://www.example.com', Chris@0: ]; Chris@0: $this->drupalPostForm(NULL, $edit, t('Save')); Chris@14: $this->assertText(t('@title field is required if there is @uri input.', ['@title' => t('Link text'), '@uri' => t('URL')])); Chris@0: Chris@0: // Verify that the link text is not required, if the URL is empty. Chris@0: $edit = [ Chris@0: "{$field_name}[0][uri]" => '', Chris@0: ]; Chris@0: $this->drupalPostForm(NULL, $edit, t('Save')); Chris@0: $this->assertNoText(t('@name field is required.', ['@name' => t('Link text')])); Chris@0: Chris@0: // Verify that a URL and link text meets requirements. Chris@0: $this->drupalGet('entity_test/add'); Chris@0: $edit = [ Chris@0: "{$field_name}[0][uri]" => 'http://www.example.com', Chris@0: "{$field_name}[0][title]" => 'Example', Chris@0: ]; Chris@0: $this->drupalPostForm(NULL, $edit, t('Save')); Chris@0: $this->assertNoText(t('@name field is required.', ['@name' => t('Link text')])); Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: // Verify that a link without link text is rendered using the URL as text. Chris@0: $value = 'http://www.example.com/'; Chris@0: $edit = [ Chris@0: "{$field_name}[0][uri]" => $value, Chris@0: "{$field_name}[0][title]" => '', Chris@0: ]; Chris@0: $this->drupalPostForm(NULL, $edit, t('Save')); Chris@0: preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match); Chris@0: $id = $match[1]; Chris@0: $this->assertText(t('entity_test @id has been created.', ['@id' => $id])); Chris@0: Chris@0: $output = $this->renderTestEntity($id); Chris@0: $expected_link = (string) \Drupal::l($value, Url::fromUri($value)); Chris@0: $this->assertContains($expected_link, $output); Chris@0: Chris@0: // Verify that a link with text is rendered using the link text. Chris@0: $title = $this->randomMachineName(); Chris@0: $edit = [ Chris@0: "{$field_name}[0][title]" => $title, Chris@0: ]; Chris@0: $this->drupalPostForm("entity_test/manage/$id/edit", $edit, t('Save')); Chris@0: $this->assertText(t('entity_test @id has been updated.', ['@id' => $id])); Chris@0: Chris@0: $output = $this->renderTestEntity($id); Chris@0: $expected_link = (string) \Drupal::l($title, Url::fromUri($value)); Chris@0: $this->assertContains($expected_link, $output); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests the default 'link' formatter. Chris@0: */ Chris@0: public function testLinkFormatter() { Chris@17: $field_name = mb_strtolower($this->randomMachineName()); Chris@0: // Create a field with settings to validate. Chris@0: $this->fieldStorage = FieldStorageConfig::create([ Chris@0: 'field_name' => $field_name, Chris@0: 'entity_type' => 'entity_test', Chris@0: 'type' => 'link', Chris@0: 'cardinality' => 3, Chris@0: ]); Chris@0: $this->fieldStorage->save(); Chris@0: FieldConfig::create([ Chris@0: 'field_storage' => $this->fieldStorage, Chris@0: 'label' => 'Read more about this entity', Chris@0: 'bundle' => 'entity_test', Chris@0: 'settings' => [ Chris@0: 'title' => DRUPAL_OPTIONAL, Chris@0: 'link_type' => LinkItemInterface::LINK_GENERIC, Chris@0: ], Chris@0: ])->save(); Chris@0: entity_get_form_display('entity_test', 'entity_test', 'default') Chris@0: ->setComponent($field_name, [ Chris@0: 'type' => 'link_default', Chris@0: ]) Chris@0: ->save(); Chris@0: $display_options = [ Chris@0: 'type' => 'link', Chris@0: 'label' => 'hidden', Chris@0: ]; Chris@0: entity_get_display('entity_test', 'entity_test', 'full') Chris@0: ->setComponent($field_name, $display_options) Chris@0: ->save(); Chris@0: Chris@0: // Create an entity with three link field values: Chris@0: // - The first field item uses a URL only. Chris@0: // - The second field item uses a URL and link text. Chris@0: // - The third field item uses a fragment-only URL with text. Chris@0: // For consistency in assertion code below, the URL is assigned to the title Chris@0: // variable for the first field. Chris@0: $this->drupalGet('entity_test/add'); Chris@0: $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com'; Chris@0: $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org'; Chris@0: $url3 = '#net'; Chris@0: $title1 = $url1; Chris@0: // Intentionally contains an ampersand that needs sanitization on output. Chris@0: $title2 = 'A very long & strange example title that could break the nice layout of the site'; Chris@0: $title3 = 'Fragment only'; Chris@0: $edit = [ Chris@0: "{$field_name}[0][uri]" => $url1, Chris@0: // Note that $title1 is not submitted. Chris@0: "{$field_name}[0][title]" => '', Chris@0: "{$field_name}[1][uri]" => $url2, Chris@0: "{$field_name}[1][title]" => $title2, Chris@0: "{$field_name}[2][uri]" => $url3, Chris@0: "{$field_name}[2][title]" => $title3, Chris@0: ]; Chris@0: // Assert label is shown. Chris@0: $this->assertText('Read more about this entity'); Chris@0: $this->drupalPostForm(NULL, $edit, t('Save')); Chris@0: preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match); Chris@0: $id = $match[1]; Chris@0: $this->assertText(t('entity_test @id has been created.', ['@id' => $id])); Chris@0: Chris@0: // Verify that the link is output according to the formatter settings. Chris@0: // Not using generatePermutations(), since that leads to 32 cases, which Chris@0: // would not test actual link field formatter functionality but rather Chris@0: // the link generator and options/attributes. Only 'url_plain' has a Chris@0: // dependency on 'url_only', so we have a total of ~10 cases. Chris@0: $options = [ Chris@0: 'trim_length' => [NULL, 6], Chris@0: 'rel' => [NULL, 'nofollow'], Chris@0: 'target' => [NULL, '_blank'], Chris@0: 'url_only' => [ Chris@0: ['url_only' => FALSE], Chris@0: ['url_only' => FALSE, 'url_plain' => TRUE], Chris@0: ['url_only' => TRUE], Chris@0: ['url_only' => TRUE, 'url_plain' => TRUE], Chris@0: ], Chris@0: ]; Chris@0: foreach ($options as $setting => $values) { Chris@0: foreach ($values as $new_value) { Chris@0: // Update the field formatter settings. Chris@0: if (!is_array($new_value)) { Chris@0: $display_options['settings'] = [$setting => $new_value]; Chris@0: } Chris@0: else { Chris@0: $display_options['settings'] = $new_value; Chris@0: } Chris@0: entity_get_display('entity_test', 'entity_test', 'full') Chris@0: ->setComponent($field_name, $display_options) Chris@0: ->save(); Chris@0: Chris@0: $output = $this->renderTestEntity($id); Chris@0: switch ($setting) { Chris@0: case 'trim_length': Chris@0: $url = $url1; Chris@0: $title = isset($new_value) ? Unicode::truncate($title1, $new_value, FALSE, TRUE) : $title1; Chris@0: $this->assertContains('' . Html::escape($title) . '', $output); Chris@0: Chris@0: $url = $url2; Chris@0: $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2; Chris@0: $this->assertContains('' . Html::escape($title) . '', $output); Chris@0: Chris@0: $url = $url3; Chris@0: $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3; Chris@0: $this->assertContains('' . Html::escape($title) . '', $output); Chris@0: break; Chris@0: Chris@0: case 'rel': Chris@0: $rel = isset($new_value) ? ' rel="' . $new_value . '"' : ''; Chris@0: $this->assertContains('' . Html::escape($title1) . '', $output); Chris@0: $this->assertContains('' . Html::escape($title2) . '', $output); Chris@0: $this->assertContains('' . Html::escape($title3) . '', $output); Chris@0: break; Chris@0: Chris@0: case 'target': Chris@0: $target = isset($new_value) ? ' target="' . $new_value . '"' : ''; Chris@0: $this->assertContains('' . Html::escape($title1) . '', $output); Chris@0: $this->assertContains('' . Html::escape($title2) . '', $output); Chris@0: $this->assertContains('' . Html::escape($title3) . '', $output); Chris@0: break; Chris@0: Chris@0: case 'url_only': Chris@0: // In this case, $new_value is an array. Chris@0: if (!$new_value['url_only']) { Chris@0: $this->assertContains('' . Html::escape($title1) . '', $output); Chris@0: $this->assertContains('' . Html::escape($title2) . '', $output); Chris@0: $this->assertContains('' . Html::escape($title3) . '', $output); Chris@0: } Chris@0: else { Chris@0: if (empty($new_value['url_plain'])) { Chris@0: $this->assertContains('' . Html::escape($url1) . '', $output); Chris@0: $this->assertContains('' . Html::escape($url2) . '', $output); Chris@0: $this->assertContains('' . Html::escape($url3) . '', $output); Chris@0: } Chris@0: else { Chris@0: $this->assertNotContains('' . Html::escape($url1) . '', $output); Chris@0: $this->assertNotContains('' . Html::escape($url2) . '', $output); Chris@0: $this->assertNotContains('' . Html::escape($url3) . '', $output); Chris@0: $this->assertContains(Html::escape($url1), $output); Chris@0: $this->assertContains(Html::escape($url2), $output); Chris@0: $this->assertContains(Html::escape($url3), $output); Chris@0: } Chris@0: } Chris@0: break; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests the 'link_separate' formatter. Chris@0: * Chris@0: * This test is mostly the same as testLinkFormatter(), but they cannot be Chris@0: * merged, since they involve different configuration and output. Chris@0: */ Chris@0: public function testLinkSeparateFormatter() { Chris@17: $field_name = mb_strtolower($this->randomMachineName()); Chris@0: // Create a field with settings to validate. Chris@0: $this->fieldStorage = FieldStorageConfig::create([ Chris@0: 'field_name' => $field_name, Chris@0: 'entity_type' => 'entity_test', Chris@0: 'type' => 'link', Chris@0: 'cardinality' => 3, Chris@0: ]); Chris@0: $this->fieldStorage->save(); Chris@0: FieldConfig::create([ Chris@0: 'field_storage' => $this->fieldStorage, Chris@0: 'bundle' => 'entity_test', Chris@0: 'settings' => [ Chris@0: 'title' => DRUPAL_OPTIONAL, Chris@0: 'link_type' => LinkItemInterface::LINK_GENERIC, Chris@0: ], Chris@0: ])->save(); Chris@0: $display_options = [ Chris@0: 'type' => 'link_separate', Chris@0: 'label' => 'hidden', Chris@0: ]; Chris@0: entity_get_form_display('entity_test', 'entity_test', 'default') Chris@0: ->setComponent($field_name, [ Chris@0: 'type' => 'link_default', Chris@0: ]) Chris@0: ->save(); Chris@0: entity_get_display('entity_test', 'entity_test', 'full') Chris@0: ->setComponent($field_name, $display_options) Chris@0: ->save(); Chris@0: Chris@0: // Create an entity with three link field values: Chris@0: // - The first field item uses a URL only. Chris@0: // - The second field item uses a URL and link text. Chris@0: // - The third field item uses a fragment-only URL with text. Chris@0: // For consistency in assertion code below, the URL is assigned to the title Chris@0: // variable for the first field. Chris@0: $this->drupalGet('entity_test/add'); Chris@0: $url1 = 'http://www.example.com/content/articles/archive?author=John&year=2012#com'; Chris@0: $url2 = 'http://www.example.org/content/articles/archive?author=John&year=2012#org'; Chris@0: $url3 = '#net'; Chris@0: // Intentionally contains an ampersand that needs sanitization on output. Chris@0: $title2 = 'A very long & strange example title that could break the nice layout of the site'; Chris@0: $title3 = 'Fragment only'; Chris@0: $edit = [ Chris@0: "{$field_name}[0][uri]" => $url1, Chris@0: "{$field_name}[1][uri]" => $url2, Chris@0: "{$field_name}[1][title]" => $title2, Chris@0: "{$field_name}[2][uri]" => $url3, Chris@0: "{$field_name}[2][title]" => $title3, Chris@0: ]; Chris@0: $this->drupalPostForm(NULL, $edit, t('Save')); Chris@0: preg_match('|entity_test/manage/(\d+)|', $this->getUrl(), $match); Chris@0: $id = $match[1]; Chris@0: $this->assertText(t('entity_test @id has been created.', ['@id' => $id])); Chris@0: Chris@0: // Verify that the link is output according to the formatter settings. Chris@0: $options = [ Chris@0: 'trim_length' => [NULL, 6], Chris@0: 'rel' => [NULL, 'nofollow'], Chris@0: 'target' => [NULL, '_blank'], Chris@0: ]; Chris@0: foreach ($options as $setting => $values) { Chris@0: foreach ($values as $new_value) { Chris@0: // Update the field formatter settings. Chris@0: $display_options['settings'] = [$setting => $new_value]; Chris@0: entity_get_display('entity_test', 'entity_test', 'full') Chris@0: ->setComponent($field_name, $display_options) Chris@0: ->save(); Chris@0: Chris@0: $output = $this->renderTestEntity($id); Chris@0: switch ($setting) { Chris@0: case 'trim_length': Chris@0: $url = $url1; Chris@0: $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url; Chris@0: $expected = ''; Chris@0: $this->assertContains($expected, $output); Chris@0: Chris@0: $url = $url2; Chris@0: $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url; Chris@0: $title = isset($new_value) ? Unicode::truncate($title2, $new_value, FALSE, TRUE) : $title2; Chris@0: $expected = ''; Chris@0: $this->assertContains($expected, $output); Chris@0: Chris@0: $url = $url3; Chris@0: $url_title = isset($new_value) ? Unicode::truncate($url, $new_value, FALSE, TRUE) : $url; Chris@0: $title = isset($new_value) ? Unicode::truncate($title3, $new_value, FALSE, TRUE) : $title3; Chris@0: $expected = ''; Chris@0: $this->assertContains($expected, $output); Chris@0: break; Chris@0: Chris@0: case 'rel': Chris@0: $rel = isset($new_value) ? ' rel="' . $new_value . '"' : ''; Chris@0: $this->assertContains('', $output); Chris@0: $this->assertContains('', $output); Chris@0: $this->assertContains('', $output); Chris@0: break; Chris@0: Chris@0: case 'target': Chris@0: $target = isset($new_value) ? ' target="' . $new_value . '"' : ''; Chris@0: $this->assertContains('', $output); Chris@0: $this->assertContains('', $output); Chris@0: $this->assertContains('', $output); Chris@0: break; Chris@0: } Chris@0: } Chris@0: } Chris@0: } Chris@0: Chris@0: /** Chris@0: * Test '#link_type' property exists on 'link_default' widget. Chris@0: * Chris@0: * Make sure the 'link_default' widget exposes a '#link_type' property on Chris@0: * its element. Modules can use it to understand if a text form element is Chris@0: * a link and also which LinkItemInterface::LINK_* is (EXTERNAL, GENERIC, Chris@0: * INTERNAL). Chris@0: */ Chris@0: public function testLinkTypeOnLinkWidget() { Chris@0: Chris@0: $link_type = LinkItemInterface::LINK_EXTERNAL; Chris@17: $field_name = mb_strtolower($this->randomMachineName()); Chris@0: Chris@0: // Create a field with settings to validate. Chris@0: $this->fieldStorage = FieldStorageConfig::create([ Chris@0: 'field_name' => $field_name, Chris@0: 'entity_type' => 'entity_test', Chris@0: 'type' => 'link', Chris@0: 'cardinality' => 1, Chris@0: ]); Chris@0: $this->fieldStorage->save(); Chris@0: FieldConfig::create([ Chris@0: 'field_storage' => $this->fieldStorage, Chris@0: 'label' => 'Read more about this entity', Chris@0: 'bundle' => 'entity_test', Chris@0: 'settings' => [ Chris@0: 'title' => DRUPAL_OPTIONAL, Chris@0: 'link_type' => $link_type, Chris@0: ], Chris@0: ])->save(); Chris@0: Chris@0: $this->container->get('entity.manager') Chris@0: ->getStorage('entity_form_display') Chris@0: ->load('entity_test.entity_test.default') Chris@0: ->setComponent($field_name, [ Chris@0: 'type' => 'link_default', Chris@0: ]) Chris@0: ->save(); Chris@0: Chris@0: $form = \Drupal::service('entity.form_builder')->getForm(EntityTest::create()); Chris@0: $this->assertEqual($form[$field_name]['widget'][0]['uri']['#link_type'], $link_type); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Tests editing a link to a non-node entity. Chris@0: */ Chris@0: public function testEditNonNodeEntityLink() { Chris@0: Chris@0: $entity_type_manager = \Drupal::entityTypeManager(); Chris@0: $entity_test_storage = $entity_type_manager->getStorage('entity_test'); Chris@0: Chris@0: // Create a field with settings to validate. Chris@0: $this->fieldStorage = FieldStorageConfig::create([ Chris@0: 'field_name' => 'field_link', Chris@0: 'entity_type' => 'entity_test', Chris@0: 'type' => 'link', Chris@0: 'cardinality' => 1, Chris@0: ]); Chris@0: $this->fieldStorage->save(); Chris@0: FieldConfig::create([ Chris@0: 'field_storage' => $this->fieldStorage, Chris@0: 'label' => 'Read more about this entity', Chris@0: 'bundle' => 'entity_test', Chris@0: 'settings' => [ Chris@0: 'title' => DRUPAL_OPTIONAL, Chris@0: ], Chris@0: ])->save(); Chris@0: Chris@0: $entity_type_manager Chris@0: ->getStorage('entity_form_display') Chris@0: ->load('entity_test.entity_test.default') Chris@0: ->setComponent('field_link', [ Chris@0: 'type' => 'link_default', Chris@0: ]) Chris@0: ->save(); Chris@0: Chris@0: // Create a node and a test entity to have a possibly valid reference for Chris@0: // both. Create another test entity that references the first test entity. Chris@0: $entity_test_link = $entity_test_storage->create(['name' => 'correct link target']); Chris@0: $entity_test_link->save(); Chris@0: Chris@0: $node = $this->drupalCreateNode(['wrong link target']); Chris@0: Chris@0: $correct_link = 'entity:entity_test/' . $entity_test_link->id(); Chris@0: $entity_test = $entity_test_storage->create([ Chris@0: 'name' => 'correct link target', Chris@0: 'field_link' => $correct_link, Chris@0: ]); Chris@0: $entity_test->save(); Chris@0: Chris@0: // Edit the entity and save it, verify the correct link is kept and not Chris@0: // changed to point to a node. Currently, widget does not support non-node Chris@0: // autocomplete and therefore must show the link unaltered. Chris@0: $this->drupalGet($entity_test->toUrl('edit-form')); Chris@0: $this->assertSession()->fieldValueEquals('field_link[0][uri]', $correct_link); Chris@0: $this->drupalPostForm(NULL, [], 'Save'); Chris@0: Chris@0: $entity_test_storage->resetCache(); Chris@0: $entity_test = $entity_test_storage->load($entity_test->id()); Chris@0: Chris@0: $this->assertEquals($correct_link, $entity_test->get('field_link')->uri); Chris@0: } Chris@0: Chris@0: /** Chris@0: * Renders a test_entity and returns the output. Chris@0: * Chris@0: * @param int $id Chris@0: * The test_entity ID to render. Chris@0: * @param string $view_mode Chris@0: * (optional) The view mode to use for rendering. Chris@0: * @param bool $reset Chris@0: * (optional) Whether to reset the entity_test storage cache. Defaults to Chris@0: * TRUE to simplify testing. Chris@0: * Chris@0: * @return string Chris@0: * The rendered HTML output. Chris@0: */ Chris@0: protected function renderTestEntity($id, $view_mode = 'full', $reset = TRUE) { Chris@0: if ($reset) { Chris@0: $this->container->get('entity.manager')->getStorage('entity_test')->resetCache([$id]); Chris@0: } Chris@0: $entity = EntityTest::load($id); Chris@0: $display = entity_get_display($entity->getEntityTypeId(), $entity->bundle(), $view_mode); Chris@0: $content = $display->build($entity); Chris@0: $output = \Drupal::service('renderer')->renderRoot($content); Chris@0: $output = (string) $output; Chris@0: $this->verbose($output); Chris@0: return $output; Chris@0: } Chris@0: Chris@0: }