Pull merge.
[yaffs-website] / web / core / modules / layout_builder / tests / src / FunctionalJavascript / InlineBlockTest.php
1 <?php
2
3 namespace Drupal\Tests\layout_builder\FunctionalJavascript;
4
5 use Drupal\node\Entity\Node;
6
7 /**
8  * Tests that the inline block feature works correctly.
9  *
10  * @group layout_builder
11  */
12 class InlineBlockTest extends InlineBlockTestBase {
13
14   /**
15    * Tests adding and editing of inline blocks.
16    */
17   public function testInlineBlocks() {
18     $assert_session = $this->assertSession();
19     $page = $this->getSession()->getPage();
20
21     $this->drupalLogin($this->drupalCreateUser([
22       'access contextual links',
23       'configure any layout',
24       'administer node display',
25       'administer node fields',
26     ]));
27
28     // Enable layout builder.
29     $this->drupalPostForm(
30       static::FIELD_UI_PREFIX . '/display/default',
31       ['layout[enabled]' => TRUE],
32       'Save'
33     );
34     $this->clickLink('Manage layout');
35     $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default');
36     // Add a basic block with the body field set.
37     $this->addInlineBlockToLayout('Block title', 'The DEFAULT block body');
38     $this->assertSaveLayout();
39
40     $this->drupalGet('node/1');
41     $assert_session->pageTextContains('The DEFAULT block body');
42     $this->drupalGet('node/2');
43     $assert_session->pageTextContains('The DEFAULT block body');
44
45     // Enable overrides.
46     $this->drupalPostForm(static::FIELD_UI_PREFIX . '/display/default', ['layout[allow_custom]' => TRUE], 'Save');
47     $this->drupalGet('node/1/layout');
48
49     // Confirm the block can be edited.
50     $this->drupalGet('node/1/layout');
51     $this->configureInlineBlock('The DEFAULT block body', 'The NEW block body!');
52     $this->assertSaveLayout();
53     $this->drupalGet('node/1');
54     $assert_session->pageTextContains('The NEW block body');
55     $assert_session->pageTextNotContains('The DEFAULT block body');
56     $this->drupalGet('node/2');
57     // Node 2 should use default layout.
58     $assert_session->pageTextContains('The DEFAULT block body');
59     $assert_session->pageTextNotContains('The NEW block body');
60
61     // Add a basic block with the body field set.
62     $this->drupalGet('node/1/layout');
63     $this->addInlineBlockToLayout('2nd Block title', 'The 2nd block body');
64     $this->assertSaveLayout();
65     $this->drupalGet('node/1');
66     $assert_session->pageTextContains('The NEW block body!');
67     $assert_session->pageTextContains('The 2nd block body');
68     $this->drupalGet('node/2');
69     // Node 2 should use default layout.
70     $assert_session->pageTextContains('The DEFAULT block body');
71     $assert_session->pageTextNotContains('The NEW block body');
72     $assert_session->pageTextNotContains('The 2nd block body');
73
74     // Confirm the block can be edited.
75     $this->drupalGet('node/1/layout');
76     /* @var \Behat\Mink\Element\NodeElement $inline_block_2 */
77     $inline_block_2 = $page->findAll('css', static::INLINE_BLOCK_LOCATOR)[1];
78     $uuid = $inline_block_2->getAttribute('data-layout-block-uuid');
79     $block_css_locator = static::INLINE_BLOCK_LOCATOR . "[data-layout-block-uuid=\"$uuid\"]";
80     $this->configureInlineBlock('The 2nd block body', 'The 2nd NEW block body!', $block_css_locator);
81     $this->assertSaveLayout();
82     $this->drupalGet('node/1');
83     $assert_session->pageTextContains('The NEW block body!');
84     $assert_session->pageTextContains('The 2nd NEW block body!');
85     $this->drupalGet('node/2');
86     // Node 2 should use default layout.
87     $assert_session->pageTextContains('The DEFAULT block body');
88     $assert_session->pageTextNotContains('The NEW block body!');
89     $assert_session->pageTextNotContains('The 2nd NEW block body!');
90
91     // The default layout entity block should be changed.
92     $this->drupalGet(static::FIELD_UI_PREFIX . '/display-layout/default');
93     $assert_session->pageTextContains('The DEFAULT block body');
94     // Confirm default layout still only has 1 entity block.
95     $assert_session->elementsCount('css', static::INLINE_BLOCK_LOCATOR, 1);
96   }
97
98   /**
99    * Tests adding a new entity block and then not saving the layout.
100    *
101    * @dataProvider layoutNoSaveProvider
102    */
103   public function testNoLayoutSave($operation, $no_save_link_text, $confirm_button_text) {
104
105     $this->drupalLogin($this->drupalCreateUser([
106       'access contextual links',
107       'configure any layout',
108       'administer node display',
109     ]));
110     $assert_session = $this->assertSession();
111     $page = $this->getSession()->getPage();
112     $this->assertEmpty($this->blockStorage->loadMultiple(), 'No entity blocks exist');
113     // Enable layout builder and overrides.
114     $this->drupalPostForm(
115       static::FIELD_UI_PREFIX . '/display/default',
116       ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
117       'Save'
118     );
119
120     $this->drupalGet('node/1/layout');
121     $this->addInlineBlockToLayout('Block title', 'The block body');
122     $this->clickLink($no_save_link_text);
123     if ($confirm_button_text) {
124       $page->pressButton($confirm_button_text);
125     }
126     $this->drupalGet('node/1');
127     $this->assertEmpty($this->blockStorage->loadMultiple(), 'No entity blocks were created when layout is canceled.');
128     $assert_session->pageTextNotContains('The block body');
129
130     $this->drupalGet('node/1/layout');
131
132     $this->addInlineBlockToLayout('Block title', 'The block body');
133     $this->assertSaveLayout();
134     $this->drupalGet('node/1');
135     $assert_session->pageTextContains('The block body');
136     $blocks = $this->blockStorage->loadMultiple();
137     $this->assertEquals(count($blocks), 1);
138     /* @var \Drupal\Core\Entity\ContentEntityBase $block */
139     $block = array_pop($blocks);
140     $revision_id = $block->getRevisionId();
141
142     // Confirm the block can be edited.
143     $this->drupalGet('node/1/layout');
144     $this->configureInlineBlock('The block body', 'The block updated body');
145
146     $this->clickLink($no_save_link_text);
147     if ($confirm_button_text) {
148       $page->pressButton($confirm_button_text);
149     }
150     $this->drupalGet('node/1');
151
152     $blocks = $this->blockStorage->loadMultiple();
153     // When reverting or canceling the update block should not be on the page.
154     $assert_session->pageTextNotContains('The block updated body');
155     if ($operation === 'cancel') {
156       // When canceling the original block body should appear.
157       $assert_session->pageTextContains('The block body');
158
159       $this->assertEquals(count($blocks), 1);
160       $block = array_pop($blocks);
161       $this->assertEquals($block->getRevisionId(), $revision_id);
162       $this->assertEquals($block->get('body')->getValue()[0]['value'], 'The block body');
163     }
164     else {
165       // The block should not be visible.
166       // Blocks are currently only deleted when the parent entity is deleted.
167       $assert_session->pageTextNotContains('The block body');
168     }
169   }
170
171   /**
172    * Provides test data for ::testNoLayoutSave().
173    */
174   public function layoutNoSaveProvider() {
175     return [
176       'cancel' => [
177         'cancel',
178         'Cancel Layout',
179         NULL,
180       ],
181       'revert' => [
182         'revert',
183         'Revert to defaults',
184         'Revert',
185       ],
186     ];
187   }
188
189   /**
190    * Tests entity blocks revisioning.
191    */
192   public function testInlineBlocksRevisioning() {
193     $assert_session = $this->assertSession();
194     $page = $this->getSession()->getPage();
195
196     $this->drupalLogin($this->drupalCreateUser([
197       'access contextual links',
198       'configure any layout',
199       'administer node display',
200       'administer node fields',
201       'administer nodes',
202       'bypass node access',
203     ]));
204     // Enable layout builder and overrides.
205     $this->drupalPostForm(
206       static::FIELD_UI_PREFIX . '/display/default',
207       ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
208       'Save'
209     );
210     $this->drupalGet('node/1/layout');
211
212     // Add an inline block.
213     $this->addInlineBlockToLayout('Block title', 'The DEFAULT block body');
214     $this->assertSaveLayout();
215     $this->drupalGet('node/1');
216
217     $assert_session->pageTextContains('The DEFAULT block body');
218
219     /** @var \Drupal\node\NodeStorageInterface $node_storage */
220     $node_storage = $this->container->get('entity_type.manager')->getStorage('node');
221     $original_revision_id = $node_storage->getLatestRevisionId(1);
222
223     // Create a new revision.
224     $this->drupalGet('node/1/edit');
225     $page->findField('title[0][value]')->setValue('Node updated');
226     $page->pressButton('Save');
227
228     $this->drupalGet('node/1');
229     $assert_session->pageTextContains('The DEFAULT block body');
230
231     $assert_session->linkExists('Revisions');
232
233     // Update the block.
234     $this->drupalGet('node/1/layout');
235     $this->configureInlineBlock('The DEFAULT block body', 'The NEW block body');
236     $this->assertSaveLayout();
237     $this->drupalGet('node/1');
238     $assert_session->pageTextContains('The NEW block body');
239     $assert_session->pageTextNotContains('The DEFAULT block body');
240
241     $revision_url = "node/1/revisions/$original_revision_id";
242
243     // Ensure viewing the previous revision shows the previous block revision.
244     $this->drupalGet("$revision_url/view");
245     $assert_session->pageTextContains('The DEFAULT block body');
246     $assert_session->pageTextNotContains('The NEW block body');
247
248     // Revert to first revision.
249     $revision_url = "$revision_url/revert";
250     $this->drupalGet($revision_url);
251     $page->pressButton('Revert');
252
253     $this->drupalGet('node/1');
254     $assert_session->pageTextContains('The DEFAULT block body');
255     $assert_session->pageTextNotContains('The NEW block body');
256   }
257
258   /**
259    * Tests that entity blocks deleted correctly.
260    */
261   public function testDeletion() {
262     /** @var \Drupal\Core\Cron $cron */
263     $cron = \Drupal::service('cron');
264     /** @var \Drupal\layout_builder\InlineBlockUsage $usage */
265     $usage = \Drupal::service('inline_block.usage');
266     $this->drupalLogin($this->drupalCreateUser([
267       'administer content types',
268       'access contextual links',
269       'configure any layout',
270       'administer node display',
271       'administer node fields',
272       'administer nodes',
273       'bypass node access',
274     ]));
275     $assert_session = $this->assertSession();
276     $page = $this->getSession()->getPage();
277
278     // Enable layout builder.
279     $this->drupalPostForm(
280       static::FIELD_UI_PREFIX . '/display/default',
281       ['layout[enabled]' => TRUE],
282       'Save'
283     );
284     // Add a block to default layout.
285     $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default');
286     $this->clickLink('Manage layout');
287     $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default');
288     $this->addInlineBlockToLayout('Block title', 'The DEFAULT block body');
289     $this->assertSaveLayout();
290
291     $this->assertCount(1, $this->blockStorage->loadMultiple());
292     $default_block_id = $this->getLatestBlockEntityId();
293
294     // Ensure the block shows up on node pages.
295     $this->drupalGet('node/1');
296     $assert_session->pageTextContains('The DEFAULT block body');
297     $this->drupalGet('node/2');
298     $assert_session->pageTextContains('The DEFAULT block body');
299
300     // Enable overrides.
301     $this->drupalPostForm(static::FIELD_UI_PREFIX . '/display/default', ['layout[allow_custom]' => TRUE], 'Save');
302
303     // Ensure we have 2 copies of the block in node overrides.
304     $this->drupalGet('node/1/layout');
305     $this->assertSaveLayout();
306     $node_1_block_id = $this->getLatestBlockEntityId();
307
308     $this->drupalGet('node/2/layout');
309     $this->assertSaveLayout();
310     $node_2_block_id = $this->getLatestBlockEntityId();
311     $this->assertCount(3, $this->blockStorage->loadMultiple());
312
313     $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default');
314     $this->clickLink('Manage layout');
315     $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default');
316
317     $this->assertNotEmpty($this->blockStorage->load($default_block_id));
318     $this->assertNotEmpty($usage->getUsage($default_block_id));
319     // Remove block from default.
320     $this->removeInlineBlockFromLayout();
321     $this->assertSaveLayout();
322     // Ensure the block in the default was deleted.
323     $this->blockStorage->resetCache([$default_block_id]);
324     $this->assertEmpty($this->blockStorage->load($default_block_id));
325     // Ensure other blocks still exist.
326     $this->assertCount(2, $this->blockStorage->loadMultiple());
327     $this->assertEmpty($usage->getUsage($default_block_id));
328
329     $this->drupalGet('node/1/layout');
330     $assert_session->pageTextContains('The DEFAULT block body');
331
332     $this->removeInlineBlockFromLayout();
333     $this->assertSaveLayout();
334     $cron->run();
335     // Ensure entity block is not deleted because it is needed in revision.
336     $this->assertNotEmpty($this->blockStorage->load($node_1_block_id));
337     $this->assertCount(2, $this->blockStorage->loadMultiple());
338
339     $this->assertNotEmpty($usage->getUsage($node_1_block_id));
340     // Ensure entity block is deleted when node is deleted.
341     $this->drupalGet('node/1/delete');
342     $page->pressButton('Delete');
343     $this->assertEmpty(Node::load(1));
344     $cron->run();
345     $this->assertEmpty($this->blockStorage->load($node_1_block_id));
346     $this->assertEmpty($usage->getUsage($node_1_block_id));
347     $this->assertCount(1, $this->blockStorage->loadMultiple());
348
349     // Add another block to the default.
350     $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default');
351     $this->clickLink('Manage layout');
352     $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default');
353     $this->addInlineBlockToLayout('Title 2', 'Body 2');
354     $this->assertSaveLayout();
355     $cron->run();
356     $default_block2_id = $this->getLatestBlockEntityId();
357     $this->assertCount(2, $this->blockStorage->loadMultiple());
358
359     // Delete the other node so bundle can be deleted.
360     $this->assertNotEmpty($usage->getUsage($node_2_block_id));
361     $this->drupalGet('node/2/delete');
362     $page->pressButton('Delete');
363     $this->assertEmpty(Node::load(2));
364     $cron->run();
365     // Ensure entity block was deleted.
366     $this->assertEmpty($this->blockStorage->load($node_2_block_id));
367     $this->assertEmpty($usage->getUsage($node_2_block_id));
368     $this->assertCount(1, $this->blockStorage->loadMultiple());
369
370     // Delete the bundle which has the default layout.
371     $this->assertNotEmpty($usage->getUsage($default_block2_id));
372     $this->drupalGet(static::FIELD_UI_PREFIX . '/delete');
373     $page->pressButton('Delete');
374     $cron->run();
375
376     // Ensure the entity block in default is deleted when bundle is deleted.
377     $this->assertEmpty($this->blockStorage->load($default_block2_id));
378     $this->assertEmpty($usage->getUsage($default_block2_id));
379     $this->assertCount(0, $this->blockStorage->loadMultiple());
380   }
381
382   /**
383    * Tests access to the block edit form of inline blocks.
384    *
385    * This module does not provide links to these forms but in case the paths are
386    * accessed directly they should accessible by users with the
387    * 'configure any layout' permission.
388    *
389    * @see layout_builder_block_content_access()
390    */
391   public function testAccess() {
392     $this->drupalLogin($this->drupalCreateUser([
393       'access contextual links',
394       'configure any layout',
395       'administer node display',
396       'administer node fields',
397     ]));
398     $assert_session = $this->assertSession();
399
400     // Enable layout builder and overrides.
401     $this->drupalPostForm(
402       static::FIELD_UI_PREFIX . '/display/default',
403       ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
404       'Save'
405     );
406
407     // Ensure we have 2 copies of the block in node overrides.
408     $this->drupalGet('node/1/layout');
409     $this->addInlineBlockToLayout('Block title', 'Block body');
410     $this->assertSaveLayout();
411     $node_1_block_id = $this->getLatestBlockEntityId();
412
413     $this->drupalGet("block/$node_1_block_id");
414     $assert_session->pageTextNotContains('You are not authorized to access this page');
415
416     $this->drupalLogout();
417     $this->drupalLogin($this->drupalCreateUser([
418       'administer nodes',
419     ]));
420
421     $this->drupalGet("block/$node_1_block_id");
422     $assert_session->pageTextContains('You are not authorized to access this page');
423
424     $this->drupalLogin($this->drupalCreateUser([
425       'configure any layout',
426     ]));
427     $this->drupalGet("block/$node_1_block_id");
428     $assert_session->pageTextNotContains('You are not authorized to access this page');
429   }
430
431   /**
432    * Tests the workflow for adding an inline block depending on number of types.
433    *
434    * @throws \Behat\Mink\Exception\ElementNotFoundException
435    * @throws \Behat\Mink\Exception\ExpectationException
436    */
437   public function testAddWorkFlow() {
438     $assert_session = $this->assertSession();
439     $page = $this->getSession()->getPage();
440     $type_storage = $this->container->get('entity_type.manager')->getStorage('block_content_type');
441     foreach ($type_storage->loadByProperties() as $type) {
442       $type->delete();
443     }
444
445     $this->drupalLogin($this->drupalCreateUser([
446       'access contextual links',
447       'configure any layout',
448       'administer node display',
449       'administer node fields',
450     ]));
451
452     // Enable layout builder and overrides.
453     $this->drupalPostForm(
454       static::FIELD_UI_PREFIX . '/display/default',
455       ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE],
456       'Save'
457     );
458
459     $layout_default_path = 'admin/structure/types/manage/bundle_with_section_field/display-layout/default';
460     $this->drupalGet($layout_default_path);
461     // Add a basic block with the body field set.
462     $page->clickLink('Add Block');
463     $assert_session->assertWaitOnAjaxRequest();
464     // Confirm that with no block content types the link does not appear.
465     $assert_session->linkNotExists('Create custom block');
466
467     $this->createBlockContentType('basic', 'Basic block');
468
469     $this->drupalGet($layout_default_path);
470     // Add a basic block with the body field set.
471     $page->clickLink('Add Block');
472     $assert_session->assertWaitOnAjaxRequest();
473     // Confirm with only 1 type the "Create custom block" link goes directly t
474     // block add form.
475     $assert_session->linkNotExists('Basic block');
476     $this->clickLink('Create custom block');
477     $assert_session->assertWaitOnAjaxRequest();
478     $assert_session->fieldExists('Title');
479
480     $this->createBlockContentType('advanced', 'Advanced block');
481
482     $this->drupalGet($layout_default_path);
483     // Add a basic block with the body field set.
484     $page->clickLink('Add Block');
485     // Confirm that, when more than 1 type exists, "Create custom block" shows a
486     // list of block types.
487     $assert_session->assertWaitOnAjaxRequest();
488     $assert_session->linkNotExists('Basic block');
489     $assert_session->linkNotExists('Advanced block');
490     $this->clickLink('Create custom block');
491     $assert_session->assertWaitOnAjaxRequest();
492     $assert_session->fieldNotExists('Title');
493     $assert_session->linkExists('Basic block');
494     $assert_session->linkExists('Advanced block');
495
496     $this->clickLink('Advanced block');
497     $assert_session->assertWaitOnAjaxRequest();
498     $assert_session->fieldExists('Title');
499   }
500
501 }