View source
<?php
namespace Drupal\Tests\book\Functional;
use Drupal\Component\Render\FormattableMarkup;
use Drupal\Core\Cache\Cache;
use Drupal\Tests\BrowserTestBase;
use Drupal\user\RoleInterface;
class BookTest extends BrowserTestBase {
use BookTestTrait;
public static $modules = [
'content_moderation',
'book',
'block',
'node_access_test',
'book_test',
];
protected $defaultTheme = 'classy';
protected $webUser;
protected $adminUser;
protected $webUserWithoutNodeAccess;
protected function setUp() {
parent::setUp();
$this
->drupalPlaceBlock('system_breadcrumb_block');
$this
->drupalPlaceBlock('page_title_block');
node_access_rebuild();
$this->bookAuthor = $this
->drupalCreateUser([
'create new books',
'create book content',
'edit own book content',
'add content to books',
'view own unpublished content',
]);
$this->webUser = $this
->drupalCreateUser([
'access printer-friendly version',
'node test view',
]);
$this->webUserWithoutNodeAccess = $this
->drupalCreateUser([
'access printer-friendly version',
]);
$this->adminUser = $this
->drupalCreateUser([
'create new books',
'create book content',
'edit any book content',
'delete any book content',
'add content to books',
'administer blocks',
'administer permissions',
'administer book outlines',
'node test view',
'administer content types',
'administer site configuration',
'view any unpublished content',
]);
}
public function testBookNavigationCacheContext() {
$this
->drupalCreateContentType([
'type' => 'page',
]);
$page = $this
->drupalCreateNode();
$book_nodes = $this
->createBook();
\Drupal::state()
->set('book_test.debug_book_navigation_cache_context', TRUE);
Cache::invalidateTags([
'book_test.debug_book_navigation_cache_context',
]);
$this
->drupalLogin($this->bookAuthor);
$this
->drupalGet($this->adminUser
->toUrl());
$this
->assertRaw('[route.book_navigation]=book.none');
$this
->drupalGet($page
->toUrl());
$this
->assertRaw('[route.book_navigation]=book.none');
$this
->drupalGet($book_nodes[0]
->toUrl());
$this
->assertRaw('[route.book_navigation]=0|2|3');
$this
->drupalGet($book_nodes[1]
->toUrl());
$this
->assertRaw('[route.book_navigation]=0|2|3|4');
$this
->drupalGet($book_nodes[2]
->toUrl());
$this
->assertRaw('[route.book_navigation]=0|2|3|5');
$this
->drupalGet($book_nodes[3]
->toUrl());
$this
->assertRaw('[route.book_navigation]=0|2|6');
$this
->drupalGet($book_nodes[4]
->toUrl());
$this
->assertRaw('[route.book_navigation]=0|2|7');
}
public function testEmptyBook() {
$this
->drupalLogin($this->bookAuthor);
$book = $this
->createBookNode('new');
$this
->drupalLogout();
$this
->drupalLogin($this->adminUser);
$this
->drupalPostForm('admin/structure/book/' . $book
->id(), [], t('Save book pages'));
$this
->assertText(t('Updated book @book.', [
'@book' => $book
->label(),
]));
}
public function testBook() {
$nodes = $this
->createBook();
$book = $this->book;
$this
->drupalLogin($this->webUser);
$this
->checkBookNode($book, [
$nodes[0],
$nodes[3],
$nodes[4],
], FALSE, FALSE, $nodes[0], []);
$this
->checkBookNode($nodes[0], [
$nodes[1],
$nodes[2],
], $book, $book, $nodes[1], [
$book,
]);
$this
->checkBookNode($nodes[1], NULL, $nodes[0], $nodes[0], $nodes[2], [
$book,
$nodes[0],
]);
$this
->checkBookNode($nodes[2], NULL, $nodes[1], $nodes[0], $nodes[3], [
$book,
$nodes[0],
]);
$this
->checkBookNode($nodes[3], NULL, $nodes[2], $book, $nodes[4], [
$book,
]);
$this
->checkBookNode($nodes[4], NULL, $nodes[3], $book, FALSE, [
$book,
]);
$this
->drupalLogout();
$this
->drupalLogin($this->bookAuthor);
$this
->drupalGet('node/add/book');
$this
->assertCacheTag('config:book.settings');
$nodes[] = $this
->createBookNode($book
->id(), $nodes[3]->book['nid']);
$this
->drupalLogout();
$this
->drupalLogin($this->webUser);
$this
->checkBookNode($nodes[3], [
$nodes[5],
], $nodes[2], $book, $nodes[5], [
$book,
]);
$this
->checkBookNode($nodes[4], NULL, $nodes[5], $book, FALSE, [
$book,
]);
$this
->drupalLogout();
$this
->drupalLogin($this->bookAuthor);
$other_book = $this
->createBookNode('new');
$node = $this
->createBookNode($book
->id());
$edit = [
'book[bid]' => $other_book
->id(),
];
$this
->drupalPostForm('node/' . $node
->id() . '/edit', $edit, t('Save'));
$this
->drupalLogout();
$this
->drupalLogin($this->webUser);
$this->book = $other_book;
$this
->checkBookNode($other_book, [
$node,
], FALSE, FALSE, $node, []);
$this
->checkBookNode($node, NULL, $other_book, $other_book, FALSE, [
$other_book,
]);
$this
->drupalLogin($this->bookAuthor);
$book = $this
->createBookNode('new');
$book
->save();
}
public function testBookExport() {
$nodes = $this
->createBook();
$this
->drupalLogin($this->webUser);
$this
->drupalGet('node/' . $this->book
->id());
$this
->clickLink(t('Printer-friendly version'));
foreach ($nodes as $node) {
$this
->assertText($node
->label(), 'Node title found in printer friendly version.');
$this
->assertRaw($node->body->processed, 'Node body found in printer friendly version.');
}
$this
->drupalGet('book/export/foobar/' . $this->book
->id());
$this
->assertSession()
->statusCodeEquals(404);
$this
->drupalGet('book/export/html/123');
$this
->assertSession()
->statusCodeEquals(404);
$this
->drupalLogout();
$this
->drupalGet('node/' . $this->book
->id());
$this
->assertSession()
->linkNotExists(t('Printer-friendly version'), 'Anonymous user is not shown link to printer-friendly version.');
$this
->drupalGet('book/export/html/' . $this->book
->id());
$this
->assertSession()
->statusCodeEquals(403);
user_role_grant_permissions(RoleInterface::ANONYMOUS_ID, [
'access printer-friendly version',
]);
$this
->drupalGet('book/export/html/' . $this->book
->id());
$this
->assertSession()
->statusCodeEquals(403);
}
public function testBookNavigationBlock() {
$this
->drupalLogin($this->adminUser);
$block = $this
->drupalPlaceBlock('book_navigation');
$edit = [];
$edit[RoleInterface::ANONYMOUS_ID . '[node test view]'] = TRUE;
$this
->drupalPostForm('admin/people/permissions/' . RoleInterface::ANONYMOUS_ID, $edit, t('Save permissions'));
$this
->assertText(t('The changes have been saved.'), "Permission 'node test view' successfully assigned to anonymous users.");
$nodes = $this
->createBook();
$this
->drupalGet('<front>');
$this
->assertText($block
->label(), 'Book navigation block is displayed.');
$this
->assertText($this->book
->label(), new FormattableMarkup('Link to book root (@title) is displayed.', [
'@title' => $nodes[0]
->label(),
]));
$this
->assertNoText($nodes[0]
->label(), 'No links to individual book pages are displayed.');
}
public function testGetTableOfContents() {
$nodes = $this
->createBook();
$book = $this->book;
$this
->drupalLogin($this->bookAuthor);
foreach ([
5 => 2,
6 => 3,
7 => 6,
8 => 7,
9 => 8,
10 => 9,
11 => 10,
] as $child => $parent) {
$nodes[$child] = $this
->createBookNode($book
->id(), $nodes[$parent]
->id());
}
$this
->drupalGet($nodes[0]
->toUrl('edit-form'));
$this
->assertNoOption('edit-book-pid', $nodes[10]
->id());
$this
->assertNoOption('edit-book-pid', $nodes[11]
->id());
$this
->assertOption('edit-book-pid', $nodes[9]
->id());
$manager = $this->container
->get('book.manager');
$options = $manager
->getTableOfContents($book
->id(), 3);
$expected_nids = [
$book
->id(),
$nodes[0]
->id(),
$nodes[1]
->id(),
$nodes[2]
->id(),
$nodes[3]
->id(),
$nodes[6]
->id(),
$nodes[4]
->id(),
];
$this
->assertEqual(count($options), count($expected_nids));
$diff = array_diff($expected_nids, array_keys($options));
$this
->assertTrue(empty($diff), 'Found all expected option keys');
$options = $manager
->getTableOfContents($book
->id(), 3, [
$nodes[3]
->id(),
]);
$expected_nids = [
$book
->id(),
$nodes[0]
->id(),
$nodes[1]
->id(),
$nodes[2]
->id(),
$nodes[4]
->id(),
];
$this
->assertEqual(count($options), count($expected_nids));
$diff = array_diff($expected_nids, array_keys($options));
$this
->assertTrue(empty($diff), 'Found all expected option keys after excluding Node 3');
}
public function testNavigationBlockOnAccessModuleInstalled() {
$this
->drupalLogin($this->adminUser);
$block = $this
->drupalPlaceBlock('book_navigation', [
'block_mode' => 'book pages',
]);
$edit = [];
$edit[RoleInterface::ANONYMOUS_ID . '[node test view]'] = TRUE;
$this
->drupalPostForm('admin/people/permissions/' . RoleInterface::ANONYMOUS_ID, $edit, t('Save permissions'));
$this
->assertText(t('The changes have been saved.'), "Permission 'node test view' successfully assigned to anonymous users.");
$this
->createBook();
$this
->drupalLogin($this->webUser);
$this
->drupalGet('node/' . $this->book
->id());
$this
->assertText($block
->label(), 'Book navigation block is displayed to registered users.');
$this
->drupalLogout();
$this
->drupalGet('node/' . $this->book
->id());
$this
->assertText($block
->label(), 'Book navigation block is displayed to anonymous users.');
$this
->drupalGet('<front>');
$this
->assertNoText($block
->label(), 'Book navigation block is not shown on non-book pages.');
}
public function testBookDelete() {
$node_storage = $this->container
->get('entity_type.manager')
->getStorage('node');
$nodes = $this
->createBook();
$this
->drupalLogin($this->adminUser);
$edit = [];
$this
->drupalGet('node/' . $this->book
->id() . '/outline/remove');
$this
->assertSession()
->statusCodeEquals(403);
$this
->drupalPostForm('node/' . $nodes[4]
->id() . '/outline/remove', $edit, t('Remove'));
$node_storage
->resetCache([
$nodes[4]
->id(),
]);
$node4 = $node_storage
->load($nodes[4]
->id());
$this
->assertTrue(empty($node4->book), 'Deleting child book node properly allowed.');
$node4
->delete();
unset($nodes[4]);
$node_storage
->delete($nodes);
$this
->drupalPostForm('node/' . $this->book
->id() . '/outline/remove', $edit, t('Remove'));
$node_storage
->resetCache([
$this->book
->id(),
]);
$node = $node_storage
->load($this->book
->id());
$this
->assertTrue(empty($node->book), 'Deleting childless top-level book node properly allowed.');
$nodes = $this
->createBook();
$this
->drupalLogin($this->adminUser);
$this
->drupalGet($this->book
->toUrl('delete-form'));
$this
->assertRaw(t('%title is part of a book outline, and has associated child pages. If you proceed with deletion, the child pages will be relocated automatically.', [
'%title' => $this->book
->label(),
]));
$this
->drupalPostForm($this->book
->toUrl('delete-form'), [], t('Delete'));
$this
->drupalGet($nodes[0]
->toUrl());
$this
->assertSession()
->statusCodeEquals(200);
$this
->assertText($nodes[0]
->label());
$node_storage = \Drupal::entityTypeManager()
->getStorage('node');
$node_storage
->resetCache();
$child = $node_storage
->load($nodes[0]
->id());
$this
->assertEqual($child
->id(), $child->book['bid'], 'Child node book ID updated when parent is deleted.');
$second = $node_storage
->load($nodes[1]
->id());
$this
->assertEqual($child
->id(), $second->book['bid'], '3rd-level child node is now second level when top-level node is deleted.');
}
public function testBookOutline() {
$this
->drupalLogin($this->bookAuthor);
$empty_book = $this
->drupalCreateNode([
'type' => 'book',
]);
$this
->drupalGet('node/' . $empty_book
->id() . '/outline');
$this
->assertSession()
->linkNotExists(t('Book outline'), 'Book Author is not allowed to outline');
$this
->drupalLogin($this->adminUser);
$this
->drupalGet('node/' . $empty_book
->id() . '/outline');
$this
->assertRaw(t('Book outline'));
$this
->assertOptionSelected('edit-book-bid', 0, 'Node does not belong to a book');
$this
->assertSession()
->linkNotExists(t('Remove from book outline'));
$edit = [];
$edit['book[bid]'] = '1';
$this
->drupalPostForm('node/' . $empty_book
->id() . '/outline', $edit, t('Add to book outline'));
$node = \Drupal::entityTypeManager()
->getStorage('node')
->load($empty_book
->id());
$this
->assertEqual($node->book['nid'], $empty_book
->id());
$this
->assertEqual($node->book['bid'], $empty_book
->id());
$this
->assertEqual($node->book['depth'], 1);
$this
->assertEqual($node->book['p1'], $empty_book
->id());
$this
->assertEqual($node->book['pid'], '0');
$this
->drupalLogin($this->bookAuthor);
$book = $this
->createBookNode('new');
$this
->drupalLogin($this->adminUser);
$this
->drupalGet('node/' . $book
->id() . '/outline');
$this
->assertRaw(t('Book outline'));
$this
->clickLink(t('Remove from book outline'));
$this
->assertRaw(t('Are you sure you want to remove %title from the book hierarchy?', [
'%title' => $book
->label(),
]));
$node = $this
->drupalCreateNode([
'type' => 'book',
]);
$edit = [];
$edit['book[bid]'] = $node
->id();
$this
->drupalPostForm('node/' . $node
->id() . '/edit', $edit, t('Save'));
$node = \Drupal::entityTypeManager()
->getStorage('node')
->load($node
->id());
$this
->assertEqual($node->book['nid'], $node
->id());
$this
->assertEqual($node->book['bid'], $node
->id());
$this
->assertEqual($node->book['depth'], 1);
$this
->assertEqual($node->book['p1'], $node
->id());
$this
->assertEqual($node->book['pid'], '0');
$this
->drupalGet('node/' . $node
->id() . '/edit');
$this
->assertOptionSelected('edit-book-bid', $node
->id());
}
public function testSaveBookLink() {
$book_manager = \Drupal::service('book.manager');
$link = [
'nid' => 1,
'has_children' => 0,
'original_bid' => 0,
'parent_depth_limit' => 8,
'pid' => 0,
'weight' => 0,
'bid' => 1,
];
$new = TRUE;
$return = $book_manager
->saveBookLink($link, $new);
$link += $book_manager
->getLinkDefaults($link['nid']);
$this
->assertEqual($return, $link);
}
public function testBookListing() {
\Drupal::service('module_installer')
->uninstall([
'node_access_test',
]);
$nodes = $this
->createBook();
$this
->drupalGet('book');
$this
->assertText($this->book
->label(), 'The book title is displayed on the book listing page.');
$this->book
->setUnpublished();
$this->book
->save();
$this
->drupalGet('book');
$this
->assertSession()
->pageTextNotContains($this->book
->label());
$this->book
->setPublished();
$this->book
->save();
$nodes[0]
->setUnpublished();
$nodes[0]
->save();
$this
->drupalGet('book');
$this
->assertSession()
->pageTextContains($this->book
->label());
$this
->drupalLogin($this->bookAuthor);
$this->book
->setUnpublished();
$this->book
->save();
$this
->drupalGet('book');
$this
->assertSession()
->pageTextContains($this->book
->label());
$this->book
->setOwner($this->webUser)
->save();
$this
->drupalGet('book');
$this
->assertSession()
->pageTextNotContains($this->book
->label());
$this
->drupalLogin($this->adminUser);
$this
->drupalGet('book');
$this
->assertSession()
->pageTextContains($this->book
->label());
}
public function testAdminBookListing() {
$this
->createBook();
$this
->drupalLogin($this->adminUser);
$this
->drupalGet('admin/structure/book');
$this
->assertText($this->book
->label(), 'The book title is displayed on the administrative book listing page.');
}
public function testAdminBookNodeListing() {
$this
->createBook();
$this
->drupalLogin($this->adminUser);
$this
->drupalGet('admin/structure/book/' . $this->book
->id());
$this
->assertText($this->book
->label(), 'The book title is displayed on the administrative book listing page.');
$elements = $this
->xpath('//table//ul[@class="dropbutton"]/li/a');
$this
->assertEqual($elements[0]
->getText(), 'View', 'View link is found from the list.');
}
public function testHookNodeLoadAccess() {
\Drupal::service('module_installer')
->install([
'node_access_test',
]);
$this
->drupalLogin($this->bookAuthor);
$this->book = $this
->createBookNode('new');
$node_storage = \Drupal::entityTypeManager()
->getStorage('node');
$node_storage
->resetCache();
$this
->drupalLogin($this->webUserWithoutNodeAccess);
$book_node = $node_storage
->load($this->book
->id());
$this
->assertTrue(!empty($book_node->book));
$this
->assertEqual($book_node->book['bid'], $this->book
->id());
$node_storage
->resetCache();
$this
->drupalLogin($this->webUser);
$book_node = $node_storage
->load($this->book
->id());
$this
->assertTrue(!empty($book_node->book));
$this
->assertEqual($book_node->book['bid'], $this->book
->id());
}
public function testBookNavigationBlockOnUnpublishedBook() {
$this
->createBook();
$administratorUser = $this
->drupalCreateUser([
'administer blocks',
'administer nodes',
'bypass node access',
]);
$this
->drupalLogin($administratorUser);
$this
->drupalPlaceBlock('book_navigation', [
'block_mode' => 'book pages',
]);
$edit = [
'status[value]' => FALSE,
];
$this
->drupalPostForm('node/' . $this->book
->id() . '/edit', $edit, t('Save'));
$this
->drupalGet('node/' . $this->book
->id());
$this
->assertText($this->book
->label(), 'Unpublished book with "Show block only on book pages" book navigation settings.');
}
public function testSettingsForm() {
$this
->drupalLogin($this->adminUser);
$this
->drupalPostForm('admin/structure/book/settings', [], 'Save configuration');
}
}