outline-8.x-1.x-dev/tests/src/Functional/EntryAutocompleteTest.php
tests/src/Functional/EntryAutocompleteTest.php
<?php
namespace Drupal\Tests\outline\Functional;
use Drupal\Component\Serialization\Json;
use Drupal\Core\Entity\Entity\EntityFormDisplay;
use Drupal\Core\Entity\Entity\EntityViewDisplay;
use Drupal\Core\Field\FieldStorageDefinitionInterface;
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
/**
* Tests the autocomplete implementation of the outline class.
*
* @group outline
*/
class EntryAutocompleteTest extends OutlineTestBase {
/**
* Modules to enable.
*
* @var array
*/
protected static $modules = ['node'];
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'stark';
/**
* The outline.
*
* @var \Drupal\outline\Entity\Outline
*/
protected $outline;
/**
* The field to add to the content type for the outline entries.
*
* @var string
*/
protected $fieldName;
/**
* The admin user.
*
* @var \Drupal\user\Entity\User
*/
protected $adminUser;
/**
* The autocomplete URL to call.
*
* @var string
*/
protected $autocompleteUrl;
/**
* The entry IDs indexed by entry names.
*
* @var array
*/
protected $entryIds;
/**
* {@inheritdoc}
*/
protected function setUp(): void {
parent::setUp();
// Create a outline.
$this->outline = $this->createOutline();
// Create 11 entries, 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 entry IDs to reuse later.
$entryNames = [
'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 ($entryNames as $entryName) {
$entry = $this->createEntry($this->outline, ['name' => $entryName]);
$this->entryIds[$entryName] = $entry->id();
}
// Create a outline_entry_reference field on the article Content Type that
// uses a outline_autocomplete widget.
$this->fieldName = mb_strtolower($this->randomMachineName());
FieldStorageConfig::create([
'field_name' => $this->fieldName,
'entity_type' => 'node',
'type' => 'entity_reference',
'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
'settings' => [
'target_type' => 'outline_entry',
],
])->save();
FieldConfig::create([
'field_name' => $this->fieldName,
'bundle' => 'article',
'entity_type' => 'node',
'settings' => [
'handler' => 'default',
'handler_settings' => [
// Restrict selection of entries to a single outline.
'target_bundles' => [
$this->outline->id() => $this->outline->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]->getAttribute('data-autocomplete-path'));
}
/**
* Helper function for JSON formatted requests.
*
* @param string|\Drupal\Core\Url $path
* Drupal path or URL to load into Mink controlled browser.
* @param array $options
* (optional) Options to be forwarded to the url generator.
* @param string[] $headers
* (optional) An array containing additional HTTP request headers.
*
* @return string[]
* Array representing decoded JSON response.
*/
protected function drupalGetJson($path, array $options = [], array $headers = []) {
$options = array_merge_recursive(['query' => ['_format' => 'json']], $options);
return Json::decode($this->drupalGet($path, $options, $headers));
}
/**
* Tests that the autocomplete method returns the good number of results.
*
* @see \Drupal\outline\Controller\EntryAutocompleteController::autocomplete()
*/
public function testAutocompleteCountResults() {
// Test that no matching entry found.
$data = $this->drupalGetJson(
$this->autocompleteUrl,
['query' => ['q' => 'zzz']]
);
$this->assertTrue(empty($data), 'Autocomplete returned no results');
// Test that only one matching entry found, when only one matches.
$data = $this->drupalGetJson(
$this->autocompleteUrl,
['query' => ['q' => 'aaa 10']]
);
$this->assertCount(1, $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->assertCount(3, $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->assertCount(10, $data, 'Autocomplete returned only 10 results (for over 10 matches)');
}
/**
* Tests that the autocomplete method returns properly ordered results.
*
* @see \Drupal\outline\Controller\EntryAutocompleteController::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 $entryName) {
$expected[] = [
'value' => $entryName . ' (' . $this->entryIds[$entryName] . ')',
'label' => $entryName,
];
}
$data = $this->drupalGetJson(
$this->autocompleteUrl,
['query' => ['q' => 'bbb']]
);
$this->assertIdentical($expected, $data);
}
}
