3 namespace Drupal\diff\Tests;
5 use Drupal\language\Entity\ConfigurableLanguage;
6 use Drupal\system\Tests\Menu\AssertBreadcrumbTrait;
7 use Drupal\Tests\diff\Functional\CoreVersionUiTestTrait;
10 * Tests the diff revisions overview.
14 class DiffRevisionTest extends DiffTestBase {
16 use AssertBreadcrumbTrait;
17 use CoreVersionUiTestTrait;
24 public static $modules = [
26 'content_translation',
31 * Run all test fragments.
33 public function testAll() {
34 $this->doTestRevisionDiffOverview();
35 $this->doTestOverviewPager();
36 $this->doTestRevisionOverviewErrorMessages();
37 $this->doTestEntityReference();
41 * Tests the revision diff overview.
43 protected function doTestRevisionDiffOverview() {
44 $this->drupalPlaceBlock('system_breadcrumb_block');
45 // Login as admin with the required permission.
46 $this->loginAsAdmin(['delete any article content']);
49 $title = 'test_title_a';
51 'title[0][value]' => $title,
52 'body[0][value]' => '<p>Revision 1</p>
53 <p>first_unique_text</p>
54 <p>second_unique_text</p>',
56 // Set to published if content moderation is enabled.
57 if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
58 $edit['moderation_state[0][state]'] = 'published';
60 $this->drupalPostNodeForm('node/add/article', $edit, t('Save and publish'));
61 $node = $this->drupalGetNodeByTitle($title);
62 $this->drupalGet('node/' . $node->id());
64 // Make sure the revision tab doesn't exist.
65 $this->assertNoLink('Revisions');
67 // Create a second revision, with a revision comment.
68 $this->drupalGet('node/add/article');
70 'body[0][value]' => '<p>Revision 2</p>
71 <p>first_unique_text</p>
72 <p>second_unique_text</p>',
74 'revision_log[0][value]' => 'Revision 2 comment',
76 // Set to published if content moderation is enabled.
77 if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
78 $edit['moderation_state[0][state]'] = 'published';
80 $this->drupalPostNodeForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
81 $this->drupalGet('node/' . $node->id());
83 // Check the revisions overview.
84 $this->clickLink(t('Revisions'));
85 $rows = $this->xpath('//tbody/tr');
86 // Make sure only two revisions available.
87 $this->assertEqual(count($rows), 2);
88 // Assert the revision summary.
89 $this->assertUniqueText('Revision 2 comment');
91 // Compare the revisions in standard mode.
92 $this->drupalPostForm(NULL, NULL, t('Compare selected revisions'));
93 $this->clickLink('Split fields');
94 // Assert breadcrumbs are properly displayed.
95 $this->assertRaw('<nav class="breadcrumb"');
98 "node/" . $node->id() => $node->label(),
99 "node/" . $node->id() . "/revisions" => 'Revisions',
101 $this->assertBreadcrumb(NULL, $trail);
102 // Extract the changes.
103 $this->assertText('Body');
104 $rows = $this->xpath('//tbody/tr');
105 $head = $this->xpath('//thead/tr');
106 $diff_row = $rows[1]->td;
107 // Assert the revision comment.
108 $this->assertRaw('diff-revision__item-message">Revision 2 comment');
109 // Assert changes made to the body, text 1 changed to 2.
110 $this->assertEqual((string) ($diff_row[0]), '1');
111 $this->assertEqual((string) ($diff_row[1]), '-');
112 $this->assertEqual((string) (($diff_row[2]->span)), '1');
113 $this->assertEqual(htmlspecialchars_decode(strip_tags($diff_row[2]->asXML())), '<p>Revision 1</p>');
114 $this->assertEqual((string) ($diff_row[3]), '1');
115 $this->assertEqual((string) ($diff_row[4]), '+');
116 $this->assertEqual((string) (($diff_row[5]->span)), '2');
117 $this->assertEqual(htmlspecialchars_decode((strip_tags($diff_row[5]->asXML()))), '<p>Revision 2</p>');
119 // Compare the revisions in markdown mode.
120 $this->clickLink('Strip tags');
121 $rows = $this->xpath('//tbody/tr');
122 // Assert breadcrumbs are properly displayed.
123 $this->assertRaw('<nav class="breadcrumb"');
126 "node/" . $node->id() => $node->label(),
127 "node/" . $node->id() . "/revisions" => 'Revisions',
129 $this->assertBreadcrumb(NULL, $trail);
130 // Extract the changes.
131 $diff_row = $rows[1]->td;
132 // Assert changes made to the body, text 1 changed to 2.
133 $this->assertEqual((string) ($diff_row[0]), '-');
134 $this->assertEqual((string) (($diff_row[1]->span)), '1');
135 $this->assertEqual(htmlspecialchars_decode(strip_tags($diff_row[1]->asXML())), 'Revision 1');
136 $this->assertEqual((string) (($diff_row[2])), '+');
137 $this->assertEqual((string) (($diff_row[3]->span)), '2');
138 $this->assertEqual(htmlspecialchars_decode((strip_tags($diff_row[3]->asXML()))), 'Revision 2');
140 // Compare the revisions in single column mode.
141 $this->clickLink('Unified fields');
142 // Assert breadcrumbs are properly displayed.
143 $this->assertRaw('<nav class="breadcrumb"');
146 "node/" . $node->id() => $node->label(),
147 "node/" . $node->id() . "/revisions" => 'Revisions',
149 $this->assertBreadcrumb(NULL, $trail);
150 // Extract the changes.
151 $rows = $this->xpath('//tbody/tr');
152 $diff_row = $rows[1]->td;
153 // Assert changes made to the body, text 1 changed to 2.
154 $this->assertEqual((string) ($diff_row[0]), '1');
155 $this->assertEqual((string) ($diff_row[1]), '');
156 $this->assertEqual((string) ($diff_row[2]), '-');
157 $this->assertEqual((string) (($diff_row[3]->span)), '1');
158 $this->assertEqual(htmlspecialchars_decode(strip_tags($diff_row[3]->asXML())), '<p>Revision 1</p>');
159 $diff_row = $rows[2]->td;
160 $this->assertEqual((string) ($diff_row[0]), '');
161 $this->assertEqual((string) ($diff_row[1]), '1');
162 $this->assertEqual((string) (($diff_row[2])), '+');
163 $this->assertEqual((string) (($diff_row[3]->span)), '2');
164 $this->assertEqual(htmlspecialchars_decode((strip_tags($diff_row[3]->asXML()))), '<p>Revision 2</p>');
165 $this->assertUniqueText('first_unique_text');
166 $this->assertUniqueText('second_unique_text');
167 $diff_row = $rows[3]->td;
168 $this->assertEqual((string) ($diff_row[0]), '2');
169 $this->assertEqual((string) ($diff_row[1]), '2');
170 $diff_row = $rows[4]->td;
171 $this->assertEqual((string) ($diff_row[0]), '3');
172 $this->assertEqual((string) ($diff_row[1]), '3');
174 $this->clickLink('Strip tags');
175 // Extract the changes.
176 $rows = $this->xpath('//tbody/tr');
177 $diff_row = $rows[1]->td;
179 // Assert changes made to the body, with strip_tags filter and make sure
180 // there are no line numbers.
181 $this->assertEqual((string) ($diff_row[0]), '-');
182 $this->assertEqual((string) (($diff_row[1]->span)), '1');
183 $this->assertEqual(htmlspecialchars_decode(strip_tags($diff_row[1]->asXML())), 'Revision 1');
184 $diff_row = $rows[2]->td;
185 $this->assertEqual((string) (($diff_row[0])), '+');
186 $this->assertEqual((string) (($diff_row[1]->span)), '2');
187 $this->assertEqual(htmlspecialchars_decode((strip_tags($diff_row[1]->asXML()))), 'Revision 2');
189 $this->drupalGet('node/' . $node->id());
190 $this->clickLink(t('Revisions'));
191 // Revert the revision, confirm.
192 $this->clickLink(t('Revert'));
193 $this->drupalPostForm(NULL, NULL, t('Revert'));
194 $this->assertText('Article ' . $title . ' has been reverted to the revision from');
196 // Make sure three revisions are available.
197 $rows = $this->xpath('//tbody/tr');
198 $this->assertEqual(count($rows), 3);
199 // Make sure the reverted comment is there.
200 $this->assertText('Copy of the revision from');
202 // Delete the first revision (last entry in table).
203 $this->clickLink(t('Delete'), 0);
204 $this->drupalPostForm(NULL, NULL, t('Delete'));
205 $this->assertText('of Article ' . $title . ' has been deleted.');
207 // Make sure two revisions are available.
208 $rows = $this->xpath('//tbody/tr');
209 $this->assertEqual(count($rows), 2);
211 // Delete one revision so that we are left with only 1 revision.
212 $this->clickLink(t('Delete'), 0);
213 $this->drupalPostForm(NULL, NULL, t('Delete'));
214 $this->assertText('of Article ' . $title . ' has been deleted.');
216 // Make sure we only have 1 revision now.
217 $rows = $this->xpath('//tbody/tr');
218 $this->assertEqual(count($rows), 0);
220 // Assert that there are no radio buttons for revision selection.
221 $this->assertNoFieldByXPath('//input[@type="radio"]');
222 // Assert that there is no submit button.
223 $this->assertNoFieldByXPath('//input[@type="submit" and text()="Compare selected revisions"]');
225 // Create two new revisions of node.
227 'title[0][value]' => 'new test title',
228 'body[0][value]' => '<p>new body</p>',
229 'revision_log[0][value]' => 'this revision message will appear twice',
231 // Set to published if content moderation is enabled.
232 if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
233 $edit['moderation_state[0][state]'] = 'published';
235 $this->drupalPostNodeForm('node/' . $node->id() . '/edit', $edit, 'Save and keep published');
238 'title[0][value]' => 'newer test title',
239 'body[0][value]' => '<p>newer body</p>',
240 'revision_log[0][value]' => 'this revision message will appear twice',
242 // Set to published if content moderation is enabled.
243 if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
244 $edit['moderation_state[0][state]'] = 'published';
246 $this->drupalPostNodeForm('node/' . $node->id() . '/edit', $edit, 'Save and keep published');
248 $this->clickLink(t('Revisions'));
249 // Assert the revision summary.
250 $this->assertNoUniqueText('this revision message will appear twice');
251 $this->assertText('Copy of the revision from');
256 $this->drupalPostForm(NULL, $edit, t('Compare selected revisions'));
257 $this->clickLink('Strip tags');
258 // Check markdown layout is used when navigating between revisions.
259 $rows = $this->xpath('//tbody/tr');
260 $diff_row = $rows[3]->td;
261 $this->assertEqual(htmlspecialchars_decode(strip_tags($diff_row[3]->asXML())), 'new body');
262 $this->clickLink('Next change');
263 // The filter should be the same as the previous screen.
264 $rows = $this->xpath('//tbody/tr');
265 $diff_row = $rows[3]->td;
266 $this->assertEqual(htmlspecialchars_decode(strip_tags($diff_row[3]->asXML())), 'newer body');
268 // Get the node, create a new revision that is not the current one.
269 $node = $this->getNodeByTitle('newer test title');
270 $node->setNewRevision(TRUE);
271 $node->isDefaultRevision(FALSE);
272 if ($node->hasField('moderation_state')) {
273 // If testing with content_moderation enabled, set as draft.
274 $node->moderation_state = 'draft';
277 $this->drupalGet('node/' . $node->id() . '/revisions');
279 // Check that the last revision is not the current one.
280 $this->assertLink(t('Set as current revision'));
281 $text = $this->xpath('//tbody/tr[2]/td[4]/em');
282 $this->assertEqual($text[0], 'Current revision');
284 // Set the last revision as current.
285 $this->clickLink('Set as current revision');
286 $this->drupalPostForm(NULL, [], t('Revert'));
288 if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
289 // With content moderation, the new revision will not be current.
290 // @see https://www.drupal.org/node/2899719
291 $text = $this->xpath('//tbody/tr[1]/td[4]/div/div/ul/li/a');
292 $this->assertEqual($text[0], 'Set as current revision');
295 // Check the last revision is set as current.
296 $text = $this->xpath('//tbody/tr[1]/td[4]/em');
297 $this->assertEqual($text[0], 'Current revision');
298 $this->assertNoLink(t('Set as current revision'));
303 * Tests pager on diff overview.
305 protected function doTestOverviewPager() {
306 $config = \Drupal::configFactory()->getEditable('diff.settings');
307 $config->set('general_settings.revision_pager_limit', 10)->save();
309 $this->loginAsAdmin(['view article revisions']);
311 $node = $this->drupalCreateNode([
315 // Create 11 more revisions in order to trigger paging on the revisions
317 for ($i = 0; $i < 11; $i++) {
320 'body[0][value]' => 'change: ' . $i,
322 $this->drupalPostNodeForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
325 // Check the number of elements on the first page.
326 $this->drupalGet('node/' . $node->id() . '/revisions');
327 $element = $this->xpath('//*[@id="edit-node-revisions-table"]/tbody/tr');
328 $this->assertEqual(count($element), 10);
329 // Check that the pager exists.
330 $this->assertRaw('page=1');
332 $this->clickLinkPartialName('Next page');
333 // Check the number of elements on the second page.
334 $element = $this->xpath('//*[@id="edit-node-revisions-table"]/tbody/tr');
335 $this->assertEqual(count($element), 2);
336 $this->assertRaw('page=0');
337 $this->clickLinkPartialName('Previous page');
341 * Tests the revisions overview error messages.
343 * @todo Move to DiffLocaleTest?
345 protected function doTestRevisionOverviewErrorMessages() {
346 // Enable some languages for this test.
347 $language = ConfigurableLanguage::createFromLangcode('de');
350 // Login as admin with the required permissions.
351 $this->loginAsAdmin([
352 'administer node form display',
353 'administer languages',
354 'administer content translation',
355 'create content translations',
356 'translate any entity',
359 // Make article content translatable.
361 'entity_types[node]' => TRUE,
362 'settings[node][article][translatable]' => TRUE,
363 'settings[node][article][settings][language][language_alterable]' => TRUE,
365 $this->drupalPostForm('admin/config/regional/content-language', $edit, t('Save configuration'));
367 // Create an article.
368 $title = 'test_title_b';
370 'title[0][value]' => $title,
371 'body[0][value]' => '<p>Revision 1</p>',
373 $this->drupalPostNodeForm('node/add/article', $edit, t('Save and publish'));
374 $node = $this->drupalGetNodeByTitle($title);
375 $revision1 = $node->getRevisionId();
377 // Create a revision, changing the node language to German.
379 'langcode[0][value]' => 'de',
380 'body[0][value]' => '<p>Revision 2</p>',
383 $this->drupalPostNodeForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
385 // Check the revisions overview, ensure only one revisions is available.
386 $this->clickLink(t('Revisions'));
387 $rows = $this->xpath('//tbody/tr');
388 $this->assertEqual(count($rows), 1);
390 // Compare the revisions and assert the first error message.
391 $this->drupalPostForm(NULL, NULL, t('Compare selected revisions'));
392 $this->assertText('Multiple revisions are needed for comparison.');
394 // Create another revision, changing the node language back to English.
396 'langcode[0][value]' => 'en',
397 'body[0][value]' => '<p>Revision 3</p>',
400 $this->drupalPostNodeForm('node/' . $node->id() . '/edit', $edit, t('Save and keep published'));
401 $node = $this->drupalGetNodeByTitle($title, TRUE);
402 $revision3 = $node->getRevisionId();
404 // Check the revisions overview, ensure two revisions are available.
405 $this->clickLink(t('Revisions'));
406 $rows = $this->xpath('//tbody/tr');
407 $this->assertEqual(count($rows), 2);
408 $this->assertNoFieldChecked('edit-node-revisions-table-0-select-column-one');
409 $this->assertFieldChecked('edit-node-revisions-table-0-select-column-two');
410 $this->assertNoFieldChecked('edit-node-revisions-table-1-select-column-one');
411 $this->assertNoFieldChecked('edit-node-revisions-table-1-select-column-two');
413 // Compare the revisions and assert the second error message.
414 $this->drupalPostForm(NULL, NULL, t('Compare selected revisions'));
415 $this->assertText('Select two revisions to compare.');
417 // Check the same revisions twice and compare.
419 'radios_left' => $revision3,
420 'radios_right' => $revision3,
422 $this->drupalPostForm('/node/' . $node->id() . '/revisions', $edit, 'Compare selected revisions');
423 // Assert the third error message.
424 $this->assertText('Select different revisions to compare.');
426 // Check different revisions and compare. This time should work correctly.
428 'radios_left' => $revision3,
429 'radios_right' => $revision1,
431 $this->drupalPostForm('/node/' . $node->id() . '/revisions', $edit, 'Compare selected revisions');
432 $this->assertLinkByHref('node/' . $node->id() . '/revisions/view/' . $revision1 . '/' . $revision3);
436 * Tests Reference to Deleted Entities.
438 protected function doTestEntityReference() {
439 // Login as admin with the required permissions.
440 $this->loginAsAdmin([
441 'administer node fields',
444 // Adding Entity Reference to Article Content Type.
445 $this->drupalPostForm('admin/structure/types/manage/article/fields/add-field', [
446 'new_storage_type' => 'field_ui:entity_reference:node',
447 'label' => 'Content reference test',
448 'field_name' => 'content',
449 ], t('Save and continue'));
451 // Create an first article.
452 $title = 'test_title_c';
454 'title[0][value]' => $title,
455 'body[0][value]' => '<p>First article</p>',
457 if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
458 $edit['moderation_state[0][state]'] = 'published';
460 $this->drupalPostNodeForm('node/add/article', $edit, t('Save and publish'));
461 $node_one = $this->drupalGetNodeByTitle($title);
463 // Create second article.
464 $title = 'test_title_d';
466 'title[0][value]' => $title,
467 'body[0][value]' => '<p>Second article</p>',
469 if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
470 $edit['moderation_state[0][state]'] = 'published';
472 $this->drupalPostNodeForm('node/add/article', $edit, t('Save and publish'));
473 $node_two = $this->drupalGetNodeByTitle($title);
475 // Create revision and add entity reference from second node to first.
477 'body[0][value]' => '<p>First Revision</p>',
478 'field_content[0][target_id]' => $node_two->getTitle(),
481 if (\Drupal::moduleHandler()->moduleExists('content_moderation')) {
482 $edit['moderation_state[0][state]'] = 'published';
484 $this->drupalPostNodeForm('node/' . $node_one->id() . '/edit', $edit, t('Save and keep published'));
486 // Delete referenced node.
489 // Access revision of first node.
490 $this->drupalGet('/node/' . $node_one->id());
491 $this->clickLink(t('Revisions'));
492 $this->drupalPostForm(NULL, NULL, t('Compare selected revisions'));
493 // Revision section should appear.
494 $this->assertResponse(200);