search_api-8.x-1.15/tests/src/Functional/OverviewPageTest.php
tests/src/Functional/OverviewPageTest.php
<?php
namespace Drupal\Tests\search_api\Functional;
use Drupal\block\Entity\Block;
use Drupal\Component\Utility\Html;
use Drupal\Core\Url;
use Drupal\search_api\Entity\Index;
use Drupal\search_api\ServerInterface;
/**
* Tests the Search API overview page.
*
* @group search_api
*/
class OverviewPageTest extends SearchApiBrowserTestBase {
/**
* {@inheritdoc}
*/
public static $modules = [
'block',
];
/**
* The path of the overview page.
*
* @var string
*/
protected $overviewPageUrl;
/**
* {@inheritdoc}
*/
public function setUp() {
parent::setUp();
$this->drupalLogin($this->adminUser);
$this->overviewPageUrl = 'admin/config/search/search-api';
}
/**
* Tests various scenarios for the overview page.
*
* Uses a single method to save time.
*/
public function testOverviewPage() {
$this->checkServerAndIndexCreation();
$this->checkServerAndIndexStatusChanges();
$this->checkOperations();
$this->checkOverviewPermissions();
}
/**
* Tests the creation of a server and an index.
*/
protected function checkServerAndIndexCreation() {
$server_name = 'WebTest server';
$index_name = 'WebTest index';
$actions = [
[Url::fromRoute('entity.search_api_server.add_form'), 'Add server'],
[Url::fromRoute('entity.search_api_index.add_form'), 'Add index'],
];
// Enable the "Local actions" block so we can verify which local actions are
// displayed.
Block::create([
'id' => 'classy_local_actions',
'theme' => 'classy',
'weight' => -20,
'plugin' => 'local_actions_block',
'region' => 'content',
])->save();
// Make sure the overview is empty.
$this->drupalGet($this->overviewPageUrl);
$this->assertLocalAction($actions);
$this->assertSession()->pageTextNotContains($server_name);
$this->assertSession()->pageTextNotContains($index_name);
// Test whether a newly created server appears on the overview page.
$server = $this->getTestServer();
$this->drupalGet($this->overviewPageUrl);
$this->assertSession()->pageTextContains($server_name);
$this->assertSession()->responseContains($server->get('description'));
$server_class = Html::cleanCssIdentifier($server->getEntityTypeId() . '-' . $server->id());
$servers = $this->xpath('//tr[contains(@class,"' . $server_class . '") and contains(@class, "search-api-list-enabled")]');
$this->assertNotEmpty($servers, 'Server is in proper table');
// Test whether a newly created index appears on the overview page.
$index = $this->getTestIndex();
$this->drupalGet($this->overviewPageUrl);
$this->assertSession()->pageTextContains($index_name);
$this->assertSession()->responseContains($index->get('description'));
$index_class = Html::cleanCssIdentifier($index->getEntityTypeId() . '-' . $index->id());
$fields = $this->xpath('//tr[contains(@class,"' . $index_class . '") and contains(@class, "search-api-list-enabled")]');
$this->assertNotEmpty($fields, 'Index is in proper table');
$this->assertSession()->linkNotExists('Execute pending tasks', 'No pending tasks to execute.');
// Tests that the "Execute pending tasks" local action is correctly
// displayed when there are pending tasks.
\Drupal::getContainer()
->get('search_api.task_manager')
->addTask('deleteItems', $server, $index, ['']);
// Due to an (apparent) Core bug we need to clear the cache, otherwise the
// "local actions" block gets displayed from cache (without the link). See
// #2722237.
\Drupal::cache('render')->invalidateAll();
$this->drupalGet($this->overviewPageUrl);
$this->assertSession()->linkExists('Execute pending tasks', 0);
}
/**
* Tests enable/disable operations for servers and indexes through the UI.
*/
protected function checkServerAndIndexStatusChanges() {
$server = $this->getTestServer();
$this->assertEntityStatusChange($server);
// Re-create the index for this test.
$this->getTestIndex()->delete();
$index = $this->getTestIndex();
$this->assertEntityStatusChange($index);
// Disable the server and test that both itself and the index have been
// disabled.
$server->setStatus(FALSE)->save();
$this->drupalGet($this->overviewPageUrl);
$server_class = Html::cleanCssIdentifier($server->getEntityTypeId() . '-' . $server->id());
$index_class = Html::cleanCssIdentifier($index->getEntityTypeId() . '-' . $index->id());
$servers = $this->xpath('//tr[contains(@class,"' . $server_class . '") and contains(@class, "search-api-list-disabled")]');
$this->assertNotEmpty($servers, 'The server has been disabled.');
$indexes = $this->xpath('//tr[contains(@class,"' . $index_class . '") and contains(@class, "search-api-list-disabled")]');
$this->assertNotEmpty($indexes, 'The index has been disabled.');
// Test that an index can't be enabled if its server is disabled.
// @todo A non-working "Enable" link should not be displayed at all.
$this->clickLink('Enable', 1);
$this->drupalGet($this->overviewPageUrl);
$indexes = $this->xpath('//tr[contains(@class,"' . $index_class . '") and contains(@class, "search-api-list-disabled")]');
$this->assertNotEmpty($indexes, 'The index could not be enabled.');
// Enable the server and try again.
$server->setStatus(TRUE)->save();
$this->drupalGet($this->overviewPageUrl);
// This time the server is enabled so the first 'enable' link belongs to the
// index.
$this->clickLink('Enable');
$this->drupalGet($this->overviewPageUrl);
$indexes = $this->xpath('//tr[contains(@class,"' . $index_class . '") and contains(@class, "search-api-list-enabled")]');
$this->assertNotEmpty($indexes, 'The index has been enabled.');
// Create a new index without a server assigned and test that it can't be
// enabled. The overview UI is not very consistent at the moment, so test
// using API functions for now.
$index2 = Index::create([
'id' => 'test_index_2',
'name' => 'WebTest index 2',
'datasource_settings' => [
'entity:node' => [],
],
]);
$index2->save();
$this->assertFalse($index2->status(), 'The newly created index without a server is disabled by default.');
$index2->setStatus(TRUE)->save();
$this->assertFalse($index2->status(), 'The newly created index without a server cannot be enabled.');
}
/**
* Asserts enable/disable operations for a search server or index.
*
* @param \Drupal\search_api\ServerInterface|\Drupal\search_api\IndexInterface $entity
* A search server or index.
*/
protected function assertEntityStatusChange($entity) {
$this->drupalGet($this->overviewPageUrl);
$row_class = Html::cleanCssIdentifier($entity->getEntityTypeId() . '-' . $entity->id());
$rows = $this->xpath('//tr[contains(@class,"' . $row_class . '") and contains(@class, "search-api-list-enabled")]');
$this->assertNotEmpty($rows, 'The newly created entity is enabled by default.');
// The first "Disable" link on the page belongs to our server, the second
// one to our index.
$this->clickLink('Disable', $entity instanceof ServerInterface ? 0 : 1);
// Submit the confirmation form and test that the entity has been disabled.
$this->submitForm([], 'Disable');
$rows = $this->xpath('//tr[contains(@class,"' . $row_class . '") and contains(@class, "search-api-list-disabled")]');
$this->assertNotEmpty($rows, 'The entity has been disabled.');
// Now enable the entity and verify that the operation succeeded.
$this->clickLink('Enable');
$this->drupalGet($this->overviewPageUrl);
$rows = $this->xpath('//tr[contains(@class,"' . $row_class . '") and contains(@class, "search-api-list-enabled")]');
$this->assertNotEmpty($rows, 'The entity has benn enabled.');
}
/**
* Tests server operations in the overview page.
*/
protected function checkOperations() {
$server = $this->getTestServer();
$this->drupalGet($this->overviewPageUrl);
$basic_url = $this->urlGenerator->generateFromRoute('entity.search_api_server.canonical', ['search_api_server' => $server->id()]);
$destination = "?destination=" . $this->urlGenerator->generateFromRoute('search_api.overview');
$this->assertSession()->responseContains("<a href=\"$basic_url/edit$destination\">Edit</a>");
$this->assertSession()->responseContains("<a href=\"$basic_url/disable$destination\">Disable</a>");
$this->assertSession()->responseContains("<a href=\"$basic_url/delete$destination\">Delete</a>");
$this->assertSession()->responseNotContains("<a href=\"$basic_url/enable$destination\">Enable</a>");
$server->setStatus(FALSE)->save();
$this->drupalGet($this->overviewPageUrl);
// Since \Drupal\Core\Access\CsrfTokenGenerator uses the current session ID,
// we cannot verify the validity of the token from here.
$params = $destination ? "$destination&token=" : '?token=';
$this->assertSession()->responseContains("<a href=\"$basic_url/enable$params");
$this->assertSession()->responseNotContains("<a href=\"$basic_url/disable$destination\">Disable</a>");
}
/**
* Tests that the overview has the correct permissions set.
*/
protected function checkOverviewPermissions() {
$this->drupalGet('admin/config');
$this->assertSession()->pageTextContains('Search API');
$this->drupalGet($this->overviewPageUrl);
$this->assertSession()->statusCodeEquals(200);
$this->drupalLogin($this->unauthorizedUser);
$this->drupalGet($this->overviewPageUrl);
$this->assertSession()->statusCodeEquals(403);
}
/**
* Asserts local actions in the page output.
*
* @param array $actions
* A list of expected action link titles, keyed by the hrefs.
*/
protected function assertLocalAction(array $actions) {
$elements = $this->xpath('//a[contains(@class, :class)]', [
':class' => 'button-action',
]);
$index = 0;
foreach ($actions as $action) {
/** @var \Drupal\Core\Url $url */
list($url, $title) = $action;
// SimpleXML gives us the unescaped text, not the actual escaped markup,
// so use a pattern instead to check the raw content.
// This behaviour is a bug in libxml, see
// https://bugs.php.net/bug.php?id=49437.
$this->assertSession()->responseMatches('@<a [^>]*class="[^"]*button-action[^"]*"[^>]*>' . preg_quote($title, '@') . '</@');
$this->assertEquals($url->toString(), $elements[$index++]->getAttribute('href'));
}
}
}
