annotate core/modules/book/tests/src/Functional/BookTestTrait.php @ 13:5fb285c0d0e3

Update Drupal core to 8.4.7 via Composer. Security update; I *think* we've been lucky to get away with this so far, as we don't support self-registration which seems to be used by the so-called "drupalgeddon 2" attack that 8.4.5 was vulnerable to.
author Chris Cannam
date Mon, 23 Apr 2018 09:33:26 +0100
parents 4c8ae668cc8c
children 129ea1e6d783
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Tests\book\Functional;
Chris@0 4
Chris@0 5 use Drupal\Component\Utility\SafeMarkup;
Chris@0 6 use Drupal\Core\Entity\EntityInterface;
Chris@0 7
Chris@0 8 /**
Chris@0 9 * Provides common functionality for Book test classes.
Chris@0 10 */
Chris@0 11 trait BookTestTrait {
Chris@0 12
Chris@0 13 /**
Chris@0 14 * A book node.
Chris@0 15 *
Chris@0 16 * @var \Drupal\node\NodeInterface
Chris@0 17 */
Chris@0 18 protected $book;
Chris@0 19
Chris@0 20 /**
Chris@0 21 * A user with permission to create and edit books.
Chris@0 22 *
Chris@0 23 * @var \Drupal\Core\Session\AccountInterface
Chris@0 24 */
Chris@0 25 protected $bookAuthor;
Chris@0 26
Chris@0 27 /**
Chris@0 28 * Creates a new book with a page hierarchy.
Chris@0 29 *
Chris@0 30 * @param array $edit
Chris@0 31 * (optional) Field data in an associative array. Changes the current input
Chris@0 32 * fields (where possible) to the values indicated. Defaults to an empty
Chris@0 33 * array.
Chris@0 34 *
Chris@0 35 * @return \Drupal\node\NodeInterface[]
Chris@0 36 */
Chris@0 37 public function createBook($edit = []) {
Chris@0 38 // Create new book.
Chris@0 39 $this->drupalLogin($this->bookAuthor);
Chris@0 40
Chris@0 41 $this->book = $this->createBookNode('new', NULL, $edit);
Chris@0 42 $book = $this->book;
Chris@0 43
Chris@0 44 /*
Chris@0 45 * Add page hierarchy to book.
Chris@0 46 * Book
Chris@0 47 * |- Node 0
Chris@0 48 * |- Node 1
Chris@0 49 * |- Node 2
Chris@0 50 * |- Node 3
Chris@0 51 * |- Node 4
Chris@0 52 */
Chris@0 53 $nodes = [];
Chris@0 54 // Node 0.
Chris@0 55 $nodes[] = $this->createBookNode($book->id(), NULL, $edit);
Chris@0 56 // Node 1.
Chris@0 57 $nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid'], $edit);
Chris@0 58 // Node 2.
Chris@0 59 $nodes[] = $this->createBookNode($book->id(), $nodes[0]->book['nid'], $edit);
Chris@0 60 // Node 3.
Chris@0 61 $nodes[] = $this->createBookNode($book->id(), NULL, $edit);
Chris@0 62 // Node 4.
Chris@0 63 $nodes[] = $this->createBookNode($book->id(), NULL, $edit);
Chris@0 64
Chris@0 65 $this->drupalLogout();
Chris@0 66
Chris@0 67 return $nodes;
Chris@0 68 }
Chris@0 69
Chris@0 70 /**
Chris@0 71 * Checks the outline of sub-pages; previous, up, and next.
Chris@0 72 *
Chris@0 73 * Also checks the printer friendly version of the outline.
Chris@0 74 *
Chris@0 75 * @param \Drupal\Core\Entity\EntityInterface $node
Chris@0 76 * Node to check.
Chris@0 77 * @param $nodes
Chris@0 78 * Nodes that should be in outline.
Chris@0 79 * @param $previous
Chris@0 80 * Previous link node.
Chris@0 81 * @param $up
Chris@0 82 * Up link node.
Chris@0 83 * @param $next
Chris@0 84 * Next link node.
Chris@0 85 * @param array $breadcrumb
Chris@0 86 * The nodes that should be displayed in the breadcrumb.
Chris@0 87 */
Chris@0 88 public function checkBookNode(EntityInterface $node, $nodes, $previous, $up, $next, array $breadcrumb) {
Chris@0 89 // $number does not use drupal_static as it should not be reset
Chris@0 90 // since it uniquely identifies each call to checkBookNode().
Chris@0 91 static $number = 0;
Chris@0 92 $this->drupalGet('node/' . $node->id());
Chris@0 93
Chris@0 94 // Check outline structure.
Chris@0 95 if ($nodes !== NULL) {
Chris@0 96 $this->assertPattern($this->generateOutlinePattern($nodes), format_string('Node @number outline confirmed.', ['@number' => $number]));
Chris@0 97 }
Chris@0 98 else {
Chris@0 99 $this->pass(format_string('Node %number does not have outline.', ['%number' => $number]));
Chris@0 100 }
Chris@0 101
Chris@0 102 // Check previous, up, and next links.
Chris@0 103 if ($previous) {
Chris@0 104 /** @var \Drupal\Core\Url $url */
Chris@0 105 $url = $previous->urlInfo();
Chris@0 106 $url->setOptions(['attributes' => ['rel' => ['prev'], 'title' => t('Go to previous page')]]);
Chris@0 107 $text = SafeMarkup::format('<b>‹</b> @label', ['@label' => $previous->label()]);
Chris@0 108 $this->assertRaw(\Drupal::l($text, $url), 'Previous page link found.');
Chris@0 109 }
Chris@0 110
Chris@0 111 if ($up) {
Chris@0 112 /** @var \Drupal\Core\Url $url */
Chris@0 113 $url = $up->urlInfo();
Chris@0 114 $url->setOptions(['attributes' => ['title' => t('Go to parent page')]]);
Chris@0 115 $this->assertRaw(\Drupal::l('Up', $url), 'Up page link found.');
Chris@0 116 }
Chris@0 117
Chris@0 118 if ($next) {
Chris@0 119 /** @var \Drupal\Core\Url $url */
Chris@0 120 $url = $next->urlInfo();
Chris@0 121 $url->setOptions(['attributes' => ['rel' => ['next'], 'title' => t('Go to next page')]]);
Chris@0 122 $text = SafeMarkup::format('@label <b>›</b>', ['@label' => $next->label()]);
Chris@0 123 $this->assertRaw(\Drupal::l($text, $url), 'Next page link found.');
Chris@0 124 }
Chris@0 125
Chris@0 126 // Compute the expected breadcrumb.
Chris@0 127 $expected_breadcrumb = [];
Chris@0 128 $expected_breadcrumb[] = \Drupal::url('<front>');
Chris@0 129 foreach ($breadcrumb as $a_node) {
Chris@0 130 $expected_breadcrumb[] = $a_node->url();
Chris@0 131 }
Chris@0 132
Chris@0 133 // Fetch links in the current breadcrumb.
Chris@0 134 $links = $this->xpath('//nav[@class="breadcrumb"]/ol/li/a');
Chris@0 135 $got_breadcrumb = [];
Chris@0 136 foreach ($links as $link) {
Chris@0 137 $got_breadcrumb[] = $link->getAttribute('href');
Chris@0 138 }
Chris@0 139
Chris@0 140 // Compare expected and got breadcrumbs.
Chris@0 141 $this->assertIdentical($expected_breadcrumb, $got_breadcrumb, 'The breadcrumb is correctly displayed on the page.');
Chris@0 142
Chris@0 143 // Check printer friendly version.
Chris@0 144 $this->drupalGet('book/export/html/' . $node->id());
Chris@0 145 $this->assertText($node->label(), 'Printer friendly title found.');
Chris@0 146 $this->assertRaw($node->body->processed, 'Printer friendly body found.');
Chris@0 147
Chris@0 148 $number++;
Chris@0 149 }
Chris@0 150
Chris@0 151 /**
Chris@0 152 * Creates a regular expression to check for the sub-nodes in the outline.
Chris@0 153 *
Chris@0 154 * @param array $nodes
Chris@0 155 * An array of nodes to check in outline.
Chris@0 156 *
Chris@0 157 * @return string
Chris@0 158 * A regular expression that locates sub-nodes of the outline.
Chris@0 159 */
Chris@0 160 public function generateOutlinePattern($nodes) {
Chris@0 161 $outline = '';
Chris@0 162 foreach ($nodes as $node) {
Chris@0 163 $outline .= '(node\/' . $node->id() . ')(.*?)(' . $node->label() . ')(.*?)';
Chris@0 164 }
Chris@0 165
Chris@0 166 return '/<nav id="book-navigation-' . $this->book->id() . '"(.*?)<ul(.*?)' . $outline . '<\/ul>/s';
Chris@0 167 }
Chris@0 168
Chris@0 169 /**
Chris@0 170 * Creates a book node.
Chris@0 171 *
Chris@0 172 * @param int|string $book_nid
Chris@0 173 * A book node ID or set to 'new' to create a new book.
Chris@0 174 * @param int|null $parent
Chris@0 175 * (optional) Parent book reference ID. Defaults to NULL.
Chris@0 176 * @param array $edit
Chris@0 177 * (optional) Field data in an associative array. Changes the current input
Chris@0 178 * fields (where possible) to the values indicated. Defaults to an empty
Chris@0 179 * array.
Chris@0 180 *
Chris@0 181 * @return \Drupal\node\NodeInterface
Chris@0 182 * The created node.
Chris@0 183 */
Chris@0 184 public function createBookNode($book_nid, $parent = NULL, $edit = []) {
Chris@0 185 // $number does not use drupal_static as it should not be reset
Chris@0 186 // since it uniquely identifies each call to createBookNode().
Chris@0 187 // Used to ensure that when sorted nodes stay in same order.
Chris@0 188 static $number = 0;
Chris@0 189
Chris@0 190 $edit['title[0][value]'] = str_pad($number, 2, '0', STR_PAD_LEFT) . ' - SimpleTest test node ' . $this->randomMachineName(10);
Chris@0 191 $edit['body[0][value]'] = 'SimpleTest test body ' . $this->randomMachineName(32) . ' ' . $this->randomMachineName(32);
Chris@0 192 $edit['book[bid]'] = $book_nid;
Chris@0 193
Chris@0 194 if ($parent !== NULL) {
Chris@0 195 $this->drupalPostForm('node/add/book', $edit, t('Change book (update list of parents)'));
Chris@0 196
Chris@0 197 $edit['book[pid]'] = $parent;
Chris@0 198 $this->drupalPostForm(NULL, $edit, t('Save'));
Chris@0 199 // Make sure the parent was flagged as having children.
Chris@0 200 $parent_node = \Drupal::entityManager()->getStorage('node')->loadUnchanged($parent);
Chris@0 201 $this->assertFalse(empty($parent_node->book['has_children']), 'Parent node is marked as having children');
Chris@0 202 }
Chris@0 203 else {
Chris@0 204 $this->drupalPostForm('node/add/book', $edit, t('Save'));
Chris@0 205 }
Chris@0 206
Chris@0 207 // Check to make sure the book node was created.
Chris@0 208 $node = $this->drupalGetNodeByTitle($edit['title[0][value]']);
Chris@0 209 $this->assertNotNull(($node === FALSE ? NULL : $node), 'Book node found in database.');
Chris@0 210 $number++;
Chris@0 211
Chris@0 212 return $node;
Chris@0 213 }
Chris@0 214
Chris@0 215 }