diff --git a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php index 3b1c2368d5ec9f53b0ab68ba8a2921b0aee0f9d0..7dd4aa2d6e57d68e85c4c5b6b6d8a414b2677d67 100644 --- a/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php +++ b/core/modules/taxonomy/src/Plugin/EntityReferenceSelection/TermSelection.php @@ -49,6 +49,7 @@ public function buildConfigurationForm(array $form, FormStateInterface $form_sta */ public function getReferenceableEntities($match = NULL, $match_operator = 'CONTAINS', $limit = 0) { if ($match || $limit) { + $this->configuration['handler_settings']['sort'] = ['field' => 'name', 'direction' => 'asc']; return parent::getReferenceableEntities($match, $match_operator, $limit); } diff --git a/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php b/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php new file mode 100644 index 0000000000000000000000000000000000000000..7ff519df94034f6c810eafde6dfb1ca18c9315fa --- /dev/null +++ b/core/modules/taxonomy/src/Tests/TermAutocompleteTest.php @@ -0,0 +1,204 @@ +vocabulary = $this->createVocabulary(); + + // Create 11 terms, which have some sub-string in common, in a + // non-alphabetical order, so that we will have more than 10 matches later + // when we test the correct number of results is returned, and we can test + // the order of the results. The location of the sub-string to match varies + // also, since it should not be necessary to start with the sub-string to + // match it. Save term IDs to reuse later. + $termNames = [ + 'aaa 20 bbb', + 'aaa 70 bbb', + 'aaa 10 bbb', + 'aaa 12 bbb', + 'aaa 40 bbb', + 'aaa 11 bbb', + 'aaa 30 bbb', + 'aaa 50 bbb', + 'aaa 80', + 'aaa 90', + 'bbb 60 aaa', + ]; + foreach ($termNames as $termName) { + $term = $this->createTerm($this->vocabulary, ['name' => $termName]); + $this->termIds[$termName] = $term->id(); + } + + // Create a taxonomy_term_reference field on the article Content Type that + // uses a taxonomy_autocomplete widget. + $this->fieldName = Unicode::strtolower($this->randomMachineName()); + FieldStorageConfig::create([ + 'field_name' => $this->fieldName, + 'entity_type' => 'node', + 'type' => 'entity_reference', + 'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED, + 'settings' => [ + 'target_type' => 'taxonomy_term', + ], + ])->save(); + FieldConfig::create([ + 'field_name' => $this->fieldName, + 'bundle' => 'article', + 'entity_type' => 'node', + 'settings' => [ + 'handler' => 'default', + 'handler_settings' => [ + // Restrict selection of terms to a single vocabulary. + 'target_bundles' => [ + $this->vocabulary->id() => $this->vocabulary->id(), + ], + ], + ], + ])->save(); + EntityFormDisplay::load('node.article.default') + ->setComponent($this->fieldName, [ + 'type' => 'entity_reference_autocomplete', + ]) + ->save(); + EntityViewDisplay::load('node.article.default') + ->setComponent($this->fieldName, [ + 'type' => 'entity_reference_label', + ]) + ->save(); + + // Create a user and then login. + $this->adminUser = $this->drupalCreateUser(['create article content']); + $this->drupalLogin($this->adminUser); + + // Retrieve the autocomplete url. + $this->drupalGet('node/add/article'); + $result = $this->xpath('//input[@name="' . $this->fieldName . '[0][target_id]"]'); + $this->autocompleteUrl = $this->getAbsoluteUrl($result[0]['data-autocomplete-path']); + } + + /** + * Tests that the autocomplete method returns the good number of results. + * + * @see \Drupal\taxonomy\Controller\TermAutocompleteController::autocomplete() + */ + public function testAutocompleteCountResults() { + // Test that no matching term found. + $data = $this->drupalGetJSON( + $this->autocompleteUrl, + ['query' => ['q' => 'zzz']] + ); + $this->assertTrue(empty($data), 'Autocomplete returned no results'); + + // Test that only one matching term found, when only one matches. + $data = $this->drupalGetJSON( + $this->autocompleteUrl, + ['query' => ['q' => 'aaa 10']] + ); + $this->assertEqual(1, count($data), 'Autocomplete returned 1 result'); + + // Test the correct number of matches when multiple are partial matches. + $data = $this->drupalGetJSON( + $this->autocompleteUrl, + ['query' => ['q' => 'aaa 1']] + ); + $this->assertEqual(3, count($data), 'Autocomplete returned 3 results'); + + // Tests that only 10 results are returned, even if there are more than 10 + // matches. + $data = $this->drupalGetJSON( + $this->autocompleteUrl, + ['query' => ['q' => 'aaa']] + ); + $this->assertEqual(10, count($data), 'Autocomplete returned only 10 results (for over 10 matches)'); + } + + /** + * Tests that the autocomplete method returns properly ordered results. + * + * @see \Drupal\taxonomy\Controller\TermAutocompleteController::autocomplete() + */ + public function testAutocompleteOrderedResults() { + $expectedResults = [ + 'aaa 10 bbb', + 'aaa 11 bbb', + 'aaa 12 bbb', + 'aaa 20 bbb', + 'aaa 30 bbb', + 'aaa 40 bbb', + 'aaa 50 bbb', + 'aaa 70 bbb', + 'bbb 60 aaa', + ]; + // Build $expected to match the autocomplete results. + $expected = []; + foreach ($expectedResults as $termName) { + $expected[] = [ + 'value' => $termName . ' (' . $this->termIds[$termName] . ')', + 'label' => $termName + ]; + } + + $data = $this->drupalGetJSON( + $this->autocompleteUrl, + ['query' => ['q' => 'bbb']] + ); + + $this->assertIdentical($expected, $data); + } + +}