class ContextualDynamicContextTest
Same name and namespace in other branches
- 11.x core/modules/contextual/tests/src/Functional/ContextualDynamicContextTest.php \Drupal\Tests\contextual\Functional\ContextualDynamicContextTest
- 9 core/modules/contextual/tests/src/Functional/ContextualDynamicContextTest.php \Drupal\Tests\contextual\Functional\ContextualDynamicContextTest
- 10 core/modules/contextual/tests/src/Functional/ContextualDynamicContextTest.php \Drupal\Tests\contextual\Functional\ContextualDynamicContextTest
Tests if contextual links are showing on the front page depending on permissions.
@group contextual
Hierarchy
- class \Drupal\Tests\BrowserTestBase uses \Drupal\Core\Test\FunctionalTestSetupTrait, \Drupal\Tests\UiHelperTrait, \Drupal\Core\Test\TestSetupTrait, \Drupal\Tests\block\Traits\BlockCreationTrait, \Drupal\FunctionalTests\AssertLegacyTrait, \Drupal\Tests\RandomGeneratorTrait, \Drupal\Tests\node\Traits\NodeCreationTrait, \Drupal\Tests\node\Traits\ContentTypeCreationTrait, \Drupal\Tests\ConfigTestTrait, \Drupal\Tests\TestRequirementsTrait, \Drupal\Tests\user\Traits\UserCreationTrait, \Drupal\Tests\XdebugRequestTrait, \Drupal\Tests\PhpunitCompatibilityTrait extends \PHPUnit\Framework\TestCase
- class \Drupal\Tests\contextual\Functional\ContextualDynamicContextTest extends \Drupal\Tests\BrowserTestBase
Expanded class hierarchy of ContextualDynamicContextTest
File
-
core/
modules/ contextual/ tests/ src/ Functional/ ContextualDynamicContextTest.php, line 18
Namespace
Drupal\Tests\contextual\FunctionalView source
class ContextualDynamicContextTest extends BrowserTestBase {
/**
* {@inheritdoc}
*/
protected $defaultTheme = 'classy';
/**
* A user with permission to access contextual links and edit content.
*
* @var \Drupal\user\UserInterface
*/
protected $editorUser;
/**
* An authenticated user with permission to access contextual links.
*
* @var \Drupal\user\UserInterface
*/
protected $authenticatedUser;
/**
* A simulated anonymous user with access only to node content.
*
* @var \Drupal\user\UserInterface
*/
protected $anonymousUser;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = [
'contextual',
'node',
'views',
'views_ui',
'language',
'menu_test',
];
protected function setUp() {
parent::setUp();
$this->drupalCreateContentType([
'type' => 'page',
'name' => 'Basic page',
]);
$this->drupalCreateContentType([
'type' => 'article',
'name' => 'Article',
]);
ConfigurableLanguage::createFromLangcode('it')->save();
$this->rebuildContainer();
$this->editorUser = $this->drupalCreateUser([
'access content',
'access contextual links',
'edit any article content',
]);
$this->authenticatedUser = $this->drupalCreateUser([
'access content',
'access contextual links',
]);
$this->anonymousUser = $this->drupalCreateUser([
'access content',
]);
}
/**
* Tests contextual links with different permissions.
*
* Ensures that contextual link placeholders always exist, even if the user is
* not allowed to use contextual links.
*/
public function testDifferentPermissions() {
$this->drupalLogin($this->editorUser);
// Create three nodes in the following order:
// - An article, which should be user-editable.
// - A page, which should not be user-editable.
// - A second article, which should also be user-editable.
$node1 = $this->drupalCreateNode([
'type' => 'article',
'promote' => 1,
]);
$node2 = $this->drupalCreateNode([
'type' => 'page',
'promote' => 1,
]);
$node3 = $this->drupalCreateNode([
'type' => 'article',
'promote' => 1,
]);
// Now, on the front page, all article nodes should have contextual links
// placeholders, as should the view that contains them.
$ids = [
'node:node=' . $node1->id() . ':changed=' . $node1->getChangedTime() . '&langcode=en',
'node:node=' . $node2->id() . ':changed=' . $node2->getChangedTime() . '&langcode=en',
'node:node=' . $node3->id() . ':changed=' . $node3->getChangedTime() . '&langcode=en',
'entity.view.edit_form:view=frontpage:location=page&name=frontpage&display_id=page_1&langcode=en',
];
// Editor user: can access contextual links and can edit articles.
$this->drupalGet('node');
for ($i = 0; $i < count($ids); $i++) {
$this->assertContextualLinkPlaceHolder($ids[$i]);
}
$response = $this->renderContextualLinks([], 'node');
$this->assertSame(400, $response->getStatusCode());
$this->assertStringContainsString('No contextual ids specified.', (string) $response->getBody());
$response = $this->renderContextualLinks($ids, 'node');
$this->assertSame(200, $response->getStatusCode());
$json = Json::decode((string) $response->getBody());
$this->assertIdentical($json[$ids[0]], '<ul class="contextual-links"><li class="entitynodeedit-form"><a href="' . base_path() . 'node/1/edit">Edit</a></li></ul>');
$this->assertIdentical($json[$ids[1]], '');
$this->assertIdentical($json[$ids[2]], '<ul class="contextual-links"><li class="entitynodeedit-form"><a href="' . base_path() . 'node/3/edit">Edit</a></li></ul>');
$this->assertIdentical($json[$ids[3]], '');
// Verify that link language is properly handled.
$node3->addTranslation('it')
->set('title', $this->randomString())
->save();
$id = 'node:node=' . $node3->id() . ':changed=' . $node3->getChangedTime() . '&langcode=it';
$this->drupalGet('node', [
'language' => ConfigurableLanguage::createFromLangcode('it'),
]);
$this->assertContextualLinkPlaceHolder($id);
// Authenticated user: can access contextual links, cannot edit articles.
$this->drupalLogin($this->authenticatedUser);
$this->drupalGet('node');
for ($i = 0; $i < count($ids); $i++) {
$this->assertContextualLinkPlaceHolder($ids[$i]);
}
$response = $this->renderContextualLinks([], 'node');
$this->assertSame(400, $response->getStatusCode());
$this->assertStringContainsString('No contextual ids specified.', (string) $response->getBody());
$response = $this->renderContextualLinks($ids, 'node');
$this->assertSame(200, $response->getStatusCode());
$json = Json::decode((string) $response->getBody());
$this->assertIdentical($json[$ids[0]], '');
$this->assertIdentical($json[$ids[1]], '');
$this->assertIdentical($json[$ids[2]], '');
$this->assertIdentical($json[$ids[3]], '');
// Anonymous user: cannot access contextual links.
$this->drupalLogin($this->anonymousUser);
$this->drupalGet('node');
for ($i = 0; $i < count($ids); $i++) {
$this->assertNoContextualLinkPlaceHolder($ids[$i]);
}
$response = $this->renderContextualLinks([], 'node');
$this->assertSame(403, $response->getStatusCode());
$this->renderContextualLinks($ids, 'node');
$this->assertSame(403, $response->getStatusCode());
// Get a page where contextual links are directly rendered.
$this->drupalGet(Url::fromRoute('menu_test.contextual_test'));
$this->assertEscaped("<script>alert('Welcome to the jungle!')</script>");
$this->assertRaw('<li class="menu-testcontextual-hidden-manage-edit"><a href="' . base_path() . 'menu-test-contextual/1/edit" class="use-ajax" data-dialog-type="modal" data-is-something>Edit menu - contextual</a></li>');
}
/**
* Tests the contextual placeholder content is protected by a token.
*/
public function testTokenProtection() {
$this->drupalLogin($this->editorUser);
// Create a node that will have a contextual link.
$node1 = $this->drupalCreateNode([
'type' => 'article',
'promote' => 1,
]);
// Now, on the front page, all article nodes should have contextual links
// placeholders, as should the view that contains them.
$id = 'node:node=' . $node1->id() . ':changed=' . $node1->getChangedTime() . '&langcode=en';
// Editor user: can access contextual links and can edit articles.
$this->drupalGet('node');
$this->assertContextualLinkPlaceHolder($id);
$http_client = $this->getHttpClient();
$url = Url::fromRoute('contextual.render', [], [
'query' => [
'_format' => 'json',
'destination' => 'node',
],
])->setAbsolute()
->toString();
$response = $http_client->request('POST', $url, [
'cookies' => $this->getSessionCookies(),
'form_params' => [
'ids' => [
$id,
],
'tokens' => [],
],
'http_errors' => FALSE,
]);
$this->assertEquals('400', $response->getStatusCode());
$this->assertStringContainsString('No contextual ID tokens specified.', (string) $response->getBody());
$response = $http_client->request('POST', $url, [
'cookies' => $this->getSessionCookies(),
'form_params' => [
'ids' => [
$id,
],
'tokens' => [
'wrong_token',
],
],
'http_errors' => FALSE,
]);
$this->assertEquals('400', $response->getStatusCode());
$this->assertStringContainsString('Invalid contextual ID specified.', (string) $response->getBody());
$response = $http_client->request('POST', $url, [
'cookies' => $this->getSessionCookies(),
'form_params' => [
'ids' => [
$id,
],
'tokens' => [
'wrong_key' => $this->createContextualIdToken($id),
],
],
'http_errors' => FALSE,
]);
$this->assertEquals('400', $response->getStatusCode());
$this->assertStringContainsString('Invalid contextual ID specified.', (string) $response->getBody());
$response = $http_client->request('POST', $url, [
'cookies' => $this->getSessionCookies(),
'form_params' => [
'ids' => [
$id,
],
'tokens' => [
$this->createContextualIdToken($id),
],
],
'http_errors' => FALSE,
]);
$this->assertEquals('200', $response->getStatusCode());
}
/**
* Asserts that a contextual link placeholder with the given id exists.
*
* @param string $id
* A contextual link id.
*/
protected function assertContextualLinkPlaceHolder($id) {
$this->assertSession()
->elementAttributeContains('css', 'div[data-contextual-id="' . $id . '"]', 'data-contextual-token', $this->createContextualIdToken($id));
}
/**
* Asserts that a contextual link placeholder with the given id does not exist.
*
* @param string $id
* A contextual link id.
*/
protected function assertNoContextualLinkPlaceHolder($id) {
$this->assertSession()
->elementNotExists('css', 'div[data-contextual-id="' . $id . '"]');
}
/**
* Get server-rendered contextual links for the given contextual link ids.
*
* @param array $ids
* An array of contextual link ids.
* @param string $current_path
* The Drupal path for the page for which the contextual links are rendered.
*
* @return \Psr\Http\Message\ResponseInterface
* The response object.
*/
protected function renderContextualLinks($ids, $current_path) {
$tokens = array_map([
$this,
'createContextualIdToken',
], $ids);
$http_client = $this->getHttpClient();
$url = Url::fromRoute('contextual.render', [], [
'query' => [
'_format' => 'json',
'destination' => $current_path,
],
]);
return $http_client->request('POST', $this->buildUrl($url), [
'cookies' => $this->getSessionCookies(),
'form_params' => [
'ids' => $ids,
'tokens' => $tokens,
],
'http_errors' => FALSE,
]);
}
/**
* Creates a contextual ID token.
*
* @param string $id
* The contextual ID to create a token for.
*
* @return string
* The contextual ID token.
*/
protected function createContextualIdToken($id) {
return Crypt::hmacBase64($id, Settings::getHashSalt() . $this->container
->get('private_key')
->get());
}
}
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.