3 namespace Drupal\KernelTests\Core\Entity;
5 use Drupal\Component\Serialization\Json;
6 use Drupal\Component\Utility\Crypt;
7 use Drupal\Component\Utility\Html;
8 use Drupal\Component\Utility\Tags;
9 use Drupal\Core\Site\Settings;
10 use Drupal\system\Controller\EntityAutocompleteController;
11 use Symfony\Component\HttpFoundation\Request;
12 use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
15 * Tests the autocomplete functionality.
19 class EntityAutocompleteTest extends EntityKernelTestBase {
22 * The entity type used in this test.
26 protected $entityType = 'entity_test';
29 * The bundle used in this test.
33 protected $bundle = 'entity_test';
38 protected function setUp() {
40 $this->installSchema('system', ['key_value']);
44 * Tests autocompletion edge cases with slashes in the names.
46 public function testEntityReferenceAutocompletion() {
47 // Add an entity with a slash in its name.
48 $entity_1 = $this->container->get('entity_type.manager')
49 ->getStorage($this->entityType)
50 ->create(['name' => '10/16/2011']);
53 // Add another entity that differs after the slash character.
54 $entity_2 = $this->container->get('entity_type.manager')
55 ->getStorage($this->entityType)
56 ->create(['name' => '10/17/2011']);
59 // Add another entity that has both a comma, a slash and markup.
60 $entity_3 = $this->container->get('entity_type.manager')
61 ->getStorage($this->entityType)
62 ->create(['name' => 'label with, and / test']);
65 // Try to autocomplete a entity label that matches both entities.
66 // We should get both entities in a JSON encoded string.
68 $data = $this->getAutocompleteResult($input);
69 $this->assertIdentical($data[0]['label'], Html::escape($entity_1->name->value), 'Autocomplete returned the first matching entity');
70 $this->assertIdentical($data[1]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity');
72 // Try to autocomplete a entity label that matches the first entity.
73 // We should only get the first entity in a JSON encoded string.
75 $data = $this->getAutocompleteResult($input);
77 'value' => $entity_1->name->value . ' (1)',
78 'label' => Html::escape($entity_1->name->value),
80 $this->assertIdentical(reset($data), $target, 'Autocomplete returns only the expected matching entity.');
82 // Try to autocomplete a entity label that matches the second entity, and
83 // the first entity is already typed in the autocomplete (tags) widget.
84 $input = $entity_1->name->value . ' (1), 10/17';
85 $data = $this->getAutocompleteResult($input);
86 $this->assertIdentical($data[0]['label'], Html::escape($entity_2->name->value), 'Autocomplete returned the second matching entity');
88 // Try to autocomplete a entity label with both a comma, a slash and markup.
89 $input = '"label with, and /"';
90 $data = $this->getAutocompleteResult($input);
91 $n = $entity_3->name->value . ' (3)';
92 // Entity labels containing commas or quotes must be wrapped in quotes.
93 $n = Tags::encode($n);
96 'label' => Html::escape($entity_3->name->value),
98 $this->assertIdentical(reset($data), $target, 'Autocomplete returns an entity label containing a comma and a slash.');
102 * Tests that missing or invalid selection setting key are handled correctly.
104 public function testSelectionSettingsHandling() {
105 $entity_reference_controller = EntityAutocompleteController::create($this->container);
106 $request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default');
107 $request->query->set('q', $this->randomString());
110 // Pass an invalid selection settings key (i.e. one that does not exist
111 // in the key/value store).
112 $selection_settings_key = $this->randomString();
113 $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key);
115 $this->fail('Non-existent selection settings key throws an exception.');
117 catch (AccessDeniedHttpException $e) {
118 $this->pass('Non-existent selection settings key throws an exception.');
122 // Generate a valid hash key but store a modified settings array.
123 $selection_settings = [];
124 $selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt());
126 $selection_settings[$this->randomMachineName()] = $this->randomString();
127 \Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings);
129 $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key);
131 catch (AccessDeniedHttpException $e) {
132 if ($e->getMessage() == 'Invalid selection settings key.') {
133 $this->pass('Invalid selection settings key throws an exception.');
136 $this->fail('Invalid selection settings key throws an exception.');
143 * Returns the result of an Entity reference autocomplete request.
145 * @param string $input
146 * The label of the entity to query by.
149 * The JSON value encoded in its appropriate PHP type.
151 protected function getAutocompleteResult($input) {
152 $request = Request::create('entity_reference_autocomplete/' . $this->entityType . '/default');
153 $request->query->set('q', $input);
155 $selection_settings = [];
156 $selection_settings_key = Crypt::hmacBase64(serialize($selection_settings) . $this->entityType . 'default', Settings::getHashSalt());
157 \Drupal::keyValue('entity_autocomplete')->set($selection_settings_key, $selection_settings);
159 $entity_reference_controller = EntityAutocompleteController::create($this->container);
160 $result = $entity_reference_controller->handleAutocomplete($request, $this->entityType, 'default', $selection_settings_key)->getContent();
162 return Json::decode($result);