LayoutBuilderUiTest.php

Same filename and directory in other branches
  1. 9 core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderUiTest.php
  2. 8.9.x core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderUiTest.php
  3. 10 core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderUiTest.php

Namespace

Drupal\Tests\layout_builder\FunctionalJavascript

File

core/modules/layout_builder/tests/src/FunctionalJavascript/LayoutBuilderUiTest.php

View source
<?php

declare (strict_types=1);
namespace Drupal\Tests\layout_builder\FunctionalJavascript;

use Drupal\block_content\Entity\BlockContentType;
use Drupal\FunctionalJavascriptTests\WebDriverTestBase;
use Drupal\Tests\contextual\FunctionalJavascript\ContextualLinkClickTrait;

/**
 * Tests the Layout Builder UI.
 *
 * @group layout_builder
 */
class LayoutBuilderUiTest extends WebDriverTestBase {
    use ContextualLinkClickTrait;
    
    /**
     * Path prefix for the field UI for the test bundle.
     *
     * @var string
     */
    const FIELD_UI_PREFIX = 'admin/structure/types/manage/bundle_with_section_field';
    
    /**
     * {@inheritdoc}
     */
    protected static $modules = [
        'layout_builder',
        'block',
        'field_ui',
        'node',
        'block_content',
        'contextual',
        'views',
    ];
    
    /**
     * {@inheritdoc}
     */
    protected $defaultTheme = 'starterkit_theme';
    
    /**
     * {@inheritdoc}
     */
    protected function setUp() : void {
        parent::setUp();
        $this->createContentType([
            'type' => 'bundle_with_section_field',
        ]);
        $this->drupalLogin($this->drupalCreateUser([
            'configure any layout',
            'create and edit custom blocks',
            'administer node display',
            'administer node fields',
            'access contextual links',
        ]));
        // Enable layout builder.
        $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default');
        $this->submitForm([
            'layout[enabled]' => TRUE,
        ], 'Save');
    }
    
    /**
     * Tests that after removing sections reloading the page does not re-add them.
     */
    public function testReloadWithNoSections() : void {
        $assert_session = $this->assertSession();
        $page = $this->getSession()
            ->getPage();
        // Remove all of the sections from the page.
        $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default/layout');
        $page->clickLink('Remove Section 1');
        $assert_session->assertWaitOnAjaxRequest();
        $page->pressButton('Remove');
        $assert_session->assertWaitOnAjaxRequest();
        // Assert that there are no sections on the page.
        $assert_session->pageTextNotContains('Remove Section 1');
        $assert_session->pageTextNotContains('Add block');
        // Reload the page.
        $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default/layout');
        // Assert that there are no sections on the page.
        $assert_session->pageTextNotContains('Remove Section 1');
        $assert_session->pageTextNotContains('Add block');
    }
    
    /**
     * Tests the message indicating unsaved changes.
     */
    public function testUnsavedChangesMessage() : void {
        $assert_session = $this->assertSession();
        $page = $this->getSession()
            ->getPage();
        $this->assertModifiedLayout(static::FIELD_UI_PREFIX . '/display/default/layout');
        // Discard then cancel.
        $page->pressButton('Discard changes');
        $page->clickLink('Cancel');
        $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display/default/layout');
        $assert_session->pageTextContainsOnce('You have unsaved changes.');
        // Discard then confirm.
        $page->pressButton('Discard changes');
        $page->pressButton('Confirm');
        $assert_session->addressEquals(static::FIELD_UI_PREFIX . '/display/default');
        $assert_session->pageTextNotContains('You have unsaved changes.');
        // Make and then save changes.
        $this->assertModifiedLayout(static::FIELD_UI_PREFIX . '/display/default/layout');
        $page->pressButton('Save layout');
        $assert_session->pageTextNotContains('You have unsaved changes.');
    }
    
    /**
     * Asserts that modifying a layout works as expected.
     *
     * @param string $path
     *   The path to a Layout Builder UI page.
     *
     * @internal
     */
    protected function assertModifiedLayout(string $path) : void {
        $assert_session = $this->assertSession();
        $page = $this->getSession()
            ->getPage();
        $this->drupalGet($path);
        $page->clickLink('Add section');
        $assert_session->waitForElementVisible('named', [
            'link',
            'One column',
        ]);
        $assert_session->pageTextNotContains('You have unsaved changes.');
        $page->clickLink('One column');
        $assert_session->waitForElementVisible('named', [
            'button',
            'Add section',
        ]);
        $page->pressButton('Add section');
        $assert_session->assertWaitOnAjaxRequest();
        $assert_session->pageTextContainsOnce('You have unsaved changes.');
        // Reload the page.
        $this->drupalGet($path);
        $assert_session->pageTextContainsOnce('You have unsaved changes.');
    }
    
    /**
     * Tests that elements that open the dialog are properly highlighted.
     */
    public function testAddHighlights() : void {
        $assert_session = $this->assertSession();
        $page = $this->getSession()
            ->getPage();
        $bundle = BlockContentType::create([
            'id' => 'basic',
            'label' => 'Basic block',
            'revision' => 1,
        ]);
        $bundle->save();
        block_content_add_body_field($bundle->id());
        $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default/layout');
        $assert_session->elementsCount('css', '.layout-builder__add-section', 2);
        $assert_session->elementNotExists('css', '.is-layout-builder-highlighted');
        $page->clickLink('Add section');
        $this->assertNotEmpty($assert_session->waitForElement('css', '#drupal-off-canvas .item-list'));
        $assert_session->assertWaitOnAjaxRequest();
        // Highlight is present with AddSectionController.
        $this->assertHighlightedElement('[data-layout-builder-highlight-id="section-0"]');
        $page->clickLink('Two column');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas input[type="submit"][value="Add section"]'));
        $assert_session->assertWaitOnAjaxRequest();
        // The highlight is present with ConfigureSectionForm.
        $this->assertHighlightedElement('[data-layout-builder-highlight-id="section-0"]');
        // Submit the form to add the section and then confirm that no element is
        // highlighted anymore.
        $page->pressButton("Add section");
        $assert_session->assertWaitOnAjaxRequest();
        $this->assertHighlightNotExists();
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '[data-layout-delta="1"]'));
        $assert_session->elementsCount('css', '.layout-builder__add-block', 3);
        // Add a content block.
        $page->clickLink('Add block');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'a:contains("Create content block")'));
        $assert_session->assertWaitOnAjaxRequest();
        // Highlight is present with ChooseBlockController::build().
        $this->assertHighlightedElement('[data-layout-builder-highlight-id="block-0-first"]');
        $page->clickLink('Create content block');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas input[value="Add block"]'));
        $assert_session->assertWaitOnAjaxRequest();
        // Highlight is present with ChooseBlockController::inlineBlockList().
        $this->assertHighlightedElement('[data-layout-builder-highlight-id="block-0-first"]');
        $page->pressButton('Close');
        $this->assertHighlightNotExists();
        // The highlight should persist with all block config dialogs.
        $page->clickLink('Add block');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', 'a:contains("Recent content")'));
        $assert_session->assertWaitOnAjaxRequest();
        $this->assertHighlightedElement('[data-layout-builder-highlight-id="block-0-first"]');
        $page->clickLink('Recent content');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas input[value="Add block"]'));
        // The highlight is present with ConfigureBlockFormBase::doBuildForm().
        $this->assertHighlightedElement('[data-layout-builder-highlight-id="block-0-first"]');
        $page->pressButton('Close');
        $this->assertHighlightNotExists();
        // The highlight is present when the "Configure section" dialog is open.
        $page->clickLink('Configure Section 1');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas'));
        $this->assertHighlightedElement('[data-layout-builder-highlight-id="section-update-0"]');
        $page->pressButton('Close');
        $this->assertHighlightNotExists();
        // The highlight is present when the "Remove Section" dialog is open.
        $page->clickLink('Remove Section 1');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas'));
        $assert_session->assertWaitOnAjaxRequest();
        $this->assertHighlightedElement('[data-layout-builder-highlight-id="section-update-0"]');
        $page->pressButton('Close');
        $this->assertHighlightNotExists();
        // A block is highlighted when its "Configure" contextual link is clicked.
        $this->clickContextualLink('.block-field-blocknodebundle-with-section-fieldbody', 'Configure');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas'));
        $assert_session->assertWaitOnAjaxRequest();
        $this->assertHighlightedElement('.block-field-blocknodebundle-with-section-fieldbody');
        // Make sure the highlight remains when contextual links are revealed with
        // the mouse.
        $this->toggleContextualTriggerVisibility('.block-field-blocknodebundle-with-section-fieldbody');
        $active_section = $page->find('css', '.block-field-blocknodebundle-with-section-fieldbody');
        $active_section->pressButton('Open configuration options');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '.block-field-blocknodebundle-with-section-fieldbody .contextual.open'));
        $page->pressButton('Close');
        $this->assertHighlightNotExists();
        // @todo Remove the reload once https://www.drupal.org/node/2918718 is
        //   completed.
        $this->getSession()
            ->reload();
        // Block is highlighted when its "Remove block" contextual link is clicked.
        $this->clickContextualLink('.block-field-blocknodebundle-with-section-fieldbody', 'Remove block');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas'));
        $assert_session->assertWaitOnAjaxRequest();
        $this->assertHighlightedElement('.block-field-blocknodebundle-with-section-fieldbody');
        $page->pressButton('Close');
        $this->assertHighlightNotExists();
    }
    
    /**
     * Tests removing newly added extra field.
     */
    public function testNewExtraField() : void {
        $assert_session = $this->assertSession();
        $page = $this->getSession()
            ->getPage();
        // At this point layout builder has been enabled for the test content type.
        // Install a test module that creates a new extra field then clear cache.
        \Drupal::service('module_installer')->install([
            'layout_builder_extra_field_test',
        ]);
        \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions();
        // View the layout and try to remove the new extra field.
        $this->drupalGet(static::FIELD_UI_PREFIX . '/display/default/layout');
        $assert_session->pageTextContains('New Extra Field');
        $this->clickContextualLink('.block-extra-field-blocknodebundle-with-section-fieldlayout-builder-extra-field-test', 'Remove block');
        $this->assertNotEmpty($assert_session->waitForElementVisible('css', '#drupal-off-canvas'));
        $assert_session->assertWaitOnAjaxRequest();
        $assert_session->pageTextContains('Are you sure you want to remove');
        $page->pressButton('Remove');
        $assert_session->assertWaitOnAjaxRequest();
        $assert_session->pageTextNotContains('New Extra Field');
    }
    
    /**
     * Confirms the presence of the 'is-layout-builder-highlighted' class.
     *
     * @param string $selector
     *   The highlighted element must also match this selector.
     */
    private function assertHighlightedElement(string $selector) : void {
        $assert_session = $this->assertSession();
        $page = $this->getSession()
            ->getPage();
        // There is only one highlighted element.
        $assert_session->elementsCount('css', '.is-layout-builder-highlighted', 1);
        // The selector is also the highlighted element.
        $this->assertTrue($page->find('css', $selector)
            ->hasClass('is-layout-builder-highlighted'));
    }
    
    /**
     * Waits for the dialog to close and confirms no highlights are present.
     */
    private function assertHighlightNotExists() : void {
        $this->markTestSkipped("Skipped temporarily for random fails.");
        $assert_session = $this->assertSession();
        $assert_session->assertNoElementAfterWait('css', '#drupal-off-canvas');
        $assert_session->assertNoElementAfterWait('css', '.is-layout-builder-highlighted');
    }

}

Classes

Title Deprecated Summary
LayoutBuilderUiTest Tests the Layout Builder UI.

Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.