PagePreviewTest.php 20 KB
Newer Older
1 2
<?php

3
namespace Drupal\Tests\node\Functional;
4

5
use Drupal\comment\Tests\CommentTestTrait;
6
use Drupal\Core\Field\FieldStorageDefinitionInterface;
7
use Drupal\Core\Language\LanguageInterface;
8
use Drupal\Core\Url;
9 10
use Drupal\field\Entity\FieldConfig;
use Drupal\field\Entity\FieldStorageConfig;
11
use Drupal\node\Entity\NodeType;
12 13
use Drupal\taxonomy\Entity\Term;
use Drupal\taxonomy\Entity\Vocabulary;
14
use Drupal\Tests\field\Traits\EntityReferenceTestTrait;
15
use Drupal\Tests\TestFileCreationTrait;
16

17 18
/**
 * Tests the node entity preview functionality.
19 20
 *
 * @group node
21
 */
22
class PagePreviewTest extends NodeTestBase {
23

24
  use EntityReferenceTestTrait;
25
  use CommentTestTrait;
26 27 28
  use TestFileCreationTrait {
    getTestFiles as drupalGetTestFiles;
  }
29

30
  /**
31
   * Enable the comment, node and taxonomy modules to test on the preview.
32 33 34
   *
   * @var array
   */
35 36 37 38 39 40 41 42 43 44
  protected static $modules = [
    'node',
    'taxonomy',
    'comment',
    'image',
    'file',
    'text',
    'node_test',
    'menu_ui',
  ];
45

46
  /**
47 48 49 50 51
   * {@inheritdoc}
   */
  protected $defaultTheme = 'classy';

  /**
52 53 54 55
   * The name of the created field.
   *
   * @var string
   */
56
  protected $fieldName;
57

58
  protected function setUp(): void {
59
    parent::setUp();
60
    $this->addDefaultCommentField('node', 'page');
61

62
    $web_user = $this->drupalCreateUser(['edit own page content', 'create page content', 'administer menu']);
63
    $this->drupalLogin($web_user);
64 65

    // Add a vocabulary so we can test different view modes.
66
    $vocabulary = Vocabulary::create([
67 68 69
      'name' => $this->randomMachineName(),
      'description' => $this->randomMachineName(),
      'vid' => $this->randomMachineName(),
70
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
71
      'help' => '',
72
    ]);
73 74 75 76 77
    $vocabulary->save();

    $this->vocabulary = $vocabulary;

    // Add a term to the vocabulary.
78
    $term = Term::create([
79 80
      'name' => $this->randomMachineName(),
      'description' => $this->randomMachineName(),
81
      'vid' => $this->vocabulary->id(),
82
      'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED,
83
    ]);
84 85 86 87
    $term->save();

    $this->term = $term;

88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106
    // Create an image field.
    FieldStorageConfig::create([
      'field_name' => 'field_image',
      'entity_type' => 'node',
      'type' => 'image',
      'settings' => [],
      'cardinality' => FieldStorageConfig::CARDINALITY_UNLIMITED,
    ])->save();

    $field_config = FieldConfig::create([
      'field_name' => 'field_image',
      'label' => 'Images',
      'entity_type' => 'node',
      'bundle' => 'page',
      'required' => FALSE,
      'settings' => [],
    ]);
    $field_config->save();

107
    // Create a field.
108
    $this->fieldName = mb_strtolower($this->randomMachineName());
109 110
    $handler_settings = [
      'target_bundles' => [
111
        $this->vocabulary->id() => $this->vocabulary->id(),
112
      ],
113
      'auto_create' => TRUE,
114
    ];
115
    $this->createEntityReferenceField('node', 'page', $this->fieldName, 'Tags', 'taxonomy_term', 'default', $handler_settings, FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED);
116

117 118 119 120
    /** @var \Drupal\Core\Entity\EntityDisplayRepositoryInterface $display_repository */
    $display_repository = \Drupal::service('entity_display.repository');

    $display_repository->getFormDisplay('node', 'page')
121
      ->setComponent($this->fieldName, [
122
        'type' => 'entity_reference_autocomplete_tags',
123
      ])
124 125 126
      ->save();

    // Show on default display and teaser.
127
    $display_repository->getViewDisplay('node', 'page')
128
      ->setComponent($this->fieldName, [
129
        'type' => 'entity_reference_label',
130
      ])
131
      ->save();
132
    $display_repository->getViewDisplay('node', 'page', 'teaser')
133
      ->setComponent($this->fieldName, [
134
        'type' => 'entity_reference_label',
135
      ])
136
      ->save();
137

138
    $display_repository->getFormDisplay('node', 'page')
139
      ->setComponent('field_image', [
140 141
        'type' => 'image_image',
        'settings' => [],
142
      ])
143 144
      ->save();

145
    $display_repository->getViewDisplay('node', 'page')
146 147
      ->setComponent('field_image')
      ->save();
148 149 150 151 152 153 154 155 156

    // Create a multi-value text field.
    $field_storage = FieldStorageConfig::create([
      'field_name' => 'field_test_multi',
      'entity_type' => 'node',
      'cardinality' => FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED,
      'type' => 'text',
      'settings' => [
        'max_length' => 50,
157
      ],
158 159 160 161 162 163 164
    ]);
    $field_storage->save();
    FieldConfig::create([
      'field_storage' => $field_storage,
      'bundle' => 'page',
    ])->save();

165
    $display_repository->getFormDisplay('node', 'page')
166
      ->setComponent('field_test_multi', [
167
        'type' => 'text_textfield',
168
      ])
169 170
      ->save();

171
    $display_repository->getViewDisplay('node', 'page')
172
      ->setComponent('field_test_multi', [
173
        'type' => 'string',
174
      ])
175
      ->save();
176 177 178
  }

  /**
179
   * Checks the node preview functionality.
180
   */
181
  public function testPagePreview() {
182
    $title_key = 'title[0][value]';
183
    $body_key = 'body[0][value]';
184
    $term_key = $this->fieldName . '[target_id]';
185 186

    // Fill in node creation form and preview node.
187
    $edit = [];
188
    $edit[$title_key] = '<em>' . $this->randomMachineName(8) . '</em>';
189
    $edit[$body_key] = $this->randomMachineName(16);
190
    $edit[$term_key] = $this->term->getName();
191 192 193

    // Upload an image.
    $test_image = current($this->drupalGetTestFiles('image', 39325));
194
    $edit['files[field_image_0][]'] = \Drupal::service('file_system')->realpath($test_image->uri);
195 196 197 198
    $this->drupalPostForm('node/add/page', $edit, t('Upload'));

    // Add an alt tag and preview the node.
    $this->drupalPostForm(NULL, ['field_image[0][alt]' => 'Picture of llamas'], t('Preview'));
199

200
    // Check that the preview is displaying the title, body and term.
201 202
    $expected_title = $edit[$title_key] . ' | Drupal';
    $this->assertSession()->titleEquals($expected_title);
203
    $this->assertEscaped($edit[$title_key], 'Title displayed and escaped.');
204
    $this->assertText($edit[$body_key], 'Body displayed.');
205
    $this->assertText($edit[$term_key], 'Term displayed.');
206 207
    $this->assertLink(t('Back to content editing'));

208 209 210 211
    // Check that we see the class of the node type on the body element.
    $body_class_element = $this->xpath("//body[contains(@class, 'page-node-type-page')]");
    $this->assertTrue(!empty($body_class_element), 'Node type body class found.');

212 213 214 215 216 217 218
    // Get the UUID.
    $url = parse_url($this->getUrl());
    $paths = explode('/', $url['path']);
    $view_mode = array_pop($paths);
    $uuid = array_pop($paths);

    // Switch view mode. We'll remove the body from the teaser view mode.
219 220
    \Drupal::service('entity_display.repository')
      ->getViewDisplay('node', 'page', 'teaser')
221 222 223
      ->removeComponent('body')
      ->save();

224
    $view_mode_edit = ['view_mode' => 'teaser'];
225
    $this->drupalPostForm('node/preview/' . $uuid . '/full', $view_mode_edit, t('Switch'));
226 227
    $this->assertRaw('view-mode-teaser', 'View mode teaser class found.');
    $this->assertNoText($edit[$body_key], 'Body not displayed.');
228

229
    // Check that the title, body and term fields are displayed with the
230 231
    // values after going back to the content edit page.
    $this->clickLink(t('Back to content editing'));
232 233
    $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
    $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
234
    $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
235
    $this->assertFieldByName('field_image[0][alt]', 'Picture of llamas');
236
    $this->getSession()->getPage()->pressButton('Add another item');
237 238
    $this->assertFieldByName('field_test_multi[0][value]');
    $this->assertFieldByName('field_test_multi[1][value]');
239

240
    // Return to page preview to check everything is as expected.
241
    $this->drupalPostForm(NULL, [], t('Preview'));
242
    $this->assertSession()->titleEquals($expected_title);
243
    $this->assertEscaped($edit[$title_key], 'Title displayed and escaped.');
244 245 246 247
    $this->assertText($edit[$body_key], 'Body displayed.');
    $this->assertText($edit[$term_key], 'Term displayed.');
    $this->assertLink(t('Back to content editing'));

248
    // Assert the content is kept when reloading the page.
249
    $this->drupalGet('node/add/page', ['query' => ['uuid' => $uuid]]);
250 251
    $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
    $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
252
    $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
253

254 255 256
    // Save the node - this is a new POST, so we need to upload the image.
    $this->drupalPostForm('node/add/page', $edit, t('Upload'));
    $this->drupalPostForm(NULL, ['field_image[0][alt]' => 'Picture of llamas'], t('Save'));
257 258 259
    $node = $this->drupalGetNodeByTitle($edit[$title_key]);

    // Check the term was displayed on the saved node.
260
    $this->drupalGet('node/' . $node->id());
261 262 263
    $this->assertText($edit[$term_key], 'Term displayed.');

    // Check the term appears again on the edit form.
264
    $this->drupalGet('node/' . $node->id() . '/edit');
265
    $this->assertFieldByName($term_key, $edit[$term_key] . ' (' . $this->term->id() . ')', 'Term field displayed.');
266 267 268

    // Check with two new terms on the edit form, additionally to the existing
    // one.
269
    $edit = [];
270 271
    $newterm1 = $this->randomMachineName(8);
    $newterm2 = $this->randomMachineName(8);
272
    $edit[$term_key] = $this->term->getName() . ', ' . $newterm1 . ', ' . $newterm2;
273
    $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
274 275 276
    $this->assertRaw('>' . $newterm1 . '<', 'First new term displayed.');
    $this->assertRaw('>' . $newterm2 . '<', 'Second new term displayed.');
    // The first term should be displayed as link, the others not.
277
    $this->assertLink($this->term->getName());
278 279 280
    $this->assertNoLink($newterm1);
    $this->assertNoLink($newterm2);

281
    $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Save'));
282 283 284

    // Check with one more new term, keeping old terms, removing the existing
    // one.
285
    $edit = [];
286
    $newterm3 = $this->randomMachineName(8);
287
    $edit[$term_key] = $newterm1 . ', ' . $newterm3 . ', ' . $newterm2;
288
    $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
289 290 291
    $this->assertRaw('>' . $newterm1 . '<', 'First existing term displayed.');
    $this->assertRaw('>' . $newterm2 . '<', 'Second existing term displayed.');
    $this->assertRaw('>' . $newterm3 . '<', 'Third new term displayed.');
292
    $this->assertNoText($this->term->getName());
293 294
    $this->assertLink($newterm1);
    $this->assertLink($newterm2);
295
    $this->assertNoLink($newterm3);
296

297 298
    // Check that editing an existing node after it has been previewed and not
    // saved doesn't remember the previous changes.
299
    $edit = [
300
      $title_key => $this->randomMachineName(8),
301
    ];
302 303 304 305 306 307 308 309 310 311
    $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
    $this->assertText($edit[$title_key], 'New title displayed.');
    $this->clickLink(t('Back to content editing'));
    $this->assertFieldByName($title_key, $edit[$title_key], 'New title value displayed.');
    // Navigate away from the node without saving.
    $this->drupalGet('<front>');
    // Go back to the edit form, the title should have its initial value.
    $this->drupalGet('node/' . $node->id() . '/edit');
    $this->assertFieldByName($title_key, $node->label(), 'Correct title value displayed.');

312 313 314 315 316 317
    // Check with required preview.
    $node_type = NodeType::load('page');
    $node_type->setPreviewMode(DRUPAL_REQUIRED);
    $node_type->save();
    $this->drupalGet('node/add/page');
    $this->assertNoRaw('edit-submit');
318
    $this->drupalPostForm('node/add/page', [$title_key => 'Preview'], t('Preview'));
319 320
    $this->clickLink(t('Back to content editing'));
    $this->assertRaw('edit-submit');
321

322 323 324 325 326
    // Check that destination is remembered when clicking on preview. When going
    // back to the edit form and clicking save, we should go back to the
    // original destination, if set.
    $destination = 'node';
    $this->drupalPostForm($node->toUrl('edit-form'), [], t('Preview'), ['query' => ['destination' => $destination]]);
327
    $parameters = ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'];
328 329 330 331 332 333 334 335 336
    $options = ['absolute' => TRUE, 'query' => ['destination' => $destination]];
    $this->assertUrl(Url::fromRoute('entity.node.preview', $parameters, $options));
    $this->drupalPostForm(NULL, ['view_mode' => 'teaser'], t('Switch'));
    $this->clickLink(t('Back to content editing'));
    $this->drupalPostForm(NULL, [], t('Save'));
    $this->assertUrl($destination);

    // Check that preview page works as expected without a destination set.
    $this->drupalPostForm($node->toUrl('edit-form'), [], t('Preview'));
337
    $parameters = ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'];
338 339 340 341 342
    $this->assertUrl(Url::fromRoute('entity.node.preview', $parameters, ['absolute' => TRUE]));
    $this->drupalPostForm(NULL, ['view_mode' => 'teaser'], t('Switch'));
    $this->clickLink(t('Back to content editing'));
    $this->drupalPostForm(NULL, [], t('Save'));
    $this->assertUrl($node->toUrl());
343
    $this->assertSession()->statusCodeEquals(200);
344

345 346
    /** @var \Drupal\Core\File\FileSystemInterface $file_system */
    $file_system = \Drupal::service('file_system');
347 348
    // Assert multiple items can be added and are not lost when previewing.
    $test_image_1 = current($this->drupalGetTestFiles('image', 39325));
349
    $edit_image_1['files[field_image_0][]'] = $file_system->realpath($test_image_1->uri);
350
    $test_image_2 = current($this->drupalGetTestFiles('image', 39325));
351
    $edit_image_2['files[field_image_1][]'] = $file_system->realpath($test_image_2->uri);
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378
    $edit['field_image[0][alt]'] = 'Alt 1';

    $this->drupalPostForm('node/add/page', $edit_image_1, t('Upload'));
    $this->drupalPostForm(NULL, $edit, t('Preview'));
    $this->clickLink(t('Back to content editing'));
    $this->assertFieldByName('files[field_image_1][]');
    $this->drupalPostForm(NULL, $edit_image_2, t('Upload'));
    $this->assertNoFieldByName('files[field_image_1][]');

    $title = 'node_test_title';
    $example_text_1 = 'example_text_preview_1';
    $example_text_2 = 'example_text_preview_2';
    $example_text_3 = 'example_text_preview_3';
    $this->drupalGet('node/add/page');
    $edit = [
      'title[0][value]' => $title,
      'field_test_multi[0][value]' => $example_text_1,
    ];
    $this->assertRaw('Storage is not set');
    $this->drupalPostForm(NULL, $edit, t('Preview'));
    $this->clickLink(t('Back to content editing'));
    $this->assertRaw('Storage is set');
    $this->assertFieldByName('field_test_multi[0][value]');
    $this->drupalPostForm(NULL, [], t('Save'));
    $this->assertText('Basic page ' . $title . ' has been created.');
    $node = $this->drupalGetNodeByTitle($title);
    $this->drupalGet('node/' . $node->id() . '/edit');
379 380
    $this->getSession()->getPage()->pressButton('Add another item');
    $this->getSession()->getPage()->pressButton('Add another item');
381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
    $edit = [
      'field_test_multi[1][value]' => $example_text_2,
      'field_test_multi[2][value]' => $example_text_3,
    ];
    $this->drupalPostForm(NULL, $edit, t('Preview'));
    $this->clickLink(t('Back to content editing'));
    $this->drupalPostForm(NULL, $edit, t('Preview'));
    $this->clickLink(t('Back to content editing'));
    $this->assertFieldByName('field_test_multi[0][value]', $example_text_1);
    $this->assertFieldByName('field_test_multi[1][value]', $example_text_2);
    $this->assertFieldByName('field_test_multi[2][value]', $example_text_3);

    // Now save the node and make sure all values got saved.
    $this->drupalPostForm(NULL, [], t('Save'));
    $this->assertText($example_text_1);
    $this->assertText($example_text_2);
    $this->assertText($example_text_3);

    // Edit again, change the menu_ui settings and click on preview.
    $this->drupalGet('node/' . $node->id() . '/edit');
    $edit = [
      'menu[enabled]' => TRUE,
      'menu[title]' => 'Changed title',
    ];
    $this->drupalPostForm(NULL, $edit, t('Preview'));
    $this->clickLink(t('Back to content editing'));
    $this->assertFieldChecked('edit-menu-enabled', 'Menu option is still checked');
    $this->assertFieldByName('menu[title]', 'Changed title', 'Menu link title is correct after preview');

    // Save, change the title while saving and make sure that it is correctly
    // saved.
    $edit = [
      'menu[enabled]' => TRUE,
      'menu[title]' => 'Second title change',
    ];
    $this->drupalPostForm(NULL, $edit, t('Save'));
    $this->drupalGet('node/' . $node->id() . '/edit');
    $this->assertFieldByName('menu[title]', 'Second title change', 'Menu link title is correct after saving');

420 421 422
  }

  /**
423
   * Checks the node preview functionality, when using revisions.
424
   */
425
  public function testPagePreviewWithRevisions() {
426
    $title_key = 'title[0][value]';
427
    $body_key = 'body[0][value]';
428
    $term_key = $this->fieldName . '[target_id]';
429
    // Force revision on "Basic page" content.
430
    $node_type = NodeType::load('page');
431
    $node_type->setNewRevision(TRUE);
432
    $node_type->save();
433 434

    // Fill in node creation form and preview node.
435
    $edit = [];
436 437
    $edit[$title_key] = $this->randomMachineName(8);
    $edit[$body_key] = $this->randomMachineName(16);
438
    $edit[$term_key] = $this->term->id();
439
    $edit['revision_log[0][value]'] = $this->randomString(32);
440
    $this->drupalPostForm('node/add/page', $edit, t('Preview'));
441

442
    // Check that the preview is displaying the title, body and term.
443
    $this->assertTitle($edit[$title_key] . ' | Drupal');
444 445
    $this->assertText($edit[$title_key], 'Title displayed.');
    $this->assertText($edit[$body_key], 'Body displayed.');
446
    $this->assertText($edit[$term_key], 'Term displayed.');
447

448 449 450
    // Check that the title and body fields are displayed with the correct
    // values after going back to the content edit page.
    $this->clickLink(t('Back to content editing'));    $this->assertFieldByName($title_key, $edit[$title_key], 'Title field displayed.');
451
    $this->assertFieldByName($body_key, $edit[$body_key], 'Body field displayed.');
452
    $this->assertFieldByName($term_key, $edit[$term_key], 'Term field displayed.');
453

454
    // Check that the revision log field has the correct value.
455
    $this->assertFieldByName('revision_log[0][value]', $edit['revision_log[0][value]'], 'Revision log field displayed.');
456 457

    // Save the node after coming back from the preview page so we can create a
458
    // pending revision for it.
459 460 461
    $this->drupalPostForm(NULL, [], t('Save'));
    $node = $this->drupalGetNodeByTitle($edit[$title_key]);

462
    // Check that previewing a pending revision of a node works. This can not be
463 464
    // accomplished through the UI so we have to use API calls.
    // @todo Change this test to use the UI when we will be able to create
465
    // pending revisions in core.
466 467 468 469 470 471 472
    // @see https://www.drupal.org/node/2725533
    $node->setNewRevision(TRUE);
    $node->isDefaultRevision(FALSE);

    /** @var \Drupal\Core\Controller\ControllerResolverInterface $controller_resolver */
    $controller_resolver = \Drupal::service('controller_resolver');
    $node_preview_controller = $controller_resolver->getControllerFromDefinition('\Drupal\node\Controller\NodePreviewController::view');
473
    $node_preview_controller($node, 'full');
474
  }
475

476 477 478 479 480
  /**
   * Checks the node preview accessible for simultaneous node editing.
   */
  public function testSimultaneousPreview() {
    $title_key = 'title[0][value]';
481
    $node = $this->drupalCreateNode([]);
482

483
    $edit = [$title_key => 'New page title'];
484 485 486
    $this->drupalPostForm('node/' . $node->id() . '/edit', $edit, t('Preview'));
    $this->assertText($edit[$title_key]);

487
    $user2 = $this->drupalCreateUser(['edit any page content']);
488 489 490 491
    $this->drupalLogin($user2);
    $this->drupalGet('node/' . $node->id() . '/edit');
    $this->assertFieldByName($title_key, $node->label(), 'No title leaked from previous user.');

492
    $edit2 = [$title_key => 'Another page title'];
493
    $this->drupalPostForm('node/' . $node->id() . '/edit', $edit2, t('Preview'));
494
    $this->assertUrl(Url::fromRoute('entity.node.preview', ['node_preview' => $node->uuid(), 'view_mode_id' => 'full'], ['absolute' => TRUE])->toString());
495 496 497
    $this->assertText($edit2[$title_key]);
  }

498
}