annotate core/modules/quickedit/tests/src/FunctionalJavascript/QuickEditLoadingTest.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents af1871eacc83
children
rev   line source
Chris@18 1 <?php
Chris@18 2
Chris@18 3 namespace Drupal\Tests\quickedit\FunctionalJavascript;
Chris@18 4
Chris@18 5 use Behat\Mink\Session;
Chris@18 6 use Drupal\block_content\Entity\BlockContent;
Chris@18 7 use Drupal\Core\Entity\Entity\EntityViewDisplay;
Chris@18 8 use Drupal\field\Entity\FieldConfig;
Chris@18 9 use Drupal\field\Entity\FieldStorageConfig;
Chris@18 10 use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
Chris@18 11 use Drupal\node\Entity\Node;
Chris@18 12 use Drupal\node\Entity\NodeType;
Chris@18 13 use Drupal\filter\Entity\FilterFormat;
Chris@18 14 use Drupal\Tests\contextual\FunctionalJavascript\ContextualLinkClickTrait;
Chris@18 15 use Drupal\Tests\TestFileCreationTrait;
Chris@18 16
Chris@18 17 /**
Chris@18 18 * Tests loading of in-place editing functionality and lazy loading of its
Chris@18 19 * in-place editors.
Chris@18 20 *
Chris@18 21 * @group quickedit
Chris@18 22 */
Chris@18 23 class QuickEditLoadingTest extends WebDriverTestBase {
Chris@18 24
Chris@18 25 use ContextualLinkClickTrait;
Chris@18 26
Chris@18 27 use TestFileCreationTrait {
Chris@18 28 getTestFiles as drupalGetTestFiles;
Chris@18 29 }
Chris@18 30
Chris@18 31 /**
Chris@18 32 * Modules to enable.
Chris@18 33 *
Chris@18 34 * @var array
Chris@18 35 */
Chris@18 36 public static $modules = [
Chris@18 37 'contextual',
Chris@18 38 'quickedit',
Chris@18 39 'filter',
Chris@18 40 'node',
Chris@18 41 'image',
Chris@18 42 ];
Chris@18 43
Chris@18 44 /**
Chris@18 45 * An user with permissions to create and edit articles.
Chris@18 46 *
Chris@18 47 * @var \Drupal\user\UserInterface
Chris@18 48 */
Chris@18 49 protected $authorUser;
Chris@18 50
Chris@18 51 /**
Chris@18 52 * A test node.
Chris@18 53 *
Chris@18 54 * @var \Drupal\node\NodeInterface
Chris@18 55 */
Chris@18 56 protected $testNode;
Chris@18 57
Chris@18 58 /**
Chris@18 59 * A author user with permissions to access in-place editor.
Chris@18 60 *
Chris@18 61 * @var \Drupal\user\UserInterface
Chris@18 62 */
Chris@18 63 protected $editorUser;
Chris@18 64
Chris@18 65 /**
Chris@18 66 * {@inheritdoc}
Chris@18 67 */
Chris@18 68 protected function setUp() {
Chris@18 69 parent::setUp();
Chris@18 70
Chris@18 71 // Create a text format.
Chris@18 72 $filtered_html_format = FilterFormat::create([
Chris@18 73 'format' => 'filtered_html',
Chris@18 74 'name' => 'Filtered HTML',
Chris@18 75 'weight' => 0,
Chris@18 76 'filters' => [],
Chris@18 77 ]);
Chris@18 78 $filtered_html_format->save();
Chris@18 79
Chris@18 80 // Create a node type.
Chris@18 81 $this->drupalCreateContentType([
Chris@18 82 'type' => 'article',
Chris@18 83 'name' => 'Article',
Chris@18 84 ]);
Chris@18 85
Chris@18 86 // Set the node type to initially not have revisions.
Chris@18 87 // Testing with revisions will be done later.
Chris@18 88 $node_type = NodeType::load('article');
Chris@18 89 $node_type->setNewRevision(FALSE);
Chris@18 90 $node_type->save();
Chris@18 91
Chris@18 92 // Create one node of the above node type using the above text format.
Chris@18 93 $this->testNode = $this->drupalCreateNode([
Chris@18 94 'type' => 'article',
Chris@18 95 'body' => [
Chris@18 96 0 => [
Chris@18 97 'value' => '<p>How are you?</p>',
Chris@18 98 'format' => 'filtered_html',
Chris@18 99 ],
Chris@18 100 ],
Chris@18 101 'revision_log' => $this->randomString(),
Chris@18 102 ]);
Chris@18 103
Chris@18 104 // Create 2 users, the only difference being the ability to use in-place
Chris@18 105 // editing
Chris@18 106 $basic_permissions = [
Chris@18 107 'access content',
Chris@18 108 'create article content',
Chris@18 109 'edit any article content',
Chris@18 110 'use text format filtered_html',
Chris@18 111 'access contextual links',
Chris@18 112 ];
Chris@18 113 $this->authorUser = $this->drupalCreateUser($basic_permissions);
Chris@18 114 $this->editorUser = $this->drupalCreateUser(array_merge($basic_permissions, ['access in-place editing']));
Chris@18 115 }
Chris@18 116
Chris@18 117 /**
Chris@18 118 * Test the loading of Quick Edit with different permissions.
Chris@18 119 */
Chris@18 120 public function testUserPermissions() {
Chris@18 121 $assert = $this->assertSession();
Chris@18 122 $this->drupalLogin($this->authorUser);
Chris@18 123 $this->drupalGet('node/1');
Chris@18 124
Chris@18 125 // Library and in-place editors.
Chris@18 126 $this->assertNoRaw('core/modules/quickedit/js/quickedit.js', 'Quick Edit library not loaded.');
Chris@18 127 $this->assertNoRaw('core/modules/quickedit/js/editors/formEditor.js', "'form' in-place editor not loaded.");
Chris@18 128
Chris@18 129 // HTML annotation and title class does not exist for users without
Chris@18 130 // permission to in-place edit.
Chris@18 131 $this->assertNoRaw('data-quickedit-entity-id="node/1"');
Chris@18 132 $this->assertNoRaw('data-quickedit-field-id="node/1/body/en/full"');
Chris@18 133 $this->assertNoFieldByXPath('//h1[contains(@class, "js-quickedit-page-title")]');
Chris@18 134 $assert->linkNotExists('Quick edit');
Chris@18 135
Chris@18 136 // Tests the loading of Quick Edit when a user does have access to it.
Chris@18 137 // Also ensures lazy loading of in-place editors works.
Chris@18 138 $nid = $this->testNode->id();
Chris@18 139 // There should be only one revision so far.
Chris@18 140 $node = Node::load($nid);
Chris@18 141 $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node);
Chris@18 142 $this->assertCount(1, $vids, 'The node has only one revision.');
Chris@18 143 $original_log = $node->revision_log->value;
Chris@18 144
Chris@18 145 $this->drupalLogin($this->editorUser);
Chris@18 146 $this->drupalGet('node/' . $nid);
Chris@18 147 $page = $this->getSession()->getPage();
Chris@18 148
Chris@18 149 // Wait "Quick edit" button for node.
Chris@18 150 $assert->waitForElement('css', '[data-quickedit-entity-id="node/' . $nid . '"] .contextual .quickedit');
Chris@18 151 // Click by "Quick edit".
Chris@18 152 $this->clickContextualLink('[data-quickedit-entity-id="node/' . $nid . '"]', 'Quick edit');
Chris@18 153 // Switch to body field.
Chris@18 154 $page->find('css', '[data-quickedit-field-id="node/' . $nid . '/body/en/full"]')->click();
Chris@18 155 $assert->assertWaitOnAjaxRequest();
Chris@18 156
Chris@18 157 // Wait and update body field.
Chris@18 158 $body_field_locator = '[name="body[0][value]"]';
Chris@18 159 $body_text = 'Fine thanks.';
Chris@18 160 $assert->waitForElementVisible('css', $body_field_locator)->setValue('<p>' . $body_text . '</p>');
Chris@18 161
Chris@18 162 // Wait and click by "Save" button after body field was changed.
Chris@18 163 $assert->waitForElementVisible('css', '.quickedit-toolgroup.ops [type="submit"][aria-hidden="false"]')->click();
Chris@18 164 $assert->assertWaitOnAjaxRequest();
Chris@18 165
Chris@18 166 $node = Node::load($nid);
Chris@18 167 $vids = \Drupal::entityManager()->getStorage('node')->revisionIds($node);
Chris@18 168 $this->assertCount(1, $vids, 'The node has only one revision.');
Chris@18 169 $this->assertSame($original_log, $node->revision_log->value, 'The revision log message is unchanged.');
Chris@18 170
Chris@18 171 // Ensure that the changes take effect.
Chris@18 172 $assert->responseMatches("|\s*$body_text\s*|");
Chris@18 173
Chris@18 174 // Reload the page and check for updated body.
Chris@18 175 $this->drupalGet('node/' . $nid);
Chris@18 176 $assert->pageTextContains($body_text);
Chris@18 177 }
Chris@18 178
Chris@18 179 /**
Chris@18 180 * Test Quick Edit does not appear for entities with pending revisions.
Chris@18 181 */
Chris@18 182 public function testWithPendingRevision() {
Chris@18 183 $this->drupalLogin($this->editorUser);
Chris@18 184
Chris@18 185 // Verify that the preview is loaded correctly.
Chris@18 186 $this->drupalPostForm('node/add/article', ['title[0][value]' => 'foo'], 'Preview');
Chris@18 187 // Verify that quickedit is not active on preview.
Chris@18 188 $this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"');
Chris@18 189 $this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"');
Chris@18 190
Chris@18 191 $this->drupalGet('node/' . $this->testNode->id());
Chris@18 192 $this->assertRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"');
Chris@18 193 $this->assertRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"');
Chris@18 194
Chris@18 195 // Wait for the page to completely load before making any changes to the
Chris@18 196 // node. This allows Quick Edit to fetch the metadata without causing
Chris@18 197 // database locks on SQLite.
Chris@18 198 $this->assertSession()->assertWaitOnAjaxRequest();
Chris@18 199 $this->testNode->title = 'Updated node';
Chris@18 200 $this->testNode->setNewRevision(TRUE);
Chris@18 201 $this->testNode->isDefaultRevision(FALSE);
Chris@18 202 $this->testNode->save();
Chris@18 203
Chris@18 204 $this->drupalGet('node/' . $this->testNode->id());
Chris@18 205 $this->assertNoRaw('data-quickedit-entity-id="node/' . $this->testNode->id() . '"');
Chris@18 206 $this->assertNoRaw('data-quickedit-field-id="node/' . $this->testNode->id() . '/title/' . $this->testNode->language()->getId() . '/full"');
Chris@18 207 }
Chris@18 208
Chris@18 209 /**
Chris@18 210 * Tests the loading of Quick Edit for the title base field.
Chris@18 211 */
Chris@18 212 public function testTitleBaseField() {
Chris@18 213 $page = $this->getSession()->getPage();
Chris@18 214 $assert = $this->assertSession();
Chris@18 215 $nid = $this->testNode->id();
Chris@18 216
Chris@18 217 $this->drupalLogin($this->editorUser);
Chris@18 218 $this->drupalGet('node/' . $nid);
Chris@18 219
Chris@18 220 // Wait "Quick edit" button for node.
Chris@18 221 $assert->waitForElement('css', '[data-quickedit-entity-id="node/' . $nid . '"] .contextual .quickedit');
Chris@18 222 // Click by "Quick edit".
Chris@18 223 $this->clickContextualLink('[data-quickedit-entity-id="node/' . $nid . '"]', 'Quick edit');
Chris@18 224 // Switch to title field.
Chris@18 225 $page->find('css', '[data-quickedit-field-id="node/' . $nid . '/title/en/full"]')->click();
Chris@18 226 $assert->assertWaitOnAjaxRequest();
Chris@18 227
Chris@18 228 // Wait and update title field.
Chris@18 229 $field_locator = '.field--name-title';
Chris@18 230 $text_new = 'Obligatory question';
Chris@18 231 $assert->waitForElementVisible('css', $field_locator)->setValue($text_new);
Chris@18 232
Chris@18 233 // Wait and click by "Save" button after title field was changed.
Chris@18 234 $this->assertSession()->waitForElementVisible('css', '.quickedit-toolgroup.ops [type="submit"][aria-hidden="false"]')->click();
Chris@18 235 $assert->assertWaitOnAjaxRequest();
Chris@18 236
Chris@18 237 // Ensure that the changes take effect.
Chris@18 238 $assert->responseMatches("|\s*$text_new\s*|");
Chris@18 239
Chris@18 240 // Reload the page and check for updated title.
Chris@18 241 $this->drupalGet('node/' . $nid);
Chris@18 242 $assert->pageTextContains($text_new);
Chris@18 243 }
Chris@18 244
Chris@18 245 /**
Chris@18 246 * Tests that Quick Edit doesn't make fields rendered with display options
Chris@18 247 * editable.
Chris@18 248 */
Chris@18 249 public function testDisplayOptions() {
Chris@18 250 $node = Node::load('1');
Chris@18 251 $display_settings = [
Chris@18 252 'label' => 'inline',
Chris@18 253 ];
Chris@18 254 $build = $node->body->view($display_settings);
Chris@18 255 $output = \Drupal::service('renderer')->renderRoot($build);
Chris@18 256 $this->assertFalse(strpos($output, 'data-quickedit-field-id'), 'data-quickedit-field-id attribute not added when rendering field using dynamic display options.');
Chris@18 257 }
Chris@18 258
Chris@18 259 /**
Chris@18 260 * Tests Quick Edit on a node that was concurrently edited on the full node
Chris@18 261 * form.
Chris@18 262 */
Chris@18 263 public function testConcurrentEdit() {
Chris@18 264 $nid = $this->testNode->id();
Chris@18 265 $this->drupalLogin($this->authorUser);
Chris@18 266
Chris@18 267 // Open the edit page in the default session.
Chris@18 268 $this->drupalGet('node/' . $nid . '/edit');
Chris@18 269
Chris@18 270 // Switch to a concurrent session and save a quick edit change.
Chris@18 271 // We need to do some bookkeeping to keep track of the logged in user.
Chris@18 272 $logged_in_user = $this->loggedInUser;
Chris@18 273 $this->loggedInUser = FALSE;
Chris@18 274 // Register a session to preform concurrent editing.
Chris@18 275 $driver = $this->getDefaultDriverInstance();
Chris@18 276 $session = new Session($driver);
Chris@18 277 $this->mink->registerSession('concurrent', $session);
Chris@18 278 $this->mink->setDefaultSessionName('concurrent');
Chris@18 279 $this->initFrontPage();
Chris@18 280 $this->drupalLogin($this->editorUser);
Chris@18 281 $this->drupalGet('node/' . $nid);
Chris@18 282
Chris@18 283 $assert = $this->assertSession();
Chris@18 284 $page = $this->getSession()->getPage();
Chris@18 285
Chris@18 286 // Wait "Quick edit" button for node.
Chris@18 287 $assert->waitForElement('css', '[data-quickedit-entity-id="node/' . $nid . '"] .contextual .quickedit');
Chris@18 288 // Click by "Quick edit".
Chris@18 289 $this->clickContextualLink('[data-quickedit-entity-id="node/' . $nid . '"]', 'Quick edit');
Chris@18 290 // Switch to body field.
Chris@18 291 $page->find('css', '[data-quickedit-field-id="node/' . $nid . '/body/en/full"]')->click();
Chris@18 292 $assert->assertWaitOnAjaxRequest();
Chris@18 293
Chris@18 294 // Wait and update body field.
Chris@18 295 $body_field_locator = '[name="body[0][value]"]';
Chris@18 296 $body_text = 'Fine thanks.';
Chris@18 297 $assert->waitForElementVisible('css', $body_field_locator)->setValue('<p>' . $body_text . '</p>');
Chris@18 298
Chris@18 299 // Wait and click by "Save" button after body field was changed.
Chris@18 300 $assert->waitForElementVisible('css', '.quickedit-toolgroup.ops [type="submit"][aria-hidden="false"]')->click();
Chris@18 301 $assert->assertWaitOnAjaxRequest();
Chris@18 302
Chris@18 303 // Ensure that the changes take effect.
Chris@18 304 $assert->responseMatches("|\s*$body_text\s*|");
Chris@18 305
Chris@18 306 // Switch back to the default session.
Chris@18 307 $this->mink->setDefaultSessionName('default');
Chris@18 308 $this->loggedInUser = $logged_in_user;
Chris@18 309 // Ensure different save timestamps for field editing.
Chris@18 310 sleep(2);
Chris@18 311 $this->drupalPostForm(NULL, ['body[0][value]' => '<p>Concurrent edit!</p>'], 'Save');
Chris@18 312
Chris@18 313 $this->getSession()->getPage()->hasContent('The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.');
Chris@18 314 }
Chris@18 315
Chris@18 316 /**
Chris@18 317 * Tests that Quick Edit's data- attributes are present for content blocks.
Chris@18 318 */
Chris@18 319 public function testContentBlock() {
Chris@18 320 \Drupal::service('module_installer')->install(['block_content']);
Chris@18 321
Chris@18 322 // Create and place a content_block block.
Chris@18 323 $block = BlockContent::create([
Chris@18 324 'info' => $this->randomMachineName(),
Chris@18 325 'type' => 'basic',
Chris@18 326 'langcode' => 'en',
Chris@18 327 ]);
Chris@18 328 $block->save();
Chris@18 329 $this->drupalPlaceBlock('block_content:' . $block->uuid());
Chris@18 330
Chris@18 331 // Check that the data- attribute is present.
Chris@18 332 $this->drupalLogin($this->editorUser);
Chris@18 333 $this->drupalGet('');
Chris@18 334 $this->assertRaw('data-quickedit-entity-id="block_content/1"');
Chris@18 335 }
Chris@18 336
Chris@18 337 /**
Chris@18 338 * Tests that Quick Edit can handle an image field.
Chris@18 339 */
Chris@18 340 public function testImageField() {
Chris@18 341 $page = $this->getSession()->getPage();
Chris@18 342 $assert = $this->assertSession();
Chris@18 343
Chris@18 344 // Add an image field to the content type.
Chris@18 345 FieldStorageConfig::create([
Chris@18 346 'field_name' => 'field_image',
Chris@18 347 'type' => 'image',
Chris@18 348 'entity_type' => 'node',
Chris@18 349 ])->save();
Chris@18 350 FieldConfig::create([
Chris@18 351 'field_name' => 'field_image',
Chris@18 352 'field_type' => 'image',
Chris@18 353 'label' => t('Image'),
Chris@18 354 'entity_type' => 'node',
Chris@18 355 'bundle' => 'article',
Chris@18 356 ])->save();
Chris@18 357 entity_get_form_display('node', 'article', 'default')
Chris@18 358 ->setComponent('field_image', [
Chris@18 359 'type' => 'image_image',
Chris@18 360 ])
Chris@18 361 ->save();
Chris@18 362 $display = EntityViewDisplay::load('node.article.default');
Chris@18 363 $display->setComponent('field_image', [
Chris@18 364 'type' => 'image',
Chris@18 365 ])->save();
Chris@18 366
Chris@18 367 // Add an image to the node.
Chris@18 368 $this->drupalLogin($this->editorUser);
Chris@18 369 $this->drupalGet('node/1/edit');
Chris@18 370 $image = $this->drupalGetTestFiles('image')[0];
Chris@18 371 $image_path = $this->container->get('file_system')->realpath($image->uri);
Chris@18 372 $page->attachFileToField('files[field_image_0]', $image_path);
Chris@18 373 $alt_field = $assert->waitForField('field_image[0][alt]');
Chris@18 374 $this->assertNotEmpty($alt_field);
Chris@18 375 $this->drupalPostForm(NULL, [
Chris@18 376 'field_image[0][alt]' => 'Vivamus aliquet elit',
Chris@18 377 ], t('Save'));
Chris@18 378
Chris@18 379 // The image field form should load normally.
Chris@18 380 // Wait "Quick edit" button for node.
Chris@18 381 $assert->waitForElement('css', '[data-quickedit-entity-id="node/1"] .contextual .quickedit');
Chris@18 382 // Click by "Quick edit".
Chris@18 383 $this->clickContextualLink('[data-quickedit-entity-id="node/1"]', 'Quick edit');
Chris@18 384 // Switch to body field.
Chris@18 385 $assert->waitForElement('css', '[data-quickedit-field-id="node/1/field_image/en/full"]')->click();
Chris@18 386 $assert->assertWaitOnAjaxRequest();
Chris@18 387
Chris@18 388 $field_locator = '.field--name-field-image';
Chris@18 389 $assert->waitForElementVisible('css', $field_locator);
Chris@18 390 }
Chris@18 391
Chris@18 392 }