Updated to Drupal 8.5. Core Media not yet in use.
[yaffs-website] / web / core / modules / layout_builder / tests / src / Functional / LayoutSectionTest.php
diff --git a/web/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php b/web/core/modules/layout_builder/tests/src/Functional/LayoutSectionTest.php
new file mode 100644 (file)
index 0000000..d5e0c75
--- /dev/null
@@ -0,0 +1,379 @@
+<?php
+
+namespace Drupal\Tests\layout_builder\Functional;
+
+use Drupal\language\Entity\ConfigurableLanguage;
+use Drupal\layout_builder\Entity\LayoutBuilderEntityViewDisplay;
+use Drupal\layout_builder\Section;
+use Drupal\layout_builder\SectionComponent;
+use Drupal\Tests\BrowserTestBase;
+
+/**
+ * Tests the rendering of a layout section field.
+ *
+ * @group layout_builder
+ */
+class LayoutSectionTest extends BrowserTestBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public static $modules = ['field_ui', 'layout_builder', 'node', 'block_test'];
+
+  /**
+   * The name of the layout section field.
+   *
+   * @var string
+   */
+  protected $fieldName = 'layout_builder__layout';
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->createContentType([
+      'type' => 'bundle_without_section_field',
+    ]);
+    $this->createContentType([
+      'type' => 'bundle_with_section_field',
+    ]);
+
+    LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default')
+      ->setOverridable()
+      ->save();
+
+    $this->drupalLogin($this->drupalCreateUser([
+      'configure any layout',
+      'administer node display',
+      'administer node fields',
+      'administer content types',
+    ], 'foobar'));
+  }
+
+  /**
+   * Provides test data for ::testLayoutSectionFormatter().
+   */
+  public function providerTestLayoutSectionFormatter() {
+    $data = [];
+    $data['block_with_global_context'] = [
+      [
+        [
+          'section' => new Section('layout_onecol', [], [
+            'baz' => new SectionComponent('baz', 'content', [
+              'id' => 'test_context_aware',
+              'context_mapping' => [
+                'user' => '@user.current_user_context:current_user',
+              ],
+            ]),
+          ]),
+        ],
+      ],
+      [
+        '.layout--onecol',
+        '#test_context_aware--username',
+      ],
+      [
+        'foobar',
+      ],
+      'user',
+      'user:2',
+      'UNCACHEABLE',
+    ];
+    $data['block_with_entity_context'] = [
+      [
+        [
+          'section' => new Section('layout_onecol', [], [
+            'baz' => new SectionComponent('baz', 'content', [
+              'id' => 'field_block:node:bundle_with_section_field:body',
+              'context_mapping' => [
+                'entity' => 'layout_builder.entity',
+              ],
+            ]),
+          ]),
+        ],
+      ],
+      [
+        '.layout--onecol',
+        '.field--name-body',
+      ],
+      [
+        'Body',
+        'The node body',
+      ],
+      '',
+      '',
+      'MISS',
+    ];
+    $data['single_section_single_block'] = [
+      [
+        [
+          'section' => new Section('layout_onecol', [], [
+            'baz' => new SectionComponent('baz', 'content', [
+              'id' => 'system_powered_by_block',
+            ]),
+          ]),
+        ],
+      ],
+      '.layout--onecol',
+      'Powered by',
+      '',
+      '',
+      'MISS',
+    ];
+    $data['multiple_sections'] = [
+      [
+        [
+          'section' => new Section('layout_onecol', [], [
+            'baz' => new SectionComponent('baz', 'content', [
+              'id' => 'system_powered_by_block',
+            ]),
+          ]),
+        ],
+        [
+          'section' => new Section('layout_twocol', [], [
+            'foo' => new SectionComponent('foo', 'first', [
+              'id' => 'test_block_instantiation',
+              'display_message' => 'foo text',
+            ]),
+            'bar' => new SectionComponent('bar', 'second', [
+              'id' => 'test_block_instantiation',
+              'display_message' => 'bar text',
+            ]),
+          ]),
+        ],
+      ],
+      [
+        '.layout--onecol',
+        '.layout--twocol',
+      ],
+      [
+        'Powered by',
+        'foo text',
+        'bar text',
+      ],
+      'user.permissions',
+      '',
+      'MISS',
+    ];
+    return $data;
+  }
+
+  /**
+   * Tests layout_section formatter output.
+   *
+   * @dataProvider providerTestLayoutSectionFormatter
+   */
+  public function testLayoutSectionFormatter($layout_data, $expected_selector, $expected_content, $expected_cache_contexts, $expected_cache_tags, $expected_dynamic_cache) {
+    $node = $this->createSectionNode($layout_data);
+
+    $canonical_url = $node->toUrl('canonical');
+    $this->drupalGet($canonical_url);
+    $this->assertLayoutSection($expected_selector, $expected_content, $expected_cache_contexts, $expected_cache_tags, $expected_dynamic_cache);
+
+    $this->drupalGet($canonical_url->toString() . '/layout');
+    $this->assertLayoutSection($expected_selector, $expected_content, $expected_cache_contexts, $expected_cache_tags, 'UNCACHEABLE');
+  }
+
+  /**
+   * Tests the access checking of the section formatter.
+   */
+  public function testLayoutSectionFormatterAccess() {
+    $node = $this->createSectionNode([
+      [
+        'section' => new Section('layout_onecol', [], [
+          'baz' => new SectionComponent('baz', 'content', [
+            'id' => 'test_access',
+          ]),
+        ]),
+      ],
+    ]);
+
+    // Restrict access to the block.
+    $this->container->get('state')->set('test_block_access', FALSE);
+
+    $this->drupalGet($node->toUrl('canonical'));
+    $this->assertLayoutSection('.layout--onecol', NULL, '', '', 'UNCACHEABLE');
+    // Ensure the block was not rendered.
+    $this->assertSession()->pageTextNotContains('Hello test world');
+
+    // Grant access to the block, and ensure it was rendered.
+    $this->container->get('state')->set('test_block_access', TRUE);
+    $this->drupalGet($node->toUrl('canonical'));
+    $this->assertLayoutSection('.layout--onecol', 'Hello test world', '', '', 'UNCACHEABLE');
+  }
+
+  /**
+   * Tests the multilingual support of the section formatter.
+   */
+  public function testMultilingualLayoutSectionFormatter() {
+    $this->container->get('module_installer')->install(['content_translation']);
+    $this->rebuildContainer();
+
+    ConfigurableLanguage::createFromLangcode('es')->save();
+    $this->container->get('content_translation.manager')->setEnabled('node', 'bundle_with_section_field', TRUE);
+
+    $entity = $this->createSectionNode([
+      [
+        'section' => new Section('layout_onecol', [], [
+          'baz' => new SectionComponent('baz', 'content', [
+            'id' => 'system_powered_by_block',
+          ]),
+        ]),
+      ],
+    ]);
+    $entity->addTranslation('es', [
+      'title' => 'Translated node title',
+      $this->fieldName => [
+        [
+          'section' => new Section('layout_twocol', [], [
+            'foo' => new SectionComponent('foo', 'first', [
+              'id' => 'test_block_instantiation',
+              'display_message' => 'foo text',
+            ]),
+            'bar' => new SectionComponent('bar', 'second', [
+              'id' => 'test_block_instantiation',
+              'display_message' => 'bar text',
+            ]),
+          ]),
+        ],
+      ],
+    ]);
+    $entity->save();
+
+    $this->drupalGet($entity->toUrl('canonical'));
+    $this->assertLayoutSection('.layout--onecol', 'Powered by');
+    $this->drupalGet($entity->toUrl('canonical')->setOption('prefix', 'es/'));
+    $this->assertLayoutSection('.layout--twocol', ['foo text', 'bar text']);
+  }
+
+  /**
+   * Ensures that the entity title is displayed.
+   */
+  public function testLayoutPageTitle() {
+    $this->drupalPlaceBlock('page_title_block');
+    $node = $this->createSectionNode([]);
+
+    $this->drupalGet($node->toUrl('canonical')->toString() . '/layout');
+    $this->assertSession()->titleEquals('Edit layout for The node title | Drupal');
+    $this->assertEquals('Edit layout for The node title', $this->cssSelect('h1.page-title')[0]->getText());
+  }
+
+  /**
+   * Tests that no Layout link shows without a section field.
+   */
+  public function testLayoutUrlNoSectionField() {
+    $node = $this->createNode([
+      'type' => 'bundle_without_section_field',
+      'title' => 'The node title',
+      'body' => [
+        [
+          'value' => 'The node body',
+        ],
+      ],
+    ]);
+    $node->save();
+
+    $this->drupalGet($node->toUrl('canonical')->toString() . '/layout');
+    $this->assertSession()->statusCodeEquals(404);
+  }
+
+  /**
+   * Tests that deleting a field removes it from the layout.
+   */
+  public function testLayoutDeletingField() {
+    $assert_session = $this->assertSession();
+
+    $this->drupalGet('/admin/structure/types/manage/bundle_with_section_field/display-layout/default');
+    $assert_session->statusCodeEquals(200);
+    $assert_session->elementExists('css', '.field--name-body');
+
+    // Delete the field from both bundles.
+    $this->drupalGet('/admin/structure/types/manage/bundle_without_section_field/fields/node.bundle_without_section_field.body/delete');
+    $this->submitForm([], 'Delete');
+    $this->drupalGet('/admin/structure/types/manage/bundle_with_section_field/display-layout/default');
+    $assert_session->statusCodeEquals(200);
+    $assert_session->elementExists('css', '.field--name-body');
+
+    $this->drupalGet('/admin/structure/types/manage/bundle_with_section_field/fields/node.bundle_with_section_field.body/delete');
+    $this->submitForm([], 'Delete');
+    $this->drupalGet('/admin/structure/types/manage/bundle_with_section_field/display-layout/default');
+    $assert_session->statusCodeEquals(200);
+    $assert_session->elementNotExists('css', '.field--name-body');
+  }
+
+  /**
+   * Tests that deleting a bundle removes the layout.
+   */
+  public function testLayoutDeletingBundle() {
+    $assert_session = $this->assertSession();
+
+    $display = LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default');
+    $this->assertInstanceOf(LayoutBuilderEntityViewDisplay::class, $display);
+
+    $this->drupalPostForm('/admin/structure/types/manage/bundle_with_section_field/delete', [], 'Delete');
+    $assert_session->statusCodeEquals(200);
+
+    $display = LayoutBuilderEntityViewDisplay::load('node.bundle_with_section_field.default');
+    $this->assertNull($display);
+  }
+
+  /**
+   * Asserts the output of a layout section.
+   *
+   * @param string|array $expected_selector
+   *   A selector or list of CSS selectors to find.
+   * @param string|array $expected_content
+   *   A string or list of strings to find.
+   * @param string $expected_cache_contexts
+   *   A string of cache contexts to be found in the header.
+   * @param string $expected_cache_tags
+   *   A string of cache tags to be found in the header.
+   * @param string $expected_dynamic_cache
+   *   The expected dynamic cache header. Either 'HIT', 'MISS' or 'UNCACHEABLE'.
+   */
+  protected function assertLayoutSection($expected_selector, $expected_content, $expected_cache_contexts = '', $expected_cache_tags = '', $expected_dynamic_cache = 'MISS') {
+    $assert_session = $this->assertSession();
+    // Find the given selector.
+    foreach ((array) $expected_selector as $selector) {
+      $element = $this->cssSelect($selector);
+      $this->assertNotEmpty($element);
+    }
+
+    // Find the given content.
+    foreach ((array) $expected_content as $content) {
+      $assert_session->pageTextContains($content);
+    }
+    if ($expected_cache_contexts) {
+      $assert_session->responseHeaderContains('X-Drupal-Cache-Contexts', $expected_cache_contexts);
+    }
+    if ($expected_cache_tags) {
+      $assert_session->responseHeaderContains('X-Drupal-Cache-Tags', $expected_cache_tags);
+    }
+    $assert_session->responseHeaderEquals('X-Drupal-Dynamic-Cache', $expected_dynamic_cache);
+  }
+
+  /**
+   * Creates a node with a section field.
+   *
+   * @param array $section_values
+   *   An array of values for a section field.
+   *
+   * @return \Drupal\node\NodeInterface
+   *   The node object.
+   */
+  protected function createSectionNode(array $section_values) {
+    return $this->createNode([
+      'type' => 'bundle_with_section_field',
+      'title' => 'The node title',
+      'body' => [
+        [
+          'value' => 'The node body',
+        ],
+      ],
+      $this->fieldName => $section_values,
+    ]);
+  }
+
+}