Core and composer updates

This commit is contained in:
Rob Davies 2017-07-03 16:47:07 +01:00
parent a82634bb98
commit 62cac30480
1118 changed files with 21770 additions and 6306 deletions

View file

@ -0,0 +1,106 @@
langcode: en
status: true
dependencies:
module:
- node
- user
id: test_argument_node_uid_revision
label: test_argument_node_uid_revision
module: views
description: ''
tag: default
base_table: node_field_data
base_field: nid
core: 8.0-dev
display:
default:
display_options:
access:
type: perm
cache:
type: tag
exposed_form:
type: basic
fields:
nid:
id: nid
table: node_field_data
field: nid
plugin_id: field
entity_type: node
entity_field: nid
filter_groups:
groups:
1: AND
operator: AND
filters: { }
sorts:
nid:
id: nid
table: node_field_data
field: nid
order: ASC
plugin_id: standard
relationship: none
entity_type: node
entity_field: nid
pager:
type: full
query:
type: views_query
style:
type: default
row:
type: fields
display_extenders: { }
arguments:
uid_revision:
id: uid_revision
table: node_field_data
field: uid_revision
relationship: none
group_type: group
admin_label: ''
default_action: empty
exception:
value: all
title_enable: false
title: All
title_enable: false
title: ''
default_argument_type: fixed
default_argument_options:
argument: ''
default_argument_skip_url: false
summary_options:
base_path: ''
count: true
items_per_page: 25
override: false
summary:
sort_order: asc
number_of_records: 0
format: default_summary
specify_validation: false
validate:
type: none
fail: 'not found'
validate_options: { }
break_phrase: false
not: false
entity_type: node
plugin_id: node_uid_revision
display_plugin: default
display_title: Master
id: default
position: 0
cache_metadata:
max-age: -1
contexts:
- 'languages:language_content'
- 'languages:language_interface'
- url
- url.query_args
- 'user.node_grants:view'
- user.permissions
tags: { }

View file

@ -0,0 +1,49 @@
<?php
namespace Drupal\Tests\node\Functional;
/**
* Asserts that buttons are present on a page.
*/
trait AssertButtonsTrait {
/**
* Assert method to verify the buttons in the dropdown element.
*
* @param array $buttons
* A collection of buttons to assert for on the page.
* @param bool $dropbutton
* Whether to check if the buttons are in a dropbutton widget or not.
*/
public function assertButtons(array $buttons, $dropbutton = TRUE) {
// Try to find a Save button.
$save_button = $this->xpath('//input[@type="submit"][@value="Save"]');
// Verify that the number of buttons passed as parameters is
// available in the dropbutton widget.
if ($dropbutton) {
$i = 0;
$count = count($buttons);
// Assert there is no save button.
$this->assertTrue(empty($save_button));
// Dropbutton elements.
/** @var \Behat\Mink\Element\NodeElement[] $elements */
$elements = $this->xpath('//div[@class="dropbutton-wrapper"]//input[@type="submit"]');
$this->assertEqual($count, count($elements));
foreach ($elements as $element) {
$value = $element->getValue() ?: '';
$this->assertEqual($buttons[$i], $value);
$i++;
}
}
else {
// Assert there is a save button.
$this->assertTrue(!empty($save_button));
$this->assertNoRaw('dropbutton-wrapper');
}
}
}

View file

@ -13,7 +13,7 @@ class MigrateNodeRevisionTest extends MigrateNodeTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['language', 'content_translation'];
public static $modules = ['language', 'content_translation', 'menu_ui'];
/**
* {@inheritdoc}

View file

@ -0,0 +1,70 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\Core\Url;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
/**
* Tests the node access automatic cacheability bubbling logic.
*
* @group node
* @group Cache
* @group cacheability_safeguards
*/
class NodeAccessAutoBubblingTest extends NodeTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node_access_test', 'node_access_test_auto_bubbling'];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
node_access_rebuild();
// Create some content.
$this->drupalCreateNode();
$this->drupalCreateNode();
$this->drupalCreateNode();
$this->drupalCreateNode();
}
/**
* Tests that the node grants cache context is auto-added, only when needed.
*
* @see node_query_node_access_alter()
*/
public function testNodeAccessCacheabilitySafeguard() {
$this->dumpHeaders = TRUE;
// The node grants cache context should be added automatically.
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
$this->assertCacheContext('user.node_grants:view');
// The root user has the 'bypass node access' permission, which means the
// node grants cache context is not necessary.
$this->drupalLogin($this->rootUser);
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
$this->assertNoCacheContext('user.node_grants:view');
$this->drupalLogout();
// Uninstall the module with the only hook_node_grants() implementation.
$this->container->get('module_installer')->uninstall(['node_access_test']);
$this->rebuildContainer();
// Because there are no node grants defined, there also is no need for the
// node grants cache context to be bubbled.
$this->drupalGet(new Url('node_access_test_auto_bubbling'));
$this->assertNoCacheContext('user.node_grants:view');
}
}

View file

@ -0,0 +1,226 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\node\Entity\NodeType;
/**
* Tests behavior of the node access subsystem if the base table is not node.
*
* @group node
*/
class NodeAccessBaseTableTest extends NodeTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node_access_test', 'views'];
/**
* The installation profile to use with this test.
*
* This test class requires the "tags" taxonomy field.
*
* @var string
*/
protected $profile = 'standard';
/**
* Nodes by user.
*
* @var array
*/
protected $nodesByUser;
/**
* A public tid.
*
* @var \Drupal\Core\Database\StatementInterface
*/
protected $publicTid;
/**
* A private tid.
*
* @var \Drupal\Core\Database\StatementInterface
*/
protected $privateTid;
/**
* A web user.
*/
protected $webUser;
/**
* The nids visible.
*
* @var array
*/
protected $nidsVisible;
protected function setUp() {
parent::setUp();
node_access_test_add_field(NodeType::load('article'));
node_access_rebuild();
\Drupal::state()->set('node_access_test.private', TRUE);
}
/**
* Tests the "private" node access functionality.
*
* - Create 2 users with "access content" and "create article" permissions.
* - Each user creates one private and one not private article.
*
* - Test that each user can view the other user's non-private article.
* - Test that each user cannot view the other user's private article.
* - Test that each user finds only appropriate (non-private + own private)
* in taxonomy listing.
* - Create another user with 'view any private content'.
* - Test that user 4 can view all content created above.
* - Test that user 4 can view all content on taxonomy listing.
*/
public function testNodeAccessBasic() {
$num_simple_users = 2;
$simple_users = [];
// Nodes keyed by uid and nid: $nodes[$uid][$nid] = $is_private;
$this->nodesByUser = [];
// Titles keyed by nid.
$titles = [];
// Array of nids marked private.
$private_nodes = [];
for ($i = 0; $i < $num_simple_users; $i++) {
$simple_users[$i] = $this->drupalCreateUser(['access content', 'create article content']);
}
foreach ($simple_users as $this->webUser) {
$this->drupalLogin($this->webUser);
foreach ([0 => 'Public', 1 => 'Private'] as $is_private => $type) {
$edit = [
'title[0][value]' => t('@private_public Article created by @user', ['@private_public' => $type, '@user' => $this->webUser->getUsername()]),
];
if ($is_private) {
$edit['private[0][value]'] = TRUE;
$edit['body[0][value]'] = 'private node';
$edit['field_tags[target_id]'] = 'private';
}
else {
$edit['body[0][value]'] = 'public node';
$edit['field_tags[target_id]'] = 'public';
}
$this->drupalPostForm('node/add/article', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertEqual($is_private, (int)$node->private->value, 'The private status of the node was properly set in the node_access_test table.');
if ($is_private) {
$private_nodes[] = $node->id();
}
$titles[$node->id()] = $edit['title[0][value]'];
$this->nodesByUser[$this->webUser->id()][$node->id()] = $is_private;
}
}
$this->publicTid = db_query('SELECT tid FROM {taxonomy_term_field_data} WHERE name = :name AND default_langcode = 1', [':name' => 'public'])->fetchField();
$this->privateTid = db_query('SELECT tid FROM {taxonomy_term_field_data} WHERE name = :name AND default_langcode = 1', [':name' => 'private'])->fetchField();
$this->assertTrue($this->publicTid, 'Public tid was found');
$this->assertTrue($this->privateTid, 'Private tid was found');
foreach ($simple_users as $this->webUser) {
$this->drupalLogin($this->webUser);
// Check own nodes to see that all are readable.
foreach ($this->nodesByUser as $uid => $data) {
foreach ($data as $nid => $is_private) {
$this->drupalGet('node/' . $nid);
if ($is_private) {
$should_be_visible = $uid == $this->webUser->id();
}
else {
$should_be_visible = TRUE;
}
$this->assertResponse($should_be_visible ? 200 : 403, strtr('A %private node by user %uid is %visible for user %current_uid.', [
'%private' => $is_private ? 'private' : 'public',
'%uid' => $uid,
'%visible' => $should_be_visible ? 'visible' : 'not visible',
'%current_uid' => $this->webUser->id(),
]));
}
}
// Check to see that the correct nodes are shown on taxonomy/private
// and taxonomy/public.
$this->assertTaxonomyPage(FALSE);
}
// Now test that a user with 'node test view' permissions can view content.
$access_user = $this->drupalCreateUser(['access content', 'create article content', 'node test view', 'search content']);
$this->drupalLogin($access_user);
foreach ($this->nodesByUser as $private_status) {
foreach ($private_status as $nid => $is_private) {
$this->drupalGet('node/' . $nid);
$this->assertResponse(200);
}
}
// This user should be able to see all of the nodes on the relevant
// taxonomy pages.
$this->assertTaxonomyPage(TRUE);
// Rebuild the node access permissions, repeat the test. This is done to
// ensure that node access is rebuilt correctly even if the current user
// does not have the bypass node access permission.
node_access_rebuild();
foreach ($this->nodesByUser as $private_status) {
foreach ($private_status as $nid => $is_private) {
$this->drupalGet('node/' . $nid);
$this->assertResponse(200);
}
}
// This user should be able to see all of the nodes on the relevant
// taxonomy pages.
$this->assertTaxonomyPage(TRUE);
}
/**
* Checks taxonomy/term listings to ensure only accessible nodes are listed.
*
* @param $is_admin
* A boolean indicating whether the current user is an administrator. If
* TRUE, all nodes should be listed. If FALSE, only public nodes and the
* user's own private nodes should be listed.
*/
protected function assertTaxonomyPage($is_admin) {
foreach ([$this->publicTid, $this->privateTid] as $tid_is_private => $tid) {
$this->drupalGet("taxonomy/term/$tid");
$this->nidsVisible = [];
foreach ($this->xpath("//a[text()='Read more']") as $link) {
// See also testTranslationRendering() in NodeTranslationUITest.
$this->assertTrue(preg_match('|node/(\d+)$|', $link->getAttribute('href'), $matches), 'Read more points to a node');
$this->nidsVisible[$matches[1]] = TRUE;
}
foreach ($this->nodesByUser as $uid => $data) {
foreach ($data as $nid => $is_private) {
// Private nodes should be visible on the private term page,
// public nodes should be visible on the public term page.
$should_be_visible = $tid_is_private == $is_private;
// Non-administrators can only see their own nodes on the private
// term page.
if (!$is_admin && $tid_is_private) {
$should_be_visible = $should_be_visible && $uid == $this->webUser->id();
}
$this->assertIdentical(isset($this->nidsVisible[$nid]), $should_be_visible, strtr('A %private node by user %uid is %visible for user %current_uid on the %tid_is_private page.', [
'%private' => $is_private ? 'private' : 'public',
'%uid' => $uid,
'%visible' => isset($this->nidsVisible[$nid]) ? 'visible' : 'not visible',
'%current_uid' => $this->webUser->id(),
'%tid_is_private' => $tid_is_private ? 'private' : 'public',
]));
}
}
}
}
}

View file

@ -0,0 +1,100 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\comment\CommentInterface;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\comment\Entity\Comment;
use Drupal\Tests\BrowserTestBase;
/**
* Tests access controlled node views have the right amount of comment pages.
*
* @group node
*/
class NodeAccessPagerTest extends BrowserTestBase {
use CommentTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node_access_test', 'comment', 'forum'];
protected function setUp() {
parent::setUp();
node_access_rebuild();
$this->drupalCreateContentType(['type' => 'page', 'name' => t('Basic page')]);
$this->addDefaultCommentField('node', 'page');
$this->webUser = $this->drupalCreateUser(['access content', 'access comments', 'node test view']);
}
/**
* Tests the comment pager for nodes with multiple grants per realm.
*/
public function testCommentPager() {
// Create a node.
$node = $this->drupalCreateNode();
// Create 60 comments.
for ($i = 0; $i < 60; $i++) {
$comment = Comment::create([
'entity_id' => $node->id(),
'entity_type' => 'node',
'field_name' => 'comment',
'subject' => $this->randomMachineName(),
'comment_body' => [
['value' => $this->randomMachineName()],
],
'status' => CommentInterface::PUBLISHED,
]);
$comment->save();
}
$this->drupalLogin($this->webUser);
// View the node page. With the default 50 comments per page there should
// be two pages (0, 1) but no third (2) page.
$this->drupalGet('node/' . $node->id());
$this->assertText($node->label());
$this->assertText(t('Comments'));
$this->assertRaw('page=1');
$this->assertNoRaw('page=2');
}
/**
* Tests the forum node pager for nodes with multiple grants per realm.
*/
public function testForumPager() {
// Look up the forums vocabulary ID.
$vid = $this->config('forum.settings')->get('vocabulary');
$this->assertTrue($vid, 'Forum navigation vocabulary ID is set.');
// Look up the general discussion term.
$tree = \Drupal::entityManager()->getStorage('taxonomy_term')->loadTree($vid, 0, 1);
$tid = reset($tree)->tid;
$this->assertTrue($tid, 'General discussion term is found in the forum vocabulary.');
// Create 30 nodes.
for ($i = 0; $i < 30; $i++) {
$this->drupalCreateNode([
'nid' => NULL,
'type' => 'forum',
'taxonomy_forums' => [
['target_id' => $tid],
],
]);
}
// View the general discussion forum page. With the default 25 nodes per
// page there should be two pages for 30 nodes, no more.
$this->drupalLogin($this->webUser);
$this->drupalGet('forum/' . $tid);
$this->assertRaw('page=1');
$this->assertNoRaw('page=2');
}
}

View file

@ -0,0 +1,118 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\node\Entity\NodeType;
/**
* Ensures that node access rebuild functions work correctly even
* when other modules implements hook_node_grants().
*
* @group node
*/
class NodeAccessRebuildNodeGrantsTest extends NodeTestBase {
/**
* A user to create nodes that only it has access to.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
/**
* A user to test the rebuild nodes feature which can't access the nodes.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->adminUser = $this->drupalCreateUser(['administer site configuration', 'access administration pages', 'access site reports']);
$this->drupalLogin($this->adminUser);
$this->webUser = $this->drupalCreateUser();
}
/**
* Tests rebuilding the node access permissions table with content.
*/
public function testNodeAccessRebuildNodeGrants() {
\Drupal::service('module_installer')->install(['node_access_test']);
\Drupal::state()->set('node_access_test.private', TRUE);
node_access_test_add_field(NodeType::load('page'));
$this->resetAll();
// Create 30 nodes so that _node_access_rebuild_batch_operation() has to run
// more than once.
for ($i = 0; $i < 30; $i++) {
$nodes[] = $this->drupalCreateNode([
'uid' => $this->webUser->id(),
'private' => [['value' => 1]]
]);
}
/** @var \Drupal\node\NodeGrantDatabaseStorageInterface $grant_storage */
$grant_storage = \Drupal::service('node.grant_storage');
// Default realm access and node records are present.
foreach ($nodes as $node) {
$this->assertTrue($node->private->value);
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'Prior to rebuilding node access the grant storage returns allowed for the node author.');
$this->assertTrue($grant_storage->access($node, 'view', $this->adminUser)->isAllowed(), 'Prior to rebuilding node access the grant storage returns allowed for the admin user.');
}
$this->assertEqual(1, \Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is an all realm access record');
$this->assertTrue(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions need to be rebuilt');
// Rebuild permissions.
$this->drupalGet('admin/reports/status');
$this->clickLink(t('Rebuild permissions'));
$this->drupalPostForm(NULL, [], t('Rebuild permissions'));
$this->assertText(t('The content access permissions have been rebuilt.'));
// Test if the rebuild by user that cannot bypass node access and does not
// have access to the nodes has been successful.
$this->assertFalse($this->adminUser->hasPermission('bypass node access'));
$this->assertNull(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions have been rebuilt');
foreach ($nodes as $node) {
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'After rebuilding node access the grant storage returns allowed for the node author.');
$this->assertFalse($grant_storage->access($node, 'view', $this->adminUser)->isForbidden(), 'After rebuilding node access the grant storage returns forbidden for the admin user.');
}
$this->assertFalse(\Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is no all realm access record');
// Test an anonymous node access rebuild from code.
$this->drupalLogout();
node_access_rebuild();
foreach ($nodes as $node) {
$this->assertTrue($grant_storage->access($node, 'view', $this->webUser)->isAllowed(), 'After rebuilding node access the grant storage returns allowed for the node author.');
$this->assertFalse($grant_storage->access($node, 'view', $this->adminUser)->isForbidden(), 'After rebuilding node access the grant storage returns forbidden for the admin user.');
}
$this->assertFalse(\Drupal::service('node.grant_storage')->checkAll($this->webUser), 'There is no all realm access record');
}
/**
* Tests rebuilding the node access permissions table with no content.
*/
public function testNodeAccessRebuildNoAccessModules() {
// Default realm access is present.
$this->assertEqual(1, \Drupal::service('node.grant_storage')->count(), 'There is an all realm access record');
// No need to rebuild permissions.
$this->assertFalse(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions need to be rebuilt');
// Rebuild permissions.
$this->drupalGet('admin/reports/status');
$this->clickLink(t('Rebuild permissions'));
$this->drupalPostForm(NULL, [], t('Rebuild permissions'));
$this->assertText(t('Content permissions have been rebuilt.'));
$this->assertNull(\Drupal::state()->get('node.node_access_needs_rebuild'), 'Node access permissions have been rebuilt');
// Default realm access is still present.
$this->assertEqual(1, \Drupal::service('node.grant_storage')->count(), 'There is an all realm access record');
}
}

View file

@ -0,0 +1,204 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\user\RoleInterface;
/**
* Tests node administration page functionality.
*
* @group node
*/
class NodeAdminTest extends NodeTestBase {
/**
* A user with permission to bypass access content.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* A user with the 'access content overview' permission.
*
* @var \Drupal\user\UserInterface
*/
protected $baseUser1;
/**
* A normal user with permission to view own unpublished content.
*
* @var \Drupal\user\UserInterface
*/
protected $baseUser2;
/**
* A normal user with permission to bypass node access content.
*
* @var \Drupal\user\UserInterface
*/
protected $baseUser3;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['views'];
protected function setUp() {
parent::setUp();
// Remove the "view own unpublished content" permission which is set
// by default for authenticated users so we can test this permission
// correctly.
user_role_revoke_permissions(RoleInterface::AUTHENTICATED_ID, ['view own unpublished content']);
$this->adminUser = $this->drupalCreateUser(['access administration pages', 'access content overview', 'administer nodes', 'bypass node access']);
$this->baseUser1 = $this->drupalCreateUser(['access content overview']);
$this->baseUser2 = $this->drupalCreateUser(['access content overview', 'view own unpublished content']);
$this->baseUser3 = $this->drupalCreateUser(['access content overview', 'bypass node access']);
}
/**
* Tests that the table sorting works on the content admin pages.
*/
public function testContentAdminSort() {
$this->drupalLogin($this->adminUser);
$changed = REQUEST_TIME;
foreach (['dd', 'aa', 'DD', 'bb', 'cc', 'CC', 'AA', 'BB'] as $prefix) {
$changed += 1000;
$node = $this->drupalCreateNode(['title' => $prefix . $this->randomMachineName(6)]);
db_update('node_field_data')
->fields(['changed' => $changed])
->condition('nid', $node->id())
->execute();
}
// Test that the default sort by node.changed DESC actually fires properly.
$nodes_query = db_select('node_field_data', 'n')
->fields('n', ['title'])
->orderBy('changed', 'DESC')
->execute()
->fetchCol();
$this->drupalGet('admin/content');
foreach ($nodes_query as $delta => $string) {
$elements = $this->xpath('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [':class' => 'views-table', ':label' => $string]);
$this->assertTrue(!empty($elements), 'The node was found in the correct order.');
}
// Compare the rendered HTML node list to a query for the nodes ordered by
// title to account for possible database-dependent sort order.
$nodes_query = db_select('node_field_data', 'n')
->fields('n', ['title'])
->orderBy('title')
->execute()
->fetchCol();
$this->drupalGet('admin/content', ['query' => ['sort' => 'asc', 'order' => 'title']]);
foreach ($nodes_query as $delta => $string) {
$elements = $this->xpath('//table[contains(@class, :class)]/tbody/tr[' . ($delta + 1) . ']/td[2]/a[normalize-space(text())=:label]', [':class' => 'views-table', ':label' => $string]);
$this->assertTrue(!empty($elements), 'The node was found in the correct order.');
}
}
/**
* Tests content overview with different user permissions.
*
* Taxonomy filters are tested separately.
*
* @see TaxonomyNodeFilterTestCase
*/
public function testContentAdminPages() {
$this->drupalLogin($this->adminUser);
// Use an explicit changed time to ensure the expected order in the content
// admin listing. We want these to appear in the table in the same order as
// they appear in the following code, and the 'content' View has a table
// style configuration with a default sort on the 'changed' field DESC.
$time = time();
$nodes['published_page'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time--]);
$nodes['published_article'] = $this->drupalCreateNode(['type' => 'article', 'changed' => $time--]);
$nodes['unpublished_page_1'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time--, 'uid' => $this->baseUser1->id(), 'status' => 0]);
$nodes['unpublished_page_2'] = $this->drupalCreateNode(['type' => 'page', 'changed' => $time, 'uid' => $this->baseUser2->id(), 'status' => 0]);
// Verify view, edit, and delete links for any content.
$this->drupalGet('admin/content');
$this->assertResponse(200);
$node_type_labels = $this->xpath('//td[contains(@class, "views-field-type")]');
$delta = 0;
foreach ($nodes as $node) {
$this->assertLinkByHref('node/' . $node->id());
$this->assertLinkByHref('node/' . $node->id() . '/edit');
$this->assertLinkByHref('node/' . $node->id() . '/delete');
// Verify that we can see the content type label.
$this->assertEqual(trim($node_type_labels[$delta]->getText()), $node->type->entity->label());
$delta++;
}
// Verify filtering by publishing status.
$this->drupalGet('admin/content', ['query' => ['status' => TRUE]]);
$this->assertLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
$this->assertLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
// Verify filtering by status and content type.
$this->drupalGet('admin/content', ['query' => ['status' => TRUE, 'type' => 'page']]);
$this->assertLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
// Verify no operation links are displayed for regular users.
$this->drupalLogout();
$this->drupalLogin($this->baseUser1);
$this->drupalGet('admin/content');
$this->assertResponse(200);
$this->assertLinkByHref('node/' . $nodes['published_page']->id());
$this->assertLinkByHref('node/' . $nodes['published_article']->id());
$this->assertNoLinkByHref('node/' . $nodes['published_page']->id() . '/edit');
$this->assertNoLinkByHref('node/' . $nodes['published_page']->id() . '/delete');
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/edit');
$this->assertNoLinkByHref('node/' . $nodes['published_article']->id() . '/delete');
// Verify no unpublished content is displayed without permission.
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id());
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/delete');
// Verify no tableselect.
$this->assertNoFieldByName('nodes[' . $nodes['published_page']->id() . ']', '', 'No tableselect found.');
// Verify unpublished content is displayed with permission.
$this->drupalLogout();
$this->drupalLogin($this->baseUser2);
$this->drupalGet('admin/content');
$this->assertResponse(200);
$this->assertLinkByHref('node/' . $nodes['unpublished_page_2']->id());
// Verify no operation links are displayed.
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_2']->id() . '/edit');
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_2']->id() . '/delete');
// Verify user cannot see unpublished content of other users.
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id());
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/edit');
$this->assertNoLinkByHref('node/' . $nodes['unpublished_page_1']->id() . '/delete');
// Verify no tableselect.
$this->assertNoFieldByName('nodes[' . $nodes['unpublished_page_2']->id() . ']', '', 'No tableselect found.');
// Verify node access can be bypassed.
$this->drupalLogout();
$this->drupalLogin($this->baseUser3);
$this->drupalGet('admin/content');
$this->assertResponse(200);
foreach ($nodes as $node) {
$this->assertLinkByHref('node/' . $node->id());
$this->assertLinkByHref('node/' . $node->id() . '/edit');
$this->assertLinkByHref('node/' . $node->id() . '/delete');
}
}
}

View file

@ -0,0 +1,160 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\block\Entity\Block;
use Drupal\Core\EventSubscriber\MainContentViewSubscriber;
use Drupal\system\Tests\Cache\AssertPageCacheContextsAndTagsTrait;
use Drupal\user\RoleInterface;
/**
* Tests node block functionality.
*
* @group node
*/
class NodeBlockFunctionalTest extends NodeTestBase {
use AssertPageCacheContextsAndTagsTrait;
/**
* An administrative user for testing.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* An unprivileged user for testing.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'views'];
protected function setUp() {
parent::setUp();
// Create users and test node.
$this->adminUser = $this->drupalCreateUser(['administer content types', 'administer nodes', 'administer blocks', 'access content overview']);
$this->webUser = $this->drupalCreateUser(['access content', 'create article content']);
}
/**
* Tests the recent comments block.
*/
public function testRecentNodeBlock() {
$this->drupalLogin($this->adminUser);
// Disallow anonymous users to view content.
user_role_change_permissions(RoleInterface::ANONYMOUS_ID, [
'access content' => FALSE,
]);
// Enable the recent content block with two items.
$block = $this->drupalPlaceBlock('views_block:content_recent-block_1', ['id' => 'test_block', 'items_per_page' => 2]);
// Test that block is not visible without nodes.
$this->drupalGet('');
$this->assertText(t('No content available.'), 'Block with "No content available." found.');
// Add some test nodes.
$default_settings = ['uid' => $this->webUser->id(), 'type' => 'article'];
$node1 = $this->drupalCreateNode($default_settings);
$node2 = $this->drupalCreateNode($default_settings);
$node3 = $this->drupalCreateNode($default_settings);
// Change the changed time for node so that we can test ordering.
db_update('node_field_data')
->fields([
'changed' => $node1->getChangedTime() + 100,
])
->condition('nid', $node2->id())
->execute();
db_update('node_field_data')
->fields([
'changed' => $node1->getChangedTime() + 200,
])
->condition('nid', $node3->id())
->execute();
// Test that a user without the 'access content' permission cannot
// see the block.
$this->drupalLogout();
$this->drupalGet('');
$this->assertNoText($block->label(), 'Block was not found.');
// Test that only the 2 latest nodes are shown.
$this->drupalLogin($this->webUser);
$this->assertNoText($node1->label(), 'Node not found in block.');
$this->assertText($node2->label(), 'Node found in block.');
$this->assertText($node3->label(), 'Node found in block.');
// Check to make sure nodes are in the right order.
$this->assertTrue($this->xpath('//div[@id="block-test-block"]//div[@class="item-list"]/ul/li[1]/div/span/a[text() = "' . $node3->label() . '"]'), 'Nodes were ordered correctly in block.');
$this->drupalLogout();
$this->drupalLogin($this->adminUser);
// Set the number of recent nodes to show to 10.
$block->getPlugin()->setConfigurationValue('items_per_page', 10);
$block->save();
// Post an additional node.
$node4 = $this->drupalCreateNode($default_settings);
// Test that all four nodes are shown.
$this->drupalGet('');
$this->assertText($node1->label(), 'Node found in block.');
$this->assertText($node2->label(), 'Node found in block.');
$this->assertText($node3->label(), 'Node found in block.');
$this->assertText($node4->label(), 'Node found in block.');
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user']);
// Enable the "Powered by Drupal" block only on article nodes.
$edit = [
'id' => strtolower($this->randomMachineName()),
'region' => 'sidebar_first',
'visibility[node_type][bundles][article]' => 'article',
];
$theme = \Drupal::service('theme_handler')->getDefault();
$this->drupalPostForm("admin/structure/block/add/system_powered_by_block/$theme", $edit, t('Save block'));
$block = Block::load($edit['id']);
$visibility = $block->getVisibility();
$this->assertTrue(isset($visibility['node_type']['bundles']['article']), 'Visibility settings were saved to configuration');
// Create a page node.
$node5 = $this->drupalCreateNode(['uid' => $this->adminUser->id(), 'type' => 'page']);
$this->drupalLogout();
$this->drupalLogin($this->webUser);
// Verify visibility rules.
$this->drupalGet('');
$label = $block->label();
$this->assertNoText($label, 'Block was not displayed on the front page.');
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route']);
$this->drupalGet('node/add/article');
$this->assertText($label, 'Block was displayed on the node/add/article page.');
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'session', 'theme', 'url.path', 'url.query_args', 'user', 'route']);
$this->drupalGet('node/' . $node1->id());
$this->assertText($label, 'Block was displayed on the node/N when node is of type article.');
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route', 'timezone']);
$this->drupalGet('node/' . $node5->id());
$this->assertNoText($label, 'Block was not displayed on nodes of type page.');
$this->assertCacheContexts(['languages:language_content', 'languages:language_interface', 'theme', 'url.query_args:' . MainContentViewSubscriber::WRAPPER_FORMAT, 'user', 'route', 'timezone']);
$this->drupalLogin($this->adminUser);
$this->drupalGet('admin/structure/block');
$this->assertText($label, 'Block was displayed on the admin/structure/block page.');
$this->assertLinkByHref($block->url());
}
}

View file

@ -0,0 +1,237 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\node\NodeInterface;
use Drupal\user\Entity\User;
/**
* Create a node and test node edit functionality.
*
* @group node
*/
class NodeEditFormTest extends NodeTestBase {
/**
* A normal logged in user.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
/**
* A user with permission to bypass content access checks.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* The node storage.
*
* @var \Drupal\node\NodeStorageInterface
*/
protected $nodeStorage;
/**
* Modules to enable.
*
* @var string[]
*/
public static $modules = ['block', 'node', 'datetime'];
protected function setUp() {
parent::setUp();
$this->webUser = $this->drupalCreateUser(['edit own page content', 'create page content']);
$this->adminUser = $this->drupalCreateUser(['bypass node access', 'administer nodes']);
$this->drupalPlaceBlock('local_tasks_block');
$this->nodeStorage = $this->container->get('entity.manager')->getStorage('node');
}
/**
* Checks node edit functionality.
*/
public function testNodeEdit() {
$this->drupalLogin($this->webUser);
$title_key = 'title[0][value]';
$body_key = 'body[0][value]';
// Create node to edit.
$edit = [];
$edit[$title_key] = $this->randomMachineName(8);
$edit[$body_key] = $this->randomMachineName(16);
$this->drupalPostForm('node/add/page', $edit, t('Save'));
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
$this->assertTrue($node, 'Node found in database.');
// Check that "edit" link points to correct page.
$this->clickLink(t('Edit'));
$this->assertUrl($node->url('edit-form', ['absolute' => TRUE]));
// Check that the title and body fields are displayed with the correct values.
// @todo Ideally assertLink would support HTML, but it doesn't.
$this->assertRaw('Edit<span class="visually-hidden">(active tab)</span>', 'Edit tab found and marked active.');
$this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
$this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
// Edit the content of the node.
$edit = [];
$edit[$title_key] = $this->randomMachineName(8);
$edit[$body_key] = $this->randomMachineName(16);
// Stay on the current page, without reloading.
$this->drupalPostForm(NULL, $edit, t('Save'));
// Check that the title and body fields are displayed with the updated values.
$this->assertText($edit[$title_key], 'Title displayed.');
$this->assertText($edit[$body_key], 'Body displayed.');
// Log in as a second administrator user.
$second_web_user = $this->drupalCreateUser(['administer nodes', 'edit any page content']);
$this->drupalLogin($second_web_user);
// Edit the same node, creating a new revision.
$this->drupalGet("node/" . $node->id() . "/edit");
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName(8);
$edit[$body_key] = $this->randomMachineName(16);
$edit['revision'] = TRUE;
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
// Ensure that the node revision has been created.
$revised_node = $this->drupalGetNodeByTitle($edit['title[0][value]'], TRUE);
$this->assertNotIdentical($node->getRevisionId(), $revised_node->getRevisionId(), 'A new revision has been created.');
// Ensure that the node author is preserved when it was not changed in the
// edit form.
$this->assertIdentical($node->getOwnerId(), $revised_node->getOwnerId(), 'The node author has been preserved.');
// Ensure that the revision authors are different since the revisions were
// made by different users.
$first_node_version = node_revision_load($node->getRevisionId());
$second_node_version = node_revision_load($revised_node->getRevisionId());
$this->assertNotIdentical($first_node_version->getRevisionUser()->id(), $second_node_version->getRevisionUser()->id(), 'Each revision has a distinct user.');
// Check if the node revision checkbox is rendered on node edit form.
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertFieldById('edit-revision', NULL, 'The revision field is present.');
// Check that details form element opens when there are errors on child
// elements.
$this->drupalGet('node/' . $node->id() . '/edit');
$edit = [];
// This invalid date will trigger an error.
$edit['created[0][value][date]'] = $this->randomMachineName(8);
// Get the current amount of open details elements.
$open_details_elements = count($this->cssSelect('details[open="open"]'));
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
// The node author details must be open.
$this->assertRaw('<details class="node-form-author js-form-wrapper form-wrapper" data-drupal-selector="edit-author" id="edit-author" open="open">');
// Only one extra details element should now be open.
$open_details_elements++;
$this->assertEqual(count($this->cssSelect('details[open="open"]')), $open_details_elements, 'Exactly one extra open &lt;details&gt; element found.');
}
/**
* Tests changing a node's "authored by" field.
*/
public function testNodeEditAuthoredBy() {
$this->drupalLogin($this->adminUser);
// Create node to edit.
$body_key = 'body[0][value]';
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName(8);
$edit[$body_key] = $this->randomMachineName(16);
$this->drupalPostForm('node/add/page', $edit, t('Save and publish'));
// Check that the node was authored by the currently logged in user.
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
$this->assertIdentical($node->getOwnerId(), $this->adminUser->id(), 'Node authored by admin user.');
$this->checkVariousAuthoredByValues($node, 'uid[0][target_id]');
// Check that normal users cannot change the authored by information.
$this->drupalLogin($this->webUser);
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertNoFieldByName('uid[0][target_id]');
// Now test with the Autcomplete (Tags) field widget.
/** @var \Drupal\Core\Entity\Display\EntityFormDisplayInterface $form_display */
$form_display = \Drupal::entityManager()->getStorage('entity_form_display')->load('node.page.default');
$widget = $form_display->getComponent('uid');
$widget['type'] = 'entity_reference_autocomplete_tags';
$widget['settings'] = [
'match_operator' => 'CONTAINS',
'size' => 60,
'placeholder' => '',
];
$form_display->setComponent('uid', $widget);
$form_display->save();
$this->drupalLogin($this->adminUser);
// Save the node without making any changes.
$this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and keep published'));
$this->nodeStorage->resetCache([$node->id()]);
$node = $this->nodeStorage->load($node->id());
$this->assertIdentical($this->webUser->id(), $node->getOwner()->id());
$this->checkVariousAuthoredByValues($node, 'uid[target_id]');
// Hide the 'authored by' field from the form.
$form_display->removeComponent('uid')->save();
// Check that saving the node without making any changes keeps the proper
// author ID.
$this->drupalPostForm('node/' . $node->id() . '/edit', [], t('Save and keep published'));
$this->nodeStorage->resetCache([$node->id()]);
$node = $this->nodeStorage->load($node->id());
$this->assertIdentical($this->webUser->id(), $node->getOwner()->id());
}
/**
* Checks that the "authored by" works correctly with various values.
*
* @param \Drupal\node\NodeInterface $node
* A node object.
* @param string $form_element_name
* The name of the form element to populate.
*/
protected function checkVariousAuthoredByValues(NodeInterface $node, $form_element_name) {
// Try to change the 'authored by' field to an invalid user name.
$edit = [
$form_element_name => 'invalid-name',
];
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
$this->assertRaw(t('There are no entities matching "%name".', ['%name' => 'invalid-name']));
// Change the authored by field to an empty string, which should assign
// authorship to the anonymous user (uid 0).
$edit[$form_element_name] = '';
$this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
$this->nodeStorage->resetCache([$node->id()]);
$node = $this->nodeStorage->load($node->id());
$uid = $node->getOwnerId();
// Most SQL database drivers stringify fetches but entities are not
// necessarily stored in a SQL database. At the same time, NULL/FALSE/""
// won't do.
$this->assertTrue($uid === 0 || $uid === '0', 'Node authored by anonymous user.');
// Go back to the edit form and check that the correct value is displayed
// in the author widget.
$this->drupalGet('node/' . $node->id() . '/edit');
$anonymous_user = User::getAnonymousUser();
$expected = $anonymous_user->label() . ' (' . $anonymous_user->id() . ')';
$this->assertFieldByName($form_element_name, $expected, 'Authored by field displays the correct value for the anonymous user.');
// Change the authored by field to another user's name (that is not
// logged in).
$edit[$form_element_name] = $this->webUser->getUsername();
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
$this->nodeStorage->resetCache([$node->id()]);
$node = $this->nodeStorage->load($node->id());
$this->assertIdentical($node->getOwnerId(), $this->webUser->id(), 'Node authored by normal user.');
}
}

View file

@ -0,0 +1,53 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\EntityViewTrait;
/**
* Tests changing view modes for nodes.
*
* @group node
*/
class NodeEntityViewModeAlterTest extends NodeTestBase {
use EntityViewTrait;
/**
* Enable dummy module that implements hook_ENTITY_TYPE_view() for nodes.
*/
public static $modules = ['node_test'];
/**
* Create a "Basic page" node and verify its consistency in the database.
*/
public function testNodeViewModeChange() {
$web_user = $this->drupalCreateUser(['create page content', 'edit own page content']);
$this->drupalLogin($web_user);
// Create a node.
$edit = [];
$edit['title[0][value]'] = $this->randomMachineName(8);
$edit['body[0][value]'] = t('Data that should appear only in the body for the node.');
$edit['body[0][summary]'] = t('Extra data that should appear only in the teaser for the node.');
$this->drupalPostForm('node/add/page', $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
// Set the flag to alter the view mode and view the node.
\Drupal::state()->set('node_test_change_view_mode', 'teaser');
Cache::invalidateTags(['rendered']);
$this->drupalGet('node/' . $node->id());
// Check that teaser mode is viewed.
$this->assertText('Extra data that should appear only in the teaser for the node.', 'Teaser text present');
// Make sure body text is not present.
$this->assertNoText('Data that should appear only in the body for the node.', 'Body text not present');
// Test that the correct build mode has been set.
$build = $this->buildEntityView($node);
$this->assertEqual($build['#view_mode'], 'teaser', 'The view mode has correctly been set to teaser.');
}
}

View file

@ -0,0 +1,129 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Tests\BrowserTestBase;
/**
* Tests multilingual support for fields.
*
* @group node
*/
class NodeFieldMultilingualTest extends BrowserTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'language'];
protected function setUp() {
parent::setUp();
// Create Basic page node type.
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
// Setup users.
$admin_user = $this->drupalCreateUser(['administer languages', 'administer content types', 'access administration pages', 'create page content', 'edit own page content']);
$this->drupalLogin($admin_user);
// Add a new language.
ConfigurableLanguage::createFromLangcode('it')->save();
// Enable URL language detection and selection.
$edit = ['language_interface[enabled][language-url]' => '1'];
$this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
// Set "Basic page" content type to use multilingual support.
$edit = [
'language_configuration[language_alterable]' => TRUE,
];
$this->drupalPostForm('admin/structure/types/manage/page', $edit, t('Save content type'));
$this->assertRaw(t('The content type %type has been updated.', ['%type' => 'Basic page']), 'Basic page content type has been updated.');
// Make node body translatable.
$field_storage = FieldStorageConfig::loadByName('node', 'body');
$field_storage->setTranslatable(TRUE);
$field_storage->save();
}
/**
* Tests whether field languages are correctly set through the node form.
*/
public function testMultilingualNodeForm() {
// Create "Basic page" content.
$langcode = language_get_default_langcode('node', 'page');
$title_key = 'title[0][value]';
$title_value = $this->randomMachineName(8);
$body_key = 'body[0][value]';
$body_value = $this->randomMachineName(16);
// Create node to edit.
$edit = [];
$edit[$title_key] = $title_value;
$edit[$body_key] = $body_value;
$this->drupalPostForm('node/add/page', $edit, t('Save'));
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
$this->assertTrue($node, 'Node found in database.');
$this->assertTrue($node->language()->getId() == $langcode && $node->body->value == $body_value, 'Field language correctly set.');
// Change node language.
$langcode = 'it';
$this->drupalGet("node/{$node->id()}/edit");
$edit = [
$title_key => $this->randomMachineName(8),
'langcode[0][value]' => $langcode,
];
$this->drupalPostForm(NULL, $edit, t('Save'));
$node = $this->drupalGetNodeByTitle($edit[$title_key], TRUE);
$this->assertTrue($node, 'Node found in database.');
$this->assertTrue($node->language()->getId() == $langcode && $node->body->value == $body_value, 'Field language correctly changed.');
// Enable content language URL detection.
$this->container->get('language_negotiator')->saveConfiguration(LanguageInterface::TYPE_CONTENT, [LanguageNegotiationUrl::METHOD_ID => 0]);
// Test multilingual field language fallback logic.
$this->drupalGet("it/node/{$node->id()}");
$this->assertRaw($body_value, 'Body correctly displayed using Italian as requested language');
$this->drupalGet("node/{$node->id()}");
$this->assertRaw($body_value, 'Body correctly displayed using English as requested language');
}
/**
* Tests multilingual field display settings.
*/
public function testMultilingualDisplaySettings() {
// Create "Basic page" content.
$title_key = 'title[0][value]';
$title_value = $this->randomMachineName(8);
$body_key = 'body[0][value]';
$body_value = $this->randomMachineName(16);
// Create node to edit.
$edit = [];
$edit[$title_key] = $title_value;
$edit[$body_key] = $body_value;
$this->drupalPostForm('node/add/page', $edit, t('Save'));
// Check that the node exists in the database.
$node = $this->drupalGetNodeByTitle($edit[$title_key]);
$this->assertTrue($node, 'Node found in database.');
// Check if node body is showed.
$this->drupalGet('node/' . $node->id());
$body = $this->xpath('//article[contains(concat(" ", normalize-space(@class), " "), :node-class)]//div[contains(concat(" ", normalize-space(@class), " "), :content-class)]/descendant::p', [
':node-class' => ' node ',
':content-class' => 'node__content',
]);
$this->assertEqual($body[0]->getText(), $node->body->value, 'Node body found.');
}
}

View file

@ -0,0 +1,135 @@
<?php
namespace Drupal\Tests\node\Functional;
/**
* Tests all the different buttons on the node form.
*
* @group node
*/
class NodeFormButtonsTest extends NodeTestBase {
use AssertButtonsTrait;
/**
* A normal logged in user.
*
* @var \Drupal\user\UserInterface
*/
protected $webUser;
/**
* A user with permission to bypass access content.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
protected function setUp() {
parent::setUp();
// Create a user that has no access to change the state of the node.
$this->webUser = $this->drupalCreateUser(['create article content', 'edit own article content']);
// Create a user that has access to change the state of the node.
$this->adminUser = $this->drupalCreateUser(['administer nodes', 'bypass node access']);
}
/**
* Tests that the right buttons are displayed for saving nodes.
*/
public function testNodeFormButtons() {
$node_storage = $this->container->get('entity.manager')->getStorage('node');
// Log in as administrative user.
$this->drupalLogin($this->adminUser);
// Verify the buttons on a node add form.
$this->drupalGet('node/add/article');
$this->assertButtons([t('Save and publish'), t('Save as unpublished')]);
// Save the node and assert it's published after clicking
// 'Save and publish'.
$edit = ['title[0][value]' => $this->randomString()];
$this->drupalPostForm('node/add/article', $edit, t('Save and publish'));
// Get the node.
$node_1 = $node_storage->load(1);
$this->assertTrue($node_1->isPublished(), 'Node is published');
// Verify the buttons on a node edit form.
$this->drupalGet('node/' . $node_1->id() . '/edit');
$this->assertButtons([t('Save and keep published'), t('Save and unpublish')]);
// Save the node and verify it's still published after clicking
// 'Save and keep published'.
$this->drupalPostForm(NULL, $edit, t('Save and keep published'));
$node_storage->resetCache([1]);
$node_1 = $node_storage->load(1);
$this->assertTrue($node_1->isPublished(), 'Node is published');
// Save the node and verify it's unpublished after clicking
// 'Save and unpublish'.
$this->drupalPostForm('node/' . $node_1->id() . '/edit', $edit, t('Save and unpublish'));
$node_storage->resetCache([1]);
$node_1 = $node_storage->load(1);
$this->assertFalse($node_1->isPublished(), 'Node is unpublished');
// Verify the buttons on an unpublished node edit screen.
$this->drupalGet('node/' . $node_1->id() . '/edit');
$this->assertButtons([t('Save and keep unpublished'), t('Save and publish')]);
// Create a node as a normal user.
$this->drupalLogout();
$this->drupalLogin($this->webUser);
// Verify the buttons for a normal user.
$this->drupalGet('node/add/article');
$this->assertButtons([t('Save')], FALSE);
// Create the node.
$edit = ['title[0][value]' => $this->randomString()];
$this->drupalPostForm('node/add/article', $edit, t('Save'));
$node_2 = $node_storage->load(2);
$this->assertTrue($node_2->isPublished(), 'Node is published');
// Log in as an administrator and unpublish the node that just
// was created by the normal user.
$this->drupalLogout();
$this->drupalLogin($this->adminUser);
$this->drupalPostForm('node/' . $node_2->id() . '/edit', [], t('Save and unpublish'));
$node_storage->resetCache([2]);
$node_2 = $node_storage->load(2);
$this->assertFalse($node_2->isPublished(), 'Node is unpublished');
// Log in again as the normal user, save the node and verify
// it's still unpublished.
$this->drupalLogout();
$this->drupalLogin($this->webUser);
$this->drupalPostForm('node/' . $node_2->id() . '/edit', [], t('Save'));
$node_storage->resetCache([2]);
$node_2 = $node_storage->load(2);
$this->assertFalse($node_2->isPublished(), 'Node is still unpublished');
$this->drupalLogout();
// Set article content type default to unpublished. This will change the
// the initial order of buttons and/or status of the node when creating
// a node.
$fields = \Drupal::entityManager()->getFieldDefinitions('node', 'article');
$fields['status']->getConfig('article')
->setDefaultValue(FALSE)
->save();
// Verify the buttons on a node add form for an administrator.
$this->drupalLogin($this->adminUser);
$this->drupalGet('node/add/article');
$this->assertButtons([t('Save as unpublished'), t('Save and publish')]);
// Verify the node is unpublished by default for a normal user.
$this->drupalLogout();
$this->drupalLogin($this->webUser);
$edit = ['title[0][value]' => $this->randomString()];
$this->drupalPostForm('node/add/article', $edit, t('Save'));
$node_3 = $node_storage->load(3);
$this->assertFalse($node_3->isPublished(), 'Node is unpublished');
}
}

View file

@ -0,0 +1,197 @@
<?php
namespace Drupal\Tests\node\Functional;
/**
* Tests that node access queries are properly altered by the node module.
*
* @group node
*/
class NodeQueryAlterTest extends NodeTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node_access_test'];
/**
* User with permission to view content.
*/
protected $accessUser;
/**
* User without permission to view content.
*/
protected $noAccessUser;
protected function setUp() {
parent::setUp();
node_access_rebuild();
// Create some content.
$this->drupalCreateNode();
$this->drupalCreateNode();
$this->drupalCreateNode();
$this->drupalCreateNode();
// Create user with simple node access permission. The 'node test view'
// permission is implemented and granted by the node_access_test module.
$this->accessUser = $this->drupalCreateUser(['access content overview', 'access content', 'node test view']);
$this->noAccessUser = $this->drupalCreateUser(['access content overview', 'access content']);
$this->noAccessUser2 = $this->drupalCreateUser(['access content overview', 'access content']);
}
/**
* Tests 'node_access' query alter, for user with access.
*
* Verifies that a non-standard table alias can be used, and that a user with
* node access can view the nodes.
*/
public function testNodeQueryAlterLowLevelWithAccess() {
// User with access should be able to view 4 nodes.
try {
$query = db_select('node', 'mytab')
->fields('mytab');
$query->addTag('node_access');
$query->addMetaData('op', 'view');
$query->addMetaData('account', $this->accessUser);
$result = $query->execute()->fetchAll();
$this->assertEqual(count($result), 4, 'User with access can see correct nodes');
}
catch (\Exception $e) {
$this->fail(t('Altered query is malformed'));
}
}
/**
* Tests 'node_access' query alter with revision-enabled nodes.
*/
public function testNodeQueryAlterWithRevisions() {
// Execute a query that only deals with the 'node_revision' table.
try {
$query = \Drupal::entityTypeManager()->getStorage('node')->getQuery();
$result = $query
->allRevisions()
->execute();
$this->assertEqual(count($result), 4, 'User with access can see correct nodes');
}
catch (\Exception $e) {
$this->fail('Altered query is malformed');
}
}
/**
* Tests 'node_access' query alter, for user without access.
*
* Verifies that a non-standard table alias can be used, and that a user
* without node access cannot view the nodes.
*/
public function testNodeQueryAlterLowLevelNoAccess() {
// User without access should be able to view 0 nodes.
try {
$query = db_select('node', 'mytab')
->fields('mytab');
$query->addTag('node_access');
$query->addMetaData('op', 'view');
$query->addMetaData('account', $this->noAccessUser);
$result = $query->execute()->fetchAll();
$this->assertEqual(count($result), 0, 'User with no access cannot see nodes');
}
catch (\Exception $e) {
$this->fail(t('Altered query is malformed'));
}
}
/**
* Tests 'node_access' query alter, for edit access.
*
* Verifies that a non-standard table alias can be used, and that a user with
* view-only node access cannot edit the nodes.
*/
public function testNodeQueryAlterLowLevelEditAccess() {
// User with view-only access should not be able to edit nodes.
try {
$query = db_select('node', 'mytab')
->fields('mytab');
$query->addTag('node_access');
$query->addMetaData('op', 'update');
$query->addMetaData('account', $this->accessUser);
$result = $query->execute()->fetchAll();
$this->assertEqual(count($result), 0, 'User with view-only access cannot edit nodes');
}
catch (\Exception $e) {
$this->fail($e->getMessage());
$this->fail((string) $query);
$this->fail(t('Altered query is malformed'));
}
}
/**
* Tests 'node_access' query alter override.
*
* Verifies that node_access_view_all_nodes() is called from
* node_query_node_access_alter(). We do this by checking that a user who
* normally would not have view privileges is able to view the nodes when we
* add a record to {node_access} paired with a corresponding privilege in
* hook_node_grants().
*/
public function testNodeQueryAlterOverride() {
$record = [
'nid' => 0,
'gid' => 0,
'realm' => 'node_access_all',
'grant_view' => 1,
'grant_update' => 0,
'grant_delete' => 0,
];
db_insert('node_access')->fields($record)->execute();
// Test that the noAccessUser still doesn't have the 'view'
// privilege after adding the node_access record.
drupal_static_reset('node_access_view_all_nodes');
try {
$query = db_select('node', 'mytab')
->fields('mytab');
$query->addTag('node_access');
$query->addMetaData('op', 'view');
$query->addMetaData('account', $this->noAccessUser);
$result = $query->execute()->fetchAll();
$this->assertEqual(count($result), 0, 'User view privileges are not overridden');
}
catch (\Exception $e) {
$this->fail(t('Altered query is malformed'));
}
// Have node_test_node_grants return a node_access_all privilege,
// to grant the noAccessUser 'view' access. To verify that
// node_access_view_all_nodes is properly checking the specified
// $account instead of the current user, we will log in as
// noAccessUser2.
$this->drupalLogin($this->noAccessUser2);
\Drupal::state()->set('node_access_test.no_access_uid', $this->noAccessUser->id());
drupal_static_reset('node_access_view_all_nodes');
try {
$query = db_select('node', 'mytab')
->fields('mytab');
$query->addTag('node_access');
$query->addMetaData('op', 'view');
$query->addMetaData('account', $this->noAccessUser);
$result = $query->execute()->fetchAll();
$this->assertEqual(count($result), 4, 'User view privileges are overridden');
}
catch (\Exception $e) {
$this->fail(t('Altered query is malformed'));
}
\Drupal::state()->delete('node_access_test.no_access_uid');
}
}

View file

@ -0,0 +1,173 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\Tests\Traits\Core\GeneratePermutationsTrait;
/**
* Tests user permissions for node revisions.
*
* @group node
*/
class NodeRevisionPermissionsTest extends NodeTestBase {
use GeneratePermutationsTrait;
/**
* The node revisions.
*
* @var array
*/
protected $nodeRevisions = [];
/**
* The accounts.
*
* @var array
*/
protected $accounts = [];
// Map revision permission names to node revision access ops.
protected $map = [
'view' => 'view all revisions',
'update' => 'revert all revisions',
'delete' => 'delete all revisions',
];
// Map revision permission names to node type revision access ops.
protected $typeMap = [
'view' => 'view page revisions',
'update' => 'revert page revisions',
'delete' => 'delete page revisions',
];
protected function setUp() {
parent::setUp();
$types = ['page', 'article'];
foreach ($types as $type) {
// Create a node with several revisions.
$nodes[$type] = $this->drupalCreateNode(['type' => $type]);
$this->nodeRevisions[$type][] = $nodes[$type];
for ($i = 0; $i < 3; $i++) {
// Create a revision for the same nid and settings with a random log.
$revision = clone $nodes[$type];
$revision->setNewRevision();
$revision->revision_log = $this->randomMachineName(32);
$revision->save();
$this->nodeRevisions[$type][] = $revision;
}
}
}
/**
* Tests general revision access permissions.
*/
public function testNodeRevisionAccessAnyType() {
// Create three users, one with each revision permission.
foreach ($this->map as $op => $permission) {
// Create the user.
$account = $this->drupalCreateUser(
[
'access content',
'edit any page content',
'delete any page content',
$permission,
]
);
$account->op = $op;
$this->accounts[] = $account;
}
// Create an admin account (returns TRUE for all revision permissions).
$admin_account = $this->drupalCreateUser(['access content', 'administer nodes']);
$admin_account->is_admin = TRUE;
$this->accounts['admin'] = $admin_account;
$accounts['admin'] = $admin_account;
// Create a normal account (returns FALSE for all revision permissions).
$normal_account = $this->drupalCreateUser();
$normal_account->op = FALSE;
$this->accounts[] = $normal_account;
$accounts[] = $normal_account;
$revision = $this->nodeRevisions['page'][1];
$parameters = [
'op' => array_keys($this->map),
'account' => $this->accounts,
];
$permutations = $this->generatePermutations($parameters);
$node_revision_access = \Drupal::service('access_check.node.revision');
foreach ($permutations as $case) {
// Skip this test if there are no revisions for the node.
if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', [':nid' => $revision->id()])->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) {
if (!empty($case['account']->is_admin) || $case['account']->hasPermission($this->map[$case['op']])) {
$this->assertTrue($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->map[$case['op']]} granted.");
}
else {
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->map[$case['op']]} not granted.");
}
}
}
// Test that access is FALSE for a node administrator with an invalid $node
// or $op parameters.
$admin_account = $accounts['admin'];
$this->assertFalse($node_revision_access->checkAccess($revision, $admin_account, 'invalid-op'), 'NodeRevisionAccessCheck() returns FALSE with an invalid op.');
}
/**
* Tests revision access permissions for a specific content type.
*/
public function testNodeRevisionAccessPerType() {
// Create three users, one with each revision permission.
foreach ($this->typeMap as $op => $permission) {
// Create the user.
$account = $this->drupalCreateUser(
[
'access content',
'edit any page content',
'delete any page content',
$permission,
]
);
$account->op = $op;
$accounts[] = $account;
}
$parameters = [
'op' => array_keys($this->typeMap),
'account' => $accounts,
];
// Test that the accounts have access to the corresponding page revision
// permissions.
$revision = $this->nodeRevisions['page'][1];
$permutations = $this->generatePermutations($parameters);
$node_revision_access = \Drupal::service('access_check.node.revision');
foreach ($permutations as $case) {
// Skip this test if there are no revisions for the node.
if (!($revision->isDefaultRevision() && (db_query('SELECT COUNT(vid) FROM {node_field_revision} WHERE nid = :nid', [':nid' => $revision->id()])->fetchField() == 1 || $case['op'] == 'update' || $case['op'] == 'delete'))) {
if (!empty($case['account']->is_admin) || $case['account']->hasPermission($this->typeMap[$case['op']], $case['account'])) {
$this->assertTrue($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} granted.");
}
else {
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} not granted.");
}
}
}
// Test that the accounts have no access to the article revisions.
$revision = $this->nodeRevisions['article'][1];
foreach ($permutations as $case) {
$this->assertFalse($node_revision_access->checkAccess($revision, $case['account'], $case['op']), "{$this->typeMap[$case['op']]} did not grant revision permission for articles.");
}
}
}

View file

@ -11,10 +11,24 @@ use Drupal\node\NodeInterface;
* @group node
*/
class NodeRevisionsAllTest extends NodeTestBase {
protected $nodes;
protected $revisionLogs;
protected $profile = "standard";
/**
* A list of nodes created to be used as starting point of different tests.
*
* @var Drupal\node\NodeInterface[]
*/
protected $nodes;
/**
* Revision logs of nodes created by the setup method.
*
* @var string[]
*/
protected $revisionLogs;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
@ -131,6 +145,11 @@ class NodeRevisionsAllTest extends NodeTestBase {
$node = node_revision_load($node->getRevisionId());
$this->assertFalse($node->isDefaultRevision(), 'Third node revision is not the current one.');
// Confirm that the node can still be updated.
$this->drupalPostForm("node/" . $reverted_node->id() . "/edit", ['body[0][value]' => 'We are Drupal.'], t('Save'));
$this->assertText(t('Basic page @title has been updated.', ['@title' => $reverted_node->getTitle()]), 'Node was successfully saved after reverting a revision.');
$this->assertText('We are Drupal.', 'Node was correctly updated after reverting a revision.');
// Confirm revisions delete properly.
$this->drupalPostForm("node/" . $node->id() . "/revisions/" . $nodes[1]->getRevisionId() . "/delete", [], t('Delete'));
$this->assertRaw(t('Revision from %revision-date of @type %title has been deleted.',

View file

@ -0,0 +1,37 @@
<?php
namespace Drupal\Tests\node\Functional;
/**
* Tests if the syndicate block is available.
*
* @group node
*/
class NodeSyndicateBlockTest extends NodeTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block'];
protected function setUp() {
parent::setUp();
// Create a user and log in.
$admin_user = $this->drupalCreateUser(['administer blocks']);
$this->drupalLogin($admin_user);
}
/**
* Tests that the "Syndicate" block is shown when enabled.
*/
public function testSyndicateBlock() {
// Place the "Syndicate" block and confirm that it is rendered.
$this->drupalPlaceBlock('node_syndicate_block', ['id' => 'test_syndicate_block']);
$this->drupalGet('');
$this->assertFieldByXPath('//div[@id="block-test-syndicate-block"]/*', NULL, 'Syndicate block found.');
}
}

View file

@ -0,0 +1,103 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\comment\Tests\CommentTestTrait;
use Drupal\Component\Utility\Html;
/**
* Tests node title.
*
* @group node
*/
class NodeTitleTest extends NodeTestBase {
use CommentTestTrait;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['comment', 'views', 'block'];
/**
* A user with permission to bypass access content.
*
* @var \Drupal\user\UserInterface
*/
protected $adminUser;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->drupalPlaceBlock('system_breadcrumb_block');
$this->adminUser = $this->drupalCreateUser(['administer nodes', 'create article content', 'create page content', 'post comments']);
$this->drupalLogin($this->adminUser);
$this->addDefaultCommentField('node', 'page');
}
/**
* Creates one node and tests if the node title has the correct value.
*/
public function testNodeTitle() {
// Create "Basic page" content with title.
// Add the node to the frontpage so we can test if teaser links are
// clickable.
$settings = [
'title' => $this->randomMachineName(8),
'promote' => 1,
];
$node = $this->drupalCreateNode($settings);
// Test <title> tag.
$this->drupalGet('node/' . $node->id());
$xpath = '//title';
$this->assertEqual($this->xpath($xpath)[0]->getText(), $node->label() . ' | Drupal', 'Page title is equal to node title.', 'Node');
// Test breadcrumb in comment preview.
$this->drupalGet('comment/reply/node/' . $node->id() . '/comment');
$xpath = '//nav[@class="breadcrumb"]/ol/li[last()]/a';
$this->assertEqual($this->xpath($xpath)[0]->getText(), $node->label(), 'Node breadcrumb is equal to node title.', 'Node');
// Test node title in comment preview.
$this->assertEqual($this->xpath('//article[contains(concat(" ", normalize-space(@class), " "), :node-class)]/h2/a/span', [':node-class' => ' node--type-' . $node->bundle() . ' '])[0]->getText(), $node->label(), 'Node preview title is equal to node title.', 'Node');
// Test node title is clickable on teaser list (/node).
$this->drupalGet('node');
$this->clickLink($node->label());
// Test edge case where node title is set to 0.
$settings = [
'title' => 0,
];
$node = $this->drupalCreateNode($settings);
// Test that 0 appears as <title>.
$this->drupalGet('node/' . $node->id());
$this->assertTitle(0 . ' | Drupal', 'Page title is equal to 0.', 'Node');
// Test that 0 appears in the template <h1>.
$xpath = '//h1';
$this->assertEqual(current($this->xpath($xpath)), 0, 'Node title is displayed as 0.', 'Node');
// Test edge case where node title contains special characters.
$edge_case_title = 'article\'s "title".';
$settings = [
'title' => $edge_case_title,
];
$node = $this->drupalCreateNode($settings);
// Test that the title appears as <title>. The title will be escaped on the
// the page.
$edge_case_title_escaped = Html::escape($edge_case_title);
$this->drupalGet('node/' . $node->id());
$this->assertRaw('<title>' . $edge_case_title_escaped . ' | Drupal</title>', 'Page title is equal to article\'s "title".', 'Node');
// Test that the title appears as <title> when reloading the node page.
$this->drupalGet('node/' . $node->id());
$this->assertRaw('<title>' . $edge_case_title_escaped . ' | Drupal</title>', 'Page title is equal to article\'s "title".', 'Node');
}
}

View file

@ -0,0 +1,43 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\Component\Utility\Html;
/**
* Create a node with dangerous tags in its title and test that they are
* escaped.
*
* @group node
*/
class NodeTitleXSSTest extends NodeTestBase {
/**
* Tests XSS functionality with a node entity.
*/
public function testNodeTitleXSS() {
// Prepare a user to do the stuff.
$web_user = $this->drupalCreateUser(['create page content', 'edit any page content']);
$this->drupalLogin($web_user);
$xss = '<script>alert("xss")</script>';
$title = $xss . $this->randomMachineName();
$edit = [];
$edit['title[0][value]'] = $title;
$this->drupalPostForm('node/add/page', $edit, t('Preview'));
$this->assertNoRaw($xss, 'Harmful tags are escaped when previewing a node.');
$settings = ['title' => $title];
$node = $this->drupalCreateNode($settings);
$this->drupalGet('node/' . $node->id());
// Titles should be escaped.
$this->assertRaw('<title>' . Html::escape($title) . ' | Drupal</title>', 'Title is displayed when viewing a node.');
$this->assertNoRaw($xss, 'Harmful tags are escaped when viewing a node.');
$this->drupalGet('node/' . $node->id() . '/edit');
$this->assertNoRaw($xss, 'Harmful tags are escaped when editing a node.');
}
}

View file

@ -0,0 +1,512 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Tests\content_translation\Functional\ContentTranslationUITestBase;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests the Node Translation UI.
*
* @group node
*/
class NodeTranslationUITest extends ContentTranslationUITestBase {
/**
* {inheritdoc}
*/
protected $defaultCacheContexts = [
'languages:language_interface',
'session',
'theme',
'route',
'timezone',
'url.path.parent',
'url.query_args:_wrapper_format',
'user'
];
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['block', 'language', 'content_translation', 'node', 'datetime', 'field_ui', 'help'];
/**
* The profile to install as a basis for testing.
*
* @var string
*/
protected $profile = 'standard';
protected function setUp() {
$this->entityTypeId = 'node';
$this->bundle = 'article';
parent::setUp();
// Ensure the help message is shown even with prefixed paths.
$this->drupalPlaceBlock('help_block', ['region' => 'content']);
// Display the language selector.
$this->drupalLogin($this->administrator);
$edit = ['language_configuration[language_alterable]' => TRUE];
$this->drupalPostForm('admin/structure/types/manage/article', $edit, t('Save content type'));
$this->drupalLogin($this->translator);
}
/**
* Tests the basic translation UI.
*/
public function testTranslationUI() {
parent::testTranslationUI();
$this->doUninstallTest();
}
/**
* Tests changing the published status on a node without fields.
*/
public function testPublishedStatusNoFields() {
// Test changing the published status of an article without fields.
$this->drupalLogin($this->administrator);
// Delete all fields.
$this->drupalGet('admin/structure/types/manage/article/fields');
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.' . $this->fieldName . '/delete', [], t('Delete'));
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_tags/delete', [], t('Delete'));
$this->drupalPostForm('admin/structure/types/manage/article/fields/node.article.field_image/delete', [], t('Delete'));
// Add a node.
$default_langcode = $this->langcodes[0];
$values[$default_langcode] = ['title' => [['value' => $this->randomMachineName()]]];
$this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
// Add a content translation.
$langcode = 'fr';
$language = ConfigurableLanguage::load($langcode);
$values[$langcode] = ['title' => [['value' => $this->randomMachineName()]]];
$entity_type_id = $entity->getEntityTypeId();
$add_url = Url::fromRoute("entity.$entity_type_id.content_translation_add", [
$entity->getEntityTypeId() => $entity->id(),
'source' => $default_langcode,
'target' => $langcode
], ['language' => $language]);
$this->drupalPostForm($add_url, $this->getEditValues($values, $langcode), t('Save and unpublish (this translation)'));
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$translation = $entity->getTranslation($langcode);
// Make sure we unpublished the node correctly.
$this->assertFalse($this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
}
/**
* {@inheritdoc}
*/
protected function getTranslatorPermissions() {
return array_merge(parent::getTranslatorPermissions(), ['administer nodes', "edit any $this->bundle content"]);
}
/**
* {@inheritdoc}
*/
protected function getEditorPermissions() {
return ['administer nodes', 'create article content'];
}
/**
* {@inheritdoc}
*/
protected function getAdministratorPermissions() {
return array_merge(parent::getAdministratorPermissions(), ['access administration pages', 'administer content types', 'administer node fields', 'access content overview', 'bypass node access', 'administer languages', 'administer themes', 'view the administration theme']);
}
/**
* {@inheritdoc}
*/
protected function getNewEntityValues($langcode) {
return ['title' => [['value' => $this->randomMachineName()]]] + parent::getNewEntityValues($langcode);
}
/**
* {@inheritdoc}
*/
protected function getFormSubmitAction(EntityInterface $entity, $langcode) {
if ($entity->getTranslation($langcode)->isPublished()) {
return t('Save and keep published') . $this->getFormSubmitSuffix($entity, $langcode);
}
else {
return t('Save and keep unpublished') . $this->getFormSubmitSuffix($entity, $langcode);
}
}
/**
* {@inheritdoc}
*/
protected function doTestPublishedStatus() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
$actions = [
t('Save and keep published'),
t('Save and unpublish'),
];
foreach ($actions as $index => $action) {
// (Un)publish the node translations and check that the translation
// statuses are (un)published accordingly.
foreach ($this->langcodes as $langcode) {
$options = ['language' => $languages[$langcode]];
$url = $entity->urlInfo('edit-form', $options);
$this->drupalPostForm($url, [], $action . $this->getFormSubmitSuffix($entity, $langcode), $options);
}
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
foreach ($this->langcodes as $langcode) {
// The node is created as unpublished thus we switch to the published
// status first.
$status = !$index;
$translation = $entity->getTranslation($langcode);
$this->assertEqual($status, $this->manager->getTranslationMetadata($translation)->isPublished(), 'The translation has been correctly unpublished.');
}
}
}
/**
* {@inheritdoc}
*/
protected function doTestAuthoringInfo() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
$values = [];
// Post different base field information for each translation.
foreach ($this->langcodes as $langcode) {
$user = $this->drupalCreateUser();
$values[$langcode] = [
'uid' => $user->id(),
'created' => REQUEST_TIME - mt_rand(0, 1000),
'sticky' => (bool) mt_rand(0, 1),
'promote' => (bool) mt_rand(0, 1),
];
$edit = [
'uid[0][target_id]' => $user->getUsername(),
'created[0][value][date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'),
'created[0][value][time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'),
'sticky[value]' => $values[$langcode]['sticky'],
'promote[value]' => $values[$langcode]['promote'],
];
$options = ['language' => $languages[$langcode]];
$url = $entity->urlInfo('edit-form', $options);
$this->drupalPostForm($url, $edit, $this->getFormSubmitAction($entity, $langcode), $options);
}
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
foreach ($this->langcodes as $langcode) {
$translation = $entity->getTranslation($langcode);
$metadata = $this->manager->getTranslationMetadata($translation);
$this->assertEqual($metadata->getAuthor()->id(), $values[$langcode]['uid'], 'Translation author correctly stored.');
$this->assertEqual($metadata->getCreatedTime(), $values[$langcode]['created'], 'Translation date correctly stored.');
$this->assertEqual($translation->isSticky(), $values[$langcode]['sticky'], 'Sticky of Translation correctly stored.');
$this->assertEqual($translation->isPromoted(), $values[$langcode]['promote'], 'Promoted of Translation correctly stored.');
}
}
/**
* Tests that translation page inherits admin status of edit page.
*/
public function testTranslationLinkTheme() {
$this->drupalLogin($this->administrator);
$article = $this->drupalCreateNode(['type' => 'article', 'langcode' => $this->langcodes[0]]);
// Set up Seven as the admin theme and use it for node editing.
$this->container->get('theme_handler')->install(['seven']);
$edit = [];
$edit['admin_theme'] = 'seven';
$edit['use_admin_theme'] = TRUE;
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
$this->drupalGet('node/' . $article->id() . '/translations');
$this->assertRaw('core/themes/seven/css/base/elements.css', 'Translation uses admin theme if edit is admin.');
// Turn off admin theme for editing, assert inheritance to translations.
$edit['use_admin_theme'] = FALSE;
$this->drupalPostForm('admin/appearance', $edit, t('Save configuration'));
$this->drupalGet('node/' . $article->id() . '/translations');
$this->assertNoRaw('core/themes/seven/css/base/elements.css', 'Translation uses frontend theme if edit is frontend.');
// Assert presence of translation page itself (vs. DisabledBundle below).
$this->assertResponse(200);
}
/**
* Tests that no metadata is stored for a disabled bundle.
*/
public function testDisabledBundle() {
// Create a bundle that does not have translation enabled.
$disabledBundle = $this->randomMachineName();
$this->drupalCreateContentType(['type' => $disabledBundle, 'name' => $disabledBundle]);
// Create a node for each bundle.
$node = $this->drupalCreateNode([
'type' => $this->bundle,
'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
]);
// Make sure that nothing was inserted into the {content_translation} table.
$rows = db_query('SELECT nid, count(nid) AS count FROM {node_field_data} WHERE type <> :type GROUP BY nid HAVING count(nid) >= 2', [':type' => $this->bundle])->fetchAll();
$this->assertEqual(0, count($rows));
// Ensure the translation tab is not accessible.
$this->drupalGet('node/' . $node->id() . '/translations');
$this->assertResponse(403);
}
/**
* Tests that translations are rendered properly.
*/
public function testTranslationRendering() {
$default_langcode = $this->langcodes[0];
$values[$default_langcode] = $this->getNewEntityValues($default_langcode);
$this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
$node = \Drupal::entityManager()->getStorage($this->entityTypeId)->load($this->entityId);
$node->setPromoted(TRUE);
// Create translations.
foreach (array_diff($this->langcodes, [$default_langcode]) as $langcode) {
$values[$langcode] = $this->getNewEntityValues($langcode);
$translation = $node->addTranslation($langcode, $values[$langcode]);
// Publish and promote the translation to frontpage.
$translation->setPromoted(TRUE);
$translation->setPublished(TRUE);
}
$node->save();
// Test that the frontpage view displays the correct translations.
\Drupal::service('module_installer')->install(['views'], TRUE);
$this->rebuildContainer();
$this->doTestTranslations('node', $values);
// Enable the translation language renderer.
$view = \Drupal::entityManager()->getStorage('view')->load('frontpage');
$display = &$view->getDisplay('default');
$display['display_options']['rendering_language'] = '***LANGUAGE_entity_translation***';
$view->save();
// Need to check from the beginning, including the base_path, in the url
// since the pattern for the default language might be a substring of
// the strings for other languages.
$base_path = base_path();
// Check the frontpage for 'Read more' links to each translation.
// See also assertTaxonomyPage() in NodeAccessBaseTableTest.
$node_href = 'node/' . $node->id();
foreach ($this->langcodes as $langcode) {
$this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]);
$num_match_found = 0;
if ($langcode == 'en') {
// Site default language does not have langcode prefix in the URL.
$expected_href = $base_path . $node_href;
}
else {
$expected_href = $base_path . $langcode . '/' . $node_href;
}
$pattern = '|^' . $expected_href . '$|';
foreach ($this->xpath("//a[text()='Read more']") as $link) {
if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) {
$num_match_found++;
}
}
$this->assertTrue($num_match_found == 1, 'There is 1 Read more link, ' . $expected_href . ', for the ' . $langcode . ' translation of a node on the frontpage. (Found ' . $num_match_found . '.)');
}
// Check the frontpage for 'Add new comment' links that include the
// language.
$comment_form_href = 'node/' . $node->id() . '#comment-form';
foreach ($this->langcodes as $langcode) {
$this->drupalGet('node', ['language' => \Drupal::languageManager()->getLanguage($langcode)]);
$num_match_found = 0;
if ($langcode == 'en') {
// Site default language does not have langcode prefix in the URL.
$expected_href = $base_path . $comment_form_href;
}
else {
$expected_href = $base_path . $langcode . '/' . $comment_form_href;
}
$pattern = '|^' . $expected_href . '$|';
foreach ($this->xpath("//a[text()='Add new comment']") as $link) {
if (preg_match($pattern, $link->getAttribute('href'), $matches) == TRUE) {
$num_match_found++;
}
}
$this->assertTrue($num_match_found == 1, 'There is 1 Add new comment link, ' . $expected_href . ', for the ' . $langcode . ' translation of a node on the frontpage. (Found ' . $num_match_found . '.)');
}
// Test that the node page displays the correct translations.
$this->doTestTranslations('node/' . $node->id(), $values);
// Test that the node page has the correct alternate hreflang links.
$this->doTestAlternateHreflangLinks($node->urlInfo());
}
/**
* Tests that the given path displays the correct translation values.
*
* @param string $path
* The path to be tested.
* @param array $values
* The translation values to be found.
*/
protected function doTestTranslations($path, array $values) {
$languages = $this->container->get('language_manager')->getLanguages();
foreach ($this->langcodes as $langcode) {
$this->drupalGet($path, ['language' => $languages[$langcode]]);
$this->assertText($values[$langcode]['title'][0]['value'], format_string('The %langcode node translation is correctly displayed.', ['%langcode' => $langcode]));
}
}
/**
* Tests that the given path provides the correct alternate hreflang links.
*
* @param \Drupal\Core\Url $url
* The path to be tested.
*/
protected function doTestAlternateHreflangLinks(Url $url) {
$languages = $this->container->get('language_manager')->getLanguages();
$url->setAbsolute();
$urls = [];
foreach ($this->langcodes as $langcode) {
$language_url = clone $url;
$urls[$langcode] = $language_url->setOption('language', $languages[$langcode]);
}
foreach ($this->langcodes as $langcode) {
$this->drupalGet($urls[$langcode]);
foreach ($urls as $alternate_langcode => $language_url) {
// Retrieve desired link elements from the HTML head.
$links = $this->xpath('head/link[@rel = "alternate" and @href = :href and @hreflang = :hreflang]',
[':href' => $language_url->toString(), ':hreflang' => $alternate_langcode]);
$this->assert(isset($links[0]), format_string('The %langcode node translation has the correct alternate hreflang link for %alternate_langcode: %link.', ['%langcode' => $langcode, '%alternate_langcode' => $alternate_langcode, '%link' => $url->toString()]));
}
}
}
/**
* {@inheritdoc}
*/
protected function getFormSubmitSuffix(EntityInterface $entity, $langcode) {
if (!$entity->isNew() && $entity->isTranslatable()) {
$translations = $entity->getTranslationLanguages();
if ((count($translations) > 1 || !isset($translations[$langcode])) && ($field = $entity->getFieldDefinition('status'))) {
return ' ' . ($field->isTranslatable() ? t('(this translation)') : t('(all translations)'));
}
}
return '';
}
/**
* Tests uninstalling content_translation.
*/
protected function doUninstallTest() {
// Delete all the nodes so there is no data.
$nodes = Node::loadMultiple();
foreach ($nodes as $node) {
$node->delete();
}
$language_count = count(\Drupal::configFactory()->listAll('language.content_settings.'));
\Drupal::service('module_installer')->uninstall(['content_translation']);
$this->rebuildContainer();
$this->assertEqual($language_count, count(\Drupal::configFactory()->listAll('language.content_settings.')), 'Languages have been fixed rather than deleted during content_translation uninstall.');
}
/**
* {@inheritdoc}
*/
protected function doTestTranslationEdit() {
$storage = $this->container->get('entity_type.manager')
->getStorage($this->entityTypeId);
$storage->resetCache([$this->entityId]);
$entity = $storage->load($this->entityId);
$languages = $this->container->get('language_manager')->getLanguages();
$type_name = node_get_type_label($entity);
foreach ($this->langcodes as $langcode) {
// We only want to test the title for non-english translations.
if ($langcode != 'en') {
$options = ['language' => $languages[$langcode]];
$url = $entity->urlInfo('edit-form', $options);
$this->drupalGet($url);
$title = t('<em>Edit @type</em> @title [%language translation]', [
'@type' => $type_name,
'@title' => $entity->getTranslation($langcode)->label(),
'%language' => $languages[$langcode]->getName(),
]);
$this->assertRaw($title);
}
}
}
/**
* Tests that revision translations are rendered properly.
*/
public function testRevisionTranslationRendering() {
$storage = \Drupal::entityTypeManager()->getStorage('node');
// Create a node.
$nid = $this->createEntity(['title' => 'First rev en title'], 'en');
$node = $storage->load($nid);
$original_revision_id = $node->getRevisionId();
// Add a French translation.
$translation = $node->addTranslation('fr');
$translation->title = 'First rev fr title';
$translation->setNewRevision(FALSE);
$translation->save();
// Create a new revision.
$node->title = 'Second rev en title';
$node->setNewRevision(TRUE);
$node->save();
// Get an English view of this revision.
$original_revision = $storage->loadRevision($original_revision_id);
$original_revision_url = $original_revision->toUrl('revision')->toString();
// Should be different from regular node URL.
$this->assertNotIdentical($original_revision_url, $original_revision->toUrl()->toString());
$this->drupalGet($original_revision_url);
$this->assertResponse(200);
// Contents should be in English, of correct revision.
$this->assertText('First rev en title');
$this->assertNoText('First rev fr title');
// Get a French view.
$url_fr = $original_revision->getTranslation('fr')->toUrl('revision')->toString();
// Should have different URL from English.
$this->assertNotIdentical($url_fr, $original_revision->toUrl()->toString());
$this->assertNotIdentical($url_fr, $original_revision_url);
$this->drupalGet($url_fr);
$this->assertResponse(200);
// Contents should be in French, of correct revision.
$this->assertText('First rev fr title');
$this->assertNoText('First rev en title');
}
}

View file

@ -0,0 +1,98 @@
<?php
namespace Drupal\Tests\node\Functional;
use Drupal\Component\Utility\Html;
/**
* Tests the node/{node} page.
*
* @group node
* @see \Drupal\node\Controller\NodeController
*/
class NodeViewTest extends NodeTestBase {
/**
* Tests the html head links.
*/
public function testHtmlHeadLinks() {
$node = $this->drupalCreateNode();
$this->drupalGet($node->urlInfo());
$result = $this->xpath('//link[@rel = "canonical"]');
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
// Link relations are checked for access for anonymous users.
$result = $this->xpath('//link[@rel = "version-history"]');
$this->assertFalse($result, 'Version history not present for anonymous users without access.');
$result = $this->xpath('//link[@rel = "edit-form"]');
$this->assertFalse($result, 'Edit form not present for anonymous users without access.');
$this->drupalLogin($this->createUser(['access content']));
$this->drupalGet($node->urlInfo());
$result = $this->xpath('//link[@rel = "canonical"]');
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
// Link relations are present regardless of access for authenticated users.
$result = $this->xpath('//link[@rel = "version-history"]');
$this->assertEqual($result[0]->getAttribute('href'), $node->url('version-history'));
$result = $this->xpath('//link[@rel = "edit-form"]');
$this->assertEqual($result[0]->getAttribute('href'), $node->url('edit-form'));
// Give anonymous users access to edit the node. Do this through the UI to
// ensure caches are handled properly.
$this->drupalLogin($this->rootUser);
$edit = [
'anonymous[edit own ' . $node->bundle() . ' content]' => TRUE
];
$this->drupalPostForm('admin/people/permissions', $edit, 'Save permissions');
$this->drupalLogout();
// Anonymous user's should now see the edit-form link but not the
// version-history link.
$this->drupalGet($node->urlInfo());
$result = $this->xpath('//link[@rel = "canonical"]');
$this->assertEqual($result[0]->getAttribute('href'), $node->url());
$result = $this->xpath('//link[@rel = "version-history"]');
$this->assertFalse($result, 'Version history not present for anonymous users without access.');
$result = $this->xpath('//link[@rel = "edit-form"]');
$this->assertEqual($result[0]->getAttribute('href'), $node->url('edit-form'));
}
/**
* Tests the Link header.
*/
public function testLinkHeader() {
$node = $this->drupalCreateNode();
$expected = [
'<' . Html::escape($node->url('canonical')) . '>; rel="canonical"',
'<' . Html::escape($node->url('canonical'), ['alias' => TRUE]) . '>; rel="shortlink"',
'<' . Html::escape($node->url('revision')) . '>; rel="revision"',
];
$this->drupalGet($node->urlInfo());
$links = $this->drupalGetHeaders()['Link'];
$this->assertEqual($links, $expected);
}
/**
* Tests that we store and retrieve multi-byte UTF-8 characters correctly.
*/
public function testMultiByteUtf8() {
$title = '🐝';
$this->assertTrue(mb_strlen($title, 'utf-8') < strlen($title), 'Title has multi-byte characters.');
$node = $this->drupalCreateNode(['title' => $title]);
$this->drupalGet($node->urlInfo());
$result = $this->xpath('//span[contains(@class, "field--name-title")]');
$this->assertEqual($result[0]->getText(), $title, 'The passed title was returned.');
}
}

View file

@ -0,0 +1,177 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Tests if entity access is respected on a node bulk operations form.
*
* @group node
* @see \Drupal\node\Plugin\views\field\BulkForm
* @see \Drupal\node\Tests\NodeTestBase
* @see \Drupal\node\Tests\NodeAccessBaseTableTest
* @see \Drupal\node\Tests\Views\BulkFormTest
*/
class BulkFormAccessTest extends NodeTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node_test_views', 'node_access_test'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_node_bulk_form'];
/**
* The node access control handler.
*
* @var \Drupal\Core\Entity\EntityAccessControlHandlerInterface
*/
protected $accessHandler;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
// Create Article node type.
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
$this->accessHandler = \Drupal::entityManager()->getAccessControlHandler('node');
node_access_test_add_field(NodeType::load('article'));
// After enabling a node access module, the access table has to be rebuild.
node_access_rebuild();
// Enable the private node feature of the node_access_test module.
\Drupal::state()->set('node_access_test.private', TRUE);
}
/**
* Tests if nodes that may not be edited, can not be edited in bulk.
*/
public function testNodeEditAccess() {
// Create an account who will be the author of a private node.
$author = $this->drupalCreateUser();
// Create a private node (author may view, edit and delete, others may not).
$node = $this->drupalCreateNode([
'type' => 'article',
'private' => [[
'value' => TRUE,
]],
'uid' => $author->id(),
]);
// Create an account that may view the private node, but not edit it.
$account = $this->drupalCreateUser(['node test view']);
$this->drupalLogin($account);
// Ensure the node is published.
$this->assertTrue($node->isPublished(), 'Node is initially published.');
// Ensure that the node can not be edited.
$this->assertEqual(FALSE, $this->accessHandler->access($node, 'update', $account), 'The node may not be edited.');
// Test editing the node using the bulk form.
$edit = [
'node_bulk_form[0]' => TRUE,
'action' => 'node_unpublish_action',
];
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
$this->assertRaw(SafeMarkup::format('No access to execute %action on the @entity_type_label %entity_label.', [
'%action' => 'Unpublish content',
'@entity_type_label' => 'Content',
'%entity_label' => $node->label(),
]));
// Re-load the node and check the status.
$node = Node::load($node->id());
$this->assertTrue($node->isPublished(), 'The node is still published.');
// Create an account that may view the private node, but can update the
// status.
$account = $this->drupalCreateUser(['administer nodes', 'node test view']);
$this->drupalLogin($account);
// Ensure the node is published.
$this->assertTrue($node->isPublished(), 'Node is initially published.');
// Ensure that the private node can not be edited.
$this->assertEqual(FALSE, $node->access('update', $account), 'The node may not be edited.');
$this->assertEqual(TRUE, $node->status->access('edit', $account), 'The node status can be edited.');
// Test editing the node using the bulk form.
$edit = [
'node_bulk_form[0]' => TRUE,
'action' => 'node_unpublish_action',
];
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
// Test that the action message isn't shown.
$this->assertNoRaw(SafeMarkup::format('%action was applied to 1 item.', [
'%action' => 'Unpublish content',
]));
// Re-load the node and check the status.
$node = Node::load($node->id());
$this->assertTrue($node->isPublished(), 'The node is still published.');
}
/**
* Tests if nodes that may not be deleted, can not be deleted in bulk.
*/
public function testNodeDeleteAccess() {
// Create an account who will be the author of a private node.
$author = $this->drupalCreateUser();
// Create a private node (author may view, edit and delete, others may not).
$private_node = $this->drupalCreateNode([
'type' => 'article',
'private' => [[
'value' => TRUE,
]],
'uid' => $author->id(),
]);
// Create an account that may view the private node, but not delete it.
$account = $this->drupalCreateUser(['access content', 'administer nodes', 'delete own article content', 'node test view']);
// Create a node that may be deleted too, to ensure the delete confirmation
// page is shown later. In node_access_test.module, nodes may only be
// deleted by the author.
$own_node = $this->drupalCreateNode([
'type' => 'article',
'private' => [[
'value' => TRUE,
]],
'uid' => $account->id(),
]);
$this->drupalLogin($account);
// Ensure that the private node can not be deleted.
$this->assertEqual(FALSE, $this->accessHandler->access($private_node, 'delete', $account), 'The private node may not be deleted.');
// Ensure that the public node may be deleted.
$this->assertEqual(TRUE, $this->accessHandler->access($own_node, 'delete', $account), 'The own node may be deleted.');
// Try to delete the node using the bulk form.
$edit = [
'node_bulk_form[0]' => TRUE,
'node_bulk_form[1]' => TRUE,
'action' => 'node_delete_action',
];
$this->drupalPostForm('test-node-bulk-form', $edit, t('Apply to selected items'));
$this->drupalPostForm(NULL, [], t('Delete'));
// Ensure the private node still exists.
$private_node = Node::load($private_node->id());
$this->assertNotNull($private_node, 'The private node has not been deleted.');
// Ensure the own node is deleted.
$own_node = Node::load($own_node->id());
$this->assertNull($own_node, 'The own node is deleted.');
}
}

View file

@ -0,0 +1,275 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\Component\Utility\SafeMarkup;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\views\Views;
/**
* Tests a node bulk form.
*
* @group node
* @see \Drupal\node\Plugin\views\field\BulkForm
*/
class BulkFormTest extends NodeTestBase {
/**
* Modules to be enabled.
*
* @var array
*/
public static $modules = ['node_test_views', 'language'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_node_bulk_form'];
/**
* The test nodes.
*
* @var \Drupal\node\NodeInterface[]
*/
protected $nodes;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
ConfigurableLanguage::createFromLangcode('en-gb')->save();
ConfigurableLanguage::createFromLangcode('it')->save();
// Create some test nodes.
$this->nodes = [];
$langcodes = ['en', 'en-gb', 'it'];
for ($i = 1; $i <= 5; $i++) {
$langcode = $langcodes[($i - 1) % 3];
$values = [
'title' => $this->randomMachineName() . ' [' . $i . ':' . $langcode . ']',
'langcode' => $langcode,
'promote' => FALSE,
];
$node = $this->drupalCreateNode($values);
$this->pass(SafeMarkup::format('Node %title created with language %langcode.', ['%title' => $node->label(), '%langcode' => $node->language()->getId()]));
$this->nodes[] = $node;
}
// Create translations for all languages for some nodes.
for ($i = 0; $i < 2; $i++) {
$node = $this->nodes[$i];
foreach ($langcodes as $langcode) {
if (!$node->hasTranslation($langcode)) {
$title = $this->randomMachineName() . ' [' . $node->id() . ':' . $langcode . ']';
$translation = $node->addTranslation($langcode, ['title' => $title, 'promote' => FALSE]);
$this->pass(SafeMarkup::format('Translation %title created with language %langcode.', ['%title' => $translation->label(), '%langcode' => $translation->language()->getId()]));
}
}
$node->save();
}
// Create a node with only one translation.
$node = $this->nodes[2];
$langcode = 'en';
$title = $this->randomMachineName() . ' [' . $node->id() . ':' . $langcode . ']';
$translation = $node->addTranslation($langcode, ['title' => $title]);
$this->pass(SafeMarkup::format('Translation %title created with language %langcode.', ['%title' => $translation->label(), '%langcode' => $translation->language()->getId()]));
$node->save();
// Check that all created translations are selected by the test view.
$view = Views::getView('test_node_bulk_form');
$view->execute();
$this->assertEqual(count($view->result), 10, 'All created translations are selected.');
// Check the operations are accessible to the logged in user.
$this->drupalLogin($this->drupalCreateUser(['administer nodes', 'access content overview', 'bypass node access']));
$this->drupalGet('test-node-bulk-form');
$elements = $this->xpath('//select[@id="edit-action"]//option');
$this->assertIdentical(count($elements), 8, 'All node operations are found.');
}
/**
* Tests the node bulk form.
*/
public function testBulkForm() {
// Unpublish a node using the bulk form.
$node = reset($this->nodes);
$this->assertTrue($node->isPublished(), 'Node is initially published');
$this->assertTrue($node->getTranslation('en-gb')->isPublished(), 'Node translation is published');
$this->assertTrue($node->getTranslation('it')->isPublished(), 'Node translation is published');
$edit = [
'node_bulk_form[0]' => TRUE,
'action' => 'node_unpublish_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$node = $this->loadNode($node->id());
$this->assertFalse($node->isPublished(), 'Node has been unpublished');
$this->assertTrue($node->getTranslation('en-gb')->isPublished(), 'Node translation has not been unpublished');
$this->assertTrue($node->getTranslation('it')->isPublished(), 'Node translation has not been unpublished');
// Publish action.
$edit = [
'node_bulk_form[0]' => TRUE,
'action' => 'node_publish_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$node = $this->loadNode($node->id());
$this->assertTrue($node->isPublished(), 'Node has been published again');
// Make sticky action.
$this->assertFalse($node->isSticky(), 'Node is not sticky');
$this->assertFalse($node->getTranslation('en-gb')->isSticky(), 'Node translation is not sticky');
$this->assertFalse($node->getTranslation('it')->isSticky(), 'Node translation is not sticky');
$edit = [
'node_bulk_form[0]' => TRUE,
'action' => 'node_make_sticky_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$node = $this->loadNode($node->id());
$this->assertTrue($node->isSticky(), 'Node has been made sticky');
$this->assertFalse($node->getTranslation('en-gb')->isSticky(), 'Node translation has not been made sticky');
$this->assertFalse($node->getTranslation('it')->isSticky(), 'Node translation has not been made sticky');
// Make unsticky action.
$edit = [
'node_bulk_form[0]' => TRUE,
'action' => 'node_make_unsticky_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$node = $this->loadNode($node->id());
$this->assertFalse($node->isSticky(), 'Node is not sticky anymore');
// Promote to front page.
$this->assertFalse($node->isPromoted(), 'Node is not promoted to the front page');
$this->assertFalse($node->getTranslation('en-gb')->isPromoted(), 'Node translation is not promoted to the front page');
$this->assertFalse($node->getTranslation('it')->isPromoted(), 'Node translation is not promoted to the front page');
$edit = [
'node_bulk_form[0]' => TRUE,
'action' => 'node_promote_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$node = $this->loadNode($node->id());
$this->assertTrue($node->isPromoted(), 'Node has been promoted to the front page');
$this->assertFalse($node->getTranslation('en-gb')->isPromoted(), 'Node translation has not been promoted to the front page');
$this->assertFalse($node->getTranslation('it')->isPromoted(), 'Node translation has not been promoted to the front page');
// Demote from front page.
$edit = [
'node_bulk_form[0]' => TRUE,
'action' => 'node_unpromote_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$node = $this->loadNode($node->id());
$this->assertFalse($node->isPromoted(), 'Node has been demoted');
// Select a bunch of translated and untranslated nodes and check that
// operations are always applied to individual translations.
$edit = [
// Original and all translations.
'node_bulk_form[0]' => TRUE, // Node 1, English, original.
'node_bulk_form[1]' => TRUE, // Node 1, British English.
'node_bulk_form[2]' => TRUE, // Node 1, Italian.
// Original and only one translation.
'node_bulk_form[3]' => TRUE, // Node 2, English.
'node_bulk_form[4]' => TRUE, // Node 2, British English, original.
'node_bulk_form[5]' => FALSE, // Node 2, Italian.
// Only a single translation.
'node_bulk_form[6]' => TRUE, // Node 3, English.
'node_bulk_form[7]' => FALSE, // Node 3, Italian, original.
// Only a single untranslated node.
'node_bulk_form[8]' => TRUE, // Node 4, English, untranslated.
'node_bulk_form[9]' => FALSE, // Node 5, British English, untranslated.
'action' => 'node_unpublish_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$node = $this->loadNode(1);
$this->assertFalse($node->getTranslation('en')->isPublished(), '1: English translation has been unpublished');
$this->assertFalse($node->getTranslation('en-gb')->isPublished(), '1: British English translation has been unpublished');
$this->assertFalse($node->getTranslation('it')->isPublished(), '1: Italian translation has been unpublished');
$node = $this->loadNode(2);
$this->assertFalse($node->getTranslation('en')->isPublished(), '2: English translation has been unpublished');
$this->assertFalse($node->getTranslation('en-gb')->isPublished(), '2: British English translation has been unpublished');
$this->assertTrue($node->getTranslation('it')->isPublished(), '2: Italian translation has not been unpublished');
$node = $this->loadNode(3);
$this->assertFalse($node->getTranslation('en')->isPublished(), '3: English translation has been unpublished');
$this->assertTrue($node->getTranslation('it')->isPublished(), '3: Italian translation has not been unpublished');
$node = $this->loadNode(4);
$this->assertFalse($node->isPublished(), '4: Node has been unpublished');
$node = $this->loadNode(5);
$this->assertTrue($node->isPublished(), '5: Node has not been unpublished');
}
/**
* Test multiple deletion.
*/
public function testBulkDeletion() {
// Select a bunch of translated and untranslated nodes and check that
// nodes and individual translations are properly deleted.
$edit = [
// Original and all translations.
'node_bulk_form[0]' => TRUE, // Node 1, English, original.
'node_bulk_form[1]' => TRUE, // Node 1, British English.
'node_bulk_form[2]' => TRUE, // Node 1, Italian.
// Original and only one translation.
'node_bulk_form[3]' => TRUE, // Node 2, English.
'node_bulk_form[4]' => TRUE, // Node 2, British English, original.
'node_bulk_form[5]' => FALSE, // Node 2, Italian.
// Only a single translation.
'node_bulk_form[6]' => TRUE, // Node 3, English.
'node_bulk_form[7]' => FALSE, // Node 3, Italian, original.
// Only a single untranslated node.
'node_bulk_form[8]' => TRUE, // Node 4, English, untranslated.
'node_bulk_form[9]' => FALSE, // Node 5, British English, untranslated.
'action' => 'node_delete_action',
];
$this->drupalPostForm(NULL, $edit, t('Apply to selected items'));
$label = $this->loadNode(1)->label();
$this->assertText("$label (Original translation) - The following content translations will be deleted:");
$label = $this->loadNode(2)->label();
$this->assertText("$label (Original translation) - The following content translations will be deleted:");
$label = $this->loadNode(3)->getTranslation('en')->label();
$this->assertText($label);
$this->assertNoText("$label (Original translation) - The following content translations will be deleted:");
$label = $this->loadNode(4)->label();
$this->assertText($label);
$this->assertNoText("$label (Original translation) - The following content translations will be deleted:");
$this->drupalPostForm(NULL, [], t('Delete'));
$node = $this->loadNode(1);
$this->assertNull($node, '1: Node has been deleted');
$node = $this->loadNode(2);
$this->assertNull($node, '2: Node has been deleted');
$node = $this->loadNode(3);
$result = count($node->getTranslationLanguages()) && $node->language()->getId() == 'it';
$this->assertTrue($result, '3: English translation has been deleted');
$node = $this->loadNode(4);
$this->assertNull($node, '4: Node has been deleted');
$node = $this->loadNode(5);
$this->assertTrue($node, '5: Node has not been deleted');
$this->assertText('Deleted 8 posts.');
}
/**
* Load the specified node from the storage.
*
* @param int $id
* The node identifier.
*
* @return \Drupal\node\NodeInterface
* The loaded node.
*/
protected function loadNode($id) {
/** @var \Drupal\node\NodeStorage $storage */
$storage = $this->container->get('entity.manager')->getStorage('node');
$storage->resetCache([$id]);
return $storage->load($id);
}
}

View file

@ -0,0 +1,114 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\node\Entity\NodeType;
/**
* Tests the node_access filter handler.
*
* @group node
* @see \Drupal\node\Plugin\views\filter\Access
*/
class FilterNodeAccessTest extends NodeTestBase {
/**
* An array of users.
*
* @var \Drupal\user\Entity\User[]
*/
protected $users;
/**
* {@inheritdoc}
*/
public static $modules = ['node_access_test'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_filter_node_access'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->drupalCreateContentType(['type' => 'article', 'name' => 'Article']);
node_access_test_add_field(NodeType::load('article'));
node_access_rebuild();
\Drupal::state()->set('node_access_test.private', TRUE);
$num_simple_users = 2;
$this->users = [];
for ($i = 0; $i < $num_simple_users; $i++) {
$this->users[$i] = $this->drupalCreateUser(['access content', 'create article content']);
}
foreach ($this->users as $web_user) {
$this->drupalLogin($web_user);
foreach ([0 => 'Public', 1 => 'Private'] as $is_private => $type) {
$settings = [
'body' => [[
'value' => $type . ' node',
'format' => filter_default_format(),
]],
'title' => t('@private_public Article created by @user', ['@private_public' => $type, '@user' => $web_user->getUsername()]),
'type' => 'article',
'uid' => $web_user->id(),
'private' => (bool) $is_private,
];
$node = $this->drupalCreateNode($settings);
$this->assertEqual($is_private, (int) $node->private->value, 'The private status of the node was properly set in the node_access_test table.');
}
}
}
/**
* Tests the node access filter.
*/
public function testFilterNodeAccess() {
$this->drupalLogin($this->users[0]);
$this->drupalGet('test_filter_node_access');
// Test that the private node of the current user is shown.
$this->assertText('Private Article created by ' . $this->users[0]->getUsername());
// Test that the private node of the other use isn't shown.
$this->assertNoText('Private Article created by ' . $this->users[1]->getUsername());
// Test that both public nodes are shown.
$this->assertText('Public Article created by ' . $this->users[0]->getUsername());
$this->assertText('Public Article created by ' . $this->users[1]->getUsername());
// Switch users and test the other private node is shown.
$this->drupalLogin($this->users[1]);
$this->drupalGet('test_filter_node_access');
// Test that the private node of the current user is shown.
$this->assertText('Private Article created by ' . $this->users[1]->getUsername());
// Test that the private node of the other use isn't shown.
$this->assertNoText('Private Article created by ' . $this->users[0]->getUsername());
// Test that a user with administer nodes permission can't see all nodes.
$administer_nodes_user = $this->drupalCreateUser(['access content', 'administer nodes']);
$this->drupalLogin($administer_nodes_user);
$this->drupalGet('test_filter_node_access');
$this->assertNoText('Private Article created by ' . $this->users[0]->getUsername());
$this->assertNoText('Private Article created by ' . $this->users[1]->getUsername());
$this->assertText('Public Article created by ' . $this->users[0]->getUsername());
$this->assertText('Public Article created by ' . $this->users[1]->getUsername());
// Test that a user with bypass node access can see all nodes.
$bypass_access_user = $this->drupalCreateUser(['access content', 'bypass node access']);
$this->drupalLogin($bypass_access_user);
$this->drupalGet('test_filter_node_access');
$this->assertText('Private Article created by ' . $this->users[0]->getUsername());
$this->assertText('Private Article created by ' . $this->users[1]->getUsername());
$this->assertText('Public Article created by ' . $this->users[0]->getUsername());
$this->assertText('Public Article created by ' . $this->users[1]->getUsername());
}
}

View file

@ -0,0 +1,54 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\views\Views;
/**
* Tests the node_uid_revision handler.
*
* @group node
*/
class FilterUidRevisionTest extends NodeTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_filter_node_uid_revision'];
/**
* Tests the node_uid_revision filter.
*/
public function testFilter() {
$author = $this->drupalCreateUser();
$no_author = $this->drupalCreateUser();
$expected_result = [];
// Create one node, with the author as the node author.
$node = $this->drupalCreateNode(['uid' => $author->id()]);
$expected_result[] = ['nid' => $node->id()];
// Create one node of which an additional revision author will be the
// author.
$node = $this->drupalCreateNode(['revision_uid' => $no_author->id()]);
$expected_result[] = ['nid' => $node->id()];
$revision = clone $node;
// Force to add a new revision.
$revision->set('vid', NULL);
$revision->set('revision_uid', $author->id());
$revision->save();
// Create one node on which the author has neither authorship of revisions
// or the main node.
$this->drupalCreateNode(['uid' => $no_author->id()]);
$view = Views::getView('test_filter_node_uid_revision');
$view->initHandlers();
$view->filter['uid_revision']->value = [$author->id()];
$this->executeView($view);
$this->assertIdenticalResultset($view, $expected_result, ['nid' => 'nid'], 'Make sure that the view only returns nodes which match either the node or the revision author.');
}
}

View file

@ -0,0 +1,374 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Language\LanguageInterface;
use Drupal\Core\Url;
use Drupal\node\Entity\Node;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\AssertViewsCacheTagsTrait;
use Drupal\views\ViewExecutable;
use Drupal\views\Views;
/**
* Tests the default frontpage provided by views.
*
* @group node
*/
class FrontPageTest extends ViewTestBase {
use AssertViewsCacheTagsTrait;
/**
* {@inheritdoc}
*/
protected $dumpHeaders = TRUE;
/**
* The entity storage for nodes.
*
* @var \Drupal\node\NodeStorage
*/
protected $nodeStorage;
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node', 'contextual'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->nodeStorage = $this->container->get('entity.manager')
->getStorage('node');
}
/**
* Tests the frontpage.
*/
public function testFrontPage() {
$site_name = $this->randomMachineName();
$this->config('system.site')
->set('name', $site_name)
->save();
$view = Views::getView('frontpage');
// Tests \Drupal\node\Plugin\views\row\RssPluginBase::calculateDependencies().
$expected = [
'config' => [
'core.entity_view_mode.node.rss',
'core.entity_view_mode.node.teaser',
],
'module' => [
'node',
'user',
],
];
$this->assertIdentical($expected, $view->getDependencies());
$view->setDisplay('page_1');
$this->executeView($view);
$view->preview();
$this->assertEqual($view->getTitle(), format_string('Welcome to @site_name', ['@site_name' => $site_name]), 'The welcome title is used for the empty view.');
$view->destroy();
// Create some nodes on the frontpage view. Add more than 10 nodes in order
// to enable paging.
$expected = [];
for ($i = 0; $i < 20; $i++) {
$values = [];
$values['type'] = 'article';
$values['title'] = $this->randomMachineName();
$values['promote'] = TRUE;
$values['status'] = TRUE;
// Test descending sort order.
$values['created'] = REQUEST_TIME - $i;
// Test the sticky order.
if ($i == 5) {
$values['sticky'] = TRUE;
$node = $this->nodeStorage->create($values);
$node->save();
// Put the sticky on at the front.
array_unshift($expected, ['nid' => $node->id()]);
}
else {
$values['sticky'] = FALSE;
$node = $this->nodeStorage->create($values);
$node->save();
array_push($expected, ['nid' => $node->id()]);
}
}
// Create some nodes which aren't on the frontpage, either because they
// aren't promoted or because they aren't published.
$not_expected_nids = [];
$values = [];
$values['type'] = 'article';
$values['title'] = $this->randomMachineName();
$values['status'] = TRUE;
$values['promote'] = FALSE;
$node = $this->nodeStorage->create($values);
$node->save();
$not_expected_nids[] = $node->id();
$values['promote'] = TRUE;
$values['status'] = FALSE;
$values['title'] = $this->randomMachineName();
$node = $this->nodeStorage->create($values);
$node->save();
$not_expected_nids[] = $node->id();
$values['promote'] = TRUE;
$values['sticky'] = TRUE;
$values['status'] = FALSE;
$values['title'] = $this->randomMachineName();
$node = $this->nodeStorage->create($values);
$node->save();
$not_expected_nids[] = $node->id();
$column_map = ['nid' => 'nid'];
$view->setDisplay('page_1');
$this->executeView($view);
$this->assertIdenticalResultset($view, array_slice($expected, 0, 10), $column_map, 'Ensure that the right nodes are displayed on the frontpage.');
$this->assertNotInResultSet($view, $not_expected_nids, 'Ensure no unexpected node is in the result.');
$view->destroy();
$view->setDisplay('page_1');
$view->setCurrentPage(1);
$this->executeView($view);
$this->assertIdenticalResultset($view, array_slice($expected, 10, 10), $column_map, 'Ensure that the right nodes are displayed on second page of the frontpage.');
$this->assertNotInResultSet($view, $not_expected_nids, 'Ensure no unexpected node is in the result.');
$view->destroy();
}
/**
* Verifies that an amount of nids aren't in the result.
*
* @param \Drupal\views\ViewExecutable $view
* An executed View.
* @param array $not_expected_nids
* An array of nids which should not be part of the resultset.
* @param string $message
* (optional) A custom message to display with the assertion.
*/
protected function assertNotInResultSet(ViewExecutable $view, array $not_expected_nids, $message = '') {
$found_nids = array_filter($view->result, function ($row) use ($not_expected_nids) {
return in_array($row->nid, $not_expected_nids);
});
$this->assertFalse($found_nids, $message);
}
/**
* Tests the frontpage when logged in as admin.
*/
public function testAdminFrontPage() {
// When a user with sufficient permissions is logged in, views_ui adds
// contextual links to the homepage view. This verifies there are no errors.
\Drupal::service('module_installer')->install(['views_ui']);
// Log in root user with sufficient permissions.
$this->drupalLogin($this->rootUser);
// Test frontpage view.
$this->drupalGet('node');
$this->assertResponse(200);
// Check that the frontpage view was rendered.
$this->assertPattern('/class=".+view-frontpage/', 'Frontpage view was rendered');
}
/**
* Tests the cache tags when using the "none" cache plugin.
*/
public function testCacheTagsWithCachePluginNone() {
$this->enablePageCaching();
$this->doTestFrontPageViewCacheTags(FALSE);
}
/**
* Tests the cache tags when using the "tag" cache plugin.
*/
public function testCacheTagsWithCachePluginTag() {
$this->enablePageCaching();
$view = Views::getView('frontpage');
$view->setDisplay('page_1');
$view->display_handler->overrideOption('cache', [
'type' => 'tag',
]);
$view->save();
$this->doTestFrontPageViewCacheTags(TRUE);
}
/**
* Tests the cache tags when using the "time" cache plugin.
*/
public function testCacheTagsWithCachePluginTime() {
$this->enablePageCaching();
$view = Views::getView('frontpage');
$view->setDisplay('page_1');
$view->display_handler->overrideOption('cache', [
'type' => 'time',
'options' => [
'results_lifespan' => 3600,
'output_lifespan' => 3600,
],
]);
$view->save();
$this->doTestFrontPageViewCacheTags(TRUE);
}
/**
* Tests the cache tags on the front page.
*
* @param bool $do_assert_views_caches
* Whether to check Views' result & output caches.
*/
protected function doTestFrontPageViewCacheTags($do_assert_views_caches) {
$view = Views::getView('frontpage');
$view->setDisplay('page_1');
$cache_contexts = [
// Cache contexts associated with the view.
'user.node_grants:view',
'languages:' . LanguageInterface::TYPE_INTERFACE,
// Cache contexts associated with the route's access checking.
'user.permissions',
// Default cache contexts of the renderer.
'theme',
'url.query_args',
// Attached feed.
'url.site',
];
$cache_context_tags = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts)->getCacheTags();
// Test before there are any nodes.
$empty_node_listing_cache_tags = [
'config:views.view.frontpage',
'node_list',
];
$render_cache_tags = Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags);
$render_cache_tags = Cache::mergeTags($render_cache_tags, ['config:system.site']);
$this->assertViewsCacheTags(
$view,
$empty_node_listing_cache_tags,
$do_assert_views_caches,
$render_cache_tags
);
$expected_tags = Cache::mergeTags($empty_node_listing_cache_tags, $cache_context_tags);
$expected_tags = Cache::mergeTags($expected_tags, ['http_response', 'rendered', 'config:user.role.anonymous', 'config:system.site']);
$this->assertPageCacheContextsAndTags(
Url::fromRoute('view.frontpage.page_1'),
$cache_contexts,
$expected_tags
);
// Create some nodes on the frontpage view. Add more than 10 nodes in order
// to enable paging.
$this->drupalCreateContentType(['type' => 'article']);
for ($i = 0; $i < 15; $i++) {
$node = Node::create([
'body' => [
[
'value' => $this->randomMachineName(32),
'format' => filter_default_format(),
]
],
'type' => 'article',
'created' => $i,
'title' => $this->randomMachineName(8),
'nid' => $i + 1,
]);
$node->enforceIsNew(TRUE);
$node->save();
}
$cache_contexts = Cache::mergeContexts($cache_contexts, [
'timezone',
]);
$this->pass('First page');
// First page.
$first_page_result_cache_tags = [
'config:views.view.frontpage',
'node_list',
'node:6',
'node:7',
'node:8',
'node:9',
'node:10',
'node:11',
'node:12',
'node:13',
'node:14',
'node:15',
];
$cache_context_tags = \Drupal::service('cache_contexts_manager')->convertTokensToKeys($cache_contexts)->getCacheTags();
$first_page_output_cache_tags = Cache::mergeTags($first_page_result_cache_tags, $cache_context_tags);
$first_page_output_cache_tags = Cache::mergeTags($first_page_output_cache_tags, [
'config:filter.format.plain_text',
'node_view',
'user_view',
'user:0',
]
);
$view->setDisplay('page_1');
$view->setCurrentPage(0);
$this->assertViewsCacheTags(
$view,
$first_page_result_cache_tags,
$do_assert_views_caches,
$first_page_output_cache_tags
);
$this->assertPageCacheContextsAndTags(
Url::fromRoute('view.frontpage.page_1'),
$cache_contexts,
Cache::mergeTags($first_page_output_cache_tags, ['http_response', 'rendered', 'config:user.role.anonymous'])
);
// Second page.
$this->pass('Second page');
$this->assertPageCacheContextsAndTags(Url::fromRoute('view.frontpage.page_1', [], ['query' => ['page' => 1]]), $cache_contexts, [
// The cache tags for the listed nodes.
'node:1',
'node:2',
'node:3',
'node:4',
'node:5',
// The rest.
'config:filter.format.plain_text',
'config:views.view.frontpage',
'node_list',
'node_view',
'user_view',
'user:0',
'http_response',
'rendered',
// FinishResponseSubscriber adds this cache tag to responses that have the
// 'user.permissions' cache context for anonymous users.
'config:user.role.anonymous',
]);
// Let's update a node title on the first page and ensure that the page
// cache entry invalidates.
$node = Node::load(10);
$title = $node->getTitle() . 'a';
$node->setTitle($title);
$node->save();
$this->drupalGet(Url::fromRoute('view.frontpage.page_1'));
$this->assertText($title);
}
}

View file

@ -0,0 +1,112 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\language\Entity\ConfigurableLanguage;
/**
* Tests node field filters with translations.
*
* @group node
*/
class NodeFieldFilterTest extends NodeTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['language'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_field_filters'];
/**
* List of node titles by language.
*
* @var array
*/
public $nodeTitles = [];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
// Create Page content type.
if ($this->profile != 'standard') {
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
}
// Add two new languages.
ConfigurableLanguage::createFromLangcode('fr')->save();
ConfigurableLanguage::createFromLangcode('es')->save();
// Set up node titles.
$this->nodeTitles = [
'en' => 'Food in Paris',
'es' => 'Comida en Paris',
'fr' => 'Nouriture en Paris',
];
// Create node with translations.
$node = $this->drupalCreateNode(['title' => $this->nodeTitles['en'], 'langcode' => 'en', 'type' => 'page', 'body' => [['value' => $this->nodeTitles['en']]]]);
foreach (['es', 'fr'] as $langcode) {
$translation = $node->addTranslation($langcode, ['title' => $this->nodeTitles[$langcode]]);
$translation->body->value = $this->nodeTitles[$langcode];
}
$node->save();
}
/**
* Tests body and title filters.
*/
public function testFilters() {
// Test the title filter page, which filters for title contains 'Comida'.
// Should show just the Spanish translation, once.
$this->assertPageCounts('test-title-filter', ['es' => 1, 'fr' => 0, 'en' => 0], 'Comida title filter');
// Test the body filter page, which filters for body contains 'Comida'.
// Should show just the Spanish translation, once.
$this->assertPageCounts('test-body-filter', ['es' => 1, 'fr' => 0, 'en' => 0], 'Comida body filter');
// Test the title Paris filter page, which filters for title contains
// 'Paris'. Should show each translation once.
$this->assertPageCounts('test-title-paris', ['es' => 1, 'fr' => 1, 'en' => 1], 'Paris title filter');
// Test the body Paris filter page, which filters for body contains
// 'Paris'. Should show each translation once.
$this->assertPageCounts('test-body-paris', ['es' => 1, 'fr' => 1, 'en' => 1], 'Paris body filter');
}
/**
* Asserts that the given node translation counts are correct.
*
* @param string $path
* Path of the page to test.
* @param array $counts
* Array whose keys are languages, and values are the number of times
* that translation should be shown on the given page.
* @param string $message
* Message suffix to display.
*/
protected function assertPageCounts($path, $counts, $message) {
// Disable read more links.
entity_get_display('node', 'page', 'teaser')->removeComponent('links')->save();
// Get the text of the page.
$this->drupalGet($path);
$text = $this->getTextContent();
// Check the counts. Note that the title and body are both shown on the
// page, and they are the same. So the title/body string should appear on
// the page twice as many times as the input count.
foreach ($counts as $langcode => $count) {
$this->assertEqual(substr_count($text, $this->nodeTitles[$langcode]), 2 * $count, 'Translation ' . $langcode . ' has count ' . $count . ' with ' . $message);
}
}
}

View file

@ -0,0 +1,63 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
/**
* Tests replacement of Views tokens supplied by the Node module.
*
* @group node
* @see \Drupal\node\Tests\NodeTokenReplaceTest
*/
class NodeFieldTokensTest extends NodeTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_node_tokens'];
/**
* Tests token replacement for Views tokens supplied by the Node module.
*/
public function testViewsTokenReplacement() {
// Create the Article content type with a standard body field.
/* @var $node_type \Drupal\node\NodeTypeInterface */
$node_type = NodeType::create(['type' => 'article', 'name' => 'Article']);
$node_type->save();
node_add_body_field($node_type);
// Create a user and a node.
$account = $this->createUser();
$body = $this->randomMachineName(32);
$summary = $this->randomMachineName(16);
/** @var $node \Drupal\node\NodeInterface */
$node = Node::create([
'type' => 'article',
'tnid' => 0,
'uid' => $account->id(),
'title' => 'Testing Views tokens',
'body' => [['value' => $body, 'summary' => $summary, 'format' => 'plain_text']],
]);
$node->save();
$this->drupalGet('test_node_tokens');
// Body: {{ body }}<br />
$this->assertRaw("Body: <p>$body</p>");
// Raw value: {{ body__value }}<br />
$this->assertRaw("Raw value: $body");
// Raw summary: {{ body__summary }}<br />
$this->assertRaw("Raw summary: $summary");
// Raw format: {{ body__format }}<br />
$this->assertRaw("Raw format: plain_text");
}
}

View file

@ -0,0 +1,67 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
/**
* Tests the node integration into views.
*
* @group node
*/
class NodeIntegrationTest extends NodeTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_node_view'];
/**
* Tests basic node view with a node type argument.
*/
public function testNodeViewTypeArgument() {
// Create two content types with three nodes each.
$types = [];
$all_nids = [];
for ($i = 0; $i < 2; $i++) {
$type = $this->drupalCreateContentType(['name' => '<em>' . $this->randomMachineName() . '</em>']);
$types[] = $type;
for ($j = 0; $j < 5; $j++) {
// Ensure the right order of the nodes.
$node = $this->drupalCreateNode(['type' => $type->id(), 'created' => REQUEST_TIME - ($i * 5 + $j)]);
$nodes[$type->id()][$node->id()] = $node;
$all_nids[] = $node->id();
}
}
$this->drupalGet('test-node-view');
$this->assertResponse(404);
$this->drupalGet('test-node-view/all');
$this->assertResponse(200);
$this->assertNids($all_nids);
foreach ($types as $type) {
$this->drupalGet("test-node-view/{$type->id()}");
$this->assertEscaped($type->label());
$this->assertNids(array_keys($nodes[$type->id()]));
}
}
/**
* Ensures that a list of nodes appear on the page.
*
* @param array $expected_nids
* An array of node IDs.
*/
protected function assertNids(array $expected_nids = []) {
$result = $this->xpath('//span[@class="field-content"]');
$nids = [];
foreach ($result as $element) {
$nids[] = (int) $element->getText();
}
$this->assertEqual($nids, $expected_nids);
}
}

View file

@ -0,0 +1,295 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\Core\Language\LanguageInterface;
use Drupal\field\Entity\FieldStorageConfig;
use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\views\Plugin\views\PluginBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\views\Views;
/**
* Tests node language fields, filters, and sorting.
*
* @group node
*/
class NodeLanguageTest extends NodeTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['language', 'node_test_views'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_language'];
/**
* List of node titles by language.
*
* @var array
*/
public $nodeTitles = [];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp(FALSE);
// Create Page content type.
if ($this->profile != 'standard') {
$this->drupalCreateContentType(['type' => 'page', 'name' => 'Basic page']);
ViewTestData::createTestViews(get_class($this), ['node_test_views']);
}
// Add two new languages.
ConfigurableLanguage::createFromLangcode('fr')->save();
ConfigurableLanguage::createFromLangcode('es')->save();
// Make the body field translatable. The title is already translatable by
// definition.
$field_storage = FieldStorageConfig::loadByName('node', 'body');
$field_storage->setTranslatable(TRUE);
$field_storage->save();
// Set up node titles. They should not include the words "French",
// "English", or "Spanish", as there is a language field in the view
// that prints out those words.
$this->nodeTitles = [
LanguageInterface::LANGCODE_NOT_SPECIFIED => [
'First node und',
],
'es' => [
'Primero nodo es',
'Segundo nodo es',
'Tercera nodo es',
],
'en' => [
'First node en',
'Second node en',
],
'fr' => [
'Premier nœud fr',
]
];
// Create nodes with translations.
foreach ($this->nodeTitles['es'] as $index => $title) {
$node = $this->drupalCreateNode(['title' => $title, 'langcode' => 'es', 'type' => 'page', 'promote' => 1]);
foreach (['en', 'fr'] as $langcode) {
if (isset($this->nodeTitles[$langcode][$index])) {
$translation = $node->addTranslation($langcode, ['title' => $this->nodeTitles[$langcode][$index]]);
$translation->body->value = $this->randomMachineName(32);
}
}
$node->save();
}
// Create non-translatable nodes.
foreach ($this->nodeTitles[LanguageInterface::LANGCODE_NOT_SPECIFIED] as $index => $title) {
$node = $this->drupalCreateNode(['title' => $title, 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, 'type' => 'page', 'promote' => 1]);
$node->body->value = $this->randomMachineName(32);
$node->save();
}
$this->container->get('router.builder')->rebuild();
$user = $this->drupalCreateUser(['access content overview', 'access content']);
$this->drupalLogin($user);
}
/**
* Tests translation language filter, field, and sort.
*/
public function testLanguages() {
// Test the page with no arguments. It is filtered to Spanish and French.
// The page shows node titles and languages.
$this->drupalGet('test-language');
$message = 'French/Spanish page';
// Test that the correct nodes are shown.
foreach ($this->nodeTitles as $langcode => $list) {
foreach ($list as $title) {
if ($langcode == 'en') {
$this->assertNoText($title, $title . ' does not appear on ' . $message);
}
else {
$this->assertText($title, $title . ' does appear on ' . $message);
}
}
}
// Test that the language field value is shown.
$this->assertNoText('English', 'English language is not shown on ' . $message);
$this->assertText('French', 'French language is shown on ' . $message);
$this->assertText('Spanish', 'Spanish language is shown on ' . $message);
// Test page sorting, which is by language code, ascending. So the
// Spanish nodes should appear before the French nodes.
$page = $this->getTextContent();
$pos_es_max = 0;
$pos_fr_min = 10000;
foreach ($this->nodeTitles['es'] as $title) {
$pos_es_max = max($pos_es_max, strpos($page, $title));
}
foreach ($this->nodeTitles['fr'] as $title) {
$pos_fr_min = min($pos_fr_min, strpos($page, $title));
}
$this->assertTrue($pos_es_max < $pos_fr_min, 'Spanish translations appear before French on ' . $message);
// Test the argument -- filter to just Spanish.
$this->drupalGet('test-language/es');
// This time, test just the language field.
$message = 'Spanish argument page';
$this->assertNoText('English', 'English language is not shown on ' . $message);
$this->assertNoText('French', 'French language is not shown on ' . $message);
$this->assertText('Spanish', 'Spanish language is shown on ' . $message);
// Test the front page view filter. Only node titles in the current language
// should be displayed on the front page by default.
foreach ($this->nodeTitles as $langcode => $titles) {
// The frontpage view does not display content without a language.
if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
continue;
}
$this->drupalGet(($langcode == 'en' ? '' : "$langcode/") . 'node');
foreach ($titles as $title) {
$this->assertText($title);
}
foreach ($this->nodeTitles as $control_langcode => $control_titles) {
if ($langcode != $control_langcode) {
foreach ($control_titles as $title) {
$this->assertNoText($title);
}
}
}
}
// Test the node admin view filter. By default all translations should show.
$this->drupalGet('admin/content');
foreach ($this->nodeTitles as $titles) {
foreach ($titles as $title) {
$this->assertText($title);
}
}
// When filtered, only the specific languages should show.
foreach ($this->nodeTitles as $langcode => $titles) {
$this->drupalGet('admin/content', ['query' => ['langcode' => $langcode]]);
foreach ($titles as $title) {
$this->assertText($title);
}
foreach ($this->nodeTitles as $control_langcode => $control_titles) {
if ($langcode != $control_langcode) {
foreach ($control_titles as $title) {
$this->assertNoText($title);
}
}
}
}
// Override the config for the front page view, so that the language
// filter is set to the site default language instead. This should just
// show the English nodes, no matter what the content language is.
$config = $this->config('views.view.frontpage');
$config->set('display.default.display_options.filters.langcode.value', [PluginBase::VIEWS_QUERY_LANGUAGE_SITE_DEFAULT => PluginBase::VIEWS_QUERY_LANGUAGE_SITE_DEFAULT]);
$config->save();
foreach ($this->nodeTitles as $langcode => $titles) {
if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) {
continue;
}
$this->drupalGet(($langcode == 'en' ? '' : "$langcode/") . 'node');
foreach ($this->nodeTitles as $control_langcode => $control_titles) {
foreach ($control_titles as $title) {
if ($control_langcode == 'en') {
$this->assertText($title, 'English title is shown when filtering is site default');
}
else {
$this->assertNoText($title, 'Non-English title is not shown when filtering is site default');
}
}
}
}
// Override the config so that the language filter is set to the UI
// language, and make that have a fixed value of 'es'.
//
// IMPORTANT: Make sure this part of the test is last -- it is changing
// language configuration!
$config->set('display.default.display_options.filters.langcode.value', ['***LANGUAGE_language_interface***' => '***LANGUAGE_language_interface***']);
$config->save();
$language_config = $this->config('language.types');
$language_config->set('negotiation.language_interface.enabled', ['language-selected' => 1]);
$language_config->save();
$language_config = $this->config('language.negotiation');
$language_config->set('selected_langcode', 'es');
$language_config->save();
// With a fixed language selected, there is no language-based URL.
$this->drupalGet('node');
foreach ($this->nodeTitles as $control_langcode => $control_titles) {
foreach ($control_titles as $title) {
if ($control_langcode == 'es') {
$this->assertText($title, 'Spanish title is shown when filtering is fixed UI language');
}
else {
$this->assertNoText($title, 'Non-Spanish title is not shown when filtering is fixed UI language');
}
}
}
}
/**
* Tests native name display in language field.
*/
public function testNativeLanguageField() {
$this->assertLanguageNames();
// Modify test view to display native language names and set translations.
$config = $this->config('views.view.test_language');
$config->set('display.default.display_options.fields.langcode.settings.native_language', TRUE);
$config->save();
\Drupal::languageManager()->getLanguageConfigOverride('fr', 'language.entity.fr')->set('label', 'Français')->save();
\Drupal::languageManager()->getLanguageConfigOverride('es', 'language.entity.es')->set('label', 'Español')->save();
$this->assertLanguageNames(TRUE);
// Modify test view to use the views built-in language field and test that.
\Drupal::state()->set('node_test_views.use_basic_handler', TRUE);
Views::viewsData()->clear();
$config = $this->config('views.view.test_language');
$config->set('display.default.display_options.fields.langcode.native_language', FALSE);
$config->clear('display.default.display_options.fields.langcode.settings');
$config->clear('display.default.display_options.fields.langcode.type');
$config->set('display.default.display_options.fields.langcode.plugin_id', 'language');
$config->save();
$this->assertLanguageNames();
$config->set('display.default.display_options.fields.langcode.native_language', TRUE)->save();
$this->assertLanguageNames(TRUE);
}
/**
* Asserts the presence of language names in their English or native forms.
*
* @param bool $native
* (optional) Whether to assert the language name in its native form.
*/
protected function assertLanguageNames($native = FALSE) {
$this->drupalGet('test-language');
if ($native) {
$this->assertText('Français', 'French language shown in native form.');
$this->assertText('Español', 'Spanish language shown in native form.');
$this->assertNoText('French', 'French language not shown in English.');
$this->assertNoText('Spanish', 'Spanish language not shown in English.');
}
else {
$this->assertNoText('Français', 'French language not shown in native form.');
$this->assertNoText('Español', 'Spanish language not shown in native form.');
$this->assertText('French', 'French language shown in English.');
$this->assertText('Spanish', 'Spanish language shown in English.');
}
}
}

View file

@ -0,0 +1,61 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\Tests\views\Functional\Wizard\WizardTestBase;
use Drupal\views\Views;
/**
* Tests the wizard with node_revision as base table.
*
* @group node
* @see \Drupal\node\Plugin\views\wizard\NodeRevision
*/
class NodeRevisionWizardTest extends WizardTestBase {
/**
* Tests creating a node revision view.
*/
public function testViewAdd() {
$this->drupalCreateContentType(['type' => 'article']);
// Create two nodes with two revision.
$node_storage = \Drupal::entityManager()->getStorage('node');
/** @var \Drupal\node\NodeInterface $node */
$node = $node_storage->create(['title' => $this->randomString(), 'type' => 'article', 'created' => REQUEST_TIME + 40]);
$node->save();
$node = $node->createDuplicate();
$node->setNewRevision();
$node->created->value = REQUEST_TIME + 20;
$node->save();
$node = $node_storage->create(['title' => $this->randomString(), 'type' => 'article', 'created' => REQUEST_TIME + 30]);
$node->save();
$node = $node->createDuplicate();
$node->setNewRevision();
$node->created->value = REQUEST_TIME + 10;
$node->save();
$view = [];
$view['label'] = $this->randomMachineName(16);
$view['id'] = strtolower($this->randomMachineName(16));
$view['description'] = $this->randomMachineName(16);
$view['page[create]'] = FALSE;
$view['show[wizard_key]'] = 'node_revision';
$this->drupalPostForm('admin/structure/views/add', $view, t('Save and edit'));
$view_storage_controller = \Drupal::entityManager()->getStorage('view');
/** @var \Drupal\views\Entity\View $view */
$view = $view_storage_controller->load($view['id']);
$this->assertEqual($view->get('base_table'), 'node_field_revision');
$executable = Views::executableFactory()->get($view);
$this->executeView($executable);
$this->assertIdenticalResultset($executable, [['vid' => 1], ['vid' => 3], ['vid' => 2], ['vid' => 4]],
['vid' => 'vid']);
}
}

View file

@ -0,0 +1,29 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\Tests\views\Functional\ViewTestBase;
use Drupal\views\Tests\ViewTestData;
/**
* Base class for all node Views tests.
*/
abstract class NodeTestBase extends ViewTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['node_test_views'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
if ($import_test_views) {
ViewTestData::createTestViews(get_class($this), ['node_test_views']);
}
}
}

View file

@ -0,0 +1,80 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\views\Views;
/**
* Tests the node row plugin.
*
* @group node
*/
class PathPluginTest extends NodeTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_node_path_plugin'];
/**
* Contains all nodes used by this test.
*
* @var Node[]
*/
protected $nodes;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->drupalCreateContentType(['type' => 'article']);
// Create two nodes.
for ($i = 0; $i < 2; $i++) {
$this->nodes[] = $this->drupalCreateNode(
[
'type' => 'article',
'body' => [
[
'value' => $this->randomMachineName(42),
'format' => filter_default_format(),
'summary' => $this->randomMachineName(),
],
],
]
);
}
}
/**
* Tests the node path plugin.
*/
public function testPathPlugin() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = $this->container->get('renderer');
$view = Views::getView('test_node_path_plugin');
$view->initDisplay();
$view->setDisplay('page_1');
$view->initStyle();
$view->rowPlugin->options['view_mode'] = 'full';
// Test with view_mode full.
$output = $view->preview();
$output = $renderer->renderRoot($output);
foreach ($this->nodes as $node) {
$this->assertTrue(strpos($output, 'This is <strong>not escaped</strong> and this is ' . $node->link('the link')) !== FALSE, 'Make sure path field rewriting is not escaped.');
}
}
}

View file

@ -0,0 +1,89 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
/**
* Tests the different revision link handlers.
*
* @group node
*
* @see \Drupal\node\Plugin\views\field\RevisionLink
* @see \Drupal\node\Plugin\views\field\RevisionLinkDelete
* @see \Drupal\node\Plugin\views\field\RevisionLinkRevert
*/
class RevisionLinkTest extends NodeTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_node_revision_links'];
/**
* Tests revision links.
*/
public function testRevisionLinks() {
// Create one user which can view/revert and delete and one which can only
// do one of them.
$this->drupalCreateContentType(['name' => 'page', 'type' => 'page']);
$account = $this->drupalCreateUser(['revert all revisions', 'view all revisions', 'delete all revisions', 'edit any page content', 'delete any page content']);
$this->drupalLogin($account);
// Create two nodes, one without an additional revision and one with a
// revision.
$nodes = [
$this->drupalCreateNode(),
$this->drupalCreateNode(),
];
$first_revision = $nodes[1]->getRevisionId();
// Create revision of the node.
$nodes[1]->setNewRevision();
$nodes[1]->save();
$second_revision = $nodes[1]->getRevisionId();
$this->drupalGet('test-node-revision-links');
$this->assertResponse(200, 'Test view can be accessed in the path expected');
// The first node revision should link to the node directly as you get an
// access denied if you link to the revision.
$url = $nodes[0]->urlInfo()->toString();
$this->assertLinkByHref($url);
$this->assertNoLinkByHref($url . '/revisions/' . $nodes[0]->getRevisionId() . '/view');
$this->assertNoLinkByHref($url . '/revisions/' . $nodes[0]->getRevisionId() . '/delete');
$this->assertNoLinkByHref($url . '/revisions/' . $nodes[0]->getRevisionId() . '/revert');
// For the second node the current revision got set to the last revision, so
// the first one should also link to the node page itself.
$url = $nodes[1]->urlInfo()->toString();
$this->assertLinkByHref($url);
$this->assertLinkByHref($url . '/revisions/' . $first_revision . '/view');
$this->assertLinkByHref($url . '/revisions/' . $first_revision . '/delete');
$this->assertLinkByHref($url . '/revisions/' . $first_revision . '/revert');
$this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/view');
$this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/delete');
$this->assertNoLinkByHref($url . '/revisions/' . $second_revision . '/revert');
$accounts = [
'view' => $this->drupalCreateUser(['view all revisions']),
'revert' => $this->drupalCreateUser(['revert all revisions', 'edit any page content']),
'delete' => $this->drupalCreateUser(['delete all revisions', 'delete any page content']),
];
$url = $nodes[1]->urlInfo()->toString();
// Render the view with users which can only delete/revert revisions.
foreach ($accounts as $allowed_operation => $account) {
$this->drupalLogin($account);
$this->drupalGet('test-node-revision-links');
// Check expected links.
foreach (['revert', 'delete'] as $operation) {
if ($operation == $allowed_operation) {
$this->assertLinkByHref($url . '/revisions/' . $first_revision . '/' . $operation);
}
else {
$this->assertNoLinkByHref($url . '/revisions/' . $first_revision . '/' . $operation);
}
}
}
}
}

View file

@ -0,0 +1,91 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\views\Views;
/**
* Tests the node row plugin.
*
* @group node
* @see \Drupal\node\Plugin\views\row\NodeRow
*/
class RowPluginTest extends NodeTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node'];
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_node_row_plugin'];
/**
* Contains all nodes used by this test.
*
* @var array
*/
protected $nodes;
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->drupalCreateContentType(['type' => 'article']);
// Create two nodes.
for ($i = 0; $i < 2; $i++) {
$this->nodes[] = $this->drupalCreateNode(
[
'type' => 'article',
'body' => [
[
'value' => $this->randomMachineName(42),
'format' => filter_default_format(),
'summary' => $this->randomMachineName(),
],
],
]
);
}
}
/**
* Tests the node row plugin.
*/
public function testRowPlugin() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = $this->container->get('renderer');
$view = Views::getView('test_node_row_plugin');
$view->initDisplay();
$view->setDisplay('page_1');
$view->initStyle();
$view->rowPlugin->options['view_mode'] = 'full';
// Test with view_mode full.
$output = $view->preview();
$output = $renderer->renderRoot($output);
foreach ($this->nodes as $node) {
$this->assertFalse(strpos($output, $node->body->summary) !== FALSE, 'Make sure the teaser appears in the output of the view.');
$this->assertTrue(strpos($output, $node->body->value) !== FALSE, 'Make sure the full text appears in the output of the view.');
}
// Test with teasers.
$view->rowPlugin->options['view_mode'] = 'teaser';
$output = $view->preview();
$output = $renderer->renderRoot($output);
foreach ($this->nodes as $node) {
$this->assertTrue(strpos($output, $node->body->summary) !== FALSE, 'Make sure the teaser appears in the output of the view.');
$this->assertFalse(strpos($output, $node->body->value) !== FALSE, 'Make sure the full text does not appears in the output of the view if teaser is set as viewmode.');
}
}
}

View file

@ -0,0 +1,76 @@
<?php
namespace Drupal\Tests\node\Functional\Views;
use Drupal\node\NodeInterface;
/**
* Tests the node.status_extra field handler.
*
* @group node
* @see \Drupal\node\Plugin\views\filter\Status
*/
class StatusExtraTest extends NodeTestBase {
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_status_extra'];
/**
* Tests the status extra filter.
*/
public function testStatusExtra() {
$node_author = $this->drupalCreateUser(['view own unpublished content']);
$node_author_not_unpublished = $this->drupalCreateUser();
$normal_user = $this->drupalCreateUser();
$admin_user = $this->drupalCreateUser(['bypass node access']);
// Create one published and one unpublished node by the admin.
$node_published = $this->drupalCreateNode(['uid' => $admin_user->id()]);
$node_unpublished = $this->drupalCreateNode(['uid' => $admin_user->id(), 'status' => NodeInterface::NOT_PUBLISHED]);
// Create one unpublished node by a certain author user.
$node_unpublished2 = $this->drupalCreateNode(['uid' => $node_author->id(), 'status' => NodeInterface::NOT_PUBLISHED]);
// Create one unpublished node by a user who does not have the `view own
// unpublished content` permission.
$node_unpublished3 = $this->drupalCreateNode(['uid' => $node_author_not_unpublished->id(), 'status' => NodeInterface::NOT_PUBLISHED]);
// The administrator should simply see all nodes.
$this->drupalLogin($admin_user);
$this->drupalGet('test_status_extra');
$this->assertText($node_published->label());
$this->assertText($node_unpublished->label());
$this->assertText($node_unpublished2->label());
$this->assertText($node_unpublished3->label());
// The node author should see the published node and his own node.
$this->drupalLogin($node_author);
$this->drupalGet('test_status_extra');
$this->assertText($node_published->label());
$this->assertNoText($node_unpublished->label());
$this->assertText($node_unpublished2->label());
$this->assertNoText($node_unpublished3->label());
// The normal user should just see the published node.
$this->drupalLogin($normal_user);
$this->drupalGet('test_status_extra');
$this->assertText($node_published->label());
$this->assertNoText($node_unpublished->label());
$this->assertNoText($node_unpublished2->label());
$this->assertNoText($node_unpublished3->label());
// The author without the permission to see his own unpublished node should
// just see the published node.
$this->drupalLogin($node_author_not_unpublished);
$this->drupalGet('test_status_extra');
$this->assertText($node_published->label());
$this->assertNoText($node_unpublished->label());
$this->assertNoText($node_unpublished2->label());
$this->assertNoText($node_unpublished3->label());
}
}

View file

@ -13,6 +13,11 @@ use Drupal\node\Entity\Node;
*/
class MigrateNodeBundleSettingsTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/

View file

@ -10,7 +10,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateNodeSettingPromoteTest extends MigrateDrupal6TestBase {
public static $modules = ['node', 'text'];
public static $modules = ['node', 'text', 'menu_ui'];
/**
* {@inheritdoc}

View file

@ -10,7 +10,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateNodeSettingStatusTest extends MigrateDrupal6TestBase {
public static $modules = ['node', 'text'];
public static $modules = ['node', 'text', 'menu_ui'];
/**
* {@inheritdoc}

View file

@ -10,7 +10,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase;
*/
class MigrateNodeSettingStickyTest extends MigrateDrupal6TestBase {
public static $modules = ['node', 'text'];
public static $modules = ['node', 'text', 'menu_ui'];
/**
* {@inheritdoc}

View file

@ -19,7 +19,7 @@ class MigrateNodeTest extends MigrateNodeTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['language', 'content_translation'];
public static $modules = ['language', 'content_translation', 'menu_ui'];
/**
* {@inheritdoc}
@ -72,6 +72,11 @@ class MigrateNodeTest extends MigrateNodeTestBase {
$this->assertIdentical('1', $node->field_test_identical2->value, 'Integer value is correct');
$this->assertIdentical('This is a field with exclude unset.', $node->field_test_exclude_unset->value, 'Field with exclude unset is correct.');
// Test that date fields are migrated.
$this->assertSame('2013-01-02T04:05:00', $node->field_test_date->value, 'Date field is correct');
$this->assertSame('1391357160', $node->field_test_datestamp->value, 'Datestamp field is correct');
$this->assertSame('2015-03-04T06:07:00', $node->field_test_datetime->value, 'Datetime field is correct');
// Test that link fields are migrated.
$this->assertIdentical('https://www.drupal.org/project/drupal', $node->field_test_link->uri);
$this->assertIdentical('Drupal project page', $node->field_test_link->title);
@ -81,6 +86,15 @@ class MigrateNodeTest extends MigrateNodeTestBase {
$this->assertIdentical('desc', $node->field_test_filefield->description);
$this->assertIdentical('4', $node->field_test_filefield->target_id);
// Test that an email field is migrated.
$this->assertSame('PrincessRuwenne@example.com', $node->field_test_email->value);
// Test that node reference field values were migrated.
$node = Node::load(18);
$this->assertCount(2, $node->field_company);
$this->assertSame('Klingon Empire', $node->field_company[0]->entity->label());
$this->assertSame('Romulan Empire', $node->field_company[1]->entity->label());
$node = Node::load(2);
$this->assertIdentical('Test title rev 3', $node->getTitle());
$this->assertIdentical('test rev 3', $node->body->value);

View file

@ -13,6 +13,11 @@ use Drupal\node\Entity\NodeType;
*/
class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['menu_ui'];
/**
* {@inheritdoc}
*/
@ -39,6 +44,12 @@ class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
$field = FieldConfig::loadByName('node', 'test_page', 'body');
$this->assertIdentical('This is the body field label', $field->getLabel(), 'Body field was found.');
// Test default menus.
$expected_available_menus = ['navigation'];
$this->assertSame($expected_available_menus, $node_type_page->getThirdPartySetting('menu_ui', 'available_menus'));
$expected_parent = 'navigation:';
$this->assertSame($expected_parent, $node_type_page->getThirdPartySetting('menu_ui', 'parent'));
// Test the test_story content type.
$node_type_story = NodeType::load('test_story');
$this->assertIdentical('test_story', $node_type_story->id(), 'Node type test_story loaded');
@ -52,6 +63,12 @@ class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
$field = FieldConfig::loadByName('node', 'test_story', 'body');
$this->assertIdentical(NULL, $field, 'No body field found');
// Test default menus.
$expected_available_menus = ['navigation'];
$this->assertSame($expected_available_menus, $node_type_story->getThirdPartySetting('menu_ui', 'available_menus'));
$expected_parent = 'navigation:';
$this->assertSame($expected_parent, $node_type_story->getThirdPartySetting('menu_ui', 'parent'));
// Test the test_event content type.
$node_type_event = NodeType::load('test_event');
$this->assertIdentical('test_event', $node_type_event->id(), 'Node type test_event loaded');
@ -64,6 +81,11 @@ class MigrateNodeTypeTest extends MigrateDrupal6TestBase {
// Test we have a body field.
$field = FieldConfig::loadByName('node', 'test_event', 'body');
$this->assertIdentical('Body', $field->getLabel(), 'Body field was found.');
$expected_available_menus = ['navigation'];
$this->assertSame($expected_available_menus, $node_type_event->getThirdPartySetting('menu_ui', 'available_menus'));
$expected_parent = 'navigation:';
$this->assertSame($expected_parent, $node_type_event->getThirdPartySetting('menu_ui', 'parent'));
}
}

View file

@ -24,6 +24,7 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
'image',
'language',
'link',
'menu_ui',
'node',
'taxonomy',
'telephone',
@ -149,6 +150,9 @@ class MigrateNodeTest extends MigrateDrupal7TestBase {
$this->assertIdentical('93', $node->field_images->height);
$this->assertIdentical('http://google.com', $node->field_link->uri);
$this->assertIdentical('Click Here', $node->field_link->title);
// Test that an email field is migrated.
$this->assertSame('default@example.com', $node->field_email->value);
$this->assertSame('another@example.com', $node->field_email[1]->value);
$node = Node::load(2);
$this->assertSame('en', $node->langcode->value);

View file

@ -12,7 +12,7 @@ use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase;
*/
class MigrateNodeTitleLabelTest extends MigrateDrupal7TestBase {
public static $modules = ['node', 'text'];
public static $modules = ['node', 'text', 'menu_ui'];
/**
* {@inheritdoc}

View file

@ -20,7 +20,7 @@ class MigrateNodeTypeTest extends MigrateDrupal7TestBase {
*
* @var array
*/
public static $modules = ['node', 'text', 'filter'];
public static $modules = ['node', 'text', 'filter', 'menu_ui'];
/**
* {@inheritdoc}
@ -45,12 +45,13 @@ class MigrateNodeTypeTest extends MigrateDrupal7TestBase {
* @param string $help
* The expected help text.
*/
protected function assertEntity($id, $label, $description, $help, $display_submitted, $new_revision, $body_label = NULL) {
protected function assertEntity($id, $label, $description, $help, $display_submitted, $new_revision, $expected_available_menus, $expected_parent, $body_label = NULL) {
/** @var \Drupal\node\NodeTypeInterface $entity */
$entity = NodeType::load($id);
$this->assertTrue($entity instanceof NodeTypeInterface);
$this->assertIdentical($label, $entity->label());
$this->assertIdentical($description, $entity->getDescription());
$this->assertIdentical($help, $entity->getHelp());
$this->assertIdentical($display_submitted, $entity->displaySubmitted(), 'Submission info is displayed');
@ -62,20 +63,32 @@ class MigrateNodeTypeTest extends MigrateDrupal7TestBase {
$this->assertTrue($body instanceof FieldConfigInterface);
$this->assertIdentical($body_label, $body->label());
}
$this->assertSame($expected_available_menus, $entity->getThirdPartySetting('menu_ui', 'available_menus'));
$this->assertSame($expected_parent, $entity->getThirdPartySetting('menu_ui', 'parent'));
}
/**
* Tests Drupal 7 node type to Drupal 8 migration.
*/
public function testNodeType() {
$this->assertEntity('article', 'Article', 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.', 'Help text for articles', TRUE, FALSE, "Body");
$this->assertEntity('blog', 'Blog entry', 'Use for multi-user blogs. Every user gets a personal blog.', 'Blog away, good sir!', TRUE, FALSE, 'Body');
$expected_available_menus = ['main-menu'];
$expected_parent = 'main-menu:0:';
$this->assertEntity('article', 'Article', 'Use <em>articles</em> for time-sensitive content like news, press releases or blog posts.', 'Help text for articles', TRUE, FALSE, $expected_available_menus, $expected_parent, "Body");
$this->assertEntity('blog', 'Blog entry', 'Use for multi-user blogs. Every user gets a personal blog.', 'Blog away, good sir!', TRUE, FALSE, $expected_available_menus, $expected_parent, 'Body');
// book's display_submitted flag is not set, so it will default to TRUE.
$this->assertEntity('book', 'Book page', '<em>Books</em> have a built-in hierarchical navigation. Use for handbooks or tutorials.', '', TRUE, TRUE, "Body");
$this->assertEntity('forum', 'Forum topic', 'A <em>forum topic</em> starts a new discussion thread within a forum.', 'No name-calling, no flame wars. Be nice.', TRUE, FALSE, 'Body');
$this->assertEntity('page', 'Basic page', "Use <em>basic pages</em> for your static content, such as an 'About us' page.", 'Help text for basic pages', FALSE, FALSE, "Body");
$this->assertEntity('book', 'Book page', '<em>Books</em> have a built-in hierarchical navigation. Use for handbooks or tutorials.', '', TRUE, TRUE, $expected_available_menus, $expected_parent, "Body");
$this->assertEntity('forum', 'Forum topic', 'A <em>forum topic</em> starts a new discussion thread within a forum.', 'No name-calling, no flame wars. Be nice.', TRUE, FALSE, $expected_available_menus, $expected_parent, 'Body');
$this->assertEntity('page', 'Basic page', "Use <em>basic pages</em> for your static content, such as an 'About us' page.", 'Help text for basic pages', FALSE, FALSE, $expected_available_menus, $expected_parent, "Body");
// This node type does not carry a body field.
$this->assertEntity('test_content_type', 'Test content type', 'This is the description of the test content type.', 'Help text for test content type', FALSE, TRUE);
$expected_available_menus = [
'main-menu',
'management',
'navigation',
'user-menu',
];
$this->assertEntity('test_content_type', 'Test content type', 'This is the description of the test content type.', 'Help text for test content type', FALSE, TRUE, $expected_available_menus, $expected_parent);
}
}

View file

@ -0,0 +1,121 @@
<?php
namespace Drupal\Tests\node\Kernel;
use Drupal\Component\Utility\Unicode;
use Drupal\Core\Datetime\Entity\DateFormat;
use Drupal\KernelTests\KernelTestBase;
use Drupal\node\Entity\Node;
use Drupal\simpletest\ContentTypeCreationTrait;
use Drupal\simpletest\NodeCreationTrait;
use Drupal\simpletest\UserCreationTrait;
use Drupal\Tests\EntityViewTrait;
/**
* Tests summary length.
*
* @group node
*/
class SummaryLengthTest extends KernelTestBase {
use NodeCreationTrait {
getNodeByTitle as drupalGetNodeByTitle;
createNode as drupalCreateNode;
}
use UserCreationTrait {
createUser as drupalCreateUser;
createRole as drupalCreateRole;
createAdminRole as drupalCreateAdminRole;
}
use ContentTypeCreationTrait {
createContentType as drupalCreateContentType;
}
use EntityViewTrait {
buildEntityView as drupalBuildEntityView;
}
/**
* {@inheritdoc}
*/
public static $modules = [
'node',
'datetime',
'user',
'system',
'filter',
'field',
'text',
];
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->installSchema('system', 'sequences');
$this->installSchema('node', 'node_access');
$this->installEntitySchema('user');
$this->installEntitySchema('node');
$this->installEntitySchema('date_format');
$this->installConfig('filter');
$this->installConfig('node');
// Create a node type.
$this->drupalCreateContentType([
'type' => 'page',
'name' => 'Basic page',
'display_submitted' => FALSE,
]);
DateFormat::create([
'id' => 'fallback',
'label' => 'Fallback',
'pattern' => 'Y-m-d',
])->save();
// Enable multibyte support.
Unicode::setStatus(Unicode::STATUS_MULTIBYTE);
}
/**
* Tests the node summary length functionality.
*/
public function testSummaryLength() {
/** @var \Drupal\Core\Render\RendererInterface $renderer */
$renderer = $this->container->get('renderer');
// Create a node to view.
$settings = [
'body' => [['value' => 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae arcu at leo cursus laoreet. Curabitur dui tortor, adipiscing malesuada tempor in, bibendum ac diam. Cras non tellus a libero pellentesque condimentum. What is a Drupalism? Suspendisse ac lacus libero. Ut non est vel nisl faucibus interdum nec sed leo. Pellentesque sem risus, vulputate eu semper eget, auctor in libero. Ut fermentum est vitae metus convallis scelerisque. Phasellus pellentesque rhoncus tellus, eu dignissim purus posuere id. Quisque eu fringilla ligula. Morbi ullamcorper, lorem et mattis egestas, tortor neque pretium velit, eget eleifend odio turpis eu purus. Donec vitae metus quis leo pretium tincidunt a pulvinar sem. Morbi adipiscing laoreet mauris vel placerat. Nullam elementum, nisl sit amet scelerisque malesuada, dolor nunc hendrerit quam, eu ultrices erat est in orci. Curabitur feugiat egestas nisl sed accumsan.']],
'promote' => 1,
];
$node = $this->drupalCreateNode($settings);
$this->assertTrue(Node::load($node->id()), 'Node created.');
// Render the node as a teaser.
$content = $this->drupalBuildEntityView($node, 'teaser');
$this->assertTrue(strlen($content['body'][0]['#markup']) < 600, 'Teaser is less than 600 characters long.');
$this->setRawContent($renderer->renderRoot($content));
// The string 'What is a Drupalism?' is between the 200th and 600th
// characters of the node body, so it should be included if the summary is
// 600 characters long.
$expected = 'What is a Drupalism?';
$this->assertRaw($expected);
// Change the teaser length for "Basic page" content type.
$display = entity_get_display('node', $node->getType(), 'teaser');
$display_options = $display->getComponent('body');
$display_options['settings']['trim_length'] = 200;
$display->setComponent('body', $display_options)
->save();
// Render the node as a teaser again and check that the summary is now only
// 200 characters in length and so does not include 'What is a Drupalism?'.
$content = $this->drupalBuildEntityView($node, 'teaser');
$this->assertTrue(strlen($content['body'][0]['#markup']) < 200, 'Teaser is less than 200 characters long.');
$this->setRawContent($renderer->renderRoot($content));
$this->assertText($node->label());
$this->assertNoRaw($expected);
}
}

View file

@ -0,0 +1,93 @@
<?php
namespace Drupal\Tests\node\Kernel\Views;
use Drupal\node\Entity\Node;
use Drupal\simpletest\UserCreationTrait;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\views\Tests\ViewTestData;
use Drupal\views\Views;
/**
* Tests the argument_node_uid_revision handler.
*
* @group node
*/
class ArgumentUidRevisionTest extends ViewsKernelTestBase {
use UserCreationTrait;
/**
* {@inheritdoc}
*/
public static $modules = ['node', 'field', 'text', 'user', 'node_test_views'];
/**
* {@inheritdoc}
*/
public static $testViews = ['test_argument_node_uid_revision'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->installEntitySchema('node');
$this->installSchema('node', ['node_access']);
$this->installEntitySchema('user');
$this->installConfig(['node', 'field']);
ViewTestData::createTestViews(get_class($this), ['node_test_views']);
}
/**
* Tests the node_uid_revision argument.
*/
public function testArgument() {
$expected_result = [];
$author = $this->createUser();
$no_author = $this->createUser();
// Create one node, with the author as the node author.
$node1 = Node::create([
'type' => 'default',
'title' => $this->randomMachineName(),
]);
$node1->setOwner($author);
$node1->save();
$expected_result[] = ['nid' => $node1->id()];
// Create one node of which an additional revision author will be the
// author.
$node2 = Node::create([
'type' => 'default',
'title' => $this->randomMachineName(),
]);
$node2->setRevisionAuthorId($no_author->id());
$node2->save();
$expected_result[] = ['nid' => $node2->id()];
// Force to add a new revision.
$node2->setNewRevision(TRUE);
$node2->setRevisionAuthorId($author->id());
$node2->save();
// Create one node on which the author has neither authorship of revisions
// or the main node.
$node3 = Node::create([
'type' => 'default',
'title' => $this->randomMachineName(),
]);
$node3->setOwner($no_author);
$node3->save();
$view = Views::getView('test_argument_node_uid_revision');
$view->initHandlers();
$view->setArguments(['uid_revision' => $author->id()]);
$this->executeView($view);
$this->assertIdenticalResultset($view, $expected_result, ['nid' => 'nid']);
}
}

View file

@ -0,0 +1,94 @@
<?php
namespace Drupal\Tests\node\Kernel\Views;
use Drupal\node\Entity\Node;
use Drupal\node\Entity\NodeType;
use Drupal\Tests\views\Kernel\ViewsKernelTestBase;
use Drupal\views\Views;
use Drupal\views\Tests\ViewTestData;
/**
* Tests the integration of node_revision table of node module.
*
* @group node
*/
class RevisionRelationshipsTest extends ViewsKernelTestBase {
/**
* Modules to enable.
*
* @var array
*/
public static $modules = ['node' , 'node_test_views'];
/**
* {@inheritdoc}
*/
protected function setUp($import_test_views = TRUE) {
parent::setUp($import_test_views);
$this->installSchema('node', 'node_access');
$this->installEntitySchema('user');
$this->installEntitySchema('node');
ViewTestData::createTestViews(get_class($this), ['node_test_views']);
}
/**
* Views used by this test.
*
* @var array
*/
public static $testViews = ['test_node_revision_nid', 'test_node_revision_vid'];
/**
* Create a node with revision and rest result count for both views.
*/
public function testNodeRevisionRelationship() {
$type = NodeType::create(['type' => 'page', 'name' => 'page']);
$type->save();
$node = Node::create(['type' => 'page', 'title' => 'test', 'uid' => 1]);
$node->save();
// Create revision of the node.
$node->setNewRevision(TRUE);
$node->save();
$column_map = [
'vid' => 'vid',
'node_field_data_node_field_revision_nid' => 'node_node_revision_nid',
'nid_1' => 'nid_1',
];
// Here should be two rows.
$view_nid = Views::getView('test_node_revision_nid');
$this->executeView($view_nid, [$node->id()]);
$resultset_nid = [
[
'vid' => '1',
'node_node_revision_nid' => '1',
'nid_1' => '1',
],
[
'vid' => '2',
'node_revision_nid' => '1',
'node_node_revision_nid' => '1',
'nid_1' => '1',
],
];
$this->assertIdenticalResultset($view_nid, $resultset_nid, $column_map);
// There should be only one row with active revision 2.
$view_vid = Views::getView('test_node_revision_vid');
$this->executeView($view_vid, [$node->id()]);
$resultset_vid = [
[
'vid' => '2',
'node_node_revision_nid' => '1',
'nid_1' => '1',
],
];
$this->assertIdenticalResultset($view_vid, $resultset_vid, $column_map);
}
}