assertSession(); $page = $this->getSession()->getPage(); $this->drupalLogin($this->drupalCreateUser([ 'access contextual links', 'configure any layout', 'administer node display', 'administer node fields', ])); // Enable layout builder. $this->drupalPostForm( static::FIELD_UI_PREFIX . '/display/default', ['layout[enabled]' => TRUE], 'Save' ); $this->clickLink('Manage layout'); $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default'); // Add a basic block with the body field set. $this->addInlineBlockToLayout('Block title', 'The DEFAULT block body'); $this->assertSaveLayout(); $this->drupalGet('node/1'); $assert_session->pageTextContains('The DEFAULT block body'); $this->drupalGet('node/2'); $assert_session->pageTextContains('The DEFAULT block body'); // Enable overrides. $this->drupalPostForm(static::FIELD_UI_PREFIX . '/display/default', ['layout[allow_custom]' => TRUE], 'Save'); $this->drupalGet('node/1/layout'); // Confirm the block can be edited. $this->drupalGet('node/1/layout'); $this->configureInlineBlock('The DEFAULT block body', 'The NEW block body!'); $this->assertSaveLayout(); $this->drupalGet('node/1'); $assert_session->pageTextContains('The NEW block body'); $assert_session->pageTextNotContains('The DEFAULT block body'); $this->drupalGet('node/2'); // Node 2 should use default layout. $assert_session->pageTextContains('The DEFAULT block body'); $assert_session->pageTextNotContains('The NEW block body'); // Add a basic block with the body field set. $this->drupalGet('node/1/layout'); $this->addInlineBlockToLayout('2nd Block title', 'The 2nd block body'); $this->assertSaveLayout(); $this->drupalGet('node/1'); $assert_session->pageTextContains('The NEW block body!'); $assert_session->pageTextContains('The 2nd block body'); $this->drupalGet('node/2'); // Node 2 should use default layout. $assert_session->pageTextContains('The DEFAULT block body'); $assert_session->pageTextNotContains('The NEW block body'); $assert_session->pageTextNotContains('The 2nd block body'); // Confirm the block can be edited. $this->drupalGet('node/1/layout'); /* @var \Behat\Mink\Element\NodeElement $inline_block_2 */ $inline_block_2 = $page->findAll('css', static::INLINE_BLOCK_LOCATOR)[1]; $uuid = $inline_block_2->getAttribute('data-layout-block-uuid'); $block_css_locator = static::INLINE_BLOCK_LOCATOR . "[data-layout-block-uuid=\"$uuid\"]"; $this->configureInlineBlock('The 2nd block body', 'The 2nd NEW block body!', $block_css_locator); $this->assertSaveLayout(); $this->drupalGet('node/1'); $assert_session->pageTextContains('The NEW block body!'); $assert_session->pageTextContains('The 2nd NEW block body!'); $this->drupalGet('node/2'); // Node 2 should use default layout. $assert_session->pageTextContains('The DEFAULT block body'); $assert_session->pageTextNotContains('The NEW block body!'); $assert_session->pageTextNotContains('The 2nd NEW block body!'); // The default layout entity block should be changed. $this->drupalGet(static::FIELD_UI_PREFIX . '/display-layout/default'); $assert_session->pageTextContains('The DEFAULT block body'); // Confirm default layout still only has 1 entity block. $assert_session->elementsCount('css', static::INLINE_BLOCK_LOCATOR, 1); } /** * Tests adding a new entity block and then not saving the layout. * * @dataProvider layoutNoSaveProvider */ public function testNoLayoutSave($operation, $no_save_link_text, $confirm_button_text) { $this->drupalLogin($this->drupalCreateUser([ 'access contextual links', 'configure any layout', 'administer node display', ])); $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $this->assertEmpty($this->blockStorage->loadMultiple(), 'No entity blocks exist'); // Enable layout builder and overrides. $this->drupalPostForm( static::FIELD_UI_PREFIX . '/display/default', ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE], 'Save' ); $this->drupalGet('node/1/layout'); $this->addInlineBlockToLayout('Block title', 'The block body'); $this->clickLink($no_save_link_text); if ($confirm_button_text) { $page->pressButton($confirm_button_text); } $this->drupalGet('node/1'); $this->assertEmpty($this->blockStorage->loadMultiple(), 'No entity blocks were created when layout is canceled.'); $assert_session->pageTextNotContains('The block body'); $this->drupalGet('node/1/layout'); $this->addInlineBlockToLayout('Block title', 'The block body'); $this->assertSaveLayout(); $this->drupalGet('node/1'); $assert_session->pageTextContains('The block body'); $blocks = $this->blockStorage->loadMultiple(); $this->assertEquals(count($blocks), 1); /* @var \Drupal\Core\Entity\ContentEntityBase $block */ $block = array_pop($blocks); $revision_id = $block->getRevisionId(); // Confirm the block can be edited. $this->drupalGet('node/1/layout'); $this->configureInlineBlock('The block body', 'The block updated body'); $this->clickLink($no_save_link_text); if ($confirm_button_text) { $page->pressButton($confirm_button_text); } $this->drupalGet('node/1'); $blocks = $this->blockStorage->loadMultiple(); // When reverting or canceling the update block should not be on the page. $assert_session->pageTextNotContains('The block updated body'); if ($operation === 'cancel') { // When canceling the original block body should appear. $assert_session->pageTextContains('The block body'); $this->assertEquals(count($blocks), 1); $block = array_pop($blocks); $this->assertEquals($block->getRevisionId(), $revision_id); $this->assertEquals($block->get('body')->getValue()[0]['value'], 'The block body'); } else { // The block should not be visible. // Blocks are currently only deleted when the parent entity is deleted. $assert_session->pageTextNotContains('The block body'); } } /** * Provides test data for ::testNoLayoutSave(). */ public function layoutNoSaveProvider() { return [ 'cancel' => [ 'cancel', 'Cancel Layout', NULL, ], 'revert' => [ 'revert', 'Revert to defaults', 'Revert', ], ]; } /** * Tests entity blocks revisioning. */ public function testInlineBlocksRevisioning() { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $this->drupalLogin($this->drupalCreateUser([ 'access contextual links', 'configure any layout', 'administer node display', 'administer node fields', 'administer nodes', 'bypass node access', ])); // Enable layout builder and overrides. $this->drupalPostForm( static::FIELD_UI_PREFIX . '/display/default', ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE], 'Save' ); $this->drupalGet('node/1/layout'); // Add an inline block. $this->addInlineBlockToLayout('Block title', 'The DEFAULT block body'); $this->assertSaveLayout(); $this->drupalGet('node/1'); $assert_session->pageTextContains('The DEFAULT block body'); /** @var \Drupal\node\NodeStorageInterface $node_storage */ $node_storage = $this->container->get('entity_type.manager')->getStorage('node'); $original_revision_id = $node_storage->getLatestRevisionId(1); // Create a new revision. $this->drupalGet('node/1/edit'); $page->findField('title[0][value]')->setValue('Node updated'); $page->pressButton('Save'); $this->drupalGet('node/1'); $assert_session->pageTextContains('The DEFAULT block body'); $assert_session->linkExists('Revisions'); // Update the block. $this->drupalGet('node/1/layout'); $this->configureInlineBlock('The DEFAULT block body', 'The NEW block body'); $this->assertSaveLayout(); $this->drupalGet('node/1'); $assert_session->pageTextContains('The NEW block body'); $assert_session->pageTextNotContains('The DEFAULT block body'); $revision_url = "node/1/revisions/$original_revision_id"; // Ensure viewing the previous revision shows the previous block revision. $this->drupalGet("$revision_url/view"); $assert_session->pageTextContains('The DEFAULT block body'); $assert_session->pageTextNotContains('The NEW block body'); // Revert to first revision. $revision_url = "$revision_url/revert"; $this->drupalGet($revision_url); $page->pressButton('Revert'); $this->drupalGet('node/1'); $assert_session->pageTextContains('The DEFAULT block body'); $assert_session->pageTextNotContains('The NEW block body'); } /** * Tests that entity blocks deleted correctly. */ public function testDeletion() { /** @var \Drupal\Core\Cron $cron */ $cron = \Drupal::service('cron'); /** @var \Drupal\layout_builder\InlineBlockUsage $usage */ $usage = \Drupal::service('inline_block.usage'); $this->drupalLogin($this->drupalCreateUser([ 'administer content types', 'access contextual links', 'configure any layout', 'administer node display', 'administer node fields', 'administer nodes', 'bypass node access', ])); $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); // Enable layout builder. $this->drupalPostForm( static::FIELD_UI_PREFIX . '/display/default', ['layout[enabled]' => TRUE], 'Save' ); // Add a block to default layout. $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default'); $this->clickLink('Manage layout'); $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default'); $this->addInlineBlockToLayout('Block title', 'The DEFAULT block body'); $this->assertSaveLayout(); $this->assertCount(1, $this->blockStorage->loadMultiple()); $default_block_id = $this->getLatestBlockEntityId(); // Ensure the block shows up on node pages. $this->drupalGet('node/1'); $assert_session->pageTextContains('The DEFAULT block body'); $this->drupalGet('node/2'); $assert_session->pageTextContains('The DEFAULT block body'); // Enable overrides. $this->drupalPostForm(static::FIELD_UI_PREFIX . '/display/default', ['layout[allow_custom]' => TRUE], 'Save'); // Ensure we have 2 copies of the block in node overrides. $this->drupalGet('node/1/layout'); $this->assertSaveLayout(); $node_1_block_id = $this->getLatestBlockEntityId(); $this->drupalGet('node/2/layout'); $this->assertSaveLayout(); $node_2_block_id = $this->getLatestBlockEntityId(); $this->assertCount(3, $this->blockStorage->loadMultiple()); $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default'); $this->clickLink('Manage layout'); $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default'); $this->assertNotEmpty($this->blockStorage->load($default_block_id)); $this->assertNotEmpty($usage->getUsage($default_block_id)); // Remove block from default. $this->removeInlineBlockFromLayout(); $this->assertSaveLayout(); // Ensure the block in the default was deleted. $this->blockStorage->resetCache([$default_block_id]); $this->assertEmpty($this->blockStorage->load($default_block_id)); // Ensure other blocks still exist. $this->assertCount(2, $this->blockStorage->loadMultiple()); $this->assertEmpty($usage->getUsage($default_block_id)); $this->drupalGet('node/1/layout'); $assert_session->pageTextContains('The DEFAULT block body'); $this->removeInlineBlockFromLayout(); $this->assertSaveLayout(); $cron->run(); // Ensure entity block is not deleted because it is needed in revision. $this->assertNotEmpty($this->blockStorage->load($node_1_block_id)); $this->assertCount(2, $this->blockStorage->loadMultiple()); $this->assertNotEmpty($usage->getUsage($node_1_block_id)); // Ensure entity block is deleted when node is deleted. $this->drupalGet('node/1/delete'); $page->pressButton('Delete'); $this->assertEmpty(Node::load(1)); $cron->run(); $this->assertEmpty($this->blockStorage->load($node_1_block_id)); $this->assertEmpty($usage->getUsage($node_1_block_id)); $this->assertCount(1, $this->blockStorage->loadMultiple()); // Add another block to the default. $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default'); $this->clickLink('Manage layout'); $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display-layout/default'); $this->addInlineBlockToLayout('Title 2', 'Body 2'); $this->assertSaveLayout(); $cron->run(); $default_block2_id = $this->getLatestBlockEntityId(); $this->assertCount(2, $this->blockStorage->loadMultiple()); // Delete the other node so bundle can be deleted. $this->assertNotEmpty($usage->getUsage($node_2_block_id)); $this->drupalGet('node/2/delete'); $page->pressButton('Delete'); $this->assertEmpty(Node::load(2)); $cron->run(); // Ensure entity block was deleted. $this->assertEmpty($this->blockStorage->load($node_2_block_id)); $this->assertEmpty($usage->getUsage($node_2_block_id)); $this->assertCount(1, $this->blockStorage->loadMultiple()); // Delete the bundle which has the default layout. $this->assertNotEmpty($usage->getUsage($default_block2_id)); $this->drupalGet(static::FIELD_UI_PREFIX . '/delete'); $page->pressButton('Delete'); $cron->run(); // Ensure the entity block in default is deleted when bundle is deleted. $this->assertEmpty($this->blockStorage->load($default_block2_id)); $this->assertEmpty($usage->getUsage($default_block2_id)); $this->assertCount(0, $this->blockStorage->loadMultiple()); } /** * Tests access to the block edit form of inline blocks. * * This module does not provide links to these forms but in case the paths are * accessed directly they should accessible by users with the * 'configure any layout' permission. * * @see layout_builder_block_content_access() */ public function testAccess() { $this->drupalLogin($this->drupalCreateUser([ 'access contextual links', 'configure any layout', 'administer node display', 'administer node fields', ])); $assert_session = $this->assertSession(); // Enable layout builder and overrides. $this->drupalPostForm( static::FIELD_UI_PREFIX . '/display/default', ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE], 'Save' ); // Ensure we have 2 copies of the block in node overrides. $this->drupalGet('node/1/layout'); $this->addInlineBlockToLayout('Block title', 'Block body'); $this->assertSaveLayout(); $node_1_block_id = $this->getLatestBlockEntityId(); $this->drupalGet("block/$node_1_block_id"); $assert_session->pageTextNotContains('You are not authorized to access this page'); $this->drupalLogout(); $this->drupalLogin($this->drupalCreateUser([ 'administer nodes', ])); $this->drupalGet("block/$node_1_block_id"); $assert_session->pageTextContains('You are not authorized to access this page'); $this->drupalLogin($this->drupalCreateUser([ 'configure any layout', ])); $this->drupalGet("block/$node_1_block_id"); $assert_session->pageTextNotContains('You are not authorized to access this page'); } /** * Tests the workflow for adding an inline block depending on number of types. * * @throws \Behat\Mink\Exception\ElementNotFoundException * @throws \Behat\Mink\Exception\ExpectationException */ public function testAddWorkFlow() { $assert_session = $this->assertSession(); $page = $this->getSession()->getPage(); $type_storage = $this->container->get('entity_type.manager')->getStorage('block_content_type'); foreach ($type_storage->loadByProperties() as $type) { $type->delete(); } $this->drupalLogin($this->drupalCreateUser([ 'access contextual links', 'configure any layout', 'administer node display', 'administer node fields', ])); // Enable layout builder and overrides. $this->drupalPostForm( static::FIELD_UI_PREFIX . '/display/default', ['layout[enabled]' => TRUE, 'layout[allow_custom]' => TRUE], 'Save' ); $layout_default_path = 'admin/structure/types/manage/bundle_with_section_field/display-layout/default'; $this->drupalGet($layout_default_path); // Add a basic block with the body field set. $page->clickLink('Add Block'); $assert_session->assertWaitOnAjaxRequest(); // Confirm that with no block content types the link does not appear. $assert_session->linkNotExists('Create custom block'); $this->createBlockContentType('basic', 'Basic block'); $this->drupalGet($layout_default_path); // Add a basic block with the body field set. $page->clickLink('Add Block'); $assert_session->assertWaitOnAjaxRequest(); // Confirm with only 1 type the "Create custom block" link goes directly t // block add form. $assert_session->linkNotExists('Basic block'); $this->clickLink('Create custom block'); $assert_session->assertWaitOnAjaxRequest(); $assert_session->fieldExists('Title'); $this->createBlockContentType('advanced', 'Advanced block'); $this->drupalGet($layout_default_path); // Add a basic block with the body field set. $page->clickLink('Add Block'); // Confirm that, when more than 1 type exists, "Create custom block" shows a // list of block types. $assert_session->assertWaitOnAjaxRequest(); $assert_session->linkNotExists('Basic block'); $assert_session->linkNotExists('Advanced block'); $this->clickLink('Create custom block'); $assert_session->assertWaitOnAjaxRequest(); $assert_session->fieldNotExists('Title'); $assert_session->linkExists('Basic block'); $assert_session->linkExists('Advanced block'); $this->clickLink('Advanced block'); $assert_session->assertWaitOnAjaxRequest(); $assert_session->fieldExists('Title'); } }